HOOK


HOOK这个名词根据普遍的说法是勾子,是一种编程机制。当程序执行到HOOK的时候,预先挂上的勾子(HOOK)是什么就执行什么

用HOOK的目的,或者说是作用:
  1. 监控
  2. 修改某些行为

监控:可以通过HOOK来找到函数参数,返回值等信息

修改某些行为:可以用HOOK修改某些API的逻辑过程

IAT HOOK


IAT HOOK是什么?在进程加载时系统会将IAT表初始化,将里面的数据替换成函数地址(例如MessageBox)。这时我们就可以将我们自己写的函数的地址替换掉原来IAT表的地址,让程序调用MessageBox时执行我们自己定义的流程。而这个自定义函数也是有要求的,他的参数想要和原来函数的参数一样,否则调用时会错误。

那为什么能通过这种方式HOOK呢?

我们来观察一下调用这个MessageBox时的反汇编:
QQ_1728217489101
这里是个call [00FBC09C] 是个间接call,我们用内存去看看FBC09C里面是什么
QQ_1728218233120
这是个值:75bfb1d0,那我们继续单步,直接步入这个call中
QQ_1728218330836

直接就跳到这个地址了,根据经验这个就是MessageBox的领空,所以[00FBC09C]存的是MessageBox的地址,而这个地址在进程加载的时候会被写进IAT表中,当我们调用函数时就通过间接call来到这个地址调用函数。所以我们在IAT表加载完成后将里面的值改成我们自己函数的地址就能实现IAT HOOK

IAT HOOK实践作用:

比原函数多出一些自己想要的操作,举一个最典型的例子,一些杀毒软件经常会在一些函数上面HOOK,做出一些检测监控的操作,监控函数的返回值,参数。

代码实现


Main.cpp

1
2
3
4
5
6
7
8
9
10
11
#include "IAT_Hook.h"

int main()
{
MessageBoxA(NULL, "Hello World", "Before Hook", MB_OK);
Set_IAT_HOOK();
MessageBoxA(NULL, "Second test", "After HOOK", MB_OK);
UnIATHOOK();
MessageBoxA(NULL, "UnIATHOOK Success", "Good", MB_OK);
return 0;
}

IAT_Hook.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
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
#include "IAT_Hook.h"

// 新旧函数的地址
PDWORD dwOldAddress;
PDWORD dwNewAddress;


// 获取目录表信息
HMODULE hImageBase = GetModuleHandle(NULL);
PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)hImageBase;
PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)((DWORD)hImageBase + pDos->e_lfanew);
PIMAGE_FILE_HEADER pFile = (PIMAGE_FILE_HEADER)((DWORD)pNt + 4);
PIMAGE_OPTIONAL_HEADER pOpt = (PIMAGE_OPTIONAL_HEADER)((DWORD)pFile + 20);
PIMAGE_SECTION_HEADER pSec = (PIMAGE_SECTION_HEADER)((DWORD)pOpt + pFile->SizeOfOptionalHeader);
PIMAGE_DATA_DIRECTORY pDir = (PIMAGE_DATA_DIRECTORY)((DWORD)pSec - sizeof(DWORD) * 32);

VOID Set_IAT_HOOK()
{
dwNewAddress = (PDWORD)MyMessageBoxA;
int flag = 0;
// 获取MessageBox的函数地址
HMODULE hUser32 = GetModuleHandleA("user32.dll");
if (!hUser32)
{
printf("User32未加载\n");
return ;
}
dwOldAddress = (PDWORD)GetProcAddress(hUser32, "MessageBoxA");


// 获取导入表地址
PIMAGE_IMPORT_DESCRIPTOR pImport = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)hImageBase + pDir[1].VirtualAddress);

// 遍历每个导入表的IAT表
while (pImport->Name != 0 && flag == 0)
{
PDWORD IAT = (PDWORD)((DWORD)hImageBase + pImport->FirstThunk);
while (*IAT != 0)
{
if (*IAT == (DWORD)dwOldAddress)
{
DWORD OldProtected;
VirtualProtect((LPVOID)IAT, 0x4, PAGE_EXECUTE_READWRITE, &OldProtected);
*IAT = (DWORD)dwNewAddress;
VirtualProtect((LPVOID)IAT, 0x4, OldProtected, &OldProtected);
flag = 1;
break;
}
IAT++;
}
pImport++;
}
}




int WINAPI MyMessageBoxA(
HWND hwnd,
LPCSTR lpText,
LPCSTR lpCaption,
UINT uType
)
{
// 定义MessageBox函数指针
typedef int (WINAPI* PFNMESSAGEBOXA)(HWND, LPCSTR, LPCSTR, UINT);

// 获取参数
printf("获取到的函数参数:\n");
printf("hwnd: %08X\tlpText: %s\nlpCaption: %s\tuType: %d\n", (DWORD)hwnd, lpText, lpCaption, uType);

// 执行真正的函数
int Ret = ((PFNMESSAGEBOXA)(PDWORD)dwOldAddress)(NULL, "HOOK Success", "IAT HOOK", MB_OK);

// 获取返回值
printf("返回值: %d\n", Ret);
return Ret;
}



// 取消IATHOOK
VOID UnIATHOOK()
{
int flag = 0;

// 获取导入表地址
PIMAGE_IMPORT_DESCRIPTOR pImport = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)hImageBase + pDir[1].VirtualAddress);

// 遍历每个导入表的IAT表
while (pImport->Name != 0 && flag == 0)
{
PDWORD IAT = (PDWORD)((DWORD)hImageBase + pImport->FirstThunk);
while (*IAT != 0)
{
if (*IAT == (DWORD)dwNewAddress)
{
DWORD OldProtected;
VirtualProtect((LPVOID)IAT, 0x4, PAGE_EXECUTE_READWRITE, &OldProtected);
*IAT = (DWORD)dwOldAddress;
VirtualProtect((LPVOID)IAT, 0x4, OldProtected, &OldProtected);
flag = 1;
break;
}
IAT++;
}
pImport++;
}
}

Main.h

1
2
3
#pragma once
#include <stdio.h>
#include <Windows.h>

IAT_Hook.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#pragma once
#include "Main.h"

// 设置HOOK
VOID Set_IAT_HOOK();

// 创建一个自己的函数MessageBox
int WINAPI MyMessageBoxA(
HWND hwnd,
LPCSTR lpText,
LPCSTR lpCaption,
UINT uType
);

// 取消IATHOOK
VOID UnIATHOOK();
运行结果:

当第一个MessageBox执行时就是系统原本的函数,然后我们启动HOOK
QQ_1728219115474

可以看见第二个窗口的显示和第二个MessageBox输入的参数不一样,命令行中输出了关于这个函数参数的信息。成功HOOK
QQ_1728219152077

当我们卸载掉HOOK时,函数就恢复成原来的样子了。
QQ_1728219185092

注意:
  1. 在修改IAT表中的地址时由于是对进程进行操作的,所以需要VirtualProtect()函数对内存权限修改才能将地址写进去。

总结


1. 课上的问题
  1. 什么时候函数地址不在IAT表当中
    答:自己手动Load时函数地址就不在IAT表当中了,这种如果查看反汇编的话就会发现用的是直接call

  2. 定义MyMessageBox时可不可以改成这样子
    ![[Pasted image 20241006222809.png]]
    答:不行,因为此时的IAT表已经被修改,用MessageBox就相当于调用了MyMessageBox,这样会造成无限递归。

2. IAT HOOK的局限性

有些不能HOOK,比如一些自己写的函数,或者自己手动load的函数,都不存在IAT表,所以都无法HOOK