#ifndef _WIN32_WINNT #define _WIN32_WINNT 0x0400 #endif #ifndef WIN32 #define WIN32 0x0400 #endif #pragma warning( disable: 4001 4035 4115 4200 4201 4204 4209 4214 4514 4699 ) #include #include #pragma warning( disable: 4201 ) #include #pragma warning( disable: 4001 4035 4115 4200 4201 4204 4209 4214 4514 4699 ) #include #include #include #include "patchapi.h" #include "patchprv.h" #include #include typedef BOOL PATCHAPI FN_CreatePatchFileA( IN LPCSTR OldFileName, IN LPCSTR NewFileName, OUT LPCSTR PatchFileName, IN ULONG OptionFlags, IN PPATCH_OPTION_DATA OptionData // optional ); typedef FN_CreatePatchFileA *PFN_CreatePatchFileA; FN_CreatePatchFileA CreatePatchFileA; // assert typedef correctness typedef BOOL PATCHAPI FN_CreatePatchFileExA( IN ULONG OldFileCount, // maximum 255 IN PPATCH_OLD_FILE_INFO_A OldFileInfoArray, IN LPCSTR NewFileName, OUT LPCSTR PatchFileName, IN ULONG OptionFlags, IN PPATCH_OPTION_DATA OptionData, // optional IN PPATCH_PROGRESS_CALLBACK ProgressCallback, IN PVOID CallbackContext ); typedef FN_CreatePatchFileExA *PFN_CreatePatchFileExA; FN_CreatePatchFileExA CreatePatchFileExA; // assert typedef correctness void CopyRight( void ) { printf( "\n" "MPATCH " VER_PRODUCTVERSION_STR " Patch Creation Utility\n" VER_LEGALCOPYRIGHT_STR "\n\n" ); } void Usage( void ) { printf( "Usage: MPATCH [options] OldFile[;OldFile2[;OldFile3]] NewFile TargetPatchFile\n" "\n" " Options:\n" "\n" " -NOBINDFIX Turn off automatic compensation for bound imports in\n" " the the old file. The default is to ignore binding\n" " data in the old file during patch creation which will\n" " cause the application of the patch to succeed whether\n" " or not the old file on the target machine is bound, not\n" " bound, or even bound to different import addresses.\n" " If the files are not Win32 binaries, this option is\n" " ignored and has no effect.\n" "\n" " -NOLOCKFIX Turn off automatic compensation for smashed lock prefix\n" " instructions. If the files are not Win32 binaries,\n" " this option is ignored and has no effect.\n" "\n" " -NOREBASE Turn off automatic internal rebasing of old file to new\n" " file's image base address. If the files are not Win32\n" " binaries, this option is ignored and has no effect.\n" "\n" " -NORESTIME Turn off automatic fixup of resource section timestamps\n" " (ignored if not Win32 binaries).\n" "\n" " -NOSTORETIME Don't store the timestamp of the new file in the patch\n" " file. Instead, set the timestamp of the patch file to\n" " the timestamp of the new file.\n" "\n" " -IGNORE:Offset,Length[,FileNumber]\n" "\n" " Ignore a range of bytes in the OldFile because those\n" " bytes might be different in the old file being patched\n" " on the target machine.\n" "\n" " -RETAIN:Offset,Length[,OffsetInNewFile[,FileNumber]]\n" "\n" " When applying the patch, preserve the range of bytes in\n" " the old file and copy them to the new file at the given\n" " OffsetInNewFile.\n" "\n" #if 0 " -RIFTINFO:FileName[,FileNumber]\n" "\n" " Use rift table information from FileName (produced from\n" " riftinfo.exe).\n" "\n" #endif " -FAILBIGGER If patch file is bigger than simple compressed file,\n" " don't create the patch file (takes longer).\n" "\n" " -FAILIFSAME If old and new files are the same (ignoring binding\n" " differences, etc), don't create the patch file.\n" "\n" " -NOCOMPARE Don't compare patch compression against ordinary non-\n" " patch compression (saves time).\n" "\n" " -NOPROGRESS Don't display percent complete while building patch.\n" "\n" " -NEWSYMPATH:PathName[;PathName]\n" "\n" " For NewFile, search for symbol file(s) in these path\n" " locations (recursive search each path until found).\n" " The default is to search for symbol files(s) starting\n" " in the same directory as the NewFile.\n" "\n" " -OLDSYMPATH:PathName[;PathName][,FileNumber]\n" "\n" " For OldFile, search for symbol file(s) in these path\n" " locations (recursive search each path until found).\n" " The default is to search for symbol files(s) starting\n" " in the same directory as the OldFile.\n" "\n" " -UNDECORATED After matching decorated symbol names, match remaining\n" " symbols using undecorated names.\n" "\n" " -NOSYMS Don't use debug symbol files when creating the patch.\n" "\n" " -NOSYMFAIL Don't fail to create patch if symbols cannot be loaded.\n" "\n" " -NOSYMWARN Don't warn if symbols can't be found or don't match the\n" " corresponding file (symbol checksum mismatch).\n" "\n" " -USEBADSYMS Rather than ignoring symbols if the checksums don't\n" " match the corresponding files, use the bad symbols.\n" "\n" " -E8 Force E8 call translation for x86 binaries.\n" "\n" " -NOE8 Force no E8 call translation for x86 binaries.\n" "\n" " If neither -E8 or -NOE8 are specified, and the files\n" " are x86 binaries, the patch will be built internally\n" " twice and the smaller will be chosen for output.\n" "\n" " -MSPATCH194COMPAT Assure the patch file can be used with version\n" " 1.94 of MSPATCH*.DLL. May increase size of patch\n" " file if old or new file is larger than 4Mb.\n" "\n" " MPATCH will also look for environment variables named \"MPATCH\"\n" " followed by an underscore and the name of the option. Command line\n" " specified options override environment variable options. Examples:\n" "\n" " MPATCH_NOCOMPARE=1\n" " MPATCH_NEWSYMPATH=c:\\winnt\\symbols;\\\\server\\share\\symbols\n" "\n" ); exit( 1 ); } BOOL bNoProgress; BOOL bNoSymWarn; BOOL bUseBadSyms; DWORDLONG GetFileSizeByName( IN LPCSTR FileName ) { DWORDLONG FileSizeReturn; ULONG FileSizeHigh; ULONG FileSizeLow; HANDLE hFile; FileSizeReturn = 0xFFFFFFFFFFFFFFFF; hFile = CreateFile( FileName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL ); if ( hFile != INVALID_HANDLE_VALUE ) { FileSizeLow = GetFileSize( hFile, &FileSizeHigh ); if (( FileSizeLow != 0xFFFFFFFF ) || ( GetLastError() == NO_ERROR )) { FileSizeReturn = ((DWORDLONG)FileSizeHigh << 32 ) | FileSizeLow; } CloseHandle( hFile ); } return FileSizeReturn; } BOOL GetMpatchEnvironString( IN LPCSTR VarName, OUT LPSTR Buffer, IN DWORD BufferSize ) { CHAR EnvironName[ 256 ]; sprintf( EnvironName, "mpatch_%s", VarName ); if ( GetEnvironmentVariable( EnvironName, Buffer, BufferSize )) { return TRUE; } return FALSE; } BOOL GetMpatchEnvironValue( IN LPCSTR VarName ) { CHAR LocalBuffer[ 256 ]; if ( GetMpatchEnvironString( VarName, LocalBuffer, sizeof( LocalBuffer ))) { if (( *LocalBuffer == '0' ) && ( strtoul( LocalBuffer, NULL, 0 ) == 0 )) { return FALSE; } return TRUE; } return FALSE; } BOOL CALLBACK MyProgressCallback( PVOID CallbackContext, ULONG CurrentPosition, ULONG MaximumPosition ) { UNREFERENCED_PARAMETER( CallbackContext ); if ( MaximumPosition != 0 ) { fprintf( stderr, "\r%3.1f%% complete", ( CurrentPosition * 100.0 ) / MaximumPosition ); } return TRUE; } BOOL CALLBACK MySymLoadCallback( IN ULONG WhichFile, IN LPCSTR SymbolFileName, IN ULONG SymType, IN ULONG SymbolFileCheckSum, IN ULONG SymbolFileTimeDate, IN ULONG ImageFileCheckSum, IN ULONG ImageFileTimeDate, IN PVOID CallbackContext ) { LPCSTR *FileNameArray = CallbackContext; LPCSTR SymTypeText; if (( SymType == SymNone ) || ( SymType == SymExport )) { // // Symbols could not be found. // if ( ! bNoSymWarn ) { printf( "\n" "WARNING: no debug symbols for %s\n\n", FileNameArray[ WhichFile ] ); } return TRUE; } // // Note that the Old file checksum is the checksum AFTER normalization, // so if the original .dbg file was updated with bound checksum, the // old file's checksum will not match the symbol file's checksum. But, // binding a file does not change its TimeDateStamp, so that should be // a valid comparison. But, .sym files don't have a TimeDateStamp, so // the SymbolFileTimeDate may be zero. If either the checksums match // or the timedate stamps match, we'll say its valid. // if (( ImageFileCheckSum == SymbolFileCheckSum ) || ( ImageFileTimeDate == SymbolFileTimeDate )) { return TRUE; } if ( ! bNoSymWarn ) { switch ( SymType ) { case SymNone: SymTypeText = "No"; break; case SymCoff: SymTypeText = "Coff"; break; case SymCv: SymTypeText = "CodeView"; break; case SymPdb: SymTypeText = "Pdb"; break; case SymExport: SymTypeText = "Export"; break; case SymDeferred: SymTypeText = "Deferred"; break; case SymSym: SymTypeText = "Sym"; break; default: SymTypeText = "Unknown"; break; } printf( "\n" "WARNING: %s symbols %s don't match %s:\n" " symbol file checksum (%08X) does not match image (%08X), and\n" " symbol file timedate (%08X) does not match image (%08X).\n\n", SymTypeText, SymbolFileName, FileNameArray[ WhichFile ], SymbolFileCheckSum, ImageFileCheckSum, SymbolFileTimeDate, ImageFileTimeDate ); } return bUseBadSyms; } PRIFT_TABLE RiftTableArray[ 256 ]; PATCH_OLD_FILE_INFO_A OldFileInfo[ 256 ]; LPSTR OldFileSymPathArray[ 256 ]; LPSTR NewFileSymPath; LPSTR FileNameArray[ 257 ]; PATCH_OPTION_DATA OptionData = { sizeof( PATCH_OPTION_DATA ) }; CHAR TextBuffer[ 65000 ]; void __cdecl main( int argc, char *argv[] ) { LPSTR OldFileName = NULL; LPSTR NewFileName = NULL; LPSTR PatchFileName = NULL; LPSTR DllFileName = NULL; ULONG OptionFlags = PATCH_OPTION_USE_LZX_BEST | PATCH_OPTION_USE_LZX_LARGE; BOOL Success; LPSTR arg; LPSTR p, q; LPSTR FileName; int i, j, n; ULONG OldOffset; ULONG NewOffset; ULONG Length; ULONG FileNum; ULONG OldFileCount; ULONG ErrorCode; ULONG NewFileSize; ULONG PatchFileSize; ULONG OldFileRva; ULONG NewFileRva; BOOL bNoCompare = FALSE; FILE *RiftFile; LPSTR FileNamePart; CHAR LoadedFileName[ MAX_PATH + 1 ]; HMODULE hLib; PFN_CreatePatchFileA pCreatePatchFileA = CreatePatchFileA; PFN_CreatePatchFileExA pCreatePatchFileExA = CreatePatchFileExA; SetErrorMode( SEM_FAILCRITICALERRORS ); #ifndef DEBUG SetErrorMode( SEM_NOALIGNMENTFAULTEXCEPT | SEM_FAILCRITICALERRORS ); #endif #ifdef TESTCODE bNoCompare = TRUE; #endif CopyRight(); for ( i = 1; i < argc; i++ ) { arg = argv[ i ]; if ( strchr( arg, '?' )) { Usage(); } } // // First get environment arguments because command-line args will // override them. // if ( GetMpatchEnvironValue( "e8" )) { OptionFlags &= ~PATCH_OPTION_USE_LZX_A; } if ( GetMpatchEnvironValue( "noe8" )) { OptionFlags &= ~PATCH_OPTION_USE_LZX_B; } if ( GetMpatchEnvironValue( "mspatch194compat" )) { OptionFlags &= ~PATCH_OPTION_USE_LZX_LARGE; } if ( GetMpatchEnvironValue( "nobindfix" )) { OptionFlags |= PATCH_OPTION_NO_BINDFIX; } if ( GetMpatchEnvironValue( "nolockfix" )) { OptionFlags |= PATCH_OPTION_NO_LOCKFIX; } if ( GetMpatchEnvironValue( "norebase" )) { OptionFlags |= PATCH_OPTION_NO_REBASE; } if ( GetMpatchEnvironValue( "norestime" )) { OptionFlags |= PATCH_OPTION_NO_RESTIMEFIX; } if ( GetMpatchEnvironValue( "nostoretime" )) { OptionFlags |= PATCH_OPTION_NO_TIMESTAMP; } if ( GetMpatchEnvironValue( "failbigger" )) { OptionFlags |= PATCH_OPTION_FAIL_IF_BIGGER; } if ( GetMpatchEnvironValue( "failifbigger" )) { OptionFlags |= PATCH_OPTION_FAIL_IF_BIGGER; } if ( GetMpatchEnvironValue( "failsame" )) { OptionFlags |= PATCH_OPTION_FAIL_IF_SAME_FILE; } if ( GetMpatchEnvironValue( "failifsame" )) { OptionFlags |= PATCH_OPTION_FAIL_IF_SAME_FILE; } if ( GetMpatchEnvironValue( "nocompare" )) { bNoCompare = TRUE; } if ( GetMpatchEnvironValue( "noprogress" )) { bNoProgress = TRUE; } if ( GetMpatchEnvironValue( "undecorated" )) { OptionData.SymbolOptionFlags |= PATCH_SYMBOL_UNDECORATED_TOO; } if ( GetMpatchEnvironValue( "nosyms" )) { OptionData.SymbolOptionFlags |= PATCH_SYMBOL_NO_IMAGEHLP; } if ( GetMpatchEnvironValue( "nosymfail" )) { OptionData.SymbolOptionFlags |= PATCH_SYMBOL_NO_FAILURES; } if ( GetMpatchEnvironValue( "nosymwarn" )) { bNoSymWarn = TRUE; } if ( GetMpatchEnvironValue( "usebadsyms" )) { bUseBadSyms = TRUE; } if ( GetMpatchEnvironString( "ignore", TextBuffer, sizeof( TextBuffer ))) { p = TextBuffer; q = strchr( p, ',' ); if ( q != NULL ) { *q = 0; OldOffset = strtoul( p, NULL, 0 ); p = q + 1; q = strchr( p, ',' ); if ( q ) { *q = 0; } Length = strtoul( p, NULL, 0 ); FileNum = 1; if ( q ) { p = q + 1; FileNum = strtoul( p, NULL, 0 ); if ( FileNum == 0 ) { FileNum = 1; } } if ( FileNum <= 255 ) { void *pv = realloc( OldFileInfo[ FileNum - 1 ].IgnoreRangeArray, OldFileInfo[ FileNum - 1 ].IgnoreRangeCount * sizeof( PATCH_IGNORE_RANGE ) + sizeof( PATCH_IGNORE_RANGE ) ); if (pv) { OldFileInfo[ FileNum - 1 ].IgnoreRangeArray = pv; } else { printf( "Out of memory\n" ); exit( 1 ); } OldFileInfo[ FileNum - 1 ].IgnoreRangeArray[ OldFileInfo[ FileNum - 1 ].IgnoreRangeCount ].OffsetInOldFile = OldOffset; OldFileInfo[ FileNum - 1 ].IgnoreRangeArray[ OldFileInfo[ FileNum - 1 ].IgnoreRangeCount ].LengthInBytes = Length; OldFileInfo[ FileNum - 1 ].IgnoreRangeCount++; } } } if ( GetMpatchEnvironString( "retain", TextBuffer, sizeof( TextBuffer ))) { p = TextBuffer; q = strchr( p, ',' ); if ( q != NULL ) { *q = 0; OldOffset = strtoul( p, NULL, 0 ); p = q + 1; q = strchr( p, ',' ); if ( q ) { *q = 0; } Length = strtoul( p, NULL, 0 ); NewOffset = OldOffset; FileNum = 1; if ( q ) { p = q + 1; q = strchr( p, ',' ); if ( q ) { *q = 0; } NewOffset = strtoul( p, NULL, 0 ); if ( q ) { p = q + 1; FileNum = strtoul( p, NULL, 0 ); if ( FileNum == 0 ) { FileNum = 1; } } } if ( FileNum <= 255 ) { void *pv = realloc( OldFileInfo[ FileNum - 1 ].RetainRangeArray, OldFileInfo[ FileNum - 1 ].RetainRangeCount * sizeof( PATCH_RETAIN_RANGE ) + sizeof( PATCH_RETAIN_RANGE ) ); if (pv) { OldFileInfo[ FileNum - 1 ].RetainRangeArray = pv; } else { printf( "Out of memory\n" ); exit( 1 ); } OldFileInfo[ FileNum - 1 ].RetainRangeArray[ OldFileInfo[ FileNum - 1 ].RetainRangeCount ].OffsetInOldFile = OldOffset; OldFileInfo[ FileNum - 1 ].RetainRangeArray[ OldFileInfo[ FileNum - 1 ].RetainRangeCount ].OffsetInNewFile = NewOffset; OldFileInfo[ FileNum - 1 ].RetainRangeArray[ OldFileInfo[ FileNum - 1 ].RetainRangeCount ].LengthInBytes = Length; OldFileInfo[ FileNum - 1 ].RetainRangeCount++; } } } if ( GetMpatchEnvironString( "riftinfo", TextBuffer, sizeof( TextBuffer ))) { p = TextBuffer; q = strchr( p, ',' ); if ( q ) { *q = 0; } FileName = p; FileNum = 1; if ( q ) { p = q + 1; FileNum = strtoul( p, NULL, 0 ); if ( FileNum == 0 ) { FileNum = 1; } } if ( FileNum <= 255 ) { RiftTableArray[ FileNum - 1 ] = malloc( sizeof( RIFT_TABLE )); if ( RiftTableArray[ FileNum - 1 ] == NULL ) { printf( "Out of memory\n" ); exit( 1 ); } RiftTableArray[ FileNum - 1 ]->RiftEntryCount = 0; RiftTableArray[ FileNum - 1 ]->RiftEntryAlloc = 0; RiftTableArray[ FileNum - 1 ]->RiftEntryArray = NULL; RiftTableArray[ FileNum - 1 ]->RiftUsageArray = NULL; RiftFile = fopen( FileName, "rt" ); if ( RiftFile == NULL ) { printf( "Could not open %s\n", FileName ); exit( 1 ); } while ( fgets( TextBuffer, sizeof( TextBuffer ), RiftFile )) { // // Line looks like "00001456 00002345" where each number // is an RVA in hexadecimal and the first column is the // OldFileRva and the second column is the NewFileRva. // Any text beyond column 17 is considered a comment, and // any line that does not begin with a digit is ignored. // if (( isxdigit( *TextBuffer )) && ( strlen( TextBuffer ) >= 17 )) { OldFileRva = strtoul( TextBuffer, NULL, 16 ); NewFileRva = strtoul( TextBuffer + 9, NULL, 16 ); if (( OldFileRva + NewFileRva ) != 0 ) { void *pv = realloc ( RiftTableArray[ FileNum - 1 ]->RiftEntryArray, RiftTableArray[ FileNum - 1 ]->RiftEntryCount * sizeof( RIFT_ENTRY ) + sizeof( RIFT_ENTRY ) ); if (pv) { RiftTableArray[ FileNum - 1 ]->RiftEntryArray = pv; } else { printf( "Out of memory\n" ); exit( 1 ); } RiftTableArray[ FileNum - 1 ]->RiftEntryArray[ RiftTableArray[ FileNum - 1 ]->RiftEntryCount ].OldFileRva = OldFileRva; RiftTableArray[ FileNum - 1 ]->RiftEntryArray[ RiftTableArray[ FileNum - 1 ]->RiftEntryCount ].NewFileRva = NewFileRva; RiftTableArray[ FileNum - 1 ]->RiftEntryCount++; } } } fclose( RiftFile ); if ( RiftTableArray[ FileNum - 1 ]->RiftEntryCount ) { RiftTableArray[ FileNum - 1 ]->RiftEntryAlloc = RiftTableArray[ FileNum - 1 ]->RiftEntryCount; RiftTableArray[ FileNum - 1 ]->RiftUsageArray = malloc( RiftTableArray[ FileNum - 1 ]->RiftEntryCount ); if ( RiftTableArray[ FileNum - 1 ]->RiftUsageArray == NULL ) { printf( "Out of memory\n" ); exit( 1 ); } ZeroMemory( RiftTableArray[ FileNum - 1 ]->RiftUsageArray, RiftTableArray[ FileNum - 1 ]->RiftEntryCount ); } OptionData.SymbolOptionFlags |= PATCH_SYMBOL_EXTERNAL_RIFT; } } if ( GetMpatchEnvironString( "oldsympath", TextBuffer, sizeof( TextBuffer ))) { p = TextBuffer; q = strchr( p, ',' ); if ( q ) { *q = 0; } FileName = p; FileNum = 1; if ( q ) { p = q + 1; FileNum = strtoul( p, NULL, 0 ); if ( FileNum == 0 ) { FileNum = 1; } } if ( FileNum <= 255 ) { OldFileSymPathArray[ FileNum - 1 ] = _strdup( FileName ); } } if ( GetMpatchEnvironString( "newsympath", TextBuffer, sizeof( TextBuffer ))) { p = TextBuffer; q = strchr( p, ',' ); if ( q ) { *q = 0; } FileName = p; FileNum = 1; if ( q ) { p = q + 1; FileNum = strtoul( p, NULL, 0 ); if ( FileNum == 0 ) { FileNum = 1; } } if ( FileNum == 1 ) { NewFileSymPath = _strdup( FileName ); } } // // Now process commandline args // for ( i = 1; i < argc; i++ ) { arg = argv[ i ]; if ( strchr( arg, '?' )) { Usage(); } if (( *arg == '-' ) || ( *arg == '/' )) { _strlwr( ++arg ); if ( strcmp( arg, "e8" ) == 0 ) { OptionFlags &= ~PATCH_OPTION_USE_LZX_A; } else if ( strcmp( arg, "noe8" ) == 0 ) { OptionFlags &= ~PATCH_OPTION_USE_LZX_B; } else if ( strcmp( arg, "mspatch194compat" ) == 0 ) { OptionFlags &= ~PATCH_OPTION_USE_LZX_LARGE; } else if ( strcmp( arg, "nobindfix" ) == 0 ) { OptionFlags |= PATCH_OPTION_NO_BINDFIX; } else if ( strcmp( arg, "bindfix" ) == 0 ) { OptionFlags &= ~PATCH_OPTION_NO_BINDFIX; } else if ( strcmp( arg, "nolockfix" ) == 0 ) { OptionFlags |= PATCH_OPTION_NO_LOCKFIX; } else if ( strcmp( arg, "lockfix" ) == 0 ) { OptionFlags &= ~PATCH_OPTION_NO_LOCKFIX; } else if ( strcmp( arg, "norebase" ) == 0 ) { OptionFlags |= PATCH_OPTION_NO_REBASE; } else if ( strcmp( arg, "rebase" ) == 0 ) { OptionFlags &= ~PATCH_OPTION_NO_REBASE; } else if ( strcmp( arg, "norestime" ) == 0 ) { OptionFlags |= PATCH_OPTION_NO_RESTIMEFIX; } else if ( strcmp( arg, "norestimefix" ) == 0 ) { OptionFlags |= PATCH_OPTION_NO_RESTIMEFIX; } else if ( strcmp( arg, "restime" ) == 0 ) { OptionFlags &= ~PATCH_OPTION_NO_RESTIMEFIX; } else if ( strcmp( arg, "restimefix" ) == 0 ) { OptionFlags &= ~PATCH_OPTION_NO_RESTIMEFIX; } else if ( strcmp( arg, "nostoretime" ) == 0 ) { OptionFlags |= PATCH_OPTION_NO_TIMESTAMP; } else if ( strcmp( arg, "storetime" ) == 0 ) { OptionFlags &= ~PATCH_OPTION_NO_TIMESTAMP; } else if ( strcmp( arg, "failbigger" ) == 0 ) { OptionFlags |= PATCH_OPTION_FAIL_IF_BIGGER; } else if ( strcmp( arg, "nofailbigger" ) == 0 ) { OptionFlags &= ~PATCH_OPTION_FAIL_IF_BIGGER; } else if ( strcmp( arg, "failifbigger" ) == 0 ) { OptionFlags |= PATCH_OPTION_FAIL_IF_BIGGER; } else if ( strcmp( arg, "nofailifbigger" ) == 0 ) { OptionFlags &= ~PATCH_OPTION_FAIL_IF_BIGGER; } else if ( strcmp( arg, "failifsame" ) == 0 ) { OptionFlags |= PATCH_OPTION_FAIL_IF_SAME_FILE; } else if ( strcmp( arg, "failsame" ) == 0 ) { OptionFlags |= PATCH_OPTION_FAIL_IF_SAME_FILE; } else if ( strcmp( arg, "nofailifsame" ) == 0 ) { OptionFlags &= ~PATCH_OPTION_FAIL_IF_SAME_FILE; } else if ( strcmp( arg, "nofailsame" ) == 0 ) { OptionFlags &= ~PATCH_OPTION_FAIL_IF_SAME_FILE; } else if ( strcmp( arg, "nocompare" ) == 0 ) { bNoCompare = TRUE; } else if ( strcmp( arg, "compare" ) == 0 ) { bNoCompare = FALSE; } else if ( strcmp( arg, "noprogress" ) == 0 ) { bNoProgress = TRUE; } else if ( strcmp( arg, "progress" ) == 0 ) { bNoProgress = FALSE; } else if ( strcmp( arg, "decorated" ) == 0 ) { OptionData.SymbolOptionFlags &= ~PATCH_SYMBOL_UNDECORATED_TOO; } else if ( strcmp( arg, "undecorated" ) == 0 ) { OptionData.SymbolOptionFlags |= PATCH_SYMBOL_UNDECORATED_TOO; } else if ( strcmp( arg, "nosyms" ) == 0 ) { OptionData.SymbolOptionFlags |= PATCH_SYMBOL_NO_IMAGEHLP; } else if ( strcmp( arg, "syms" ) == 0 ) { OptionData.SymbolOptionFlags &= ~PATCH_SYMBOL_NO_IMAGEHLP; } else if ( strcmp( arg, "nosymfail" ) == 0 ) { OptionData.SymbolOptionFlags |= PATCH_SYMBOL_NO_FAILURES; } else if ( strcmp( arg, "symfail" ) == 0 ) { OptionData.SymbolOptionFlags &= ~PATCH_SYMBOL_NO_FAILURES; } else if ( strcmp( arg, "nosymwarn" ) == 0 ) { bNoSymWarn = TRUE; } else if ( strcmp( arg, "symwarn" ) == 0 ) { bNoSymWarn = FALSE; } else if ( strcmp( arg, "usebadsyms" ) == 0 ) { bUseBadSyms = TRUE; } else if ( strcmp( arg, "nousebadsyms" ) == 0 ) { bUseBadSyms = FALSE; } else if ( strcmp( arg, "nobadsyms" ) == 0 ) { bUseBadSyms = FALSE; } else if ( memcmp( arg, "ignore:", 7 ) == 0 ) { p = strchr( arg, ':' ) + 1; q = strchr( p, ',' ); if ( q == NULL ) { Usage(); } *q = 0; OldOffset = strtoul( p, NULL, 0 ); p = q + 1; q = strchr( p, ',' ); if ( q ) { *q = 0; } Length = strtoul( p, NULL, 0 ); FileNum = 1; if ( q ) { p = q + 1; FileNum = strtoul( p, NULL, 0 ); if ( FileNum == 0 ) { FileNum = 1; } } if ( FileNum > 255 ) { Usage(); } { void *pv = realloc( OldFileInfo[ FileNum - 1 ].IgnoreRangeArray, OldFileInfo[ FileNum - 1 ].IgnoreRangeCount * sizeof( PATCH_IGNORE_RANGE ) + sizeof( PATCH_IGNORE_RANGE ) ); if (pv) { OldFileInfo[ FileNum - 1 ].IgnoreRangeArray = pv; } else { printf( "Out of memory\n" ); exit( 1 ); } } OldFileInfo[ FileNum - 1 ].IgnoreRangeArray[ OldFileInfo[ FileNum - 1 ].IgnoreRangeCount ].OffsetInOldFile = OldOffset; OldFileInfo[ FileNum - 1 ].IgnoreRangeArray[ OldFileInfo[ FileNum - 1 ].IgnoreRangeCount ].LengthInBytes = Length; OldFileInfo[ FileNum - 1 ].IgnoreRangeCount++; } else if ( memcmp( arg, "retain:", 7 ) == 0 ) { p = strchr( arg, ':' ) + 1; q = strchr( p, ',' ); if ( q == NULL ) { Usage(); } *q = 0; OldOffset = strtoul( p, NULL, 0 ); p = q + 1; q = strchr( p, ',' ); if ( q ) { *q = 0; } Length = strtoul( p, NULL, 0 ); NewOffset = OldOffset; FileNum = 1; if ( q ) { p = q + 1; q = strchr( p, ',' ); if ( q ) { *q = 0; } NewOffset = strtoul( p, NULL, 0 ); if ( q ) { p = q + 1; FileNum = strtoul( p, NULL, 0 ); if ( FileNum == 0 ) { FileNum = 1; } } } if ( FileNum > 255 ) { Usage(); } { void *pv = realloc( OldFileInfo[ FileNum - 1 ].RetainRangeArray, OldFileInfo[ FileNum - 1 ].RetainRangeCount * sizeof( PATCH_RETAIN_RANGE ) + sizeof( PATCH_RETAIN_RANGE ) ); if (pv) { OldFileInfo[ FileNum - 1 ].RetainRangeArray = pv; } else { printf( "Out of memory\n" ); exit( 1 ); } } OldFileInfo[ FileNum - 1 ].RetainRangeArray[ OldFileInfo[ FileNum - 1 ].RetainRangeCount ].OffsetInOldFile = OldOffset; OldFileInfo[ FileNum - 1 ].RetainRangeArray[ OldFileInfo[ FileNum - 1 ].RetainRangeCount ].OffsetInNewFile = NewOffset; OldFileInfo[ FileNum - 1 ].RetainRangeArray[ OldFileInfo[ FileNum - 1 ].RetainRangeCount ].LengthInBytes = Length; OldFileInfo[ FileNum - 1 ].RetainRangeCount++; } else if ( memcmp( arg, "riftinfo:", 9 ) == 0 ) { OptionData.SymbolOptionFlags |= PATCH_SYMBOL_EXTERNAL_RIFT; p = strchr( arg, ':' ) + 1; q = strchr( p, ',' ); if ( q ) { *q = 0; } FileName = p; FileNum = 1; if ( q ) { p = q + 1; FileNum = strtoul( p, NULL, 0 ); if ( FileNum == 0 ) { FileNum = 1; } } if ( FileNum > 255 ) { Usage(); } RiftTableArray[ FileNum - 1 ] = malloc( sizeof( RIFT_TABLE )); if ( RiftTableArray[ FileNum - 1 ] == NULL ) { printf( "Out of memory\n" ); exit( 1 ); } RiftTableArray[ FileNum - 1 ]->RiftEntryCount = 0; RiftTableArray[ FileNum - 1 ]->RiftEntryAlloc = 0; RiftTableArray[ FileNum - 1 ]->RiftEntryArray = NULL; RiftTableArray[ FileNum - 1 ]->RiftUsageArray = NULL; RiftFile = fopen( FileName, "rt" ); if ( RiftFile == NULL ) { printf( "Could not open %s\n", FileName ); exit( 1 ); } while ( fgets( TextBuffer, sizeof( TextBuffer ), RiftFile )) { // // Line looks like "00001456 00002345" where each number // is an RVA in hexadecimal and the first column is the // OldFileRva and the second column is the NewFileRva. // Any text beyond column 17 is considered a comment, and // any line that does not begin with a digit is ignored. // if (( isxdigit( *TextBuffer )) && ( strlen( TextBuffer ) >= 17 )) { OldFileRva = strtoul( TextBuffer, NULL, 16 ); NewFileRva = strtoul( TextBuffer + 9, NULL, 16 ); if (( OldFileRva + NewFileRva ) != 0 ) { void *pv = realloc( RiftTableArray[ FileNum - 1 ]->RiftEntryArray, RiftTableArray[ FileNum - 1 ]->RiftEntryCount * sizeof( RIFT_ENTRY ) + sizeof( RIFT_ENTRY ) ); if (pv) { RiftTableArray[ FileNum - 1 ]->RiftEntryArray = pv; } else { printf( "Out of memory\n" ); exit( 1 ); } RiftTableArray[ FileNum - 1 ]->RiftEntryArray[ RiftTableArray[ FileNum - 1 ]->RiftEntryCount ].OldFileRva = OldFileRva; RiftTableArray[ FileNum - 1 ]->RiftEntryArray[ RiftTableArray[ FileNum - 1 ]->RiftEntryCount ].NewFileRva = NewFileRva; RiftTableArray[ FileNum - 1 ]->RiftEntryCount++; } } } fclose( RiftFile ); if ( RiftTableArray[ FileNum - 1 ]->RiftEntryCount ) { RiftTableArray[ FileNum - 1 ]->RiftEntryAlloc = RiftTableArray[ FileNum - 1 ]->RiftEntryCount; RiftTableArray[ FileNum - 1 ]->RiftUsageArray = malloc( RiftTableArray[ FileNum - 1 ]->RiftEntryCount ); if ( RiftTableArray[ FileNum - 1 ]->RiftUsageArray == NULL ) { printf( "Out of memory\n" ); exit( 1 ); } ZeroMemory( RiftTableArray[ FileNum - 1 ]->RiftUsageArray, RiftTableArray[ FileNum - 1 ]->RiftEntryCount ); } } else if ( memcmp( arg, "oldsympath:", 11 ) == 0 ) { p = strchr( arg, ':' ) + 1; q = strchr( p, ',' ); if ( q ) { *q = 0; } FileName = p; FileNum = 1; if ( q ) { p = q + 1; FileNum = strtoul( p, NULL, 0 ); if ( FileNum == 0 ) { FileNum = 1; } } if ( FileNum > 255 ) { Usage(); } OldFileSymPathArray[ FileNum - 1 ] = _strdup( FileName ); } else if ( memcmp( arg, "newsympath:", 11 ) == 0 ) { p = strchr( arg, ':' ) + 1; q = strchr( p, ',' ); if ( q ) { *q = 0; } FileName = p; FileNum = 1; if ( q ) { p = q + 1; FileNum = strtoul( p, NULL, 0 ); if ( FileNum == 0 ) { FileNum = 1; } } if ( FileNum != 1 ) { Usage(); } NewFileSymPath = _strdup( FileName ); } else if ( strncmp( arg, "dll:", 4 ) == 0 ) { DllFileName = arg + 4; } else { Usage(); } } else if ( OldFileName == NULL ) { OldFileName = arg; } else if ( NewFileName == NULL ) { NewFileName = arg; } else if ( PatchFileName == NULL ) { PatchFileName = arg; } else { Usage(); } } if (( OldFileName == NULL ) || ( NewFileName == NULL ) || ( PatchFileName == NULL )) { Usage(); } if ( DllFileName != NULL ) { hLib = LoadLibrary( DllFileName ); if ( hLib == NULL ) { printf( "Unable to load DLL '%s'\n", DllFileName ); exit( 1 ); } if ( GetModuleFileName( hLib, LoadedFileName, sizeof( LoadedFileName ))) { printf( "Loaded DLL '%s'\n", LoadedFileName ); } pCreatePatchFileA = (PFN_CreatePatchFileA) GetProcAddress( hLib, "CreatePatchFileA" ); pCreatePatchFileExA = (PFN_CreatePatchFileExA) GetProcAddress( hLib, "CreatePatchFileExA" ); if (( pCreatePatchFileExA == NULL ) || ( pCreatePatchFileExA == NULL )) { printf( "Unable to import CreatePatchFileA and CreatePatchFileExA from DLL\n" ); exit( 1 ); } } OldFileCount = 0; p = OldFileName; q = strchr( OldFileName, ';' ); while ( q ) { *q = 0; if ( *p ) { OldFileInfo[ OldFileCount ].SizeOfThisStruct = sizeof( PATCH_OLD_FILE_INFO_A ); OldFileInfo[ OldFileCount ].OldFileName = p; OldFileCount++; } p = q + 1; q = strchr( p, ';' ); } if ( *p ) { OldFileInfo[ OldFileCount ].SizeOfThisStruct = sizeof( PATCH_OLD_FILE_INFO_A ); OldFileInfo[ OldFileCount ].OldFileName = p; OldFileCount++; } // // Make sure rift tables are ascending and don't contain duplicate // OldRva values (ambiguous). // for ( i = 0; i < (int)OldFileCount; i++ ) { if (( RiftTableArray[ i ] ) && ( RiftTableArray[ i ]->RiftEntryCount > 1 )) { n = RiftTableArray[ i ]->RiftEntryCount - 1; RiftQsort( &RiftTableArray[ i ]->RiftEntryArray[ 0 ], &RiftTableArray[ i ]->RiftEntryArray[ n ] ); #ifdef TESTCODE for ( j = 0; j < n; j++ ) { if ( RiftTableArray[ i ]->RiftEntryArray[ j ].OldFileRva > RiftTableArray[ i ]->RiftEntryArray[ j + 1 ].OldFileRva ) { printf( "\nRift sort failed at index %d of %d\n", j, n + 1 ); for ( j = 0; j <= n; j++ ) { printf( "%08X\n", RiftTableArray[ i ]->RiftEntryArray[ j ].OldFileRva ); } exit( 1 ); break; } } #endif // TESTCODE for ( j = 0; j < n; j++ ) { while (( j < n ) && ( RiftTableArray[ i ]->RiftEntryArray[ j ].OldFileRva == RiftTableArray[ i ]->RiftEntryArray[ j + 1 ].OldFileRva )) { if ( RiftTableArray[ i ]->RiftEntryArray[ j ].NewFileRva != RiftTableArray[ i ]->RiftEntryArray[ j + 1 ].NewFileRva ) { // // This is an ambiguous entry since the OldRva values // match but the NewRva values do not. Report and // discard the former. // printf( "RiftInfo for %s contains ambiguous entries:\n" " OldRva:%08X NewRva:%08X (discarded)\n" " OldRva:%08X NewRva:%08X (kept)\n\n", OldFileInfo[ i ].OldFileName, RiftTableArray[ i ]->RiftEntryArray[ j ].OldFileRva, RiftTableArray[ i ]->RiftEntryArray[ j ].NewFileRva, RiftTableArray[ i ]->RiftEntryArray[ j + 1 ].OldFileRva, RiftTableArray[ i ]->RiftEntryArray[ j + 1 ].NewFileRva ); } else { // // This is a completely duplicate entry, so just // silently remove it. // } MoveMemory( &RiftTableArray[ i ]->RiftEntryArray[ j ], &RiftTableArray[ i ]->RiftEntryArray[ j + 1 ], ( n - j ) * sizeof( RIFT_ENTRY ) ); --n; } } RiftTableArray[ i ]->RiftEntryCount = n + 1; } } for ( i = 0; i < (int)OldFileCount; i++ ) { if ( OldFileSymPathArray[ i ] == NULL ) { GetFullPathName( OldFileInfo[ i ].OldFileName, sizeof( TextBuffer ), TextBuffer, &FileNamePart ); if (( FileNamePart > TextBuffer ) && ( *( FileNamePart - 1 ) == '\\' )) { *( FileNamePart - 1 ) = 0; } OldFileSymPathArray[ i ] = _strdup( TextBuffer ); } } if ( NewFileSymPath == NULL ) { GetFullPathName( NewFileName, sizeof( TextBuffer ), TextBuffer, &FileNamePart ); if (( FileNamePart > TextBuffer ) && ( *( FileNamePart - 1 ) == '\\' )) { *( FileNamePart - 1 ) = 0; } NewFileSymPath = _strdup( TextBuffer ); } OptionData.NewFileSymbolPath = NewFileSymPath; OptionData.OldFileSymbolPathArray = OldFileSymPathArray; if ( OptionData.SymbolOptionFlags & PATCH_SYMBOL_EXTERNAL_RIFT ) { OptionData.OldFileSymbolPathArray = (PVOID)RiftTableArray; } OptionData.SymLoadCallback = MySymLoadCallback; OptionData.SymLoadContext = FileNameArray; FileNameArray[ 0 ] = (LPSTR)NewFileName; for ( i = 0; i < (int)OldFileCount; i++ ) { FileNameArray[ i + 1 ] = (LPSTR)OldFileInfo[ i ].OldFileName; } Success = pCreatePatchFileExA( OldFileCount, OldFileInfo, NewFileName, PatchFileName, OptionFlags, &OptionData, bNoProgress ? NULL : MyProgressCallback, NULL ); ErrorCode = GetLastError(); printf( "\n\n" ); if ( ! Success ) { CHAR ErrorText[ 16 ]; sprintf( ErrorText, ( ErrorCode < 0x10000000 ) ? "%d" : "%X", ErrorCode ); printf( "Failed to create patch (%s)\n", ErrorText ); exit( 1 ); } NewFileSize = (ULONG) GetFileSizeByName( NewFileName ); PatchFileSize = (ULONG) GetFileSizeByName( PatchFileName ); if (( NewFileSize != 0xFFFFFFFF ) && ( NewFileSize != 0 ) && ( PatchFileSize != 0xFFFFFFFF )) { printf( "%d bytes (%3.1f%% compression, %.1f:1)\n", PatchFileSize, ((((LONG)NewFileSize - (LONG)PatchFileSize ) * 100.0 ) / NewFileSize ), ((double)NewFileSize / PatchFileSize ) ); if ( ! bNoCompare ) { CHAR TempFile1[ MAX_PATH ]; CHAR TempFile2[ MAX_PATH ]; HANDLE hFile; GetTempPath( MAX_PATH, TempFile1 ); GetTempPath( MAX_PATH, TempFile2 ); strcat( TempFile1, "\\tt$$src.$$$" ); strcat( TempFile2, "\\tt$$pat.$$$" ); hFile = CreateFile( TempFile1, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY, NULL ); if ( hFile != INVALID_HANDLE_VALUE ) { CloseHandle( hFile ); Success = pCreatePatchFileA( TempFile1, NewFileName, TempFile2, OptionFlags & ~PATCH_OPTION_FAIL_IF_BIGGER, NULL ); if ( Success ) { ULONG CompFileSize = (ULONG) GetFileSizeByName( TempFile2 ); if (( CompFileSize != 0xFFFFFFFF ) && ( CompFileSize != 0 )) { if ( CompFileSize <= PatchFileSize ) { printf( "\nWARNING: Simply compressing %s would be %d bytes smaller (%3.1f%%)\n", NewFileName, PatchFileSize - CompFileSize, ((((LONG)PatchFileSize - (LONG)CompFileSize ) * 100.0 ) / CompFileSize ) ); } else if ( NewFileSize != 0 ) { printf( "\n%d bytes saved (%3.1f%%) over non-patching compression\n", CompFileSize - PatchFileSize, ((((LONG)CompFileSize - (LONG)PatchFileSize ) * 100.0 ) / NewFileSize ) ); } } } } DeleteFile( TempFile1 ); DeleteFile( TempFile2 ); } } else { printf( "OK\n" ); } exit( 0 ); }