IAT_Hook
说明:
利用IAT表对函数进行替换。把MessageBox()替换成自己定义的MyMessageBox(),不再出现消息框,而是输出字符或其他内容。
IAT表可以理解为一个数组。
目的:找到IMAGE_IMPORT_DESCRIPTOR结构体。
过程:
根据图片算偏移。。
1.找到IMAGE_DOS_HEADER,即文件开始的首地址。其他e_lfanew是一个偏移量。首地址+偏移量=下一结构的首地址 。
2.图看出,IMAGE_OPTIONAL_HEADER 在IMAGE_NT_HEADERS中,获取IMAGE_OPTIONAL_HEADER 。
注意:IMAGE_NT_HEADERS中有两个结构体,如果偏移IMAGE_NT_HEADERS的大小,就要再减去IMAGE_OPTIONAL_HEADER 的大小。否则会偏移多了。
3.IMAGE_IMPORT_DESCRIPTOR *img_import_desc =
(IMAGE_IMPORT_DESCRIPTOR *)((BYTE*)img_dos_header + img_optional_header->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
注意:每次的偏移都从头部开始,并且都是按字节数来偏移,所以要强转 (BYTE*)img_dos_header。
VirtualAddress 数据的RVA,其本质就是偏移量。
4.此时可以得到IMAGE_IMPORT_DESCRIPTOR ,即IAT,首先要寻找要替换函数在哪个DLL中,通过不断搜索img_import_desc->FirstThunk,
找到USER32.dll。因为MessageBox在个dll中。
char *szImpartDesc = (char *)((BYTE *)img_dos_header + img_import_desc->Name);
(strcmp(szImpartDesc, "USER32.dll") == 0
注意:返回的函数名是ASCII。可以通过DWORD(MessageBox)取得函数名,和char *进行转换。获取代表函数的字符串。
5.
IMAGE_THUNK_DATA *img_thunk_data = (IMAGE_THUNK_DATA *)((BYTE*)hModule + img_import_desc->FirstThunk);
找到函数表,同时img_thunk_data->u1.Function可以返回函数名。
6.内存权限的修改。
上面已经找到函数所在的地址。可以img_thunk_data->u1.Function = (DWORD)MyMessageBox;
重点:此时会发现,该内存无法访问。
那么,就要修改内存的权限。为可读可写
DWORD *lpAddr = &(img_thunk_data->u1.Function);
//改变虚拟地址的读写权限
DWORD dwOldProtect;
MEMORY_BASIC_INFORMATION mbi;
VirtualQuery(lpAddr, &mbi, sizeof(mbi));
VirtualProtect(lpAddr, sizeof(DWORD), PAGE_READWRITE, &dwOldProtect);
//更换函数地址
img_thunk_data->u1.Function = (DWORD)MyMessageBox;
//权限还原
VirtualProtect(lpAddr, sizeof(DWORD), dwOldProtect, NULL);
6.函数替换
img_thunk_data->u1.Function = (DWORD)MyMessageBox;
注意点:
1.替换的函数与原函数,必须返回类型、函数参数一致。即函数名不同,其他完全相同。
2.在MyMessageBox中,不可再调用MessageBox,会造成递归。因为系统中的MessageBox已经被替换,再调用就是调用自己定义的MyMessageBox。
3.还原函数。在函数之前,可以先定义一个全局的DWORD类型的变量,进行原函数的保存。在不用的时候,进行还原。
另外:
通过函数ImageDirectoryEntryToData可以直接获取到IMAGE_IMPORT_DESCRIPTOR的首地址,不需要按字节进行计算。
IMAGE_IMPORT_DESCRIPTOR pImportDesc=
(PIMAGE_IMPORT_DESCRIPTOR)ImageDirectoryEntryToData(hModCallerModule,TRUE,IMAGE_DIRECTORY_ENTRY_IMPORT,&size);
函数原型:
PVOID WINAPI ImageDirectoryEntryToData(
_In_ PVOID Base,
_In_ BOOLEAN MappedAsImage,
_In_ USHORT DirectoryEntry,
_Out_ PULONG Size
);
源代码: #include "stdafx.h" #include <Windows.h> int WINAPI MyMessageBox( _In_opt_ HWND hWnd, _In_opt_ LPCWSTR lpText, _In_opt_ LPCWSTR lpCaption, _In_ UINT uType) { // //使用消息框,地址冲突。 // try // { // MessageBox(NULL, L"My MessageBox!", L"Please!", MB_OK); // } // catch (...) // { // printf("Error=%d", GetLastError()); // } // //输出一句话,不冲突 //printf("Hook Sucess!"); return 0; } //替换函数要与原函数返回类型、参数一致 /*int MyMessageBox() { printf("Hook Sucess!"); return 0; }*/ bool SetMyHook() { //获取DOS头 HANDLE hModule = GetModuleHandle(NULL); IMAGE_DOS_HEADER *img_dos_header = (IMAGE_DOS_HEADER*)hModule; //从DOS头直接到OPTIONAL_HEADER。减sizeof(IMAGE_OPTIONAL_HEADER),因为此结构体包含在IMAGE_NT_HEADERS中,偏移多了。 IMAGE_OPTIONAL_HEADER *img_optional_header = (IMAGE_OPTIONAL_HEADER *)((BYTE *)img_dos_header + img_dos_header->e_lfanew + sizeof(IMAGE_NT_HEADERS)-sizeof(IMAGE_OPTIONAL_HEADER)); //从IMAGE_OPTIONAL_HEADER到IAT表 IMAGE_IMPORT_DESCRIPTOR *img_import_desc = (IMAGE_IMPORT_DESCRIPTOR *)((BYTE*)img_dos_header + img_optional_header->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress); //不断搜索IAT表的数据 while (img_import_desc->FirstThunk) { char *szImpartDesc = (char *)((BYTE *)img_dos_header + img_import_desc->Name); //注意:IAT表的函数名是ASCII if (strcmp(szImpartDesc, "USER32.dll") == 0) { break; } img_import_desc++; } if (img_import_desc->FirstThunk) { //取真实MessageBox的地址。后面此地址替换 DWORD dwMsgAdrr = (DWORD)MessageBox; //函数表 IMAGE_THUNK_DATA *img_thunk_data = (IMAGE_THUNK_DATA *)((BYTE*)hModule + img_import_desc->FirstThunk); //搜索函数表,找到Messagebox的函数地址 while (img_import_desc->FirstThunk) { if (img_thunk_data->u1.Function == dwMsgAdrr) { DWORD *lpAddr = &(img_thunk_data->u1.Function); //改变虚拟地址的读写权限 DWORD dwOldProtect; MEMORY_BASIC_INFORMATION mbi; VirtualQuery(lpAddr, &mbi, sizeof(mbi)); VirtualProtect(lpAddr, sizeof(DWORD), PAGE_READWRITE, &dwOldProtect); //更换函数地址 img_thunk_data->u1.Function = (DWORD)MyMessageBox; //权限还原 VirtualProtect(lpAddr, sizeof(DWORD), dwOldProtect, NULL); return true; } img_thunk_data++; } } return false; } int _tmain(int argc, _TCHAR* argv[]) { //正常消息框 MessageBox(NULL, L"MessageBox!", L"Pleadse!", MB_OK); bool isSucc=SetMyHook(); if (!isSucc) { printf("Error!"); return -1; } //hook后,应不在出现此消息框 MessageBox(NULL, L"MessageBox!", L"Pleadse!", MB_OK); getchar(); return 0; }