void GetLinkedModulesInfo(TCHAR *moduleName, CMString &buffer) { HANDLE hDllFile = CreateFile(moduleName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hDllFile == INVALID_HANDLE_VALUE) return; HANDLE hDllMapping = CreateFileMapping(hDllFile, NULL, PAGE_READONLY, 0, 0, NULL); if (hDllMapping == INVALID_HANDLE_VALUE) { CloseHandle(hDllFile); return; } LPVOID dllAddr = MapViewOfFile(hDllMapping, FILE_MAP_READ, 0, 0, 0); static const TCHAR format[] = TEXT(" Plugin statically linked to missing module: %S\r\n"); __try { PIMAGE_NT_HEADERS nthdrs = ImageNtHeader(dllAddr); ULONG tableSize; PIMAGE_IMPORT_DESCRIPTOR importData = (PIMAGE_IMPORT_DESCRIPTOR)ImageDirectoryEntryToData(dllAddr, FALSE, IMAGE_DIRECTORY_ENTRY_IMPORT, &tableSize); if (importData) { while (importData->Name) { char* moduleName = (char*)ImageRvaToVa(nthdrs, dllAddr, importData->Name, NULL); if (!SearchPathA(NULL, moduleName, NULL, NULL, 0, NULL)) buffer.AppendFormat(format, moduleName); importData++; //go to next record } } bool found = false; PIMAGE_EXPORT_DIRECTORY exportData = (PIMAGE_EXPORT_DIRECTORY)ImageDirectoryEntryToData(dllAddr, FALSE, IMAGE_DIRECTORY_ENTRY_EXPORT, &tableSize); if (exportData) { ULONG* funcAddr = (ULONG*)ImageRvaToVa(nthdrs, dllAddr, exportData->AddressOfNames, NULL); for (unsigned i = 0; i < exportData->NumberOfNames && !found; ++i) { char* funcName = (char*)ImageRvaToVa(nthdrs, dllAddr, funcAddr[i], NULL); found = strcmp(funcName, "MirandaPluginInfoEx") == 0 || strcmp(funcName, "MirandaPluginInfo") == 0; if (strcmp(funcName, "DatabasePluginInfo") == 0) { buffer.Append(TEXT(" This dll is a Miranda database plugin, another database is active right now\r\n")); found = true; } } } if (!found) buffer.Append(TEXT(" This dll is not a Miranda plugin and should be removed from plugins directory\r\n")); } __except (EXCEPTION_EXECUTE_HANDLER) {} UnmapViewOfFile(dllAddr); CloseHandle(hDllMapping); CloseHandle(hDllFile); }
void APIHook::ReplaceIATInOneMod(PROC pfnOrig, PROC pfnNew) { HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, GetCurrentProcessId()); MODULEENTRY32 me32 = {sizeof(MODULEENTRY32)}; Module32First(hSnapshot, &me32); HMODULE hThisMod = GetModuleHandleA(m_strCurrentModule.data()); do { if (me32.hModule == hThisMod) { continue; } ULONG ulSize; PIMAGE_IMPORT_DESCRIPTOR pImportDesc = NULL; __try { pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)ImageDirectoryEntryToData(me32.hModule, TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &ulSize); } __except (EXCEPTION_EXECUTE_HANDLER) { } if (pImportDesc == NULL) continue; for (; pImportDesc->Name; pImportDesc++) { PSTR pszName = (PSTR)((PBYTE)me32.hModule + pImportDesc->Name); //cout << pszName << endl; if (lstrcmpiA(m_strModule.data(), pszName) == 0) { PIMAGE_THUNK_DATA pThunk = (PIMAGE_THUNK_DATA)((PBYTE)me32.hModule + pImportDesc->FirstThunk); for (; pThunk->u1.Function; pThunk++) { // Get the address of the function address PROC* ppfn = (PROC*) &pThunk->u1.Function; // Is this the function we're looking for? BOOL bFound = (*ppfn == pfnOrig); if (bFound) { //PROC pfnNew = (PROC)Hook_ExitProcess; if (!WriteProcessMemory(GetCurrentProcess(), ppfn, &pfnNew, sizeof(char*), NULL) && ERROR_NOACCESS == GetLastError()) { DWORD dwOldProtect; if (VirtualProtect(ppfn, sizeof(char*), PAGE_WRITECOPY, &dwOldProtect)) { WriteProcessMemory(GetCurrentProcess(), ppfn, &pfnNew, sizeof(char*), NULL); VirtualProtect(ppfn, sizeof(char*), dwOldProtect, &dwOldProtect); } } CloseHandle(hSnapshot); return ; } } } } } while (Module32Next(hSnapshot, &me32)); CloseHandle(hSnapshot); }
void InitTable() { IMAGE_IMPORT_DESCRIPTOR *pIID; DWORD base = (DWORD)hModule; DWORD size; pIID = (IMAGE_IMPORT_DESCRIPTOR*) ImageDirectoryEntryToData(hModule, TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &size); while(pIID->FirstThunk != 0){ char *name1 = (char*) (base+pIID->Name); static char modname[1024]; strncpy(modname, name1, 1024); for (char *p = modname; *p; p++) *p = tolower(*p); if (strcmp(modname, "common.dll") == 0) { IMAGE_THUNK_DATA *piatthunk = (IMAGE_THUNK_DATA*) ( base + pIID->FirstThunk); IMAGE_THUNK_DATA *pintthunk = (IMAGE_THUNK_DATA*) ( base + *(DWORD*)pIID ); while(piatthunk->u1.Function != 0) { char *name2 = (char*)(((IMAGE_IMPORT_BY_NAME*) (base+pintthunk->u1.AddressOfData))->Name); static char out[2048]; sprintf(out, "%s.%s", modname, name2); IATAddress[out]=&piatthunk->u1.Function; RealAddress[out]=(void *)piatthunk->u1.Function; piatthunk++; pintthunk++; } } pIID++; } }
int InstallAPIHook(const char *modname, const char *fnname, const char *targetmod, int newfn) { int len, i, *blah, lookingfor; unsigned long oldprotect; HANDLE hModule = GetModuleHandle(targetmod); if (!hModule) { DispMsg("Failed to get base of %s!", targetmod); return 0; } blah = (int *)ImageDirectoryEntryToData((void *)hModule, 1, IMAGE_DIRECTORY_ENTRY_IAT, (unsigned long *)&len); lookingfor = (int)GetProcAddress(GetModuleHandle(modname), fnname); for (i = 0; i != len; i++) { if (lookingfor == blah[i]) goto success; } DispMsg("Failed to find %s in IAT of %s!", fnname, modname); return 0; success: VirtualProtect(&blah[i], sizeof(int), PAGE_EXECUTE_READWRITE, &oldprotect); blah[i] = newfn; VirtualProtect(&blah[i], sizeof(int), oldprotect, &oldprotect); return 1; }
void CAPIHook::ReplaceIATEntryInOneMod(PCSTR pszCalleeModName, PROC pfnCurrent, PROC pfnNew, HMODULE hmodCaller) { // Get the address of the module's import section ULONG ulSize; // An exception was triggered by Explorer (when browsing the content of // a folder) into imagehlp.dll. It looks like one module was unloaded... // Maybe some threading problem: the list of modules from Toolhelp might // not be accurate if FreeLibrary is called during the enumeration. PIMAGE_IMPORT_DESCRIPTOR pImportDesc = NULL; __try { pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR) ImageDirectoryEntryToData( hmodCaller, TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &ulSize); } __except (InvalidReadExceptionFilter(GetExceptionInformation())) { // Nothing to do in here, thread continues to run normally // with NULL for pImportDesc } if (pImportDesc == NULL) return; // This module has no import section or is no longer loaded // Find the import descriptor containing references to callee's functions for (; pImportDesc->Name; pImportDesc++) { PSTR pszModName = (PSTR) ((PBYTE) hmodCaller + pImportDesc->Name); if (lstrcmpiA(pszModName, pszCalleeModName) == 0) { // Get caller's import address table (IAT) for the callee's functions PIMAGE_THUNK_DATA pThunk = (PIMAGE_THUNK_DATA) ((PBYTE) hmodCaller + pImportDesc->FirstThunk); // Replace current function address with new function address for (; pThunk->u1.Function; pThunk++) { // Get the address of the function address PROC* ppfn = (PROC*) &pThunk->u1.Function; // Is this the function we're looking for? BOOL bFound = (*ppfn == pfnCurrent); if (bFound) { if (!WriteProcessMemory(GetCurrentProcess(), ppfn, &pfnNew, sizeof(pfnNew), NULL) && (ERROR_NOACCESS == GetLastError())) { DWORD dwOldProtect; if (VirtualProtect(ppfn, sizeof(pfnNew), PAGE_WRITECOPY, &dwOldProtect)) { WriteProcessMemory(GetCurrentProcess(), ppfn, &pfnNew, sizeof(pfnNew), NULL); VirtualProtect(ppfn, sizeof(pfnNew), dwOldProtect, &dwOldProtect); } } return; // We did it, get out } } } // Each import section is parsed until the right entry is found and patched } }
BOOL InstallHook(LPCSTR module, LPCSTR function, void* hook, void** original) { HMODULE process = GetModuleHandle(NULL); // Save original address to function *original = (void*)GetProcAddress( GetModuleHandleA(module), function); ULONG entrySize; IMAGE_IMPORT_DESCRIPTOR* iid = (IMAGE_IMPORT_DESCRIPTOR*)ImageDirectoryEntryToData( process, 1, IMAGE_DIRECTORY_ENTRY_IMPORT, &entrySize); // Search for module while (iid->Name) { const char* name = ((char*)process) + iid->Name; if (_stricmp(name, module) == 0) { return ModifyImportTable(iid, *original, hook); } iid += 1; } return FALSE; }
void CAPIHook::ReplaceEATEntryInOneMod(HMODULE hmod, PCSTR pszFunctionName, PROC pfnNew) { // Get the address of the module's export section ULONG ulSize; PIMAGE_EXPORT_DIRECTORY pExportDir = NULL; __try { pExportDir = (PIMAGE_EXPORT_DIRECTORY) ImageDirectoryEntryToData( hmod, TRUE, IMAGE_DIRECTORY_ENTRY_EXPORT, &ulSize); } __except (InvalidReadExceptionFilter(GetExceptionInformation())) { // Nothing to do in here, thread continues to run normally // with NULL for pExportDir } if (pExportDir == NULL) return; // This module has no export section or is unloaded PDWORD pdwNamesRvas = (PDWORD) ((PBYTE) hmod + pExportDir->AddressOfNames); PWORD pdwNameOrdinals = (PWORD) ((PBYTE) hmod + pExportDir->AddressOfNameOrdinals); PDWORD pdwFunctionAddresses = (PDWORD) ((PBYTE) hmod + pExportDir->AddressOfFunctions); // Walk the array of this module's function names for (DWORD n = 0; n < pExportDir->NumberOfNames; n++) { // Get the function name PSTR pszFuncName = (PSTR) ((PBYTE) hmod + pdwNamesRvas[n]); // If not the specified function, try the next function if (lstrcmpiA(pszFuncName, pszFunctionName) != 0) continue; // We found the specified function // --> Get this function's ordinal value WORD ordinal = pdwNameOrdinals[n]; // Get the address of this function's address PROC* ppfn = (PROC*) &pdwFunctionAddresses[ordinal]; // Turn the new address into an RVA pfnNew = (PROC) ((PBYTE) pfnNew - (PBYTE) hmod); // Replace current function address with new function address if (!WriteProcessMemory(GetCurrentProcess(), ppfn, &pfnNew, sizeof(pfnNew), NULL) && (ERROR_NOACCESS == GetLastError())) { DWORD dwOldProtect; if (VirtualProtect(ppfn, sizeof(pfnNew), PAGE_WRITECOPY, &dwOldProtect)) { WriteProcessMemory(GetCurrentProcess(), ppfn, &pfnNew, sizeof(pfnNew), NULL); VirtualProtect(ppfn, sizeof(pfnNew), dwOldProtect, &dwOldProtect); } } break; // We did it, get out } }
BOOL hook_getprocaddress() { HMODULE vim; PIMAGE_IMPORT_DESCRIPTOR impdesc; PIMAGE_IMPORT_DESCRIPTOR kernel32; PIMAGE_THUNK_DATA lookuptbl; PIMAGE_THUNK_DATA addrtbl; MEMORY_BASIC_INFORMATION bufinfo; BOOL succeeded; DWORD oldprotect; DWORD retsize; int i; vim = GetModuleHandle(NULL); impdesc = (PIMAGE_IMPORT_DESCRIPTOR)ImageDirectoryEntryToData((HMODULE)vim, TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &retsize); if (retsize == 0) return FALSE; OutputDebugString("dynfont: importdir found"); kernel32 = NULL; for (i = 0; impdesc[i].Characteristics != 0; ++i) { LPCSTR libname = (LPCSTR)((char*)vim + impdesc[i].Name); OutputDebugString(libname); if (lstrcmpi(libname, "KERNEL32.DLL") == 0) { kernel32 = &impdesc[i]; break; } } if (kernel32 == NULL) return FALSE; OutputDebugString("dynfont: kernel32 found."); lookuptbl = (PIMAGE_THUNK_DATA)((char*)vim + kernel32->OriginalFirstThunk); addrtbl = (PIMAGE_THUNK_DATA)((char*)vim + kernel32->FirstThunk); for (; lookuptbl->u1.Function != 0; ++lookuptbl, ++addrtbl) { if (!IMAGE_SNAP_BY_ORDINAL(lookuptbl->u1.Ordinal)) { PIMAGE_IMPORT_BY_NAME impfun = (PIMAGE_IMPORT_BY_NAME)((char*)vim + lookuptbl->u1.AddressOfData); if (strcmp((LPCSTR)impfun->Name, "GetProcAddress") == 0) { break; } } } if (lookuptbl->u1.Function == 0) return FALSE; OutputDebugString("dynfont: getprocaddr found."); orig_GetProcAddress = (GETPROCADDRESS_PROTO)addrtbl->u1.Function; retsize = VirtualQuery((LPCVOID)&addrtbl->u1.Function, &bufinfo, sizeof(bufinfo)); if (retsize == 0) return FALSE; succeeded = VirtualProtect(bufinfo.BaseAddress, bufinfo.RegionSize, PAGE_READWRITE, &oldprotect); if (succeeded == FALSE) return FALSE; addrtbl->u1.Function = (DWORD_PTR)oncall_GetProcAddress; VirtualProtect(bufinfo.BaseAddress, bufinfo.RegionSize, bufinfo.Protect, &oldprotect); OutputDebugString("dynfont: hooked."); return TRUE; }
void SetupLibProfiling (LPCSTR ImageName, BOOL fFirstLevelCall) { BOOL fStat; PPROFBLK pProfBlk; ULONG ulImportSize; PIMAGE_IMPORT_DESCRIPTOR pImports; if (fFirstLevelCall) { // Get the GLOBAL semaphore... (valid accross all process contexts) // Prevents anyone else from updating profile block data if (WAIT_FAILED == WaitForSingleObject (hGlobalSem, INFINITE)) { CapDbgPrint ("CAP: CAP_GetLibSyms() - ERROR - " "Wait for GLOBAL semaphore failed - 0x%lx\n", GetLastError()); return; } } // Setup profiling block for the module pProfBlk = SetupProfiling(ImageName); // If successfully loaded if (pProfBlk != NULL && pProfBlk->ImageBase != NULL) { // Locate the import array for this image/dll pImports = (PIMAGE_IMPORT_DESCRIPTOR) ImageDirectoryEntryToData(pProfBlk->ImageBase, TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &ulImportSize); // Recursively call SetupLibProfiling on each imported dll for (;pImports; pImports++) { if (!pImports->Name) break; SetupLibProfiling((PTCHAR)((ULONG)pProfBlk->ImageBase + pImports->Name), FALSE); } } if (fFirstLevelCall) { // Release the GLOBAL semaphore fStat = ReleaseSemaphore (hGlobalSem, 1, NULL); if (!fStat) { CapDbgPrint ("CAP: CAP_GetLibSyms() - " "Error releasing GLOBAL semaphore - 0x%lx\n", GetLastError()); } } return; }
// ひとつのモジュールに対してAPIフックを行う関数 void CAPIHook::ReplaceIATEntryInOneMod( PCSTR pszModuleName, PROC pfnCurrent, PROC pfnNew, HMODULE hmodCaller) { // モジュールのインポートセクションのアドレスを取得 ULONG ulSize; PIMAGE_IMPORT_DESCRIPTOR pImportDesc; pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)ImageDirectoryEntryToData( hmodCaller, TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &ulSize); // インポートセクションを持っていない if (pImportDesc == NULL) return; // インポートディスクリプタを検索 while(pImportDesc->Name) { PSTR pszModName = (PSTR) ((PBYTE) hmodCaller + pImportDesc->Name); if (lstrcmpiA(pszModName, pszModuleName) == 0) break; pImportDesc++; } // このモジュールは呼び出し先から関数をインポートしていない if (pImportDesc->Name == 0) return; // インポートアドレステーブルを取得 PIMAGE_THUNK_DATA pThunk = (PIMAGE_THUNK_DATA) ((PBYTE) hmodCaller + pImportDesc->FirstThunk); // 新しい関数アドレスに置き換える while(pThunk->u1.Function) { // 関数アドレスのアドレスを取得 PROC *ppfn = (PROC*) &pThunk->u1.Function; // 該当関数であるならば発見! BOOL fFound = (*ppfn == pfnCurrent); if (fFound) { // アドレスが一致したので、インポートセクションのアドレスを書き換える DWORD dwDummy; VirtualProtect(ppfn, sizeof(ppfn), PAGE_EXECUTE_READWRITE, &dwDummy); WriteProcessMemory( GetCurrentProcess(), ppfn, &pfnNew, sizeof(pfnNew), NULL); return; } pThunk++; } // ここに処理が移った場合、インポートセクションに該当関数がなかったことになる return; }
void HookOnImport(HMODULE hModule, char *lpszImpModName, DWORD lpOrigFunc, DWORD lpNewFunc) { ULONG ulSize; PIMAGE_IMPORT_DESCRIPTOR pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR) ImageDirectoryEntryToData( hModule, TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &ulSize); if(pImportDesc == NULL) return; for(; pImportDesc->Name; pImportDesc++) { char *pszModName = (char *)((PBYTE)hModule + pImportDesc->Name); if (lstrcmpiA(lpszImpModName, pszModName) == 0) { PIMAGE_THUNK_DATA pThunk = (PIMAGE_THUNK_DATA)((PBYTE)hModule + pImportDesc->FirstThunk); for (; pThunk->u1.Function; pThunk++) { DWORD* ppfn = (DWORD*) &pThunk->u1.Function; if(*ppfn == lpOrigFunc) { DWORD oldProtect; g_mod++; if(!VirtualProtect((LPVOID)ppfn, 4, PAGE_EXECUTE_READWRITE, &oldProtect)) { if(!g_HookError) { TCHAR buf[200]; g_HookError = TRUE; mir_sntprintf(buf, SIZEOF(buf), TranslateT("VirtualProtect failed. Code %d\nTry to call the author"), GetLastError()); prevMessageBox(0, buf, TranslateT("Error"), MB_OK); } } *(DWORD*)ppfn = lpNewFunc; if(*(DWORD*)ppfn != lpNewFunc) { if(!g_HookError2) { g_HookError2 = TRUE; prevMessageBox(0, TranslateT("Hmm. Something goes wrong. I can't write into the memory.\nAnd as you can see, there are no any exception raised...\nTry to call the author"), TranslateT("Error"), MB_OK); } } } } } } }
FARPROC CHookAPI::HookAPI(LPCTSTR pstrDllName,LPCSTR pstrFuncName,FARPROC pfnNewFunc,HMODULE hModCaller) { if(hModCaller==NULL) return NULL; ULONG size; //获取指向PE文件中的Import中IMAGE_DIRECTORY_DESCRIPTOR数组的指针 PIMAGE_IMPORT_DESCRIPTOR pImportDesc=(PIMAGE_IMPORT_DESCRIPTOR) ImageDirectoryEntryToData(hModCaller,TRUE,IMAGE_DIRECTORY_ENTRY_IMPORT,&size); if(pImportDesc==NULL) return NULL; HMODULE hModule=LoadLibrary(pstrDllName); //纪录函数地址 FARPROC pfnOriginFunc=GetProcAddress(hModule,pstrFuncName); //查找记录,看看有没有我们想要的DLL USES_CONVERSION; char* pstrDest=W2A(pstrDllName); for(;pImportDesc->Name;pImportDesc++) { LPSTR pszDllName=(LPSTR)((PBYTE)hModCaller+pImportDesc->Name); if(lstrcmpiA(pszDllName,pstrDest)==0) break; } if(pImportDesc->Name==NULL) { return NULL; } //寻找我们想要的函数 PIMAGE_THUNK_DATA pThunk=(PIMAGE_THUNK_DATA)((PBYTE)hModCaller+pImportDesc->FirstThunk); for(;pThunk->u1.Function;pThunk++) { //ppfn记录了与IAT表相应的地址 PROC*ppfn=(PROC*)&pThunk->u1.Function ; if(*ppfn==pfnOriginFunc) { DWORD dwOldProtect; //修改内存包含属性 VirtualProtect(ppfn, sizeof(DWORD), PAGE_READWRITE, &dwOldProtect); WriteProcessMemory(GetCurrentProcess(),ppfn,&(pfnNewFunc),sizeof(pfnNewFunc),NULL); return pfnOriginFunc; } } return NULL; }
static //std::vector<WineModuleInfo> WineEnumModuleInfos(void) void win32_find_implicit_tls(void) { //std::vector<WineModuleInfo> v_result; HMODULE hMods[1024]; DWORD cbNeeded; HANDLE hProcess = GetCurrentProcess(); if(EnumProcessModules(hProcess, hMods, sizeof(hMods), &cbNeeded)) { for ( unsigned int i = 0; i < (cbNeeded / sizeof(HMODULE)); i++ ) { MODULEINFO mi; GetModuleInformation(hProcess, hMods[i], &mi, sizeof(MODULEINFO)); WineModuleInfo wmi; wmi.BaseAddress = (HMODULE)mi.lpBaseOfDll; //WINE_DBG("wmi.BaseAddress=0x%08x", wmi.BaseAddress); wmi.SizeOfImage = mi.SizeOfImage; wmi.EntryPoint = mi.EntryPoint; wmi.FileFullPath = WineGetModuleFileName(wmi.BaseAddress); WineSplitPath v_split_path(wmi.FileFullPath); wmi.FileLocalName = v_split_path.FName + v_split_path.Ext; ULONG v_size; PIMAGE_TLS_DIRECTORY v_tls_dir = (PIMAGE_TLS_DIRECTORY)ImageDirectoryEntryToData( (PVOID)wmi.BaseAddress, TRUE, IMAGE_DIRECTORY_ENTRY_TLS, &v_size ); if(v_tls_dir) { //WINE_DBG("WineEnumModuleInfos(): AddressOfIndex=0x%08x", v_tls_dir->AddressOfIndex); assert(v_tls_dir->AddressOfIndex); wmi.TlsIndex = (LONG)((DWORD *)v_tls_dir->AddressOfIndex)[0]; } else { wmi.TlsIndex = -1; } v_result.push_back(wmi); } } return v_result; }
// http://ruffnex.oc.to/kenji/text/api_hook/ex2.cpp // ひとつのモジュールに対してAPIフックを行う関数 bool ReplaceIATEntryInOneMod( PCSTR pszModuleName, PROC pfnCurrent, PROC pfnNew, HMODULE hmodCaller) { ULONG ulSize; PIMAGE_IMPORT_DESCRIPTOR pImportDesc; pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)ImageDirectoryEntryToData( hmodCaller, TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &ulSize); if (pImportDesc == NULL) { return false; } while(pImportDesc->Name) { PSTR pszModName = (PSTR) ((PBYTE) hmodCaller + pImportDesc->Name); if (lstrcmpiA(pszModName, pszModuleName) == 0) break; pImportDesc++; } if (pImportDesc->Name == 0) { return false; } PIMAGE_THUNK_DATA pThunk = (PIMAGE_THUNK_DATA) ((PBYTE) hmodCaller + pImportDesc->FirstThunk); while(pThunk->u1.Function) { PROC *ppfn = (PROC*) &pThunk->u1.Function; BOOL fFound = (*ppfn == pfnCurrent); if (fFound) { DWORD dwDummy; VirtualProtect(ppfn, sizeof(ppfn), PAGE_EXECUTE_READWRITE, &dwDummy); WriteProcessMemory( GetCurrentProcess(), ppfn, &pfnNew, sizeof(pfnNew), NULL); return true; } pThunk++; } return false; }
void Replace( HMODULE hModule, // .idata를 가진 모듈의 주소 PCSTR dllname, // Hooking 할 함수를 가진 DLL이름 PROC oldfunc, // 훅킹할 API 함수. PROC newfunc) // 바꿀 함수 { // 1. module 에서 .idata section 의주소를 찾는다. ULONG sz = 0; PIMAGE_IMPORT_DESCRIPTOR pImage = (PIMAGE_IMPORT_DESCRIPTOR) ImageDirectoryEntryToData( hModule, TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, & sz); // 2. .idata section 에서 원하는 Dll의 위치를 찾는다. for ( ; pImage->Name; ++pImage ) { char* p = ((char*)hModule + pImage->Name); if ( strcmpi( p, dllname) == 0 ) break; // 찾은 경우 } //-------------------------------------------------------------- // 3. Thunk Table 에서 원하는 주소를 얻는다. PIMAGE_THUNK_DATA pThunk = (PIMAGE_THUNK_DATA)( (char*)hModule + pImage->FirstThunk); for ( ; pThunk->u1.Function; ++pThunk ) { if ( (PROC)(pThunk->u1.Function) == oldfunc ) // 찾은 경우. { PROC* p = (PROC*)&(pThunk->u1.Function); // .idata section 부분의 보호 속성을 변경한다. DWORD old; VirtualProtect( p, // 이 주소의 부터 4, // 4 Byte 를 PAGE_READWRITE, // 읽고 쓰기로 &old); // 이전 보호속성 리턴. *p = newfunc; // 덮어 쓴다. } } }
int main(int argc, char *argv[]) { util_suppress_errmsg(); if (argc < 2) { fprintf(stderr, "usage: %s dllname\n", argv[0]); exit(1); } const char *dllname = argv[1]; LOADED_IMAGE img; if (MapAndLoad(dllname, NULL, &img, 1, 1) == FALSE) { fprintf(stderr, "cannot load DLL image\n"); exit(2); } IMAGE_EXPORT_DIRECTORY *dir; ULONG dirsize; dir = (IMAGE_EXPORT_DIRECTORY *)ImageDirectoryEntryToData( img.MappedAddress, 0 /* mapped as image */, IMAGE_DIRECTORY_ENTRY_EXPORT, &dirsize); if (dir == NULL) { fprintf(stderr, "cannot read image directory\n"); UnMapAndLoad(&img); exit(3); } DWORD *rva; rva = (DWORD *)ImageRvaToVa(img.FileHeader, img.MappedAddress, dir->AddressOfNames, NULL); for (DWORD i = 0; i < dir->NumberOfNames; i++) { char *name = (char *)ImageRvaToVa(img.FileHeader, img.MappedAddress, rva[i], NULL); printf("%s\n", name); } UnMapAndLoad(&img); return 0; }
VOID Dummy ( VOID ) { PVOID Base; ULONG size; PIMAGE_LOAD_CONFIG_DIRECTORY loadConfig; DWORD old; PIMAGE_NT_HEADERS64 ntHeaders; // // Hardcoded for notepad -- get its base address // Base = GetModuleHandle("notepad.exe"); // // Get the NT headers // ntHeaders = ImageNtHeader(Base); // // Get the load config directory // loadConfig = ImageDirectoryEntryToData(Base, TRUE, IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG, &size); // // Make the CFG pointer writeable // VirtualProtect((PVOID)loadConfig->GuardCFCheckFunctionPointer, sizeof(PVOID), PAGE_READWRITE, &old); // // Take over it // *(PVOID*)loadConfig->GuardCFCheckFunctionPointer = (PVOID)(ULONG_PTR)CfgHook; // // Restore protection // VirtualProtect((PVOID)loadConfig->GuardCFCheckFunctionPointer, sizeof(PVOID), old, &old); }
///////////////////////////////////// HookOneAPI 函数 ///////////////////////////////////////// //进行IAT转换的关键函数,其参数含义: //pszCalleeModuleName:需要hook的模块名 //pfnOriginApiAddress:要替换的自己API函数的地址 //pfnDummyFuncAddress:需要hook的模块名的地址 //hModCallerModule:我们要查找的模块名称,如果没有被赋值, // 将会被赋值为枚举的程序所有调用的模块 void WINAPI HookOneAPI(LPCTSTR pszCalleeModuleName,PROC pfnOriginApiAddress, PROC pfnDummyFuncAddress,HMODULE hModCallerModule) { ULONG size; short sts =0; //获取指向PE文件中的Import中IMAGE_DIRECTORY_DESCRIPTOR数组的指针 PIMAGE_IMPORT_DESCRIPTOR pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR) ImageDirectoryEntryToData(hModCallerModule,TRUE,IMAGE_DIRECTORY_ENTRY_IMPORT,&size); MY_ASSERT (pImportDesc != NULL) ; //查找记录,看看有没有我们想要的DLL for (;pImportDesc->Name;pImportDesc++) { LPSTR pszDllName = (LPSTR)((PBYTE)hModCallerModule+pImportDesc->Name); if (lstrcmpiA(pszDllName,pszCalleeModuleName) == 0) break; } MY_ASSERT (pImportDesc->Name != NULL) ; //寻找我们想要的函数 PIMAGE_THUNK_DATA pThunk = (PIMAGE_THUNK_DATA)((PBYTE)hModCallerModule+pImportDesc->FirstThunk);//IAT BOOL bFind=0; for (;pThunk->u1.Function;pThunk++) { //ppfn记录了与IAT表项相应的函数的地址 PROC * ppfn= (PROC *)&pThunk->u1.Function; if (*ppfn == pfnOriginApiAddress) { //如果地址相同,也就是找到了我们想要的函数,进行改写,将其指向我们所定义的函数 MY_ASSERT(WriteProcessMemory(GetCurrentProcess(),ppfn,&(pfnDummyFuncAddress), sizeof(pfnDummyFuncAddress),NULL)); bFind=1; break; } } MY_ASSERT(bFind); Exit: TRACE("HookOneAPI %s %x->%x %x %d\n",pszCalleeModuleName,pfnOriginApiAddress, pfnDummyFuncAddress,hModCallerModule, sts); }
HRESULT GetExportsFromFile( _In_ HMODULE hmod, _Out_ PEXPORT_DATA* ppExportData) { PBYTE pjImageBase; PIMAGE_EXPORT_DIRECTORY pExportDir; ULONG i, cjExportSize, cFunctions, cjTableSize; PEXPORT_DATA pExportData; PULONG pulAddressTable, pulNameTable; PUSHORT pusOrdinalTable; pjImageBase = (PBYTE)hmod; /* Get the export directory */ pExportDir = ImageDirectoryEntryToData(pjImageBase, TRUE, IMAGE_DIRECTORY_ENTRY_EXPORT, &cjExportSize); if (pExportDir == NULL) { fprintf(stderr, "Failed to get export directory\n"); return E_FAIL; } cFunctions = pExportDir->NumberOfFunctions; cjTableSize = FIELD_OFFSET(EXPORT_DATA, aExports[cFunctions]); pExportData = malloc(cjTableSize); if (pExportData == NULL) { error("Failed to allocate %u bytes of memory for export table\n", cjTableSize); return E_OUTOFMEMORY; } RtlZeroMemory(pExportData, cjTableSize); pulAddressTable = (PULONG)(pjImageBase + pExportDir->AddressOfFunctions); pExportData->cNumberOfExports = cFunctions; /* Loop through the function table */ for (i = 0; i < cFunctions; i++) { PVOID pvFunction = (pjImageBase + pulAddressTable[i]); /* Check if this is a forwarder */ if ((ULONG_PTR)((PUCHAR)pvFunction - (PUCHAR)pExportDir) < cjExportSize) { pExportData->aExports[i].pszForwarder = _strdup(pvFunction); } else { pExportData->aExports[i].ulRva = pulAddressTable[i]; } } pulNameTable = (PULONG)(pjImageBase + pExportDir->AddressOfNames); pusOrdinalTable = (PUSHORT)(pjImageBase + pExportDir->AddressOfNameOrdinals); /* Loop through the name table */ for (i = 0; i < pExportDir->NumberOfNames; i++) { ULONG iIndex = pusOrdinalTable[i]; PSTR pszName = (PSTR)(pjImageBase + pulNameTable[i]); pExportData->aExports[iIndex].pszName = _strdup(pszName); } *ppExportData = pExportData; return S_OK; }
//--------------------------------------------------------------------------- //ReplaceIATEntryInModule void WINAPI TAPIHook::ReplaceIATEntryInModule(PCSTR DllName, PROC pfnCurrent,PROC pfnNew,HMODULE hModCaller) { // 查询模块函数引入表的地址 DWORD ulSize; PIMAGE_IMPORT_DESCRIPTOR pImportDesc; pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)ImageDirectoryEntryToData(hModCaller, true, IMAGE_DIRECTORY_ENTRY_IMPORT, &ulSize); if(pImportDesc == NULL) return ; // 模块没有函数引入表 // 查询模块是否引入了某个DLL的函数 for(;pImportDesc->Name; pImportDesc++) { PSTR pszModName = (PSTR) ((PBYTE)hModCaller + pImportDesc->Name); if(lstrcmpiA(pszModName, DllName) == 0) break; // Found } if(pImportDesc->Name == 0) return ; // 这个模块没有引入指定DLL的函数 //查询模块是否在DLL中引入了指定的函数 PIMAGE_THUNK_DATA pThunk = (PIMAGE_THUNK_DATA)((PBYTE)hModCaller + pImportDesc->FirstThunk); for(; pThunk->u1.Function; pThunk++) { // 得到这个模块引入的每一个函数的地址 PROC* ppfn = (PROC*) &pThunk->u1.Function; // 比较这个地址是否是我们准备Hook的函数地址 bool IsFound = (*ppfn == pfnCurrent); if(!IsFound && (*ppfn > pvMaxAppAddr)) { // If this is not the function and the address is in a shared DLL, // then maybe we're running under a debugger on Windows 98. In this // case, this address points to an instruction that may have the // correct address. PBYTE pbInFunc = (PBYTE) *ppfn; if(pbInFunc[0] == PushOpCode) { // We see the PUSH instruction, the real function address follows ppfn = (PROC*) &pbInFunc[1]; // Is this the function we're looking for? IsFound = (*ppfn == pfnCurrent); } } //找到我们要Hook的函数,替换它的地址为我们自定义的函数的地址 if(IsFound) { DWORD dwOldProtect; //必须修改相应的页面属性,否则有可能无法写入代码 VirtualProtect(ppfn,sizeof(pfnNew),PAGE_READWRITE,&dwOldProtect); WriteProcessMemory(GetCurrentProcess(), ppfn, &pfnNew,sizeof(pfnNew), NULL); VirtualProtect(ppfn,sizeof(pfnNew),dwOldProtect,0); return ; // We did it, get out } } }
static void process_exports(void *dcontext, char *dllname, LOADED_IMAGE *img) { IMAGE_EXPORT_DIRECTORY *dir; IMAGE_SECTION_HEADER *sec; DWORD *name, *code; WORD *ordinal; const char *string; ULONG size; uint i; byte *addr, *start_exports, *end_exports; verbose_print("Processing exports of \"%s\"\n", dllname); dir = (IMAGE_EXPORT_DIRECTORY *) ImageDirectoryEntryToData(img->MappedAddress, FALSE, IMAGE_DIRECTORY_ENTRY_EXPORT, &size); verbose_print("mapped at "PFX" (preferred "PFX"), exports 0x%08x, size 0x%x\n", img->MappedAddress, get_preferred_base(img), dir, size); start_exports = (byte *) dir; end_exports = start_exports + size; verbose_print("name=%s, ord base=0x%08x, names=%d 0x%08x\n", (char *) ImageRvaToVa(img->FileHeader, img->MappedAddress, dir->Name, NULL), dir->Base, dir->NumberOfNames, dir->AddressOfNames); /* don't limit functions to lie in .text -- * for ntdll, some exported routines have their code after .text, inside * ECODE section! */ sec = img->Sections; for (i = 0; i < img->NumberOfSections; i++) { verbose_print("Section %d %s: 0x%x + 0x%x == 0x%08x through 0x%08x\n", i, sec->Name, sec->VirtualAddress, sec->SizeOfRawData, ImageRvaToVa(img->FileHeader, img->MappedAddress, sec->VirtualAddress, NULL), (ptr_uint_t) ImageRvaToVa(img->FileHeader, img->MappedAddress, sec->VirtualAddress, NULL) + sec->SizeOfRawData); sec++; } name = (DWORD *) ImageRvaToVa(img->FileHeader, img->MappedAddress, dir->AddressOfNames, NULL); code = (DWORD *) ImageRvaToVa(img->FileHeader, img->MappedAddress, dir->AddressOfFunctions, NULL); ordinal = (WORD *) ImageRvaToVa(img->FileHeader, img->MappedAddress, dir->AddressOfNameOrdinals, NULL); verbose_print("names: from 0x%08x to 0x%08x\n", ImageRvaToVa(img->FileHeader, img->MappedAddress, name[0], NULL), ImageRvaToVa(img->FileHeader, img->MappedAddress, name[dir->NumberOfNames-1], NULL)); for (i = 0; i < dir->NumberOfNames; i++) { string = (char *) ImageRvaToVa(img->FileHeader, img->MappedAddress, name[i], NULL); /* ordinal is biased (dir->Base), but don't add base when using as index */ assert(dir->NumberOfFunctions > ordinal[i]); /* I don't understand why have to do RVA to VA here, when dumpbin /exports * seems to give the same offsets but by simply adding them to base we * get the appropriate code location -- but that doesn't work here... */ addr = ImageRvaToVa(img->FileHeader, img->MappedAddress, code[ordinal[i]], NULL); verbose_print("name=%s 0x%08x, ord=%d, code=0x%x -> 0x%08x\n", string, string, ordinal[i], code[ordinal[i]], addr); if (list_exports) { print("ord %3d offs 0x%08x %s\n", ordinal[i], addr - img->MappedAddress, string); } if (list_Ki && string[0] == 'K' && string[1] == 'i') { print("\n==================================================\n"); print("%s\n\n", string); check_Ki(string); print("\ndisassembly:\n"); decode_function(dcontext, addr); print( "==================================================\n"); } /* forwarded export points inside exports section */ if (addr >= start_exports && addr < end_exports) { if (list_forwards || verbose) { /* I've had issues w/ forwards before, so avoid printing crap */ if (addr[0] > 0 && addr[0] < 127) print("%s is forwarded to %.128s\n", string, addr); else print("ERROR identifying forwarded entry for %s\n", string); } } else if (list_syscalls) { process_syscall_wrapper(dcontext, addr, string, "export", img); } } }
PROC HookManager::HookInOneModule(HMODULE hModule, LPCSTR baseLibraryName, PROC oldFunctionPointer, PROC newFunctionPointer) { char text[1000]; ULONG size; PROC result = NULL; // If the function is found, returns the old address //Modules that are global hooked will not need to be hooked again if (hModule==NULL) return NULL; // Retrieving the position of the first Import Descriptor Table // This table holds the information about the imports from one // single dll. It holds the address to dll's name, to dll's IAT // and some other data. PIMAGE_IMPORT_DESCRIPTOR pImportDesc; pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR) ImageDirectoryEntryToData( hModule, TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &size ); // No Import Descriptor Tables found or some error occured if (pImportDesc == NULL) { return NULL; } // For each Import Descriptor Table (for each DLL) check for the // existance of wanted DLL while (pImportDesc -> Name) { // We add the base address of the module whose IAT we are going to read and the // RVA offset to the DLL (module's) name. We cast this to pointer to string. PSTR IDTModName = (PSTR)((PBYTE)hModule + pImportDesc -> Name); // We compare the library names. If the comparrison returns zero, we have found // out that this module imports something from the library which holds a function // that we want to control. That means we have to check all the functions and see // if our function is one of them. if (_stricmp(baseLibraryName, IDTModName) == 0) { break; } //If the compared names were not the same, go to the next Import Descriptor Table. pImportDesc++; } // If we went through all the descriptor tables and haven't found wanted dll, we are // done with this module. if (pImportDesc -> Name == NULL) { return NULL; } // If we found a module we want to hook, we need to search through all of the functions // for the one we want. // First we add the RVA to the first IAT entry (first address of the imported function) to // the base of the module. PIMAGE_THUNK_DATA pThunk = (PIMAGE_THUNK_DATA)((PBYTE)hModule + pImportDesc -> FirstThunk); // For each function in the IAT we check if it's the wanted one while (pThunk -> u1.Function) { // Retrieve the pointer to pointer to function. pfn will point inside the IAT where // the pointer to function is stored. PROC *ppfn = (PROC *) &pThunk -> u1.Function; // If the pointer to current function equals our wanted function, we found a hit sprintf_s(text, "%p <--> %p", *ppfn, oldFunctionPointer); if (*ppfn == oldFunctionPointer) { // First we store the page information of the IAT. Note the pfn holds // the pointer to IAT entry, and not directy to function MEMORY_BASIC_INFORMATION info; if (VirtualQuery(ppfn, &info, sizeof(MEMORY_BASIC_INFORMATION)) == 0) { return NULL; } // This function attempts to change the protection scheme of a region of pages. // It also copies the old protection scheme to the last parameter (in this case // it is info.Protect). if (VirtualProtect(info.BaseAddress, info.RegionSize, PAGE_EXECUTE_READWRITE, &info.Protect) == FALSE) { return NULL; } // We store the old pointer in the IAT memory and change it to point to our function. result = *ppfn; *ppfn = *newFunctionPointer; sprintf_s(text, "%p <--> %p", result); // Lastly we attempt to restore the previous protection. "dummyProtect is used // to store the PAGE_READWRITE protection, although we don't need it. NULL can't // be used because in that case the function fails. DWORD dummyProtect; if (VirtualProtect(info.BaseAddress, info.RegionSize, info.Protect, &dummyProtect) == FALSE) { /* Actually this should not be vital, so could be turned off in case of trouble. But if the first VirtualProtect succeeded, this one should, too. */ return NULL; } return result; } pThunk++; } return NULL; }
BOOL CHookedFunctions::GetFunctionNameFromExportSection( HMODULE hmodOriginal, DWORD dwFuncOrdinalNum, PSTR pszFuncName ) { BOOL bResult = FALSE; // Make sure we return a valid string (atleast an empty one) strcpy(pszFuncName, "\0"); __try { ULONG ulSize; // Get the address of the module's export section PIMAGE_EXPORT_DIRECTORY pExportDir = (PIMAGE_EXPORT_DIRECTORY)ImageDirectoryEntryToData( hmodOriginal, TRUE, IMAGE_DIRECTORY_ENTRY_EXPORT, &ulSize ); // Does this module has export section ? if (pExportDir == NULL) __leave; // Get the name of the DLL PSTR pszDllName = reinterpret_cast<PSTR>( pExportDir->Name + (DWORD)hmodOriginal); // Get the starting ordinal value. By default is 1, but // is not required to be so DWORD dwFuncNumber = pExportDir->Base; // The number of entries in the EAT DWORD dwNumberOfExported = pExportDir->NumberOfFunctions; // Get the address of the ENT PDWORD pdwFunctions = (PDWORD)( pExportDir->AddressOfFunctions + (DWORD)hmodOriginal); // Get the export ordinal table PWORD pwOrdinals = (PWORD)(pExportDir->AddressOfNameOrdinals + (DWORD)hmodOriginal); // Get the address of the array with all names DWORD *pszFuncNames = (DWORD *)(pExportDir->AddressOfNames + (DWORD)hmodOriginal); PSTR pszExpFunName; // Walk through all of the entries and try to locate the // one we are looking for for (long i = 0; i < dwNumberOfExported; i++, pdwFunctions++) { DWORD entryPointRVA = *pdwFunctions; if ( entryPointRVA == 0 ) // Skip over gaps in exported function continue; // ordinals (the entrypoint is 0 for // these functions). // See if this function has an associated name exported for it. for ( unsigned j=0; j < pExportDir->NumberOfNames; j++ ) { // Note that pwOrdinals[x] return values starting form 0.. (not from 1) if ( pwOrdinals[j] == i ) { pszExpFunName = (PSTR)(pszFuncNames[j] + (DWORD)hmodOriginal); // Is this the same ordinal value ? // Notice that we need to add 1 to pwOrdinals[j] to get actual // number if (dwFuncOrdinalNum == pwOrdinals[j] + 1) { if ((pszExpFunName != NULL) && (strlen(pszExpFunName) > 0)) strcpy(pszFuncName, pszExpFunName); __leave; } } } } // for } __finally { // do nothing } // This function is not in the caller's import section return bResult; }
BOOL CHookedFunction::ReplaceInOneModule( PCSTR pszCalleeModName, PROC pfnCurrent, PROC pfnNew, HMODULE hmodCaller ) { BOOL bResult = FALSE; __try { ULONG ulSize; // Get the address of the module's import section PIMAGE_IMPORT_DESCRIPTOR pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)ImageDirectoryEntryToData( hmodCaller, TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &ulSize ); // Does this module has import section ? if (pImportDesc == NULL) __leave; // Loop through all descriptors and // find the import descriptor containing references to callee's functions while (pImportDesc->Name) { PSTR pszModName = (PSTR)((PBYTE) hmodCaller + pImportDesc->Name); if (stricmp(pszModName, pszCalleeModName) == 0) break; // Found pImportDesc++; } // while // Does this module import any functions from this callee ? if (pImportDesc->Name == 0) __leave; // Get caller's IAT PIMAGE_THUNK_DATA pThunk = (PIMAGE_THUNK_DATA)( (PBYTE) hmodCaller + pImportDesc->FirstThunk ); // Replace current function address with new one while (pThunk->u1.Function) { // Get the address of the function address PROC* ppfn = (PROC*) &pThunk->u1.Function; // Is this the function we're looking for? BOOL bFound = (*ppfn == pfnCurrent); // Is this Windows 9x if (!bFound && (*ppfn > sm_pvMaxAppAddr)) { PBYTE pbInFunc = (PBYTE) *ppfn; // Is this a wrapper (debug thunk) represented by PUSH instruction? if (pbInFunc[0] == cPushOpCode) { ppfn = (PROC*) &pbInFunc[1]; // Is this the function we're looking for? bFound = (*ppfn == pfnCurrent); } // if } // if if (bFound) { MEMORY_BASIC_INFORMATION mbi; ::VirtualQuery(ppfn, &mbi, sizeof(MEMORY_BASIC_INFORMATION)); // In order to provide writable access to this part of the // memory we need to change the memory protection if (FALSE == ::VirtualProtect( mbi.BaseAddress, mbi.RegionSize, PAGE_READWRITE, &mbi.Protect) ) __leave; // Hook the function. *ppfn = *pfnNew; bResult = TRUE; // Restore the protection back DWORD dwOldProtect; ::VirtualProtect( mbi.BaseAddress, mbi.RegionSize, mbi.Protect, &dwOldProtect ); break; } // if pThunk++; } // while } __finally { // do nothing } // This function is not in the caller's import section return bResult; }
int wmain() { HMODULE Module; HANDLE File = { 0 }; BYTE* MappedFile = { 0 }; BYTE* MappedFile_End = { 0 }; HANDLE FileMapping = { 0 }; ULARGE_INTEGER FileSize; DWORD Error = { 0 }; IMAGE_NT_HEADERS* NtHeaders = { 0 }; IMAGE_DOS_HEADER* DosHeader = { 0 }; size_t OffsetToPE = { 0 }; IMAGE_RESOURCE_DIRECTORY* TopDirectory = { 0 }; ULONG ResourcesSize = { 0 }; Module = LoadLibraryW(L".\\notepad.exe"); if (!EnumResourceTypesW(Module, EnumTypesProc, 0)) { DWORD Error = GetLastError(); wprintf(L"EnumResourceTypes failed %x\n", Error); } FreeLibrary(Module); File = CreateFileW(L".\\notepad.exe", GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL); if (INVALID_HANDLE_VALUE == File) { Error = GetLastError(); wprintf(L"CreateFile fails with error: %u", Error); goto Exit; } FileSize.LowPart = GetFileSize(File, &FileSize.HighPart); if ((FileSize.LowPart == -1) && ((Error = GetLastError()) != 0)) { wprintf(L"GetFileSize failed with error %u\n", Error); goto Exit; } if (FileSize.HighPart != 0) { wprintf(L"file too large\n"); goto Exit; } if (FileSize.QuadPart <= (sizeof(IMAGE_DOS_HEADER) + sizeof(IMAGE_NT_HEADERS))) { wprintf(L"file too small\n"); goto Exit; } FileMapping = CreateFileMappingW(File, NULL, PAGE_READONLY, 0, 0, NULL); if (FileMapping == NULL) { Error = GetLastError(); wprintf(L"CreateFileMapping fails with error: %u", Error); goto Exit; } MappedFile = (BYTE*) MapViewOfFile(FileMapping, FILE_MAP_READ, 0, 0, 0); if (MappedFile == NULL) goto Exit; wprintf(L"MappedFile:%p\n", MappedFile); MappedFile_End = (MappedFile + FileSize.QuadPart); DosHeader = (IMAGE_DOS_HEADER*) MappedFile; if (DosHeader->e_magic != IMAGE_DOS_SIGNATURE) { wprintf(L"invalid file"); goto Exit; } OffsetToPE = DosHeader->e_lfanew; if (OffsetToPE >= FileSize.QuadPart) { wprintf(L"invalid file"); goto Exit; } if ((OffsetToPE + sizeof(IMAGE_NT_HEADERS)) >= FileSize.QuadPart) { wprintf(L"invalid file"); goto Exit; } NtHeaders = (IMAGE_NT_HEADERS*) (MappedFile + OffsetToPE); if (NtHeaders->Signature != IMAGE_NT_SIGNATURE) { wprintf(L"invalid file"); goto Exit; } if (NtHeaders->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC) { wprintf(L"invalid file"); goto Exit; } if (NtHeaders->OptionalHeader.NumberOfRvaAndSizes < IMAGE_DIRECTORY_ENTRY_RESOURCE) { wprintf(L"no resources"); goto Exit; } if (NtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress == 0) { wprintf(L"no resources"); goto Exit; } if (NtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].Size == 0) { wprintf(L"no resources"); goto Exit; } TopDirectory = (IMAGE_RESOURCE_DIRECTORY*) ImageDirectoryEntryToData(MappedFile, FALSE, IMAGE_DIRECTORY_ENTRY_RESOURCE, &ResourcesSize); if (TopDirectory == NULL) { wprintf(L"no resources"); goto Exit; } DumpResourceDirectory(MappedFile, 0, TopDirectory, TopDirectory); Exit: if (MappedFile != NULL) UnmapViewOfFile(MappedFile); if (FileMapping != NULL) CloseHandle(FileMapping); if ((File != NULL) && (File != INVALID_HANDLE_VALUE)) CloseHandle(File); return 0; }
static PROC SetProcAddressA( __in HINSTANCE targetModule, __in LPCSTR lpLibFileName, __in LPCSTR lpProcName, __in PROC newFunction ) { PROC pfnHookAPIAddr = GetProcAddress( LoadLibraryA( lpLibFileName ), lpProcName ); HINSTANCE hInstance = targetModule; ULONG ulSize; PIMAGE_IMPORT_DESCRIPTOR pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)ImageDirectoryEntryToData( hInstance, TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &ulSize ); while (pImportDesc->Name) { PSTR pszModName = (PSTR)((PBYTE) hInstance + pImportDesc->Name); if (_stricmp(pszModName, lpLibFileName) == 0) break; pImportDesc++; } PIMAGE_THUNK_DATA pThunk = (PIMAGE_THUNK_DATA)((PBYTE) hInstance + pImportDesc->FirstThunk); while (pThunk->u1.Function) { PROC* ppfn = (PROC*) &pThunk->u1.Function; BOOL bFound = (*ppfn == pfnHookAPIAddr); if (bFound) { MEMORY_BASIC_INFORMATION mbi; VirtualQuery( ppfn, &mbi, sizeof(MEMORY_BASIC_INFORMATION) ); VirtualProtect( mbi.BaseAddress, mbi.RegionSize, PAGE_READWRITE, &mbi.Protect); *ppfn = *newFunction; DWORD dwOldProtect; VirtualProtect( mbi.BaseAddress, mbi.RegionSize, mbi.Protect, &dwOldProtect ); break; } pThunk++; } return pfnHookAPIAddr; }
HMODULE CRemoteLoader::LoadLibraryFromMemory( PVOID BaseAddress, DWORD SizeOfModule, BOOL PEHeader, PCHAR OptionalPath ) { DebugShout( "[LoadLibraryFromMemory] BaseAddress (0x%X) - SizeOfModule (0x%X)", BaseAddress, SizeOfModule ); IMAGE_NT_HEADERS* ImageNtHeaders = ToNts( BaseAddress ); if( ImageNtHeaders == NULL ) { DebugShout( "[LoadLibraryFromMemory] Invalid Image: No IMAGE_NT_HEADERS" ); return NULL; } DebugShout( "[LoadLibraryFromMemory] SizeOfImage (0x%X)", ImageNtHeaders->OptionalHeader.SizeOfImage ); if( ImageNtHeaders->FileHeader.NumberOfSections == 0 ) { DebugShout( "[LoadLibraryFromMemory] Invalid Image: No Sections" ); return NULL; } if( ( ImageNtHeaders->OptionalHeader.ImageBase % 4096 ) != 0 ) { DebugShout( "[LoadLibraryFromMemory] Invalid Image: Not Page Aligned" ); return NULL; } if( ImageNtHeaders->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR ].Size ) { if( ImageDirectoryEntryToData( BaseAddress, IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR ) ) { DebugShout( "[LoadLibraryFromMemory] This method is not supported for Managed executables!" ); return NULL; } } DebugShout( "[LoadLibraryFromMemory] No COM/CLR data found!" ); // SizeOfImage NOT the same as module size M**********R // http://www.youtube.com/watch?v=pele5vptVgc PVOID AllocatedRemoteMemory = RemoteAllocateMemory( ImageNtHeaders->OptionalHeader.SizeOfImage ); if( AllocatedRemoteMemory == NULL ) { DebugShout( "[LoadLibraryFromMemory] Failed to allocate remote memory for module!" ); return NULL; } DebugShout( "[LoadLibraryFromMemory] Allocated remote module at [0x%X]!", AllocatedRemoteMemory ); if( ProcessImportTable( BaseAddress, AllocatedRemoteMemory, OptionalPath ) == FALSE ) { DebugShout( "[LoadLibraryFromMemory] Failed to fix imports!" ); return NULL; } DebugShout( "[LoadLibraryFromMemory] Fixed Imports!" ); if( ProcessRelocations( BaseAddress, AllocatedRemoteMemory ) == FALSE ) { DebugShout( "[LoadLibraryFromMemory] Failed to process relocations!" ); RemoteFreeMemory( AllocatedRemoteMemory, SizeOfModule ); return NULL; } DebugShout( "[LoadLibraryFromMemory] Fixed Relocations!" ); if( ProcessSections( BaseAddress, AllocatedRemoteMemory, PEHeader ) == FALSE ) { DebugShout( "[LoadLibraryFromMemory] Failed to process sections!" ); } DebugShout( "[LoadLibraryFromMemory] Processed sections!" ); if( ProcessTlsEntries( BaseAddress, AllocatedRemoteMemory ) == FALSE ) { DebugShout( "[LoadModuleFromMemory] ProcessTlsEntries Failed!" ); // we can also choose to continue here.. return NULL; } DebugShout( "[LoadModuleFromMemory] Processed Tls Entries!" ); if( ImageNtHeaders->OptionalHeader.AddressOfEntryPoint ) { FARPROC DllEntryPoint = MakePtr( FARPROC, AllocatedRemoteMemory, ImageNtHeaders->OptionalHeader.AddressOfEntryPoint ); DebugShout( "[LoadModuleFromMemory] DllEntrypoint = 0x%X", DllEntryPoint ); if( CallEntryPoint( AllocatedRemoteMemory, DllEntryPoint ) == false ) { DebugShout( "[LoadModuleFromMemory] Failed to execute remote thread buffer" ); } else { DebugShout( "[LoadModuleFromMemory] Executed the remote thread buffer successfully [0x%X]", DllEntryPoint ); } } else { DebugShout( "[LoadModuleFromMemory] AddressOfEntryPoint is NULL" ); } DebugShout( "[LoadModuleFromMemory] Returning Pointer (0x%X)", AllocatedRemoteMemory ); return ( HMODULE ) AllocatedRemoteMemory; }
BOOL EditSymbols( LPSTR pImageName, LPSTR pDbgFileName ) { PIMAGE_NT_HEADERS NtHeaders; HANDLE FileHandle, SymbolFileHandle; HANDLE hMappedFile; LPVOID ImageBase; PIMAGE_DEBUG_DIRECTORY DebugDirectories; PIMAGE_DEBUG_DIRECTORY DebugDirectoriesSave; DWORD DebugDirectorySize, NumberOfDebugDirectories; DWORD SavedErrorCode; PIMAGE_DEBUG_DIRECTORY DebugDirectory; DWORD i; DWORD NewFileSize, HeaderSum, CheckSum; DWORD ImageNameOffset, DebugDataSize; LPBYTE DebugData; IMAGE_SEPARATE_DEBUG_HEADER DbgFileHeader; ImageBase = NULL; hMappedFile = 0; FileHandle = SymbolFileHandle = 0; DebugDirectoriesSave = NULL; // // open and map the file. // FileHandle = CreateFile( pImageName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL ); if (FileHandle == INVALID_HANDLE_VALUE) { return FALSE; } hMappedFile = CreateFileMapping( FileHandle, NULL, PAGE_READWRITE, 0, 0, NULL ); if (!hMappedFile) { CloseHandle( FileHandle ); return FALSE; } ImageBase = MapViewOfFile( hMappedFile, FILE_MAP_WRITE, 0, 0, 0 ); if (!ImageBase) { CloseHandle( hMappedFile ); CloseHandle( FileHandle ); return FALSE; } // // Everything is mapped. Now check the image and find nt image headers // NtHeaders = ImageNtHeader( ImageBase ); if (NtHeaders == NULL) { SetLastError( ERROR_BAD_EXE_FORMAT ); goto nosyms; } if ((NtHeaders->OptionalHeader.MajorLinkerVersion < 3) && (NtHeaders->OptionalHeader.MinorLinkerVersion < 5) ) { SetLastError( ERROR_BAD_EXE_FORMAT ); goto nosyms; } if (!(NtHeaders->FileHeader.Characteristics & IMAGE_FILE_DEBUG_STRIPPED)) { SetLastError( ERROR_ALREADY_ASSIGNED ); goto nosyms; } DebugDirectories = (PIMAGE_DEBUG_DIRECTORY) ImageDirectoryEntryToData( ImageBase, FALSE, IMAGE_DIRECTORY_ENTRY_DEBUG, &DebugDirectorySize ); if (!DebugDirectoryIsUseful(DebugDirectories, DebugDirectorySize)) { SetLastError( ERROR_BAD_EXE_FORMAT ); goto nosyms; } NumberOfDebugDirectories = DebugDirectorySize / sizeof( IMAGE_DEBUG_DIRECTORY ); SymbolFileHandle = CreateFile( pDbgFileName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL ); if (SymbolFileHandle == INVALID_HANDLE_VALUE) goto nosyms; if (!ReadFile( SymbolFileHandle, &DbgFileHeader, sizeof(DbgFileHeader), &DebugDataSize, NULL) || DebugDataSize != sizeof(DbgFileHeader)) { SetLastError( ERROR_BAD_EXE_FORMAT ); goto nosyms; } if (DbgFileHeader.Signature != IMAGE_SEPARATE_DEBUG_SIGNATURE || (DbgFileHeader.Flags & ~IMAGE_SEPARATE_DEBUG_FLAGS_MASK) != 0 || DbgFileHeader.Machine != NtHeaders->FileHeader.Machine || DbgFileHeader.Characteristics != NtHeaders->FileHeader.Characteristics || DbgFileHeader.TimeDateStamp != NtHeaders->FileHeader.TimeDateStamp || DbgFileHeader.CheckSum != NtHeaders->OptionalHeader.CheckSum || DbgFileHeader.ImageBase != NtHeaders->OptionalHeader.ImageBase || DbgFileHeader.SizeOfImage != NtHeaders->OptionalHeader.SizeOfImage) { SetLastError( ERROR_BAD_EXE_FORMAT ); goto nosyms; } if (DbgFileHeader.Flags & IMAGE_SEPARATE_DEBUG_MISMATCH) { fprintf(stderr, "Warning: %s updated unsafely; symbols may be wrong\n", pDbgFileName); } // check if this is the right dbg file // save the DebugDirectory and get ready to write the // debug data to the image file. DebugDirectoriesSave = (PIMAGE_DEBUG_DIRECTORY) malloc( DebugDirectorySize ); if (DebugDirectoriesSave == NULL) goto nosyms; RtlMoveMemory( DebugDirectoriesSave, DebugDirectories, DebugDirectorySize); DebugDirectory = DebugDirectoriesSave; NewFileSize = SetFilePointer( FileHandle, 0, NULL, FILE_END ); NewFileSize = (NewFileSize + 3) & ~3; for (i=0; i<NumberOfDebugDirectories; i++) { // Is it one of the debug sections we need to special case? if (DebugDirectory->Type == IMAGE_DEBUG_TYPE_MISC) { // fix the mage name ImageNameOffset = (DWORD) ((PCHAR) ImageBase + DebugDirectory->PointerToRawData + FIELD_OFFSET( IMAGE_DEBUG_MISC, Data )); RtlCopyMemory((LPVOID)ImageNameOffset, FilePart, strlen(FilePart) + 1); } else if (DebugDirectory->Type != IMAGE_DEBUG_TYPE_FPO) { DebugData = (LPBYTE) malloc( DebugDirectory->SizeOfData ); if (SetFilePointer( SymbolFileHandle, DebugDirectory->PointerToRawData, NULL, FILE_BEGIN ) != DebugDirectory->PointerToRawData) { SetLastError( ERROR_BAD_EXE_FORMAT ); goto nosyms; } if (ReadFile( SymbolFileHandle, DebugData, DebugDirectory->SizeOfData, &DebugDataSize, NULL) && DebugDataSize == DebugDirectory->SizeOfData) { if (WriteFile( FileHandle, DebugData, DebugDirectory->SizeOfData, &DebugDataSize, NULL) && DebugDataSize == DebugDirectory->SizeOfData) { DebugDirectory->PointerToRawData = NewFileSize; NewFileSize += DebugDataSize; NewFileSize = (NewFileSize + 3) & ~3; } else { SetLastError( ERROR_WRITE_FAULT ); free( DebugData ); goto nosyms; } } else { SetLastError( ERROR_BAD_EXE_FORMAT ); free( DebugData ); goto nosyms; } free( DebugData ); } DebugDirectory += 1; } // somehow I needed to close the file and re-open it again. // otherwise it would AV inside CheckSumMappedFile. UnmapViewOfFile( ImageBase ); CloseHandle( hMappedFile ); ImageBase = NULL; hMappedFile = 0; SetFilePointer( FileHandle, NewFileSize, NULL, FILE_BEGIN ); SetEndOfFile( FileHandle ); CloseHandle( FileHandle ); // // re-open and map the file. // FileHandle = CreateFile( pImageName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL ); hMappedFile = CreateFileMapping( FileHandle, NULL, PAGE_READWRITE, 0, 0, NULL ); if (!hMappedFile) { goto nosyms; } ImageBase = MapViewOfFile( hMappedFile, FILE_MAP_WRITE, 0, 0, 0 ); if (!ImageBase) { goto nosyms; } NtHeaders = ImageNtHeader( ImageBase ); if (NtHeaders == NULL) { SetLastError( ERROR_BAD_EXE_FORMAT ); goto nosyms; } DebugDirectories = (PIMAGE_DEBUG_DIRECTORY) ImageDirectoryEntryToData( ImageBase, FALSE, IMAGE_DIRECTORY_ENTRY_DEBUG, &DebugDirectorySize ); if (DebugDirectories == NULL || DebugDirectorySize == 0) { SetLastError( ERROR_BAD_EXE_FORMAT ); goto nosyms; } RtlMoveMemory( DebugDirectories, DebugDirectoriesSave, DebugDirectorySize); free( DebugDirectoriesSave ); NtHeaders->FileHeader.Characteristics &= ~IMAGE_FILE_DEBUG_STRIPPED; CheckSumMappedFile( ImageBase, NewFileSize, &HeaderSum, &CheckSum ); NtHeaders->OptionalHeader.CheckSum = CheckSum; CloseHandle( SymbolFileHandle ); UnmapViewOfFile( ImageBase ); CloseHandle( hMappedFile ); CloseHandle( FileHandle ); return TRUE; nosyms: SavedErrorCode = GetLastError(); if (DebugDirectoriesSave) free( DebugDirectoriesSave ); if (SymbolFileHandle && SymbolFileHandle != INVALID_HANDLE_VALUE) { CloseHandle( SymbolFileHandle ); } if (ImageBase) UnmapViewOfFile( ImageBase ); if (hMappedFile) CloseHandle( hMappedFile ); if (FileHandle && FileHandle != INVALID_HANDLE_VALUE) { CloseHandle( FileHandle ); } SetLastError( SavedErrorCode ); return FALSE; }
PROC HookManager::HookExportTable(LPCSTR lpDllName, PROC oldFunctionPointer, PROC newFunctionPointer) { ULONG size; PROC result = NULL; // If the function is found, returns the old address HMODULE hModule=LoadLibraryA(lpDllName); // Retrieving the position of the first Export Table // This table holds the information about the imports from one // single dll. It holds the address to dll's name, to dll's EAT // and some other data. PIMAGE_EXPORT_DIRECTORY pExportDesc; pExportDesc = (PIMAGE_EXPORT_DIRECTORY) ImageDirectoryEntryToData( hModule, TRUE, IMAGE_DIRECTORY_ENTRY_EXPORT , &size ); // No Import Descriptor Tables found or some error occured if (pExportDesc == NULL) { return NULL; } // EAT Redirection - The EAT, or Export Address Table is similar to the // IAT. Except in the opposite direction. When a module exports a function // so that it can be used by other modules, it stores the address of // that function in it's EAT. EAT redirection overwrites that address // with the offset of your hook. EAT redirection will not effect any // currently loaded modules. It will only effect modules loaded after the // redirection has been made. It will also effect subsequent calls to // GetProcAddress(), as they will return the address of your hook instead of // the real function. ULONG *addressoffunctions=(ULONG*)((BYTE*) hModule +pExportDesc->AddressOfFunctions); ULONG * addressofnames=(ULONG*)((BYTE*) hModule +pExportDesc->AddressOfNames); // For each function in the EAT we check if it's the wanted one for(DWORD x=0; x < pExportDesc->NumberOfFunctions;x++) { char*functionname=(char*)((BYTE*) hModule +addressofnames[x]); DWORD functionaddress=(DWORD)((BYTE*)hModule +addressoffunctions[x]); PROC *pfunc=(PROC*)&functionaddress; if (*pfunc==oldFunctionPointer) { PROC* ppfn = (PROC *) &addressoffunctions[x]; MEMORY_BASIC_INFORMATION info; if (VirtualQuery(ppfn, &info, sizeof(MEMORY_BASIC_INFORMATION)) == 0) { return NULL; } // This function attempts to change the protection scheme of a region of pages. // It also copies the old protection scheme to the last parameter (in this case // it is info.Protect). if (VirtualProtect(info.BaseAddress, info.RegionSize, PAGE_EXECUTE_READWRITE, &info.Protect) == FALSE) { return NULL; } // We store the old pointer in the IAT memory and change it to point to our function. result = *ppfn; *ppfn = GetAddressOfInjectedFunction(PROC,newFunctionPointer,hModule); // Lastly we attempt to restore the previous protection. "dummyProtect is used // to store the PAGE_READWRITE protection, although we don't need it. NULL can't // be used because in that case the function fails. DWORD dummyProtect; if (VirtualProtect(info.BaseAddress, info.RegionSize, info.Protect, &dummyProtect) == FALSE) { /* Actually this should not be vital, so could be turned off in case of trouble. But if the first VirtualProtect succeeded, this one should, too. */ return NULL; } return result; } } }