Click here to Skip to main content
15,893,722 members
Articles / Programming Languages / C#

The NAR loader - single file .NET deployment

Rate me:
Please Sign up or sign in to vote.
4.51/5 (23 votes)
27 Oct 2006GPL34 min read 104.7K   455   36  
JAR archives for .NET = NAR archives.
#include "stdafx.h"

#include "Config.h"
#include "Resource.h"
#include "Tools.h"
#include "PayLoad.h"
#include "Resources.h"
#include "OptArg.h"
#include "TokenEx.h"
#include "HostControl.h"
#include "AssemblyManager.h"
#include "InfoDlg.h"
//#include "aes\crypt.h"

#include "NARLoader.h"

#ifdef _WINDOWS
	#define ARGC __argc
	#define ARGV __argv
#else // _CONSOLE
	#define ARGC argc
	#define ARGV argv
#endif

BYTE MKx[] = {
				0xD1, 0xFF, 0x82, 0x21, 0x09, 0xFE, 0xA0, 0x10, 0x69, 0x22, 0xFB, 0xD6, 0xF7, 0x38, 0x71, 0x56, 
				0xB4, 0x08, 0x5E, 0x6C, 0x96, 0x4F, 0xE6, 0xB5, 0x74, 0x8A, 0x12, 0x3A, 0x36, 0xC8, 0x85, 0x28,
				0xBE, 0x3E, 0xB6, 0xB6, 0xA8, 0xAB, 0xB8, 0x86, 0xD0, 0x7C, 0x61, 0xFF, 0x79, 0x92, 0x4A, 0x01,
				0x06, 0x4F, 0x34, 0x13, 0xC6, 0x01, 0x3F, 0x7D, 0x43, 0xA5, 0x0B, 0x7E, 0xC8, 0x27, 0x49, 0x54
			};

CTokenEx tokenizer;
CComPtr<ICLRRuntimeHost> pCLR = NULL;
zipFile zf = NULL;
prng_ctx rctx;

CString LoaderName;
CString LoaderNameFQ;
SAFEARRAY *saARGV = NULL;
PayloadHeader Payload;

CString NARFileName;
CString NARFilePassword;
CString NARFileComment;
BOOL ShowNARFileComment = FALSE;
CString SFXFileName;
CString SFXIconName;
CString SFXIconNameCL;

CString BaseFileDrive;
CString BaseFilePath;
CString BaseFileName;
CString BaseFileExtension;
CString BaseFile;

CString ManifestFileName;
PCHAR RawManifest = NULL;

CString Assembly2LoadName;
CString Type2LoadName;

CString AppConfigName;
CString TempAppConfigName;
SharedMemory *smAppConfigName = NULL;
BOOL DeleteTempAppConfig = FALSE;

PCHAR RawAppConfig = NULL;
SharedMemory *smAppConfigContent = NULL;

CStringArray privatePath;
CString CLRSecurityConfigName;



