HOOK
HOOK这个名词根据普遍的说法是勾子,是一种编程机制。当程序执行到HOOK的时候,预先挂上的勾子(HOOK)是什么就执行什么
用HOOK的目的,或者说是作用:
- 监控
- 修改某些行为
监控:可以通过HOOK来找到函数参数,返回值等信息
修改某些行为:可以用HOOK修改某些API的逻辑过程
IAT HOOK
IAT HOOK是什么?在进程加载时系统会将IAT表初始化,将里面的数据替换成函数地址(例如MessageBox)。这时我们就可以将我们自己写的函数的地址替换掉原来IAT表的地址,让程序调用MessageBox时执行我们自己定义的流程。而这个自定义函数也是有要求的,他的参数想要和原来函数的参数一样,否则调用时会错误。
那为什么能通过这种方式HOOK呢?
我们来观察一下调用这个MessageBox时的反汇编:

这里是个call [00FBC09C]
是个间接call,我们用内存去看看FBC09C里面是什么

这是个值:75bfb1d0,那我们继续单步,直接步入这个call中

直接就跳到这个地址了,根据经验这个就是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; 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); 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 ) { 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; }
VOID UnIATHOOK() { int flag = 0;
PIMAGE_IMPORT_DESCRIPTOR pImport = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)hImageBase + pDir[1].VirtualAddress);
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"
VOID Set_IAT_HOOK();
int WINAPI MyMessageBoxA( HWND hwnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType );
VOID UnIATHOOK();
|
运行结果:
当第一个MessageBox执行时就是系统原本的函数,然后我们启动HOOK

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

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

注意:
- 在修改IAT表中的地址时由于是对进程进行操作的,所以需要
VirtualProtect()
函数对内存权限修改才能将地址写进去。
总结
1. 课上的问题
什么时候函数地址不在IAT表当中
答:自己手动Load时函数地址就不在IAT表当中了,这种如果查看反汇编的话就会发现用的是直接call
定义MyMessageBox时可不可以改成这样子
![[Pasted image 20241006222809.png]]
答:不行,因为此时的IAT表已经被修改,用MessageBox就相当于调用了MyMessageBox,这样会造成无限递归。
2. IAT HOOK的局限性
有些不能HOOK,比如一些自己写的函数,或者自己手动load的函数,都不存在IAT表,所以都无法HOOK