#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);
}