#ifdef _WINDOWS
int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow)
{

	NARInstance = hInstance;
#else // _CONSOLE
int main(int argc, char* argv[])
{
#endif
	HRESULT hr = S_OK;
	ULONG l = 0;
	long ReturnValue = 0;
	int rtc = 0;



	// -------------------------------------------------------------------------
	// initialize
	atexit(CleanUp);
	CoInitialize(NULL);



	// -------------------------------------------------------------------------
	//Retrieve a pointer to the ICorRuntimeHost interface
	hr = CorBindToRuntimeEx(
											 NULL,		//Retrieve latest version by default
											 L"wks",	//Request a WorkStation build of the CLR
											 STARTUP_LOADER_OPTIMIZATION_SINGLE_DOMAIN | STARTUP_CONCURRENT_GC, 
											 CLSID_CLRRuntimeHost,
											 IID_ICLRRuntimeHost,
											 (PVOID*)&pCLR
									);
	if (FAILED(hr)) 
		return ErrorExit(hr, "Unable to load the CLR\n");



	// -------------------------------------------------------------------------
	// check command line parameters
	switch ( (rtc = InitializeCommandLineArgs()) )
	{
		// -------------------------------------------------------------------------
		// Check prerequisites like zLib or .Net Framework
		// under normal operation we only check for the existance of the All_Code and NARLoader
		// CAS CodeGroups. If they exist we assume the permissions are set correct for these
		// code groups. When starting NARLoader with the -i parameter all attributes are checked
		// and modified/added if required to ensure the permissions are set correct.

		// normal operation
		case 0:
			if ( (rtc = InitializePreRequisites(NormalOperation)) )
				return rtc;
			break;

		// install
		case 10:
			InitializePreRequisites(InstallCodeGroup);	
			InstallLoader();
			return rtc;

		// uninstall
		case 11:
			InitializePreRequisites(RemoveCodeGroup);
			UnInstallLoader();
			return rtc;

		// create SFX archive
		case 12:
			if ( (rtc = InitializePreRequisites(SFXCreation)) )
				return rtc;
			break;

		// SFX operation
		case 13:
			if ( (rtc = InitializePreRequisites(SFXOperation)) )
				return rtc;
			break;

		// everything else
		default:
			return rtc;
	}



	// -------------------------------------------------------------------------
	// if we create a self running archive close the archive so that it
	// is not in use while modifiying it and then append the loader to
	// it and change the icon
	if (isSFXCreation)
	{
		prng_init(GetEntropy, &rctx);
		rtc = CreateSFXArchive();
		prng_end(&rctx);
		return rtc;
	}



	// -------------------------------------------------------------------------
	// open zip ZIP
	if (!(zf = unzOpen(NARFileName)))
		return ErrorExit(0, "Unable to open Archive \"%s\"\n", NARFileName);



	// -------------------------------------------------------------------------
	// try to find <ArchiveName>.manifest or if it does not exists NAR.manifest
	// inside the archives root directory
	if ( (rtc = InitializeManifest()) )
		if ( rtc == UNZ_BADPASSWORD)
			return ErrorExit(0, "Bad or no password for protected archive entered.");
		else
			return ErrorExit(0, "Unable to process archive manifest.");



	// -------------------------------------------------------------------------
	// if the archive contains a comment and the manifest allows it's display
	// show it
	if (ShowNARFileComment)
	{
		NARFileComment = CString(' ', 32768);
		l = unzGetGlobalComment (zf, NARFileComment.GetBuffer(), NARFileComment.GetLength());
		NARFileComment.ReleaseBuffer();

		if (l > 0)
		{
			//MessageBox(NULL, NARFileComment, "Comment", MB_ICONEXCLAMATION | MB_OK);
			DisplayInfoDlg(NARFileComment);
		}
	}



	// -------------------------------------------------------------------------
	// check if we have a app.config file outside the archive in the directory
	// of the archive. if so - use this app config file. if not extract the
	// app.config file from the archive, place it in the temp directory and
	// delete it after program exit.
	if ( (rtc = InitializeAppConfig()) )
		return rtc;



	// -------------------------------------------------------------------------
	// Register unmanaged HostControl
	CAssemblyManager *pAssemblyManager = new CAssemblyManager(zf);
	CHostControl *pHostControl = new CHostControl(NULL, NULL, NULL, NULL, NULL, pAssemblyManager, NULL, NULL);
	hr = pCLR->SetHostControl((IHostControl *)pHostControl);
	if (FAILED(hr)) 
		return ErrorExit(hr, "pCLR->SetHostControl");



	// -------------------------------------------------------------------------
	// Register managed AppDomainManager
	ICLRControl *pControl = NULL;
	hr = pCLR->GetCLRControl(&pControl);
	if (FAILED(hr)) 
		return ErrorExit(hr, "GetCLRControl");
	if (!pControl)
		return ErrorExit(0, "pControl == NULL");

	hr = pControl->SetAppDomainManagerType(
		L"NARLoaderManager, Version=1.2.0.0, Culture=neutral, PublicKeyToken=1952f306d17dd427", 
		L"NARLoader.LoaderManager");
	if (FAILED(hr)) 
		return ErrorExit(hr, "SetAppDomainManagerType");



	// -------------------------------------------------------------------------
	// Start the CLR
	hr = pCLR->Start();
	if (FAILED(hr)) 
		return ErrorExit(hr, "Start CLR");



	try
	{
		// -------------------------------------------------------------------------
		// call the managed domain manager using the assembly, type and parameters
		// from the manifest and the comman dline
		ILoaderManager *pLoaderManager = pHostControl->GetDomainManagerForDefaultDomain();
		if (!pLoaderManager)
			return ErrorExit(0, "ILoaderManager pointer is NULL");

		pLoaderManager->Configure((VARIANT_BOOL)isDebug, _bstr_t(NARFileName));
		ReturnValue = pLoaderManager->Run(_bstr_t(Assembly2LoadName), _bstr_t(Type2LoadName), saARGV);
		DebugOut("Return code from EntryPoint was (%ld, 0x%X)\n", ReturnValue, ReturnValue);
	}
	catch (...)
	{
		return ErrorExit(0, "Unknown Exception in CLR.");
	}

	return ReturnValue;
}



VOID ShowUsage()
{
	CString w;

	w.Format("\n"
			"Usage:\t%s [-d] [-p pwd] Archive [...]\n"
			"\t%s [-d] -i | -u\n"
			"\t%s [-d] -c [-p pwd] [-o Output] [-I Icon] Archive\n"
			"\t%s [-d] -v | -h\n\n"

			"\t-d\tTurns debugging on.\n\n"

			"\t-i\tInstalles the loader to handle .nar files.\n"
			"\t\t(When specifying -i with -d the loader is installed\n"
			"\t\twith debug support.)\n"
			"\t-u\tRemoves the handling of .nar files.\n\n"

			"\t-c\tCreates a self running application from Archive.\n"
			"\t\t(When specifying -c with -d the self running\n"
			"\t\tarchive starts in debug mode.)\n"
			"\t-o\tThe name of the self running archive. If omitted\n"
			"\t\tthe name of the archive with appended .exe is used.\n"
			"\t-I\tThe name of an Icon (.ico, .exe or .dll) for the self\n"
			"\t\trunning archive. Overrides manifest inside archive.\n\n"

			"\t-p\tUsed to specify the password for password protected\n"
			"\t\tArchives. If not specified when opening such an Archive\n"
			"\t\ta Dialog is displayed to enter the password.\n\n"

			"\t-v\tShows %s version information.\n"
			"\t-h\tShows this help screen.\n\n"

			"\tArchive\tIs the filename of a NAR archive.\n\n"

			"\t[...]\tEverything following the archive name will be the\n"
			"\t\tcommand line for the application inside the\n"
			"\t\tarchive.\n"
			"\n", LoaderName, LoaderName, LoaderName, LoaderName, LoaderName);

	if (isDebug)
		printf(w);
	else
		MessageBox(NULL, w, "NARLoader usage", MB_ICONINFORMATION | MB_OK);
}



VOID ShowVersion()
{
	CHAR c[1024];
	CString w;

	sCOPYRIGHT(c, sizeof(c));
	w.Format("\n%s Version %s\n\n%s\n", LoaderName, NAR_VERSION, c);

	if (isDebug)
		printf(w);
	else
		MessageBox(NULL, w, "Version", MB_ICONINFORMATION | MB_OK);
}



VOID CleanUp()
{
	// Stop CLR for this process
	if (pCLR)
	{
		pCLR->Stop();
		pCLR = NULL;
	}

	// close archive
	if (zf)
	{
		unzClose(zf);
		zf = NULL;
	}

	// remove manifest xml from memory - if not already done so
	if (RawManifest)
	{
		free(RawManifest);
		RawManifest = NULL;
	}

	// remove app.config name and xml content from memory
	CloseSharedMemory(smAppConfigName);
	CloseSharedMemory(smAppConfigContent);
	if (RawAppConfig)
	{
		free(RawAppConfig);
		RawAppConfig = NULL;
	}

	// we there was a temp app.config file - remove it
	if (DeleteTempAppConfig && PathFileExists(TempAppConfigName))
		_unlink(TempAppConfigName);

	// clean up ARGV safearray
	if (saARGV)
	{
		for (LONG i = 0; i < (LONG)saARGV->rgsabound->cElements; i++)
		{
			BSTR w;
			SafeArrayGetElement(saARGV, &i, &w);
			SysFreeString(w);
		}
		SafeArrayDestroy(saARGV);
		saARGV = NULL;
	}

	// and finally - if we had a console handler - remove it also
	if (isDebug)
		SetConsoleCtrlHandler((PHANDLER_ROUTINE)ConsoleHandler, FALSE);

	//MessageBox(NULL, "CleanUp Ping", "CleanUp", MB_ICONINFORMATION | MB_OK);
	if (isDebug)
		Pause();
}



VOID InstallLoader()
{
	HRESULT hr;
	CString Application(' ', MAX_PATH);
	CString Parameters;

	Parameters.Format("%s", ((isDebug) ? "-d " : ""));

	hr = GetModuleFileName(NULL, Application.GetBuffer(), Application.GetLength());
	Application.ReleaseBuffer();
	if (FAILED(hr))
		exit(ErrorExit(hr, "GetModuleFileName"));

	if (!RegisterFileExtension(NAR_EXTENSION, NAR_PROGID, Application, Parameters))
		MessageBox(NULL, "Unable to install NAR Loader", "Error", MB_ICONERROR | MB_OK);
	else
		Application.Format("NAR Loader installed %s", ((isDebug) ? "\n(with Debugging turned on)" : ""));
		MessageBox(NULL, Application, "Info", MB_ICONINFORMATION | MB_OK);
}



VOID UnInstallLoader()
{
	UnRegisterFileExtension(NAR_EXTENSION);
	MessageBox(NULL, "NAR Loader uninstalled", "Info", MB_ICONINFORMATION | MB_OK);
}



BOOL WINAPI ConsoleHandler(DWORD CEvent)
{
	BOOL rtc = FALSE;
	PCHAR w = "The loader can only be closed by closing the managed application.";

    switch(CEvent)
    {
		case CTRL_C_EVENT:
			DebugOut("E: CTRL+C received!\n   %s\n", w);
			rtc = TRUE;
			break;
		case CTRL_BREAK_EVENT:
			DebugOut("E: CTRL+BREAK received!\n   %s\n", w);
			rtc = TRUE;
			break;

		case CTRL_CLOSE_EVENT:		// 5 seconds timeout
			DebugOut("E: Program being closed!\n   %s\n", w);
			rtc = TRUE;
			break;
		case CTRL_LOGOFF_EVENT:		// 20 seconds timeout
			DebugOut("E: User is logging off!\n");
			break;
		case CTRL_SHUTDOWN_EVENT:	// 20 seconds timeout
			DebugOut("E: System is being shutdown!\n");
			break;
    }

    return rtc;
}



INT InitializeCommandLineArgs()
{
	HRESULT hr;
	BOOL doInstall = FALSE;
	BOOL doUnInstall = FALSE;
	BOOL doUsage = FALSE;
	BOOL doVersion = FALSE;
	BOOL doErrorArg = FALSE;
	INT i = 0;
	CString w;

	// check command line parameters
	//LoaderNameFQ = ARGV[0];
	LoaderNameFQ = CString(' ', MAX_PATH);
	GetModuleFileName(NULL, LoaderNameFQ.GetBuffer(), LoaderNameFQ.GetLength());
	LoaderNameFQ.ReleaseBuffer();

	tokenizer.SplitPath(FALSE, LoaderNameFQ, BaseFileDrive, BaseFilePath, BaseFileName, BaseFileExtension);
	LoaderName.Format("%s%s%s", 
		BaseFileName, 
		(BaseFileName.GetLength() && BaseFileExtension.GetLength() ? CString(".") : CString()), 
		BaseFileExtension);

	// check if we are in SFX mode or in stand alone loader mode
	isSFX = CheckPayload(LoaderNameFQ, &Payload);

	if (!isSFX)
	{

		// now cycle through command line parameters
		// BUT ONLY IF WE ARE NOT IN SFX MODE!!!
		while ((i = getopt(ARGC, ARGV, "diuhvp:co:I:")) != EOF)
		{
			switch (i)
			{
				case 'd':
					isDebug = TRUE;
					break;

				case 'i':
					doInstall = TRUE;
					break;

				case 'u':
					doUnInstall = TRUE;
					break;

				case 'h':
					doUsage = TRUE;
					break;

				case 'v':
					doVersion = TRUE;
					break;

				case 'c':
					isSFXCreation = TRUE;
					break;

				case 'p':
					NARFilePassword = optarg;
					break;

				case 'o':
					SFXFileName = optarg;
					break;

				case 'I':
					SFXIconNameCL = optarg;
					break;

				case '?':
					doErrorArg = TRUE;
					break;

				default:
					break;
			}

			if (doErrorArg)
				break;
		}

		if (doInstall && doUnInstall)
		{
			ShowUsage();
			return ErrorExit(0, "-d and -u are mutually exclusive\n");
		}

		if (isSFXCreation)
		{
			//isDebug = TRUE;
			doUsage = FALSE;
			doVersion = FALSE;
		}

#ifdef _WINDOWS
		if (isDebug)
		{
			FILE *fp;
			AllocConsole();
			freopen_s(&fp, "CONOUT$", "w", stdout);
			freopen_s(&fp, "CONOUT$", "w", stderr);
			freopen_s(&fp, "CONIN$", "r", stdin); 
		}
#else
		if (!isDebug)
			FreeConsole();
#endif

		if (doInstall)
		{
			return 10;
		}

		if (doUnInstall)
		{
			return 11;
		}

		if (doUsage)
		{
			ShowUsage();
			return 2;
		}

		if (doVersion)
		{
			ShowVersion();
			return 2;
		}

		if (doErrorArg)
		{
			ShowUsage();
			return ErrorExit(0, "Invalid command line parameter \"%s\"\n", ARGV[optind]);
		}

		if (!optarg)
		{
			ShowUsage();
			return ErrorExit(0,"Archive File not specified\n");
		}

		if (isDebug)
			if (SetConsoleCtrlHandler((PHANDLER_ROUTINE)ConsoleHandler,TRUE) == FALSE)
				return ErrorExit(0, "Unable to install Console handler!\n");

		SetCursor(LoadCursor(NULL, IDC_WAIT));
		ShowCursor(TRUE);

		DebugOut("NAR Loader: Version %s\n", NAR_VERSION);
	}
	else
	{
		fcrypt_ctx zctx;
		UCHAR PV[PWD_VER_LENGTH];
		UCHAR AC[MAC_LENGTH(AES_MODE)];

		// all command line params folowing the loader name
		// will be handed over to the embedded application
		// in SFX mode
		isDebug = Payload.Flags & PF_DEBUG;

		// decrypt payload
		fcrypt_init(AES_MODE, MKx, sizeof(MKx), Payload.IV, PV, &zctx);

		fcrypt_decrypt((UCHAR *)Payload.Password, sizeof(Payload.Password), &zctx);
		fcrypt_decrypt((UCHAR *)Payload.OriginalName, sizeof(Payload.OriginalName), &zctx);

		fcrypt_end(AC, &zctx);

		// check if decrypt was ok
		if ( memcmp(PV, Payload.PV, sizeof(PV)) || memcmp(AC, Payload.AC, sizeof(AC)) )
			return ErrorExit(0, "Internal AES error 1!\n");

		NARFilePassword = Payload.Password;

#ifdef _WINDOWS
		if (isDebug)
		{
			FILE *fp;
			AllocConsole();
			freopen_s(&fp, "CONOUT$", "w", stdout);
			freopen_s(&fp, "CONOUT$", "w", stderr);
			freopen_s(&fp, "CONIN$", "r", stdin); 
		}
#else
		if (!isDebug)
			FreeConsole();
#endif

		i = getopt(ARGC, ARGV, "");
	}

	// split the full archive name into its components
	if (!isSFX)
		NARFileName = optarg;
	else
	{
		if (*Payload.OriginalName)
			NARFileName = Payload.OriginalName;
		else
			NARFileName = LoaderNameFQ;
	}

	tokenizer.SplitPath(FALSE, NARFileName, BaseFileDrive, BaseFilePath, BaseFileName, BaseFileExtension);
	BaseFile.Format("%s%s%s", 
		BaseFileName, 
		(BaseFileName.GetLength() && BaseFileExtension.GetLength() ? CString(".") : CString()), 
		BaseFileExtension);

	// hand over unmanaged loader command line arguments to
	// managed loader arguments
	if (!isSFX)
		optind++;
	saARGV = SafeArrayCreateVector(VT_BSTR, 0, ARGC - optind);

	// copy remaining command line arguments into a safearray. this is handed
	// over to the managed assembly later
	LONG j = 0;
	for (LONG i = optind; i < ARGC; i++)
	{
		hr = SafeArrayPutElement(saARGV, &j, SysAllocString(_bstr_t(ARGV[i])));
		if (FAILED(hr)) 
			return ErrorExit(hr, "SafeArrayPutElement");
		j++;
	}

	DebugOut("NAR File Name: %s\n", NARFileName);
	if (isSFX)
		DebugOut("NAR loader in SFX mode\n");
	DebugOut("NAR File Password: %s\n", NARFilePassword);

	DebugOut("NAR BaseFile: %s\n", BaseFileName);
	DebugOut("NAR BaseFileExtension: %s\n", BaseFileExtension);
	DebugOut("NAR BaseFilePath: %s\n", BaseFilePath);

	// change directory to the archive directory
	_chdir(BaseFilePath);

	// get current working directory
	w = CString(' ', MAX_PATH);
	_getcwd( w.GetBuffer(), w.GetLength() );
	w.ReleaseBuffer();
	DebugOut("NAR WorkingPath: %s\n", w);

	if (isSFXCreation)
		return 12;

	if (isSFX)
		return 13;

	return 0;
}



INT InitializePreRequisites(SecurityActions Action)
{
	HRESULT hr;
	VARIANT_BOOL hrb;
	BSTR bw;
	BSTR bVersion;
	ULONG l;
	INT i;
	CHAR w[MAX_PATH + 1];
	STARTUPINFO si;
    PROCESS_INFORMATION pi; 

	BOOL useCasPol = false;
	CString CLRCasPolName;
	PCHAR CASGroupName =	"NARLoader";
	PCHAR CASGroupDesc =	"NARLoader StrongName membership CodeGroup "
							"created by NARLoader. See http://www.min.at/nar "
							"for more infos.";
	PCHAR CASPublicKey =	"00240000048000009400000006020000"
							"00240000525341310004000001000100"
							"2154D6C05D0A43F1C31AEB8A9A2A5235"
							"AB55477E8AE74F8B94B72F411E24FB61"
							"82E27714224DD8D211D4A889D5276977"
							"C99662EDA54F9A456E72CBB941E87BB9"
							"144ECCE8FBB44C8E789737DD5BF3C98E"
							"7408D7E4FD0A432EA3294069F0318FF8"
							"B4CB18A3DC90E0DF252B92AD876C60C0"
							"CEF86FC35F10CAE3B4A011AC1E7283C1";

	// get running CLR basepath
	l = MAX_PATH;
	if (FrameworkBasePath(w, &l)) 
		DebugOut("CLR Path: %s\n", w);

	// construct CLR framework security.config path
	DWORD _major, _minor, _patch;
	CLRSecurityConfigName.Empty();
	CLRCasPolName.Empty();
	if (FrameworkVersion(&_major, &_minor, &_patch)) 
	{
		DebugOut("ZIP zLib Version: %s\n", zlibVersion());
		DebugOut("CLR Version: %d.%d.%d\n", _major, _minor, _patch);
		CLRSecurityConfigName.Format("%sv%d.%d.%d\\CONFIG\\security.config", w, _major, _minor, _patch);
		CLRCasPolName.Format("%sv%d.%d.%d\\CasPol.exe", w, _major, _minor, _patch);
		DebugOut("CLR Security: %s\n", CLRSecurityConfigName);

		// try to locate the caspol.exe utility
		// if it is found use it to install the loader rights
		// if it is not found - fall back and create
		// the loader rights in code
		if (PathFileExists(CLRCasPolName))
		{
			DebugOut("CLR CasPol.exe found at: %s\n", CLRCasPolName);
			useCasPol = true;
		}
		else
			DebugOut("CLR CasPol.exe not found - using code instead\n");

		// just to be sure check security.config also in code
		// open the security.config file
		MSXML2::IXMLDOMDocument2Ptr pDoc = NULL;
		MSXML2::IXMLDOMElementPtr pRoot = NULL; 
		MSXML2::IXMLDOMNodePtr pCodeGroupStartNode = NULL; 
		MSXML2::IXMLDOMNodePtr pCodeGroupNode = NULL; 
		MSXML2::IXMLDOMNodePtr pMembershipNode = NULL; 
		MSXML2::IXMLDOMNodePtr pNode = NULL; 
		MSXML2::IXMLDOMNamedNodeMapPtr pNodeAttributes = NULL;

		hr = pDoc.CreateInstance(XML_PROG_ID);
		if (FAILED(hr)) 
			return ErrorExit(hr, XML_PROG_ID);		

		pDoc->async = VARIANT_FALSE;
		pDoc->preserveWhiteSpace = VARIANT_FALSE;
		pDoc->setProperty(_T("SelectionLanguage"), _T("XPath"));

		try
		{
			// if security.config is missing 
			// create a default one - even when
			// deinstalling!
			if (!PathFileExists(CLRSecurityConfigName))
			{
				DebugOut("CLR Security config file not found - create one\n");

				if (useCasPol)
				{
					ZeroMemory( &si, sizeof(si) ); si.cb = sizeof(si);
					ZeroMemory( &pi, sizeof(pi) );

					CString p(CLRCasPolName + " -quiet -polchgprompt off"
											" -all -reset");
					if (CreateProcess(CLRCasPolName, (LPSTR)(LPCTSTR)p, 
							NULL, NULL, FALSE, DETACHED_PROCESS, NULL, NULL, &si, &pi))
					{
						if (WaitForSingleObject( pi.hProcess, 10000L ) == WAIT_TIMEOUT)
							DebugOut("CLR CasPol.exe -reset timed out (> 10 sec).\n");
						CloseHandle(pi.hProcess);
						CloseHandle(pi.hThread);
					}
					else
					{
						CString err;
						GetLastErrorMessage(0, err);
						return ErrorExit(0, "Unable to create new security.config using CasPol.exe -reset\n%s", err);	
					}
				}
				else
				{
					if (!SaveResourceToFile("BINARY", MAKEINTRESOURCE(IDR_DEFAULT_SECURITY_CONFIG), CLRSecurityConfigName))
						return ErrorExit(0, "Unable to create new security.config using code\n");	
				}
			}

			hrb = pDoc->load(_variant_t(CLRSecurityConfigName));
			pDoc->get_documentElement(&pRoot);
			pDoc->get_xml(&bw);
			l = 0;

			// example xml NARLoader CodeGroup node
			/*
			<CodeGroup class="UnionCodeGroup" version="1" PermissionSetName="FullTrust" 
				Name="NARLoader" Description="NAR">
			<IMembershipCondition class="StrongNameMembershipCondition" version="1" 
				PublicKeyBlob="00240000048000009400000006020000002400005253413100040000010001007182FFF98972BF50C7326BE99ED8A676EF7BA7DF831214458FDBFB2A65A23C95E3085FB1CBC0484BA71E23CC32F5ED3FE8BDDBF04B4E51BEAF0B3719DF69A51C82AF21704B5CF2534E5926FD4B52C15F262D9B0701BCDBA0FB3A86AC923E8CAADB777F0D0BE57D430832A8B74BB1F8FC3796E6C4A78715B5E9E429C48354CAA5" /> 
			</CodeGroup>
			*/

			// find All_Code CodeGroup
			pCodeGroupStartNode = pRoot->selectSingleNode(L"/configuration/mscorlib/security/policy/PolicyLevel/CodeGroup[@Name='All_Code']");
			if (!pCodeGroupStartNode)
				return ErrorExit(0, "Error finding CAS CodeGroup (All_Code)");		

			// --------------------------------------------------------------------------------
			// node CodeGroup
			pCodeGroupNode = pCodeGroupStartNode->selectSingleNode(L"CodeGroup[@Name='NARLoader']");
			if (!pCodeGroupNode)
			{
				// add CodeGroup node
				// (only if InstallCodeGroup is TRUE or silently if we are a self running archive)
				if (Action == NormalOperation)
					return ErrorExit(0, "NARLoader CodeGroup not found!\n"
										"Install %s first by using -i command line parameter.\n"
										"Use %s -h for more infos.", LoaderName, LoaderName);

				if (Action == InstallCodeGroup || Action == SFXOperation)
				{
					if (useCasPol)
					{
						ZeroMemory( &si, sizeof(si) ); si.cb = sizeof(si);
						ZeroMemory( &pi, sizeof(pi) );

						CString p(CLRCasPolName + " -quiet -polchgprompt off"
												" -machine -addgroup All_Code"
												" -strong -hex " + CASPublicKey +
												" -noname -noversion FullTrust"
												" -name " + CASGroupName +
												" -description \"" + CASGroupDesc + "\"");
						if (CreateProcess(CLRCasPolName, (LPSTR)(LPCTSTR)p, 
								NULL, NULL, FALSE, DETACHED_PROCESS, NULL, NULL, &si, &pi))
						{
							if (WaitForSingleObject( pi.hProcess, 10000L ) == WAIT_TIMEOUT)
								DebugOut("CLR CasPol.exe -addgroup timed out (> 10 sec).\n");
							CloseHandle(pi.hProcess);
							CloseHandle(pi.hThread);
						}
						else
						{
							CString err;
							GetLastErrorMessage(0, err);
							return ErrorExit(0, "Unable to create code group using CasPol.exe -addgroup\n%s", err);	
						}
					}
					else
					{
						pCodeGroupNode = pDoc->createNode(1, "CodeGroup", "");
						if (!pCodeGroupNode)
							return ErrorExit(0, "Unable to create CAS CodeGroup (NARLoader)");

						pCodeGroupStartNode->appendChild(pCodeGroupNode);
					}
					DebugOut("CLR NARLoader CodeGroup added to security.config\n");
					l++;
				}
			}
			if (!pCodeGroupNode && Action != RemoveCodeGroup && Action != SFXCreation && !useCasPol)
				return ErrorExit(0, "Error processing CAS CodeGroup (NARLoader)");		

			if ((Action == InstallCodeGroup || Action == SFXOperation) && !useCasPol)
			{
				// get PolicyLevel version
				pCodeGroupStartNode = pRoot->selectSingleNode(L"/configuration/mscorlib/security/policy/PolicyLevel");
				if (!pCodeGroupStartNode)
					return ErrorExit(0, "Error finding PolicyLevel xml node");		

				hr = pCodeGroupStartNode->get_attributes(&pNodeAttributes);
				if (FAILED(hr) || !pNodeAttributes)
					return ErrorExit(hr, "Error finding PolicyLevel xml attributes");

				pNode = pNodeAttributes->getNamedItem(L"version");
				if (pNode)
					hr = pNode->get_text(&bVersion);
				if (FAILED(hr) || !pNode)
					return ErrorExit(hr, "Unable to accquire PolicyLevel xml version attribute");

				// attribute class = UnionCodeGroup
				// attribute version = ...
				// attribute PermissionSetName = FullTrust			
				// attribute Name = NARLoader
				// attribute Description = ...
				if ( (i = EnsureAttribute(pCodeGroupNode, "class", "UnionCodeGroup")) < 0)
					return 1;
				l += i;
				
				if ( (i = EnsureAttribute(pCodeGroupNode, "version", CString(bVersion))) < 0)
					return 1;
				l += i;

				if ( (i = EnsureAttribute(pCodeGroupNode, "PermissionSetName", "FullTrust")) < 0)
					return 1;
				l += i;

				if ( (i = EnsureAttribute(pCodeGroupNode, "Name", CASGroupName)) < 0)
					return 1;
				l += i;
				
				if ( (i = EnsureAttribute(pCodeGroupNode, "Description", CASGroupDesc)) < 0)
					return 1;
				l += i;

				// --------------------------------------------------------------------------------
				// node MembershipCondition
				pMembershipNode = pCodeGroupNode->selectSingleNode(L"IMembershipCondition");
				if (!pMembershipNode)
				{
					// add MembershipCondition node
					pMembershipNode = pDoc->createNode(1, "IMembershipCondition", "");
					if (!pMembershipNode)
						return ErrorExit(0, "Unable to create IMembershipCondition XML node");		

					pCodeGroupNode->appendChild(pMembershipNode);
					l++;
				}
				if (!pMembershipNode)
					return ErrorExit(0, "Error processing CAS MembershipCondition");		

				// attribute class = StrongNameMembershipCondition
				// attribute version = ...
				// attribute PublicKeyBlob = ...
				if ( (i = EnsureAttribute(pMembershipNode, "class", "StrongNameMembershipCondition")) < 0 )
					return 1;
				l += i;
				
				if ( (i = EnsureAttribute(pMembershipNode, "version", CString(bVersion))) < 0)
					return 1;
				l += i;

				if ( (i = EnsureAttribute(pMembershipNode, "PublicKeyBlob",	CASPublicKey)) < 0 )
					return 1;
				l += i;
			}

			if (Action == RemoveCodeGroup)
			{
				if (pCodeGroupNode)
				{
					if (useCasPol)
					{
						ZeroMemory( &si, sizeof(si) ); si.cb = sizeof(si);
						ZeroMemory( &pi, sizeof(pi) );

						CString p(CLRCasPolName + " -quiet -polchgprompt off"
												" -machine -remgroup " + CASGroupName);
						if (CreateProcess(CLRCasPolName, (LPSTR)(LPCTSTR)p, 
								NULL, NULL, FALSE, DETACHED_PROCESS, NULL, NULL, &si, &pi))
						{
							if (WaitForSingleObject( pi.hProcess, 10000L ) == WAIT_TIMEOUT)
								DebugOut("CLR CasPol.exe -remgroup timed out (> 10 sec).\n");
							CloseHandle(pi.hProcess);
							CloseHandle(pi.hThread);
						}
						else
						{
							CString err;
							GetLastErrorMessage(0, err);
							return ErrorExit(0, "Unable to remove code group using CasPol.exe -remgroup\n%s\n", err);	
						}
					}
					else
						pCodeGroupStartNode->removeChild(pCodeGroupNode);
					DebugOut("CLR NARLoader CodeGroup removed from security.config\n");
					l++;
				}
			}

			// save modified security.config xml
			if (l)
			{
				// save backup of security.config
				CTime t(CTime::GetCurrentTime());
				CFile f;
				CFileException e = NULL;
				CString CLRBackupSecurityConfigName(CLRSecurityConfigName);
				CLRBackupSecurityConfigName.Append(".NARLoader.backup.");
				CLRBackupSecurityConfigName.Append(t.Format("%Y%m%d-%H%M"));				

				if (!f.Open(CLRBackupSecurityConfigName, CFile::modeCreate | CFile::modeWrite, &e))
				{
					TCHAR szError[1024];
					e.GetErrorMessage(szError, 1024);
					return ErrorExit(0, "Unable to create security.config backup xml (%s)\n%s", 
						CLRBackupSecurityConfigName, szError);
				}
				CString rawBackup(!bw ? L"" : bw);
				f.Write(rawBackup, rawBackup.GetLength());
				f.Close();
				DebugOut("CLR Backup of original security.config created.\n");

				if (!useCasPol)
				{
					// save security.config
					hr = pDoc->save(_variant_t(CLRSecurityConfigName));
					if (FAILED(hr))
						return ErrorExit(hr, "Error saveing security.config xml");
				}

				DebugOut("CLR Modified security.config saved.\n");
			}
			else
				DebugOut("CLR Nothing to do.\n");
		}
		catch(_com_error e)
		{
			if(FAILED(e.Error()))
				return ErrorExit(e.Error(), "COM Exception while parsing security.config (%s) XML\n%s", 
					CLRSecurityConfigName, e.Description());
		}
		catch(...)
		{
			return ErrorExit(0, "Unknown Exception while parsing security.config (%s) XML", 
				CLRSecurityConfigName);
		}
	}
	else
		return ErrorExit(0, "Unable to accquire framework version");

	return 0;
}

INT EnsureAttribute(MSXML2::IXMLDOMNodePtr pStartNode, LPCTSTR Name, LPCTSTR Value)
{
	// return:
	//  0 - attribute was found and it's value was OK
	//  1 - attribute created or existing attributes value was changed
	// -1 - an error has occured
	INT rtc = 0;
	HRESULT hr;
	BSTR bw;
	MSXML2::IXMLDOMNamedNodeMapPtr pNodeAttributes = NULL;
	MSXML2::IXMLDOMNodePtr pNode = NULL;
	MSXML2::IXMLDOMAttributePtr pAttribute = NULL;
	MSXML2::IXMLDOMElementPtr pElement = NULL;

	if (pStartNode)
	{
		hr = pStartNode->get_attributes(&pNodeAttributes);
		if (FAILED(hr) || !pNodeAttributes)
		{
			ErrorExit(hr, "Unable to accquire xml attributes for node (%d)\n", pStartNode->nodeName);
			return -1;
		}

		pNode = pNodeAttributes->getNamedItem(Name);
		if (pNode)
		{
			hr = pNode->get_text(&bw);
			if (FAILED(hr))
			{
				ErrorExit(hr, "Unable to accquire attribute value for attribute (%d)\n", Name);
				return -1;
			}
			if (CString(bw).Compare(Value))
			{
				hr = pNode->put_text(_bstr_t(Value));
				if (FAILED(hr))
				{
					ErrorExit(hr, "Unable to set attribute value for attribute (%d)\n", Name);
					return -1;
				}
				rtc = 1;
			}
		}
		else
		{
			// add attribute
			pElement = pStartNode;
			hr = pElement->setAttribute(_bstr_t(Name), _variant_t(Value));
			if (FAILED(hr))
			{
				ErrorExit(hr, "Unable to add attribute to node (%d)\n", pStartNode->nodeName);
				return -1;
			}
			rtc = 1;
		}
	}

	return rtc;
}



INT InitializeManifest()
{
	HRESULT hr;
	VARIANT_BOOL hrb;
	BSTR bw;
	INT rtc = 0;

	// try to find <ArchiveName>.manifest or if it does not exists NAR.manifest
	// inside the archives root directory
	Assembly2LoadName = BaseFileName;
	Type2LoadName.Format("%s.Program", BaseFileName);
	AppConfigName.Format("%s.exe.config", Assembly2LoadName);
	ManifestFileName.Format("%s.manifest", BaseFile);

	rtc = GetZIPFileContent(zf, (NARFilePassword.IsEmpty() ? (LPCTSTR)NULL : NARFilePassword), 
									ManifestFileName, &RawManifest, NULL, NULL, TRUE);
	if ( rtc == UNZ_BADPASSWORD)
		return rtc;
	if ( !RawManifest )
	{
		ManifestFileName = "NAR.manifest";
		rtc = GetZIPFileContent(zf, (NARFilePassword.IsEmpty() ? (LPCTSTR)NULL : NARFilePassword), 
										ManifestFileName, &RawManifest, NULL, NULL, TRUE);
	}
	if ( rtc == UNZ_BADPASSWORD)
		return rtc;

	if ( RawManifest )
	{
		DebugOut("MFT Manifest: %s\n", ManifestFileName);

		// open the xml manifest file
		MSXML2::IXMLDOMDocument2Ptr pDoc = NULL;
		MSXML2::IXMLDOMElementPtr pRoot = NULL; 
		MSXML2::IXMLDOMNodePtr pNode = NULL; 

		hr = pDoc.CreateInstance(XML_PROG_ID);
		if (FAILED(hr)) 
			return ErrorExit(hr, XML_PROG_ID);		
		try
		{
			pDoc->async = VARIANT_FALSE;
			hrb = pDoc->loadXML((bstr_t)RawManifest);
			pDoc->setProperty(_T("SelectionLanguage"), _T("XPath"));
			pDoc->get_documentElement(&pRoot); 

			// extract the name of the assembly to load from the xml manifest file
			if (pRoot)
			{
				pNode = pRoot->selectSingleNode(L"/NARLoader/Start/@Assembly");
				if (pNode)
				{
					hr = pNode->get_text(&bw);
					if (SUCCEEDED(hr)) 
					{
						Assembly2LoadName = bw;

						// only use the friendly name to construct the app.config
						// filename here
						AssemblyNameEx an = SplitAssemblyName(Assembly2LoadName);
						AppConfigName.Format("%s.exe.config", an.FriendlyName);
						DestroyAssemblyName(&an);
					}
				}

				// extract the name of the type inside the assembly whos Main method should
				// be called
				pNode = pRoot->selectSingleNode(L"/NARLoader/Start/@Type");
				if (pNode)
				{
					hr = pNode->get_text(&bw);
					if (SUCCEEDED(hr)) 
						Type2LoadName = bw;
				}

				// extract the name of the app.config file from the xml manifest file
				pNode = pRoot->selectSingleNode(L"/NARLoader/Start/@AppConfig");
				if (pNode) 
				{
					hr = pNode->get_text(&bw);
					if (SUCCEEDED(hr)) 
						AppConfigName = bw;
				}

				// extract the name of the application icon - used when
				// creating a self executable archive
				pNode = pRoot->selectSingleNode(L"/NARLoader/Start/@Icon");
				if (pNode)
				{
					hr = pNode->get_text(&bw);
					if (SUCCEEDED(hr)) 
						SFXIconName = bw;
				}

				// is the loader allowed to show archive comments
				// if there are is one
				pNode = pRoot->selectSingleNode(L"/NARLoader/Start/@ShowComment");
				if (pNode)
				{
					hr = pNode->get_text(&bw);
					if (SUCCEEDED(hr)) 
					{
						ShowNARFileComment = (*bw == '1') ? TRUE : FALSE;
					}
				}
			}
		}
		catch(_com_error e)
		{
			if(FAILED(e.Error()))
				return ErrorExit(e.Error(), "COM Exception while parsing manifest (%s) XML  %s", 
					ManifestFileName, e.Description());
		}
		catch(...)
		{
			return ErrorExit(0, "Unknown Exception while parsing manifest (%s) XML\n", 
				ManifestFileName);
		}

		// close and clean up the xml manifest file
		free(RawManifest);
		RawManifest = NULL;
	}

	// if no manifest is found in the archive root show a warning
	else
	{
		DebugOut("MFT Manifest: WARNING - No manifest found in archive!\n");
	}

	// ensure that the assembly name to load is fully qualified. which means add any
	// missing fields like version=, culture= etc.
	AssemblyNameEx an = SplitAssemblyName(Assembly2LoadName);
	Assembly2LoadName = CString(" ", 1024);
	AssemblyNameToString(&an, Assembly2LoadName.GetBuffer(), Assembly2LoadName.GetLength(), FALSE, FALSE);
	Assembly2LoadName.ReleaseBuffer();
	DestroyAssemblyName(&an);

	// show it
	DebugOut("MFT StartAssembly: %s\n", Assembly2LoadName);
	DebugOut("MFT StartType: %s\n", Type2LoadName);
	DebugOut("MFT AppConfig: %s\n", AppConfigName);

	return 0;
}



INT InitializeAppConfig()
{
	CFile f;
	CFileException e = NULL;
	HRESULT hr;
	VARIANT_BOOL hrb;
	BSTR bw;
	ULONG l;
	INT rtc = 0;

	// check if we have a app.config file outside the archive in the directory
	// of the archive. if so - use this app config file. if not extract the
	// app.config file from the archive, place it in the temp directory and
	// delete it after program exit.
	DeleteTempAppConfig = FALSE;
	TempAppConfigName.Format("%s%s", BaseFilePath, AppConfigName);
	DebugOut("MFT AppConfig: Checking \"%s\"\n", TempAppConfigName);

	l = 0;
	RawAppConfig = NULL;
	if (!PathFileExists(TempAppConfigName))
	{
		// we don't have an app.config file in the archive directory
		rtc = GetZIPFileContent(zf, (NARFilePassword.IsEmpty() ? (LPCTSTR)NULL : NARFilePassword), 
									AppConfigName, &RawAppConfig, &l, NULL, TRUE);
		if (rtc == UNZ_BADPASSWORD)
			return ErrorExit(0, "Bad or no password for protected archive entered.");

		if (rtc == UNZ_OK)
		{
			// we have an app.config inside the archive
			TempAppConfigName = _tempnam("c:\\tmp", "nar");
			if (!f.Open(TempAppConfigName, CFile::modeCreate | CFile::modeWrite, &e))
			{
				TCHAR szError[1024];
				e.GetErrorMessage(szError, 1024);
				return ErrorExit(0, "Error writing temp app.config file (%s)\n%s", 
					TempAppConfigName, szError);
			}
			f.Write(RawAppConfig, l);
			f.Close();
			DeleteTempAppConfig = TRUE;
		}
		else
		{
			// there is no app config inside the archive and no app.config file
			// in the archive directory
			DebugOut("MFT AppConfig: WARNING - No AppConfig found in archive!\n");
			TempAppConfigName.Empty();
		}
	}
	else
	{
		// we found an app.config file in the archive directory
		// so read in this existing app.config file from disk
		if (!f.Open(TempAppConfigName, CFile::modeRead, &e))
		{
			TCHAR szError[1024];
			e.GetErrorMessage(szError, 1024);
			return ErrorExit(0, "Error reading existing app.config file (%s)\n%s", 
				TempAppConfigName, szError);
		}
		l = f.GetLength() + 1;
		RawAppConfig = (PCHAR)malloc(l);
		if (!RawAppConfig)
			return ErrorExit(0, "Error reading existing app.config file (%s)\nmalloc error", 
				TempAppConfigName);
		memset(RawAppConfig, 0, l);
		f.Read(RawAppConfig, l);
		f.Close();
	}

	// safe the app.config name and some other configuration parameters to shared memory
	// each parameter on its own line, each line terminated with \n
	// line				description
	// ----------------------------------------------------
	// 1				ApplicationName
	// 2				Configuration file path
	CString ConfigData, _Drive, _Path, _File, _Extension;
	CStringArray Extensions; Extensions.Add(".dll"); Extensions.Add(".exe");
	AssemblyNameEx an = SplitAssemblyName(Assembly2LoadName);
	PCHAR FoundPath = ProbeArchive(zf, &an, privatePath, Extensions );
	DestroyAssemblyName(&an);
	tokenizer.SplitPath(FALSE, CString(FoundPath), _Drive, _Path, _File, _Extension);
	if (FoundPath)
		free(FoundPath);
	ConfigData.Format("%s%s%s\n", 
		_File, 
		(_File.GetLength() && _Extension.GetLength() ? "." : ""), 
		_Extension);
	ConfigData.Append(TempAppConfigName);

	smAppConfigName = StoreInSharedMemory(SM_APP_CONFIG_NAME, ConfigData.GetLength(), (PVOID)((LPCTSTR)ConfigData));
	if (!smAppConfigName || !smAppConfigName->Handle || smAppConfigName->Handle == INVALID_HANDLE_VALUE)
		return ErrorExit(0, "MFT AppConfigName: Unable to store in shared memory");

	// safe the complete app.config xml content to it's own shared memory
	l = (l < 0) ? 0 : l;
	smAppConfigContent = StoreInSharedMemory(SM_APP_CONFIG_CONTENT, l, RawAppConfig);
	if (!smAppConfigContent || !smAppConfigContent->Handle || smAppConfigContent->Handle == INVALID_HANDLE_VALUE)
		return ErrorExit(0, "MFT AppConfigContent: Unable to store in shared memory");



	// if we have an app.config file
	// extract some clr runtime parameters from the it - if available
	// e.g <asm:probing privatePath="...;..." />
	if (TempAppConfigName.GetLength())
	{
		DebugOut("MFT AppConfig: Using \"%s\"\n", TempAppConfigName);

		// open the xml app.config file
		MSXML2::IXMLDOMDocument2Ptr pDoc = NULL;
		MSXML2::IXMLDOMElementPtr pRoot = NULL; 
		MSXML2::IXMLDOMNodePtr pNode = NULL; 

		hr = pDoc.CreateInstance(XML_PROG_ID);
		if (FAILED(hr)) 
			return ErrorExit(hr, XML_PROG_ID);		
		
		try
		{
			pDoc->async = VARIANT_FALSE;
			pDoc->setProperty(_T("SelectionLanguage"), _T("XPath"));
			pDoc->setProperty(_T("SelectionNamespaces"), _T("xmlns:asm='urn:schemas-microsoft-com:asm.v1'"));

			hrb = pDoc->load(_variant_t(TempAppConfigName));
			//printf("~~~~~ %s\n", smAppConfig->Content );
			//hrb = pDoc->loadXML(_bstr_t((char *)smAppConfig->Content));
			pDoc->get_documentElement(&pRoot);

			// extract the private search path
			if (pRoot)
			{
				pNode = pRoot->selectSingleNode(L"/configuration/runtime/asm:assemblyBinding/asm:probing/@privatePath");
				if (pNode)
				{
					hr = pNode->get_text(&bw);
					if (SUCCEEDED(hr)) 
					{
						CTokenEx tokenizer;
						pNode->get_text(&bw);
						CString temp = bw;
						temp.Trim();
						if (temp.GetLength() > 0)
						{
							DebugOut("MFT privatePath: %s\n", temp);
							tokenizer.Split(temp, ";", privatePath, FALSE, TRUE);
						}
					}
				}
			}
			else
				DebugOut("MFT invalid app.config structure.\n", TempAppConfigName);
		}
		catch(_com_error e)
		{
			if(FAILED(e.Error()))
				return ErrorExit(e.Error(), "COM Exception while parsing app.config (%s) XML  %s", 
					TempAppConfigName, e.Description());
		}
		catch(...)
		{
			return ErrorExit(0, "Unknown Exception while parsing app.config (%s) XML\n", 
				TempAppConfigName);
		}
	}

	return 0;
}



INT CreateSFXArchive()
{
	BOOL rtc = FALSE;

	if (SFXFileName.IsEmpty())
	{
		SFXFileName = BaseFileName + ".exe";
		if (PathFileExists(SFXFileName))
			SFXFileName = BaseFile + ".exe";
			if (PathFileExists(SFXFileName))
				return ErrorExit(0, "All possible filename alternatives for the SFX archive exists.\n"
									"(%s.exe), (%s.exe)\n"
									"Unable to create SFX archive.\n",
					BaseFileName, BaseFile);
	}
	if (PathFileExists(SFXFileName))
		return ErrorExit(0, "A File with name (%s) already exists.\n"
							"Unable to create SFX archive.\n", SFXFileName);

	DebugOut("SFX File Name: %s\n", SFXFileName);

	CopyFile(LoaderNameFQ, SFXFileName, FALSE);
	if (!PathFileExists(SFXFileName))
		return ErrorExit(0, "Unable to create SFX archive.", SFXFileName);

	// if we have to change resources like icons - do it now
	// patch icon if icon name is given
	// in manifest file
	CString TempFileName;
	if (!SFXIconNameCL.IsEmpty())
	{
		TempFileName = SFXIconNameCL;
		rtc = TRUE;
		DebugOut("SFX Icon (from command line) Name: %s\n", SFXIconNameCL);
	}
	else
	{
		DebugOut("SFX no icon specified on command line - try to read manifest inside archive.\n");
		if (!(zf = unzOpen(NARFileName)))
			return ErrorExit(0, "Unable to open Archive \"%s\"\n", NARFileName);
		rtc = InitializeManifest();
		unzClose(zf);
		zf = NULL;
		if (rtc != UNZ_OK && rtc != UNZ_BADPASSWORD)
			return ErrorExit(0, "Unable to process archive manifest.");

		if (!SFXIconName.IsEmpty())
		{
			// extract icon from archive to temp file
			TempFileName = CString(' ', MAX_PATH);
			rtc = ExtractFileFromZIP2TempEx(NARFileName, (NARFilePassword.IsEmpty() ? (LPCTSTR)NULL : NARFilePassword), 
											SFXIconName, TempFileName.GetBuffer(), TempFileName.GetLength());
			if (!rtc)
				DebugOut("SFX WARNING unable to extract icon from archive\n");
			TempFileName.ReleaseBuffer();
			DebugOut("SFX Icon (from manifest) Name: %s\n", SFXIconName);
		}
		else
			DebugOut("SFX no icon specified in archive manifest.\n");
	}

	if (rtc && PathFileExists(TempFileName))
	{
		// check if we are a binary icon (.exe or .dll) or
		// if we are an icon file
		CFile f;
		CFileException e = NULL;

		if (f.Open(TempFileName, CFile::typeBinary | CFile::modeReadWrite, &e))
		{
			CString Header = CString(' ', 5);
			f.Read(Header.GetBuffer(), Header.GetLength());
			Header.ReleaseBuffer();
			f.Close();

			if (!Header.Left(2).Compare("MZ"))
				// copy icon's from source binary to destination binary
				rtc = PatchIcon(TempFileName, SFXFileName);
			else
				// insert icon file into destination binary
				rtc = CreateProgramIcon(TempFileName, SFXFileName);
		}
	}
	else
		if (!TempFileName.IsEmpty())
			ErrorExit(0, "SFX Unable to read icon file (%s).", TempFileName);

	// delete temp file in case it was extracted
	// from the archive.
	if (SFXIconNameCL.IsEmpty())
		if (PathFileExists(TempFileName))
			_unlink(TempFileName);

	// append application archive to new loader
	PayloadHeader p;
	fcrypt_ctx zctx;

	ClearPayload(&p);
	p.Flags ^= isDebug ? PF_DEBUG : PF_NONE;


	// encrypt some parts of the payload
	prng_rand(p.IV, sizeof(p.IV), &rctx);
	fcrypt_init(AES_MODE, MKx, sizeof(MKx), p.IV, p.PV, &zctx);

	if (!NARFilePassword.IsEmpty())
	{
		if (MessageBox(NULL, "Include encrypted archive password in self running archive?", "Include password", 
			MB_ICONQUESTION | MB_YESNO) == IDNO)
		{
			NARFilePassword.Empty();
		}
	}
	strncpy_s(p.Password, sizeof(p.Password), NARFilePassword, _TRUNCATE);
	fcrypt_encrypt((UCHAR *)p.Password, sizeof(p.Password), &zctx);

	strncpy_s(p.OriginalName, sizeof(p.OriginalName) - 1, BaseFile, _TRUNCATE);
	fcrypt_encrypt((UCHAR *)p.OriginalName, sizeof(p.OriginalName), &zctx);

	fcrypt_end(p.AC, &zctx);


	if (WritePayloadFile(SFXFileName, &p, NARFileName))
	{
		CString msg;
		msg.Format("SFX self running archive (%s) created.\n", SFXFileName);

		if (isDebug)
		{
			printf (msg);
		}
		else
			MessageBox(NULL, msg, "Info", MB_ICONINFORMATION | MB_OK);
		return 0;
	}

	return ErrorExit(0, "SFX Payload error - unable to create SFX archive.\n",
		SFXFileName);
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The GNU General Public License (GPLv3)


Written By
Architect
Austria Austria
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions