// 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++; } }
// search in IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT for the special module BOOL ReplaceIATEntryInDelayImageImportTable(HANDLE hBaseAddress, LPCSTR lpszDllName, LPVOID pfnCurrent, LPVOID pfnNew) { DWORD dwSize = 0; PIMAGE_SECTION_HEADER pFoundHeader = NULL; PImgDelayDescr pImgDelayDescr = (PImgDelayDescr)ImageDirectoryEntryToDataEx( hBaseAddress , TRUE , IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT , &dwSize , &pFoundHeader ); if( pImgDelayDescr == NULL ){ return FALSE; } while (pImgDelayDescr->rvaDLLName) { try { if ( lstrcmpiA((CHAR*)((PBYTE)hBaseAddress+pImgDelayDescr->rvaDLLName), lpszDllName) == 0 ) { break; } } catch(...) { } ++pImgDelayDescr; } // Not found in IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT if( !pImgDelayDescr->rvaDLLName ) return FALSE; // retrieve IAT PIMAGE_THUNK_DATA pThunk = NULL; if( (pImgDelayDescr->grAttrs & dlattrRva) == 0 ) return FALSE; pThunk = (PIMAGE_THUNK_DATA)(((LPBYTE)hBaseAddress) + pImgDelayDescr->rvaIAT); // enumerate functions in the IAT while(pThunk->u1.Function) { PDWORD lpAddr = (PDWORD)&(pThunk->u1.Function); if(*lpAddr == (DWORD)pfnCurrent) { // modify the address ::WriteProcessMemory(::GetCurrentProcess(), lpAddr, &pfnNew, sizeof(DWORD), NULL); return TRUE; } pThunk++; } return FALSE; }
// 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; }
// search in IMAGE_IMPORT_DESCRIPTOR for the special module BOOL ReplaceIATEntryInImageImportTable(HANDLE hBaseAddress, LPCSTR lpszDllName, LPVOID pfnCurrent, LPVOID pfnNew) { try { DWORD dwSize = 0; PIMAGE_SECTION_HEADER pFoundHeader = NULL; PIMAGE_IMPORT_DESCRIPTOR pImgImportDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)ImageDirectoryEntryToDataEx( hBaseAddress , TRUE , IMAGE_DIRECTORY_ENTRY_IMPORT , &dwSize , &pFoundHeader ); if( pImgImportDescriptor == NULL ){ return FALSE; } while (pImgImportDescriptor->Name) { if ( lstrcmpiA((CHAR*)((PBYTE)hBaseAddress+pImgImportDescriptor->Name), lpszDllName) == 0 ) { break; // found } ++pImgImportDescriptor; } if( !pImgImportDescriptor->Name ) return ReplaceIATEntryInDelayImageImportTable( hBaseAddress, lpszDllName, pfnCurrent, pfnNew); // retrieve IAT PIMAGE_THUNK_DATA pThunk = (PIMAGE_THUNK_DATA)(((LPBYTE)hBaseAddress) + pImgImportDescriptor->FirstThunk); // enumerate functions in the IAT while(pThunk->u1.Function) { PDWORD lpAddr = (PDWORD)&(pThunk->u1.Function); if(*lpAddr == (DWORD)pfnCurrent) { // modify the address if(::WriteProcessMemory(::GetCurrentProcess(), lpAddr, &pfnNew, sizeof(DWORD), NULL)) return TRUE; else return FALSE; } pThunk++; } } catch(...) { } return FALSE; }
void *PeGetProcAddressA(void *Base, LPCSTR Name) { DWORD Tmp; IMAGE_NT_HEADERS *NT=ImageNtHeader(Base); IMAGE_EXPORT_DIRECTORY *Exp=(IMAGE_EXPORT_DIRECTORY*)ImageDirectoryEntryToDataEx(Base,TRUE,IMAGE_DIRECTORY_ENTRY_EXPORT,&Tmp,0); if(Exp==0 || Exp->NumberOfFunctions==0) { SetLastError(ERROR_NOT_FOUND); return 0; } DWORD *Names=(DWORD*)(Exp->AddressOfNames+(DWORD_PTR)Base); WORD *Ordinals=(WORD*)(Exp->AddressOfNameOrdinals+(DWORD_PTR)Base); DWORD *Functions=(DWORD*)(Exp->AddressOfFunctions+(DWORD_PTR)Base); FARPROC Ret=0; if((DWORD_PTR)Name<65536) { if((DWORD_PTR)Name-Exp->Base<Exp->NumberOfFunctions) Ret=(FARPROC)(Functions[(DWORD_PTR)Name-Exp->Base]+(DWORD_PTR)Base); } else { for(DWORD i=0; i<Exp->NumberOfNames && Ret==0; i++) { char *Func=(char*)(Names[i]+(DWORD_PTR)Base); if(Func && strcmp(Func,Name)==0) Ret=(FARPROC)(Functions[Ordinals[i]]+(DWORD_PTR)Base); } } if(Ret) { DWORD ExpStart=NT->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress+(DWORD)Base; DWORD ExpSize=NT->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size; if((DWORD)Ret>=ExpStart && (DWORD)Ret<=ExpStart+ExpSize) { return 0; } return Ret; } return 0; }
/*********************************************************************** * ImageDirectoryEntryToData (DBGHELP.@) * * NOTES * See ImageDirectoryEntryToDataEx */ PVOID WINAPI ImageDirectoryEntryToData( PVOID base, BOOLEAN image, USHORT dir, PULONG size ) { return ImageDirectoryEntryToDataEx( base, image, dir, size, NULL ); }
int main(int argc, char** argv) { if (argc != 2) { fprintf(stderr, "usage: fileid <file>\n"); return 1; } HANDLE file = CreateFileA(argv[1], GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); if (file == INVALID_HANDLE_VALUE) { fprintf(stderr, "Couldn't open file: %s\n", argv[1]); return 1; } HANDLE mapFile = CreateFileMappingA(file, NULL, PAGE_READONLY, 0, 0, 0); if (mapFile == nullptr) { fprintf(stderr, "Couldn't create file mapping\n"); CloseHandle(file); return 1; } uint8_t* base = reinterpret_cast<uint8_t*>(MapViewOfFile(mapFile, FILE_MAP_READ, 0, 0, 0)); if (base == nullptr) { fprintf(stderr, "Couldn't map file\n"); CloseHandle(mapFile); CloseHandle(file); return 1; } DWORD size; PIMAGE_DEBUG_DIRECTORY debug_dir = reinterpret_cast<PIMAGE_DEBUG_DIRECTORY>( ImageDirectoryEntryToDataEx(base, FALSE, IMAGE_DIRECTORY_ENTRY_DEBUG, &size, nullptr)); bool found = false; if (debug_dir->Type == IMAGE_DEBUG_TYPE_CODEVIEW) { CV_INFO_PDB70* cv = reinterpret_cast<CV_INFO_PDB70*>(base + debug_dir->PointerToRawData); if (cv->CvSignature == CV_SIGNATURE_RSDS) { found = true; print_guid(cv->Signature, cv->Age); } } UnmapViewOfFile(base); CloseHandle(mapFile); CloseHandle(file); return found ? 0 : 1; }
bool PLH::IATHook::FindIATFunc(const char* LibraryName,const char* FuncName, PIMAGE_THUNK_DATA* pFuncThunkOut,const char* Module) { bool UseModuleName = true; if (Module == NULL || Module[0] == '\0') UseModuleName = false; HINSTANCE hInst = GetModuleHandleA(UseModuleName ? Module:NULL); if (!hInst) { PostError(RuntimeError(RuntimeError::Severity::UnRecoverable, "PolyHook IATHook:Failed to find Module")); return false; } ULONG Sz; PIMAGE_IMPORT_DESCRIPTOR pImports = (PIMAGE_IMPORT_DESCRIPTOR) ImageDirectoryEntryToDataEx(hInst, TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &Sz, nullptr); for (int i = 0; pImports[i].Characteristics != 0; i++) { char* _ModuleName = (char*)ResolveRVA(hInst, pImports[i].Name); if (_stricmp(_ModuleName, LibraryName) != 0) continue; //Original holds the API Names PIMAGE_THUNK_DATA pOriginalThunk = (PIMAGE_THUNK_DATA) ResolveRVA(hInst, pImports->OriginalFirstThunk); //FirstThunk is overwritten by loader with API addresses, we change this PIMAGE_THUNK_DATA pThunk = (PIMAGE_THUNK_DATA) ResolveRVA(hInst, pImports->FirstThunk); if (!pOriginalThunk) { PostError(RuntimeError(RuntimeError::Severity::Critical, "PolyHook IATHook:PE Files without OriginalFirstThunk are unsupported")); return false; } //Table is null terminated, increment both tables for (; pOriginalThunk->u1.Function != NULL; pOriginalThunk++,pThunk++) { if (IMAGE_SNAP_BY_ORDINAL(pOriginalThunk->u1.Ordinal)) { XTrace("Import By Ordinal:[Ordinal:%d]\n",IMAGE_ORDINAL(pOriginalThunk->u1.Ordinal)); continue; } PIMAGE_IMPORT_BY_NAME pImport = (PIMAGE_IMPORT_BY_NAME) ResolveRVA(hInst, pOriginalThunk->u1.AddressOfData); XTrace("Import By Name: [Ordinal:%d] [Name:%s]\n", IMAGE_ORDINAL(pOriginalThunk->u1.Ordinal),pImport->Name); //Check the name of API given by OriginalFirthThunk if (_stricmp(FuncName, pImport->Name) != 0) continue; /*Name matched in OriginalFirstThunk, return FirstThunk so we can changed it's address*/ *pFuncThunkOut = pThunk; return true; } } PostError(RuntimeError(RuntimeError::Severity::UnRecoverable, "PolyHook IATHook:Failed to find 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; }