学一学 hook 专题,今天是 IAT hook。

课堂笔记

IAT

IAT 就是 Import Address Table,导入地址表,其实感觉跟 Linux ELF 的全局偏量表(GOT)是一样一样的,IAT hook 原理就很简单,我们只需要改写这个表到我们自己的函数,如果需要执行原函数的逻辑,那么我在 hook 的时候保存 IAT 的初始值,直接 call 过去即可。

hook步骤

我们需要分析 PE 文件头,先拿到 IAT 表的一个位置,然后拿到该函数的偏移,去改写它。

那么我们先获取一个 PE DOS HEADER,使用 API GetModuleHandle 第一个参数传 NULL 可以获取该 PE 文件的 DOS 头。再从这个 DOS 头里面获取 NT 头,从 NT 头拿扩展头。有了扩展头之后,我们就能找到 IAT 了。

因为一个 PE 文件,他可能有很多模块,也就是很多的 IAT,因此我们后面需要遍历所有的 IAT,找到了 MessageBoxA 进行一个替换。

dllmain.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
#include<Windows.h>



typedef int (WINAPI* PMessageBoxA)(
_In_opt_ HWND hWnd,
_In_opt_ LPCSTR lpText,
_In_opt_ LPCSTR lpCaption,
_In_ UINT uType);

PMessageBoxA OldMessageBoxA=NULL;

int WINAPI MyMessageBoxA(
_In_opt_ HWND hWnd,
_In_opt_ LPCSTR lpText,
_In_opt_ LPCSTR lpCaption,
_In_ UINT uType) {
return OldMessageBoxA(hWnd, "xia0ji233", "hooked by xia0ji233", uType);

}

VOID IATHook()
{
//__asm INT 3h;
OldMessageBoxA=(PMessageBoxA)GetProcAddress(GetModuleHandleA("user32.dll"),"MessageBoxA");

PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)GetModuleHandle(NULL);

PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);
PIMAGE_OPTIONAL_HEADER pOptionalHeader = (PIMAGE_OPTIONAL_HEADER)&pNtHeader->OptionalHeader;
DWORD dwIAT = pOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
PIMAGE_IMPORT_DESCRIPTOR pImportTable = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)pDosHeader + dwIAT);
DWORD * pFirstThunk;
//MessageBoxA((HWND)GetCurrentProcess(),"开始注入","warning",0);

while (pImportTable->Characteristics && pImportTable->FirstThunk != NULL) {
pFirstThunk = (DWORD *)(pImportTable->FirstThunk + (DWORD)pDosHeader);
while (*(DWORD*)pFirstThunk != NULL) {
if (*(DWORD *)pFirstThunk == (DWORD)OldMessageBoxA) {
DWORD oldProtect;
VirtualProtect(pFirstThunk, 0x1000, PAGE_EXECUTE_READWRITE, &oldProtect);
DWORD dwFuncAddr = (DWORD)MyMessageBoxA;
//memcpy(pFirstThunk, (DWORD*)&dwFuncAddr, 4);

WriteProcessMemory(GetCurrentProcess(), pFirstThunk, (DWORD *)& dwFuncAddr, 4, &oldProtect);
//memcpy(pFirstThunk, (DWORD *)& MyMessageBoxA, 4);

VirtualProtect(pFirstThunk, 0x1000, oldProtect, &oldProtect);
}
pFirstThunk++;
}
pImportTable++;
}
}

BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
IATHook();
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}


这里还有要注意的,就是我直接用了 DEV C++ 去编译得到的 EXE 文件并不能被 HOOK,用 VS 的才行,不过就一直不知道是什么原因,这里我放一个能用的 EXE 的下载地址

运行结果

MessageBoxA 成功被 HOOK

注意事项

这次写这个是因为遇到了很多困难,首先就是 VirtualProtect 函数这里,一定要使用,不能单纯地用 WriteProcessMemory 函数去写,会有问题,大概原因就是 WriteProcessMemory 这个函数在修改内存页属性的时候是有先决条件的,在这里这个 IAT 肯定就不太符合这个条件,因此写入不会成功,如果尝试直接 memcpy 好像是会报错的。而用 WriteProcessMemory 会直接没有反应,这里花了挺多时间的。

还有就是! PAGE_EXECUTE | PAGE_READWRITE 和 PAGE_EXECUTE_READWRITE 是不一样的,只有后面那个能用我之前用的前者也是一直不行的。。

总结

今天学的这个 Hook 感觉也略微有点简单,因为就跟我们 Linux ELF 的 got 表差不多嘛,就直接把 got 改掉,达到劫持的一个目的。