HOOK IAT 代码示例
// include #include "stdio.h" #include "wchar.h" #include "windows.h" // typedef typedef BOOL(WINAPI* PFSETWINDOWTEXTW)(HWND hWnd, LPWSTR lpString); // globals FARPROC g_pOrgFunc = NULL; // BOOL WINAPI MySetWindowTextW(HWND hWnd, LPWSTR lpString) { wchar_t* pNum = (wchar_t*)L"零一二三四五六七八九"; wchar_t temp[2] = { 0, }; int i = 0, nLen = 0, nIndex = 0; nLen = wcslen(lpString); for (i = 0; i < nLen; i++) { // 将阿拉伯数字转换为中文数字 // lpString 是 wide-character (2 byte) 字符串 if (L'0' <= lpString[i] && lpString[i] <= L'9') { temp[0] = lpString[i]; nIndex = _wtoi(temp); lpString[i] = pNum[nIndex]; } } // 调用user32!SetWindowTextW() API // (修改 lpString 缓冲区中的内容) return ((PFSETWINDOWTEXTW)g_pOrgFunc)(hWnd, lpString); } // hook_iat // // BOOL hook_iat(LPCSTR szDllName, PROC pfnOrg, PROC pfnNew) { HMODULE hMod; LPCSTR szLibName; PIMAGE_IMPORT_DESCRIPTOR pImportDesc; PIMAGE_THUNK_DATA pThunk; DWORD dwOldProtect, dwRVA; PBYTE pAddr; // hMod, pAddr = ImageBase of calc.exe // = VA to MZ signature (IMAGE_DOS_HEADER) hMod = GetModuleHandle(NULL); pAddr = (PBYTE)hMod; // pAddr = VA to PE signature (IMAGE_NT_HEADERS) pAddr += *((DWORD*)& pAddr[0x3C]); // dwRVA = RVA to IMAGE_IMPORT_DESCRIPTOR Table dwRVA = *((DWORD*)& pAddr[0x80]); // pImportDesc = VA to IMAGE_IMPORT_DESCRIPTOR Table pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)hMod + dwRVA); for (; pImportDesc->Name; pImportDesc++) { // szLibName = VA to IMAGE_IMPORT_DESCRIPTOR.Name szLibName = (LPCSTR)((DWORD)hMod + pImportDesc->Name); if (!_stricmp(szLibName, szDllName)) { // pThunk = IMAGE_IMPORT_DESCRIPTOR.FirstThunk // = VA to IAT(Import Address Table) pThunk = (PIMAGE_THUNK_DATA)((DWORD)hMod + pImportDesc->FirstThunk); // pThunk->u1.Function = VA to API for (; pThunk->u1.Function; pThunk++) { if (pThunk->u1.Function == (DWORD)pfnOrg) { // 更改内存属性为 E/R/W VirtualProtect((LPVOID)& pThunk->u1.Function, 4, PAGE_EXECUTE_READWRITE, &dwOldProtect); // 修改 IAT (钩取) pThunk->u1.Function = (DWORD)pfnNew; // 恢复内存属性 VirtualProtect((LPVOID)& pThunk->u1.Function, 4, dwOldProtect, &dwOldProtect); return TRUE; } } } } return FALSE; } BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { switch (fdwReason) { case DLL_PROCESS_ATTACH: // 保存原始API地址 g_pOrgFunc = GetProcAddress(GetModuleHandle(L"user32.dll"), "SetWindowTextW"); // # hook // 用hookiat!MySetWindowText()钩取 user32!SetWindowTextW() hook_iat("user32.dll", g_pOrgFunc, (PROC)MySetWindowTextW); break; case DLL_PROCESS_DETACH: // # unhook // calc.exe 的IAT恢复原值 hook_iat("user32.dll", (PROC)MySetWindowTextW, g_pOrgFunc); break; } return TRUE; }
DllMain 判断。
存储SetWindowTextW的地址
hookiat钩取和恢复
MySetWindowTextW 是对显示内容进行更改。
为了知道一个可执行文件或DLL文件被加载到进程地址空间的什么位置,可以用GetModuleHandle函数来返回一个句柄/基地址:
HMODULE GetModuleHandle(PCTSTR pszModule);
调用这个函数时,要传递一个以0为终止的字符串,它指定了已在主调进程的地址空间中加载的一个可执行文件或DLL文件的名称。如果系统找到了指定的可执行文件或DLL文件名称,GetModuleHandle就会返回可执行文件/DLL文件映像加载到的基地址。如果参数为NULL,这样可以返回主调进程的可执行文件的基地址,也就是说即使调用GetModuleHandle(NULL)的代码是在一个DLL文件中,返回值仍是可执行文件的基地址,而非DLL文件的基地址。
代码中很多结构体类型,通过VS2019 可以很方便查看结构体中的具体情况,非常方便,以前排除VS 现在的2019版不可谓不强大啊。
这里主要说一下怎么查找到SetWindowWextW的。
首先 hMod拿到程序的基地址,就是DOS文件开头,然后传给pAddr ,偏移0x3C处的数据就是PE头的位置,也就是0XF0。
再便宜0x80 就是 0x170 ,然后取数据,再次偏移。
拿到 01012B80 看看此处,正好是IDT的开头。
找到这个地址我们将它定义为IDT结构体PIMAGE_IMPORT_DESCRIPTOR。
然后遍历找到user32.dll这个名称,并且拿到IAT的地址,也就是FirstThunk。
给它定义为另一个结构体PIMAGE_THUNK_DATA。这个结构体也就是为IAT而生的。。。
然后遍历寻找Function和我们传进来的参数(也就是SetWindowTextW的地址)相等,这样就找到了这个API。
然后将Function 的值改为MySetWindowText的地址。
修改前要保证有可写权限,
VirtualProtect((LPVOID)& pThunk->u1.Function, 4, PAGE_EXECUTE_READWRITE, &dwOldProtect);
BOOL VirtualProtect(
LPVOID lpAddress,
DWORD dwSize,
DWORD flNewProtect,
PDWORD lpflOldProtect
);
各参数的意义为:
lpAddress,要改变属性的内存起始地址。
dwSize,要改变属性的内存区域大小。
flNewProtect,内存新的属性类型,设置为PAGE_EXECUTE_READWRITE(0x40)时该内存页为可读可写可执行。
pflOldProtect,内存原始属性类型保存地址。
修改内存属性成功时函数返回非0,修改失败时返回0。
到此,IAT钩取成功。
如果要恢复的话,将hookiat参数对调即可。