这两天研究下IAT表的hook,看来很多帖子,也试过很多代码,但是都会遇到一些问题。下面我结合被人的东西还有一些自己修改,写了一个简单的hook IAT表。首先自己写一个IAThook,要先对PE有一定的了解,知道IAT做什么的,IAT在PE文件中的位置,当你有了这些知识之后就可以对IAT做hook了。这里我就不对IAT表做什么的和PE文件做介绍了。下面介绍一下hook原理,当你要hook一个函数的时候首先应该是替换这个函数的地址,之后再你的函数中再去调用这个函数。这样你就可以hook这个函数。其实对IAT表的hook也是一样的,IAT是存动态库的导入函数地址,我们只要把IAT表的函数地址改成我们自己的函数名就可以实现对IAT表的hook,听起来是不是很简单。下面我通过代码分析来讲解如何对IAT表的hook。
DWORD g_oldaddr = (DWORD)GetProcAddress((HMODULE)LoadLibraryA("User32.dll"), "MessageBoxA");
这行代码是获取MessageboxA函数的地址,也是我们要替换的函数地址。
int WINAPI MyMessageboxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType) { typedef int (WINAPI *_MessageBoxA)( _In_opt_ HWND hWnd, _In_opt_ LPCSTR lpText, _In_opt_ LPCSTR lpCaption, _In_ UINT uType ); OutputDebugStringA(lpText); int ret = ((_MessageBoxA)g_oldaddr)(hWnd, lpText, lpCaption, uType); return ret; }
这个是我用用来替换的函数,也就是所谓的hook函数,函数实现就是调用下以前的函数地址,让程序继续进行。
bool CInjection::SetIATHook(DWORD oldaddr, DWORD newaddr) { bool Is64 = false; bool bflag = false; DWORD dwImageBase = 0; PDWORD pFuncAddr = NULL; PIMAGE_NT_HEADERS pNtHeader = NULL; PIMAGE_IMPORT_DESCRIPTOR pImportDesciptor = NULL; PIMAGE_NT_HEADERS64 pNtHeader64; dwImageBase = (DWORD)GetModuleHandle(NULL); pNtHeader = (PIMAGE_NT_HEADERS)(dwImageBase + ((PIMAGE_DOS_HEADER)dwImageBase)->e_lfanew);//获取Nt头 if (pNtHeader->FileHeader.Machine == IMAGE_FILE_MACHINE_AMD64) { Is64 = true; pNtHeader64 = (PIMAGE_NT_HEADERS64)(dwImageBase + ((PIMAGE_DOS_HEADER)dwImageBase)->e_lfanew); pImportDesciptor = (PIMAGE_IMPORT_DESCRIPTOR)(dwImageBase + pNtHeader64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress); } else if (pNtHeader->FileHeader.Machine == IMAGE_FILE_MACHINE_I386) //32bit { pImportDesciptor = (PIMAGE_IMPORT_DESCRIPTOR)(dwImageBase + pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);//获取导入表位置 } while (pImportDesciptor->FirstThunk != 0 && bflag == false) { pFuncAddr = (PDWORD)(dwImageBase + pImportDesciptor->FirstThunk);//获取函数地址 while (*pFuncAddr) { if (oldaddr == *pFuncAddr) { DWORD ierror; MEMORY_BASIC_INFORMATION mbi; VirtualQueryEx(GetCurrentProcess(), pFuncAddr, &mbi, sizeof(MEMORY_BASIC_INFORMATION));//查询当前内存块的保护属性 VirtualProtectEx(GetCurrentProcess(), pFuncAddr, 4, PAGE_EXECUTE_READWRITE, &mbi.Protect);//修改当前内存块保护属性为可读可写 //VirtualQueryEx(GetCurrentProcess(), pFuncAddr, &mbi, sizeof(MEMORY_BASIC_INFORMATION));//查询下Protect 是否还是保护 if (!WriteProcessMemory(GetCurrentProcess(), pFuncAddr, &newaddr, sizeof(pFuncAddr), NULL))//修改IAT表函数地址 { ierror = GetLastError(); } DWORD oldProtect; VirtualProtectEx(GetCurrentProcess(), pFuncAddr, 4, mbi.Protect, &oldProtect);//写完后修改回可读保护属性 bflag = true; break; } pFuncAddr++; } pImportDesciptor = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)pImportDesciptor + sizeof(IMAGE_IMPORT_DESCRIPTOR));//获取下一个IAT表 } return true; }这个函数就是具体实现的函数了,首先获取进程的基地址也就是我们常说的imagebase。之后获取NT头,和PE可选头的地址,这些需要你对PE文件有一定了解,才能理解为什么这么写。之后循环遍历IAT表的函数地址。如果发现和MessageA的函数地址一样说明我们已经找到了MessageA在IAT表中的位置。我们只需要替换
*pFuncAddr = newaddr;发现报错 这是因为内存访问没有权限,这块内存只有读取的权限
所以需要VirtualProtectEx修改内存的权限改为读写权。之后我们通过WriteProcessMemory函数把新的函数地址写入到IAT表中
当我们运行MessageBosA函数时,你会发现先进入我们的MyMessageboxA函数中,是不是很容易理解。