Loading... ##### IAT hooking 当应用程序使用另一个动态库的函数时,PE装载器会找到IMAGE_IMPORT_BY_NAME结构所指向的输入函数的地址,然后把这些地址存储在一个叫做IAT的表。当函数CALL一个输入函数的时候,会先在IAT找到对应的函数地址,紧接着再进入该函数空间。我们知道IAT是一个IMAGE_THUNK_DATA结构的数组。 ###### 实现流程 * 在dll里构造Detour函数(也就是我们自己的函数) * 获取Target函数地址,并找到Target函数所在的IAT的地址; * 保存原始的IAT地址和IAT地址所存储的内容; * 修改IAT地址中的数据(前提:修改内存属性为可写) * 恢复IAT 如果在IAT表中把某个函数的地址修改为钩子函数的地址,当调用到函数的时候,就会执行到该钩子函数中去。示例代码如下: 钩子函数,这里只是简单的弹出个窗口: ``` int WINAPI MyMessageBoxW( /* __in_opt HWND hWnd, __in_opt LPCWSTR lpText, __in_opt LPCWSTR lpCaption, __in UINT uType*/) { //todo yourself return MessageBox(NULL, "hello world", "caption", MB_OK); } ``` ROOKIT函数,获取目标函数的地址,然后调用IAT HOOK函数ImportAddressTableHook,修改IAT表的值。 ``` BOOL Rookits ( IN HMODULE hModule, IN LPCTSTR pImageName, IN LPCTSTR pTargetFuncName ) { LPDWORD pTargetFuncAddr = NULL; HMODULE hLib = LoadLibrary(pImageName); if (NULL != hLib) { pTargetFuncAddr = (LPDWORD)GetProcAddress(hLib, pTargetFuncName); //获取目标函数的地址 return ImportAddressTableHook(hModule, pImageName, pTargetFuncAddr, MyMessageBoxW); } return FALSE; } ``` 修改IAT表的函数,通过所在模块的基地址hModule找到输入表的数据目录项,获取IAT的首指针,查询目标函数地址所在的位置,然后修改钩子函数的地址。 ``` BOOL ImportAddressTableHook ( IN HMODULE hModule, IN LPCTSTR pImageName, IN LPCVOID pTargetFuncAddr, IN LPCVOID pReplaceFuncAddr ) { IMAGE_DOS_HEADER* pImageDosHearder = (IMAGE_DOS_HEADER*)hModule; IMAGE_OPTIONAL_HEADER* pImageOptionalHeader = (IMAGE_OPTIONAL_HEADER*)((DWORD)hModule+pImageDosHearder->e_lfanew+24); IMAGE_IMPORT_DESCRIPTOR* pImageImportDescriptor = (IMAGE_IMPORT_DESCRIPTOR*) //导入表 ((DWORD)hModule+ pImageOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress); IMAGE_THUNK_DATA* pImageThunkData = NULL; string TargetLibraryName; DWORD Value = 0, OldProtect = 0, NewProtect = 0; LPDWORD FunctionAddress = NULL; MEMORY_BASIC_INFORMATION InforMation; while(pImageImportDescriptor->Characteristics != 0) { TargetLibraryName=(LPCTSTR)((DWORD)hModule+pImageImportDescriptor->Name); //通过导入表,获取模块名 if(TargetLibraryName.compare(pImageName) == 0) //将模块名与目标dll相比较,找到目标模块 { pImageThunkData = (IMAGE_THUNK_DATA*)((DWORD)hModule + pImageImportDescriptor->FirstThunk); //IAT break; } pImageImportDescriptor++; } if (pImageThunkData == NULL) { return FALSE; } while(pImageThunkData->u1.Function) //循环获取IAT表中的内容(导入函数地址) { //循环查找目标函数地址所在的位置 FunctionAddress = (LPDWORD)&(pImageThunkData->u1.Function); if(*FunctionAddress == (DWORD)pTargetFuncAddr) //和目标函数的地址相比较,找到目标函数地址在IAT中的位置 { //找到目标函数的地址,然后修改为钩子函数的地址 VirtualProtect(FunctionAddress, sizeof(DWORD), PAGE_READWRITE, &OldProtect);//修改内存属性 if(!WriteProcessMemory((HANDLE)-1, FunctionAddress, &pReplaceFuncAddr, 4, NULL)) //hook 修改目标函数地址为挂钩函数的地址 { return FALSE; } VirtualProtect(FunctionAddress, sizeof(DWORD), OldProtect, NewProtect ); return TRUE; } pImageThunkData++; } return FALSE; } ``` 也许你会注意到,如果使用了延迟装入数据,那么示例代码是无效的,因为程序会使用__delayLoadHelper2来加载,具体代码可在delayhlp.cpp查看,里边直接GetProcAddress获取该函数地址,对于这种情况只能再HOOK多一个函数,即GetProcAddress。 __delayLoadHelper2代码片段: ``` // Go for the procedure now. // dli.hmodCur = hmod; if (__pfnDliNotifyHook2) { pfnRet = (*__pfnDliNotifyHook2)(dliNotePreGetProcAddress, &dli); } if (pfnRet == 0) { if (pidd->rvaBoundIAT && pidd->dwTimeStamp) { // bound imports exist...check the timestamp from the target image // PIMAGE_NT_HEADERS pinh(PinhFromImageBase(hmod)); if (pinh->Signature == IMAGE_NT_SIGNATURE && TimeStampOfImage(pinh) == idd.dwTimeStamp && FLoadedAtPreferredAddress(pinh, hmod)) { // Everything is good to go, if we have a decent address // in the bound IAT! // pfnRet = FARPROC(UINT_PTR(idd.pBoundIAT[iIAT].u1.Function)); if (pfnRet != 0) { goto SetEntryHookBypass; } } } pfnRet = ::GetProcAddress(hmod, dli.dlp.szProcName); } ``` ##### 总结 * GetProcAddress获取目标函数的地址; * 定位Import,IMAGE_IMPORT_DESCRIPTOR.name获取导入模块名称,和目标模块对比(找到模块模块); * 定位IAT,IMPORT_THUNK_DATA->u1.Function获取目标模块的函数地址,和目标函数地址对比(找到目标函数地址); * VirtualProtect修改内存属性; * WriteProcessMemory改写目标地址为挂钩函数。 参考:https://www.c0ug4r.top/posts/iat_hook/ 最后修改:2021 年 11 月 03 日 10 : 02 AM © 允许规范转载