/* Get the relative address of the 32-bit LoadLibraryW function from 64-bit code. This was originally done via executing a helper program (errout-LLW.exe), but I never liked doing that, so now I do it the "hard" way - load the 32-bit kernel32.dll directly and search the exports. */ BOOL get_LLW32( void ) { HMODULE kernel32; TCHAR buf[MAX_PATH]; UINT len; PIMAGE_NT_HEADERS32 pNTHeader; PIMAGE_EXPORT_DIRECTORY pExportDir; PDWORD fun_table, name_table; PWORD ord_table; PDWORD pLLW; len = GetSystemWow64Directory( buf, MAX_PATH ); wcscpy( buf + len, L"\\kernel32.dll" ); // MinGW-w64 had a typo, calling it LINRARY. kernel32 = LoadLibraryEx( buf, NULL, 0x20/*LOAD_LIBRARY_AS_IMAGE_RESOURCE*/ ); if (kernel32 == NULL) return FALSE; // The handle uses low bits as flags, so strip 'em off. pDosHeader = (PIMAGE_DOS_HEADER)((DWORD_PTR)kernel32 & ~0xFFFF); pNTHeader = MakeVA( PIMAGE_NT_HEADERS32, pDosHeader->e_lfanew ); pExportDir = MakeVA( PIMAGE_EXPORT_DIRECTORY, pNTHeader->OptionalHeader. DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]. VirtualAddress ); fun_table = MakeVA( PDWORD, pExportDir->AddressOfFunctions ); name_table = MakeVA( PDWORD, pExportDir->AddressOfNames ); ord_table = MakeVA( PWORD, pExportDir->AddressOfNameOrdinals ); pLLW = bsearch( "LoadLibraryW", name_table, pExportDir->NumberOfNames, sizeof(DWORD), export_cmp ); if (pLLW == NULL) { FreeLibrary( kernel32 ); return FALSE; } LLW32 = fun_table[ord_table[pLLW - name_table]]; FreeLibrary( kernel32 ); return TRUE; }
BOOL HookAPIOneMod( HMODULE hFromModule, // Handle of the module to intercept calls from PHookFn Hooks, // Functions to replace BOOL restore // Restore the original functions ) { PIMAGE_DOS_HEADER pDosHeader; PIMAGE_NT_HEADERS pNTHeader; PIMAGE_IMPORT_DESCRIPTOR pImportDesc; PIMAGE_THUNK_DATA pThunk; PHookFn hook; // Tests to make sure we're looking at a module image (the 'MZ' header) pDosHeader = (PIMAGE_DOS_HEADER)hFromModule; if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) return FALSE; // The MZ header has a pointer to the PE header pNTHeader = MakeVA( PIMAGE_NT_HEADERS, pDosHeader->e_lfanew ); // One more test to make sure we're looking at a "PE" image if (pNTHeader->Signature != IMAGE_NT_SIGNATURE) return FALSE; // We now have a valid pointer to the module's PE header. // Get a pointer to its imports section. pImportDesc = MakeVA( PIMAGE_IMPORT_DESCRIPTOR, pNTHeader->OptionalHeader. DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]. VirtualAddress ); // Bail out if the RVA of the imports section is 0 (it doesn't exist) if (pImportDesc == (PIMAGE_IMPORT_DESCRIPTOR)pDosHeader) return TRUE; // Iterate through the array of imported module descriptors, looking // for the module whose name matches the pszFunctionModule parameter. for (; pImportDesc->Name; pImportDesc++) { BOOL kernel = TRUE; PSTR pszModName = MakeVA( PSTR, pImportDesc->Name ); if (_stricmp( pszModName, APIKernel ) != 0) { PAPI_DATA lib; for (lib = APIs; lib->name; ++lib) { if (_strnicmp( pszModName, lib->name, lib->len ) == 0) { if (lib->base == NULL) { lib->base = GetModuleHandleA( pszModName ); for (hook = Hooks; hook->name; ++hook) if (hook->lib == lib->name) hook->apifunc = GetProcAddress( lib->base, hook->name ); } break; } } if (lib->name == NULL) continue; kernel = FALSE; } // Get a pointer to the found module's import address table (IAT). pThunk = MakeVA( PIMAGE_THUNK_DATA, pImportDesc->FirstThunk ); // Blast through the table of import addresses, looking for the ones // that match the original addresses. while (pThunk->u1.Function) { for (hook = Hooks; hook->name; ++hook) { PROC patch = 0; if (restore) { if ((PROC)pThunk->u1.Function == hook->newfunc) patch = (kernel) ? hook->oldfunc : hook->apifunc; } else if ((PROC)pThunk->u1.Function == hook->oldfunc || (PROC)pThunk->u1.Function == hook->apifunc) { patch = hook->newfunc; } if (patch) { DWORD flOldProtect, flNewProtect, flDummy; MEMORY_BASIC_INFORMATION mbi; // Get the current protection attributes. VirtualQuery( &pThunk->u1.Function, &mbi, sizeof(mbi) ); // Take the access protection flags. flNewProtect = mbi.Protect; // Remove ReadOnly and ExecuteRead flags. flNewProtect &= ~(PAGE_READONLY | PAGE_EXECUTE_READ); // Add on ReadWrite flag flNewProtect |= (PAGE_READWRITE); // Change the access protection on the region of committed pages in the // virtual address space of the current process. VirtualProtect( &pThunk->u1.Function, sizeof(PVOID), flNewProtect, &flOldProtect ); // Overwrite the original address with the address of the new function. if (!WriteProcessMemory( GetCurrentProcess(), &pThunk->u1.Function, &patch, sizeof(patch), NULL )) { return FALSE; } // Put the page attributes back the way they were. VirtualProtect( &pThunk->u1.Function, sizeof(PVOID), flOldProtect, &flDummy ); } } pThunk++; // Advance to next imported function address } } return TRUE; // Function not found }
DWORD GetProcRVA( LPCTSTR module, LPCSTR func ) #endif { HMODULE hMod; TCHAR buf[MAX_PATH]; UINT len; PIMAGE_NT_HEADERS pNTHeader; PIMAGE_EXPORT_DIRECTORY pExportDir; PDWORD fun_table, name_table; PWORD ord_table; PDWORD pFunc; DWORD rva; #ifdef _WIN64 if (bits == 32) len = GetSystemWow64Directory( buf, MAX_PATH ); else #endif len = GetSystemDirectory( buf, MAX_PATH ); buf[len++] = '\\'; wcscpy( buf + len, module ); hMod = LoadLibraryEx( buf, NULL, LOAD_LIBRARY_AS_IMAGE_RESOURCE ); if (hMod == NULL) { #ifdef _WIN64 DEBUGSTR( 1, L"Unable to load %d-bit %s (%lu)!", bits, module, GetLastError() ); #else DEBUGSTR( 1, L"Unable to load %s (%lu)!", module, GetLastError() ); #endif return 0; } // The handle uses low bits as flags, so strip 'em off. pDosHeader = (PIMAGE_DOS_HEADER)((DWORD_PTR)hMod & ~0xFFFF); pNTHeader = MakeVA( PIMAGE_NT_HEADERS, pDosHeader->e_lfanew ); #ifdef _WIN64 if (bits == 32) pExportDir = MakeVA( PIMAGE_EXPORT_DIRECTORY, ((PIMAGE_NT_HEADERS32)pNTHeader)->EXPORTDIR.VirtualAddress ); else #endif pExportDir = MakeVA( PIMAGE_EXPORT_DIRECTORY, pNTHeader->EXPORTDIR.VirtualAddress ); fun_table = MakeVA( PDWORD, pExportDir->AddressOfFunctions ); name_table = MakeVA( PDWORD, pExportDir->AddressOfNames ); ord_table = MakeVA( PWORD, pExportDir->AddressOfNameOrdinals ); pFunc = bsearch( func, name_table, pExportDir->NumberOfNames, sizeof(DWORD), export_cmp ); if (pFunc == NULL) { #ifdef _WIN64 DEBUGSTR( 1, L"Could not find %d-bit %s!", bits, func ); #else DEBUGSTR( 1, L"Could not find %s!", func ); #endif rva = 0; } else { rva = fun_table[ord_table[pFunc - name_table]]; } FreeLibrary( hMod ); return rva; }
static int export_cmp( const void* a, const void* b ) { return strcmp( (LPCSTR)a, MakeVA( LPCSTR, *(const PDWORD)b ) ); }