【免杀】C2免杀技术(十三)Inline Hook 概念篇
Hook 技术
Hook 技术是操作系统、软件开发和安全攻防中非常核心的技术手段之一。它的本质是“截获函数调用并插入自定义逻辑”。你可以把它理解为“在别人执行某个功能之前或之后偷偷插一脚”。
一、Hook 技术本质
Hook = 挂钩技术,指的是:
修改程序运行过程中某些函数或代码的执行流程,使其转而执行攻击者(或开发者)定义的代码。
二、常见 Hook 分类
按实现方式分类:
类型 | 描述 | 常见用途 |
---|---|---|
Inline Hook(函数内联) | 在目标函数的开头插入跳转指令 | 木马注入、监控、反作弊 |
IAT Hook | 修改 Import Address Table 表项,改变函数调用地址 | DLL 注入、API 替换、持久化 |
EAT Hook | 修改 Export Address Table,劫持导出的函数 | DLL 劫持,系统调用重定向 |
SSDT Hook | 修改系统服务描述表(系统调用表)地址(内核级) | Rootkit、驱动监控、内核后门 |
VMT Hook(虚函数表) | 修改 C++ 类的虚函数表指针 | 游戏外挂、面向对象劫持 |
Windows 消息 Hook | 使用 SetWindowsHookEx 挂钩消息(键盘、鼠标等) | 键盘记录器、自动化脚本、UI拦截 |
VEH/SEH Hook | 设置异常处理函数拦截访问/异常等行为 | Anti-debug、内存控制 |
Inline Hook
Inline Hook(内联钩子)是所有 Hook 技术中最强大也最危险的一种,它属于修改目标函数指令流的一种方式,本质上是在函数入口直接插入跳转指令来重定向执行流。
一、Inline Hook 原理详解
通过修改目标函数的机器指令,使其执行流程跳转到自定义的代码(Hook函数),然后再返回原函数继续执行或完全替代原函数。
示意图(Hook MessageBoxA):
原始指令:
MessageBoxA:push ebpmov ebp, esp...Hook后变成:
MessageBoxA:jmp my_hook_func ← 插入跳转指令; 原来的 push ebp mov ebp 被覆盖(备份)your_hook_func:; 你想干的事(比如打印、修改参数、记录调用); 如果你要调用原始函数,则需要:- 还原前几条原始指令(trampoline)- jmp 回 MessageBoxA + x
二、主要实现步骤
其中(2、3、4步)如果使用 MinHook 库,由 MinHook 内部自动完成,代码中看不见
1、定位目标函数:确定需要Hook的函数的地址
2、备份原指令:保存将被覆盖的原始指令
3、构造跳转指令:编写跳转到Hook函数的指令
4、修改目标函数:用跳转指令覆盖目标函数的开始部分
5、处理执行流:在Hook函数中处理逻辑后决定是否返回原函数
三、常见实现方式
# | 典型写法 | 指令序列(十六进制) | 说明 / 关键代码片段 |
---|---|---|---|
1 | JMP rel32 (x86/x64 通用,±2 GB 内) | E9 XX XX XX XX (总 5 字节) | cpp DWORD src = (DWORD)pTarget; DWORD dst = (DWORD)pHook; DWORD rel = dst - src - 5; BYTE patch[5] = {0xE9}; memcpy(patch+1,&rel,4); WritePatch(pTarget, patch, 5); |
2 | PUSH addr ; RET(绝对跳转,避开特征检测) | 68 XX XX XX XX C3 (6 字节) | cpp DWORD addr = (DWORD)pHook; BYTE patch[6] = {0x68}; memcpy(patch+1,&addr,4); patch[5]=0xC3; WritePatch(pTarget, patch, 6); |
3 | MOV RAX, addr ; JMP RAX(x64 远距) | 48 B8 <addr64> FF E0 (12 字节) | cpp uint8_t patch[12] = { 0x48,0xB8 }; *(uint64_t*)(patch+2) = (uint64_t)pHook; patch[10]=0xFF; patch[11]=0xE0; WritePatch(pTarget, patch, 12); |
代码示例
下面是一个 Inline Hook 示例(64位),我们将 Hook MessageBoxA
函数,在不改变其正常行为的前提下,劫持它并打印额外信息。
在该代码中,构造跳转指令的操作是由 MinHook 库内部完成的,并没有直接在代码中显式地编写跳转指令(如 JMP
或 CALL
)。MinHook 封装了这些底层细节,开发者只需调用 MH_CreateHook
和 MH_EnableHook
即可完成 Hook。
#include <windows.h>
#include <iostream>
#include "MinHook.h"typedef int (WINAPI* MessageBoxA_t)(HWND, LPCSTR, LPCSTR, UINT);
MessageBoxA_t fpMessageBoxA = NULL; // 保存原始函数指针int WINAPI MyHookMessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType) {std::cout << "[HOOKED] MessageBoxA intercepted!" << std::endl;// 修改文本内容LPCSTR newText = "你被 x64 Inline Hook 成功拦截!";return fpMessageBoxA(hWnd, newText, lpCaption, uType);
}int main() {// 初始化 MinHookif (MH_Initialize() != MH_OK) {std::cerr << "MinHook 初始化失败" << std::endl;return 1;}// 获取 MessageBoxA 地址void* pTarget = GetProcAddress(GetModuleHandleA("user32"), "MessageBoxA");// 创建 Hookif (MH_CreateHook(pTarget, &MyHookMessageBoxA, reinterpret_cast<LPVOID*>(&fpMessageBoxA)) != MH_OK) {std::cerr << "创建 Hook 失败" << std::endl;return 1;}// 启用 Hookif (MH_EnableHook(pTarget) != MH_OK) {std::cerr << "启用 Hook 失败" << std::endl;return 1;}// 测试调用MessageBoxA(NULL, "测试内容", "测试标题", MB_OK);// 等待std::cin.get();// 清理MH_DisableHook(pTarget);MH_Uninitialize();return 0;
}
这段代码是怎么 Hook MessageBoxA 的?
1、找到 MessageBoxA
的地址(就像查电话簿找某个人的电话号码)
void* pTarget = GetProcAddress(GetModuleHandleA("user32"), "MessageBoxA");
2、告诉 MinHook:
- “我要 Hook
MessageBoxA
- “如果
MessageBoxA
被调用,先跳转到我的函数MyHookMessageBoxA
”
- “记得把原来的
MessageBoxA
存起来,我后面还要用”
MH_CreateHook(pTarget, &MyHookMessageBoxA, reinterpret_cast<LPVOID*>(&fpMessageBoxA));
3、MinHook 偷偷修改 MessageBoxA
的代码(让它一执行就先跳转到我们的函数)
MH_EnableHook(pTarget); // 启用 Hook(真正“劫持”快递)
4、我们的 MyHookMessageBoxA
函数接管控制权,可以改参数、记录日志等
int WINAPI MyHookMessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType) {std::cout << "[HOOKED] MessageBoxA intercepted!" << std::endl;LPCSTR newText = "你被 x64 Inline Hook 成功拦截!"; // 修改弹窗内容return fpMessageBoxA(hWnd, newText, lpCaption, uType); // 再调用原函数
}
5、最后再调用原来的 MessageBoxA
(如果不调用,弹窗就不会显示)
MinHook 框架
MinHook 是一个轻量级的 Windows API 钩取(hooking)库,主要用于拦截和修改函数调用。
优势
为什么要用,尤其是64位环境?
-
自动反汇编前几条指令
-
检查这些指令有没有
rip
相对寻址 -
自动构造 trampoline,重新修复相对地址或选择更长的覆盖范围
-
自动补跳转返回指令(jmp back)
-
处理 x64 栈对齐(shadow space)
安装
1、我这里使用 vcpkg 安装 MinHook
vcpkg install minhook:x64-windows #动态版本
vcpkg install minhook:x64-windows-static #静态版本(推荐)
2、继续使用以下命令自动集成,这会将 vcpkg 的库路径自动挂入 VS 项目的环境中
vcpkg integrate install
3、VS设置
4、项目中引入头文件
#include <MinHook.h>