// findpatch - Determines if the specified module has been patched to use the // specified replacement. // // - importmodule (IN): Handle (base address) of the module to be searched to // see if it imports the specified replacement export. // // - exportmodulename (IN): ANSI string containing the name of the module that // normally exports that import that would have been patched to use the // replacement export. // // - replacement (IN): Address of the replacement, or destination, function or // variable to search for. // // Return Value: // // Returns TRUE if the module has been patched to use the specified // replacement export. // BOOL findpatch (HMODULE importmodule, moduleentry_t *module) { IMAGE_IMPORT_DESCRIPTOR *idte; IMAGE_SECTION_HEADER *section; ULONG size; // Locate the importing module's Import Directory Table (IDT) entry for the // exporting module. The importing module actually can have several IATs -- // one for each export module that it imports something from. The IDT entry // gives us the offset of the IAT for the module we are interested in. EnterCriticalSection(&imagelock); idte = (IMAGE_IMPORT_DESCRIPTOR*)ImageDirectoryEntryToDataEx((PVOID)importmodule, TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &size, §ion); LeaveCriticalSection(&imagelock); if (idte == NULL) { // This module has no IDT (i.e. it imports nothing). return FALSE; } while (idte->OriginalFirstThunk != 0x0) { if (_stricmp((PCHAR)R2VA(importmodule, idte->Name), module->exportmodulename) == 0) { // Found the IDT entry for the exporting module. break; } idte++; } if (idte->OriginalFirstThunk == 0x0) { // The importing module does not import anything from the exporting // module. return FALSE; } int i = 0; patchentry_t *entry = module->patchtable; while(entry->importname) { LPCVOID replacement = entry->replacement; IMAGE_THUNK_DATA *iate; // Locate the replacement's IAT entry. iate = (IMAGE_THUNK_DATA*)R2VA(importmodule, idte->FirstThunk); while (iate->u1.Function != 0x0) { if (iate->u1.Function == (DWORD_PTR)replacement) { // Found the IAT entry for the replacement. This patch has been // installed. return TRUE; } iate++; } entry++; i++; } // The module does not import the replacement. The patch has not been // installed. return FALSE; }
// restoreimport - Restores the IAT entry for an import previously patched via // a call to "patchimport" to the original address of the import. // // - importmodule (IN): Handle (base address) of the target module for which // calls or references to the import should be restored. // // - exportmodule (IN): Handle (base address) of the module that exports the // function or variable to be patched. // // - exportmodulename (IN): ANSI string containing the name of the module that // exports the function or variable to be patched. // // - importname (IN): ANSI string containing the name of the imported function // or variable to be restored. May be an integer cast to a string if the // import is exported by ordinal. // // - replacement (IN): Address of the function or variable which the import was // previously patched through to via a call to "patchimport". // // Return Value: // // None. // VOID restoreimport (HMODULE importmodule, HMODULE exportmodule, LPCSTR exportmodulename, LPCSTR importname, LPCVOID replacement) { IMAGE_THUNK_DATA *iate; IMAGE_IMPORT_DESCRIPTOR *idte; FARPROC import; DWORD protect; IMAGE_SECTION_HEADER *section; ULONG size; // Locate the importing module's Import Directory Table (IDT) entry for the // exporting module. The importing module actually can have several IATs -- // one for each export module that it imports something from. The IDT entry // gives us the offset of the IAT for the module we are interested in. EnterCriticalSection(&imagelock); idte = (IMAGE_IMPORT_DESCRIPTOR*)ImageDirectoryEntryToDataEx((PVOID)importmodule, TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &size, §ion); LeaveCriticalSection(&imagelock); if (idte == NULL) { // This module has no IDT (i.e. it imports nothing). return; } while (idte->OriginalFirstThunk != 0x0) { if (_stricmp((PCHAR)R2VA(importmodule, idte->Name), exportmodulename) == 0) { // Found the IDT entry for the exporting module. break; } idte++; } if (idte->OriginalFirstThunk == 0x0) { // The importing module does not import anything from the exporting // module. return; } // Get the *real* address of the import. import = GetProcAddress(exportmodule, importname); assert(import != NULL); // Perhaps the named export module does not actually export the named import? // Locate the import's original IAT entry (it currently has the replacement // address in it). iate = (IMAGE_THUNK_DATA*)R2VA(importmodule, idte->FirstThunk); while (iate->u1.Function != 0x0) { if (iate->u1.Function == (DWORD_PTR)replacement) { // Found the IAT entry. Overwrite the address stored in the IAT // entry with the import's real address. Note that the IAT entry may // be write-protected, so we must first ensure that it is writable. VirtualProtect(&iate->u1.Function, sizeof(iate->u1.Function), PAGE_READWRITE, &protect); iate->u1.Function = (DWORD_PTR)import; VirtualProtect(&iate->u1.Function, sizeof(iate->u1.Function), protect, &protect); break; } iate++; } }
// findimport - Determines if the specified module imports the named import // from the named exporting module. // // - importmodule (IN): Handle (base address) of the module to be searched to // see if it imports the specified import. // // - exportmodule (IN): Handle (base address) of the module that exports the // import to be searched for. // // - exportmodulename (IN): ANSI string containing the name of the module that // exports the import to be searched for. // // - importname (IN): ANSI string containing the name of the import to search // for. May be an integer cast to a string if the import is exported by // ordinal. // // Return Value: // // Returns TRUE if the module imports to the specified import. Otherwise // returns FALSE. // BOOL findimport (HMODULE importmodule, HMODULE exportmodule, LPCSTR exportmodulename, LPCSTR importname) { IMAGE_THUNK_DATA *iate; IMAGE_IMPORT_DESCRIPTOR *idte; FARPROC import; IMAGE_SECTION_HEADER *section; ULONG size; // Locate the importing module's Import Directory Table (IDT) entry for the // exporting module. The importing module actually can have several IATs -- // one for each export module that it imports something from. The IDT entry // gives us the offset of the IAT for the module we are interested in. EnterCriticalSection(&imagelock); idte = (IMAGE_IMPORT_DESCRIPTOR*)ImageDirectoryEntryToDataEx((PVOID)importmodule, TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &size, §ion); LeaveCriticalSection(&imagelock); if (idte == NULL) { // This module has no IDT (i.e. it imports nothing). return FALSE; } while (idte->OriginalFirstThunk != 0x0) { if (_stricmp((PCHAR)R2VA(importmodule, idte->Name), exportmodulename) == 0) { // Found the IDT entry for the exporting module. break; } idte++; } if (idte->OriginalFirstThunk == 0x0) { // The importing module does not import anything from the exporting // module. return FALSE; } // Get the *real* address of the import. If we find this address in the IAT, // then we've found that the module does import the named import. import = GetProcAddress(exportmodule, importname); assert(import != NULL); // Perhaps the named export module does not actually export the named import? // Locate the import's IAT entry. iate = (IMAGE_THUNK_DATA*)R2VA(importmodule, idte->FirstThunk); while (iate->u1.Function != 0x0) { if (iate->u1.Function == (DWORD_PTR)import) { // Found the IAT entry. The module imports the named import. return TRUE; } iate++; } // The module does not import the named import. return FALSE; }
// patchimport - Patches all future calls to an imported function, or references // to an imported variable, through to a replacement function or variable. // Patching is done by replacing the import's address in the specified target // module's Import Address Table (IAT) with the address of the replacement // function or variable. // // - importmodule (IN): Handle (base address) of the target module for which // calls or references to the import should be patched. // // - exportmodule (IN): Handle (base address) of the module that exports the // the function or variable to be patched. // // - exportmodulename (IN): ANSI string containing the name of the module that // exports the function or variable to be patched. // // - importname (IN): ANSI string containing the name of the imported function // or variable to be patched. May be an integer cast to a string if the // import is exported by ordinal. // // - replacement (IN): Address of the function or variable to which future // calls or references should be patched through to. This function or // variable can be thought of as effectively replacing the original import // from the point of view of the module specified by "importmodule". // // Return Value: // // Returns TRUE if the patch was installed into the import module. If the // import module does not import the specified export, so nothing changed, // then FALSE will be returned. // BOOL patchimport (HMODULE importmodule, moduleentry_t *module) { DWORD result = 0; HMODULE exportmodule = (HMODULE)module->modulebase; LPCSTR exportmodulename = module->exportmodulename; IMAGE_IMPORT_DESCRIPTOR *idte; IMAGE_SECTION_HEADER *section; ULONG size; // Locate the importing module's Import Directory Table (IDT) entry for the // exporting module. The importing module actually can have several IATs -- // one for each export module that it imports something from. The IDT entry // gives us the offset of the IAT for the module we are interested in. EnterCriticalSection(&imagelock); idte = (IMAGE_IMPORT_DESCRIPTOR*)ImageDirectoryEntryToDataEx((PVOID)importmodule, TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &size, §ion); LeaveCriticalSection(&imagelock); if (idte == NULL) { // This module has no IDT (i.e. it imports nothing). return FALSE; } while (idte->FirstThunk != 0x0) { if (_stricmp((PCHAR)R2VA(importmodule, idte->Name), exportmodulename) == 0) { // Found the IDT entry for the exporting module. break; } idte++; } if (idte->FirstThunk == 0x0) { // The importing module does not import anything from the exporting // module. return FALSE; } patchentry_t *entry = module->patchtable; int i = 0; while(entry->importname) { LPCSTR importname = entry->importname; LPCVOID replacement = entry->replacement; IMAGE_THUNK_DATA *iate; DWORD protect; FARPROC import = NULL; FARPROC import2 = NULL; // Get the *real* address of the import. If we find this address in the IAT, // then we've found the entry that needs to be patched. import2 = VisualLeakDetector::_RGetProcAddress(exportmodule, importname); import = GetProcAddress(exportmodule, importname); if ( import2 ) import = import2; assert(import != NULL); // Perhaps the named export module does not actually export the named import? // Locate the import's IAT entry. iate = (IMAGE_THUNK_DATA*)R2VA(importmodule, idte->FirstThunk); while (iate->u1.Function != 0x0) { if (iate->u1.Function == (DWORD_PTR)import) { // Found the IAT entry. Overwrite the address stored in the IAT // entry with the address of the replacement. Note that the IAT // entry may be write-protected, so we must first ensure that it is // writable. if ( import != replacement ) { VirtualProtect(&iate->u1.Function, sizeof(iate->u1.Function), PAGE_READWRITE, &protect); iate->u1.Function = (DWORD_PTR)replacement; VirtualProtect(&iate->u1.Function, sizeof(iate->u1.Function), protect, &protect); } // The patch has been installed in the import module. result++; } iate++; } entry++; i++; } // The import's IAT entry was not found. The importing module does not // import the specified import. return result > 0; }