IAT‑Hook 与 Inline‑Hook核心区别
| 对比项 | IAT Hook | Inline Hook(内联Hook) |
|---|---|---|
| 修改位置 | 修改 IAT 表里面保存的函数地址,不改动函数本身机器码 | 修改目标函数头部机器指令代码 |
| 可 Hook 范围 | 只能 Hook 本模块导入表 IAT 中存在的外部 API 函数;自己模块内部自定义函数不能 IAT Hook | 任意函数都可以Hook:系统 API、自己写的自定义函数、C++ 虚函数都支持 |
| 检测难度 | 相对容易检测;直接校验 IAT 表项的函数地址是否真实 | 较难检测;需要校验函数头部机器码 |
Inline Hook 实现原理
- 修改目标函数最开头的机器码,植入一条 JMP 跳转指令,跳转到我们自己写的钩子函数
- x86 下 JMP 指令占5 个字节,会直接覆盖目标函数开头 5 字节原始指令
- 必须先备份被覆盖掉的 5 字节原始机器码
- 钩子函数执行自己逻辑(修改参数、拦截打印等)
- 执行完后,先执行备份好的被覆盖的 5 字节原指令;再跳回原函数剩下的代码继续运行
流程:调用原函数 → jmp 跳到钩子函数 → 执行自定义逻辑 → 执行备份的 5 字节原指令 → 跳回原函数剩余代码
Inline Hook 代码层面要点
- 构造 JMP 机器码:0xE9 + 4 字节跳转偏移;需要正确计算相对偏移
jmp 是相对跳转,偏移 = 目标钩子地址 − (当前 jmp 指令下一条指令地址) - 操作前修改内存页属性,改为可读可写可执行,否则写内存报错
- Hook 卸载恢复:把备份的原始 5 字节机器码写回函数头部
Inline Hook 重要注意事项
- 钩子函数建议写裸函数(__declspec(naked)) 裸函数不会编译器自动生成栈帧,方便手动管理寄存器
- 手动保存 / 恢复寄存器(pushad /popad) 防止钩子函数破坏原本程序寄存器的值,导致程序崩溃
- JMP 相对偏移必须计算正确,偏移算错直接跑飞、程序崩溃
- 被覆盖的 5 字节指令,有可能正好是多条指令的一部分,会产生指令断裂问题(高级难点)
要点
- IAT‑Hook:改IAT 表函数指针;只能 Hook 导入的外部 API,容易检测
- Inline‑Hook:改函数头部机器码;覆盖 5 字节,jmp 跳转;可以 Hook 任意函数,更难检测
- Inline Hook 三件套:①备份原 5 字节代码;②写入 jmp;③钩子执行完还原被覆盖指令再返回原函数
- 钩子用裸函数,手动保存寄存器;jmp 要算对偏移