精选10:Windows安全|InlineHook实战:从x86到x64

admin 2026-02-02 00:42:55 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 本文深入解析WindowsInlineHook技术,对比x86与x64架构实现差异。x86利用5字节相对跳转实现Hook,x64则需14字节绝对跳转,易引发指令截断崩溃。文章提供完整代码演示劫持与Trampoline回溯机制,并提出使用LDE引擎或中转跳板解决截断问题,极具参考价值。 综合评分: 96 文章分类: 二进制安全,逆向分析,实战经验


cover_image

精选10:Windows 安全 | Inline Hook 实战:从 x86 到 x64

八九 八九

希水涵精选录

2026年1月24日 20:19 山东

「希水涵精选录」是 ABC_123 运营的第2个公众号,主要转载经过筛选的优秀技术文章,欢迎大家积极投稿!

Part1 Hook 基础

Hook 技术主要分为两大类:

1.  IAT Hook (基于 PE 结构):修改导入表,针对性强,但容易被检测。

2.  Inline Hook (基于机器码修改):直接修改内存中函数的前几行指令,跳转到我们的函数。

Inline Hook 的核心三步曲:

1.  劫持 (Hook):修改原函数开头,强行把执行流引到我们的地盘。

2.  执行 (Payload):在我们的函数中做坏事(记录日志、篡改参数、拦截执行)。

3.  回溯 (Trampoline):为了保证程序不崩溃,我们通过“跳板”执行原函数被覆盖的指令,然后跳回原函数继续执行。

Part2 x86 Inline Hook

  • ### 为什么32位Hook很简单?(Hotpatch 机制)

在 Windows XP/Win7 (32位) 时代,微软为了方便打补丁,在很多 API (如 MessageBoxA) 开头预留了 5 个字节 的标准序言。

标准开头机器码:

8B FF       MOV EDI, EDI   ; 2字节 (无意义,仅占位)55          PUSH EBP       ; 1字节8B EC       MOV EBP, ESP   ; 2字节

总长度正好 5 字节。32 位的相对跳转指令 JMP (0xE9) 也正好是 5 字节。我们可以完美覆盖这 5 字节,不会截断任何指令。

在汇编中,jmp指令后跟着绝对地址,但这是编译器优化后的结果。真实机器码后跟着相对偏移量,而非绝对地址   $Offset = 目标地址 – 当前地址 – 5$

#include&nbsp;<windows.h>#include&nbsp;<iostream>
// ==========================================// 1. 定义函数指针与全局变量// ==========================================
// 定义 MessageBoxA 的函数原型// WINAPI (即 __stdcall) 在 32 位下非常重要,用来处理堆栈平衡typedef&nbsp;int&nbsp;(WINAPI* PMessageBoxA)(HWND, LPCSTR, LPCSTR, UINT);
// 保存原函数地址void* g_pOrgMsgBox =&nbsp;NULL;
// 保存跳板的内存地址 (使用 VirtualAlloc 申请)unsigned&nbsp;char* g_pTrampoline =&nbsp;NULL;
// ==========================================// 2. 我们的 Hook 函数 (Payload)// ==========================================int&nbsp;WINAPI&nbsp;MyMessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType)&nbsp;{&nbsp; &nbsp; std::cout <<&nbsp;"[!] [HOOK] 拦截成功!正在修改弹窗内容..."&nbsp;<< std::endl;
&nbsp; &nbsp;&nbsp;// 篡改参数&nbsp; &nbsp; LPCSTR newText =&nbsp;"这是被 32位 Hook 篡改的内容!";&nbsp; &nbsp; LPCSTR newCaption =&nbsp;"警告 (Hooked)";
&nbsp; &nbsp;&nbsp;// 调用跳板,执行原函数逻辑&nbsp; &nbsp;&nbsp;// 必须强转为 PMessageBoxA 才能正确传递参数&nbsp; &nbsp; PMessageBoxA pOriginal = (PMessageBoxA)g_pTrampoline;&nbsp; &nbsp;&nbsp;return&nbsp;pOriginal(hWnd, newText, newCaption, uType);}
// ==========================================// 3. 安装 Hook (32位经典逻辑)// ==========================================void&nbsp;InstallHook()&nbsp;{&nbsp; &nbsp; HMODULE hUser32 =&nbsp;LoadLibraryA("user32.dll");&nbsp; &nbsp; g_pOrgMsgBox = (void*)GetProcAddress(hUser32,&nbsp;"MessageBoxA");
&nbsp; &nbsp;&nbsp;if&nbsp;(!g_pOrgMsgBox) {&nbsp; &nbsp; &nbsp; &nbsp; std::cout <<&nbsp;"[-] 无法找到 MessageBoxA"&nbsp;<< std::endl;&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return;&nbsp; &nbsp; }
&nbsp; &nbsp; std::cout <<&nbsp;"[*] MessageBoxA 地址: 0x"&nbsp;<< g_pOrgMsgBox << std::endl;
&nbsp; &nbsp;&nbsp;// --- 步骤 1: 准备跳板 (VirtualAlloc) ---
&nbsp; &nbsp;&nbsp;// 申请 4KB 内存,权限为 可读可写可执行 (PAGE_EXECUTE_READWRITE)&nbsp; &nbsp; g_pTrampoline = (unsigned&nbsp;char*)VirtualAlloc(NULL,&nbsp;4096, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
&nbsp; &nbsp;&nbsp;if&nbsp;(!g_pTrampoline) {&nbsp; &nbsp; &nbsp; &nbsp; std::cout <<&nbsp;"[-] 内存申请失败"&nbsp;<< std::endl;&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return;&nbsp; &nbsp; }&nbsp; &nbsp; std::cout <<&nbsp;"[+] 跳板内存申请成功: 0x"&nbsp;<< (void*)g_pTrampoline << std::endl;
&nbsp; &nbsp;&nbsp;// --- 步骤 2: 填充跳板内容 ---
&nbsp; &nbsp;&nbsp;// 2.1 【备份】把原函数的前 5 字节复制到跳板开头&nbsp; &nbsp;&nbsp;memcpy(g_pTrampoline, g_pOrgMsgBox,&nbsp;5);
&nbsp; &nbsp;&nbsp;// 2.2 【回跳】在跳板第 5 字节处,写入 JMP 跳回原函数 +5 的位置&nbsp; &nbsp;&nbsp;// 计算回跳偏移:(原函数地址 + 5) - (跳板地址 + 5) - 5&nbsp; &nbsp;&nbsp;// 解释:目标是 (g_pOrgMsgBox + 5),当前指令在 (g_pTrampoline + 5)&nbsp; &nbsp; DWORD srcAddrInTramp = (DWORD)g_pTrampoline +&nbsp;5;&nbsp; &nbsp; DWORD targetAddrInOrg = (DWORD)g_pOrgMsgBox +&nbsp;5;&nbsp; &nbsp; DWORD jmpBackOffset = targetAddrInOrg - srcAddrInTramp -&nbsp;5;
&nbsp; &nbsp;&nbsp;// 写入 JMP 指令 (E9)&nbsp; &nbsp; g_pTrampoline[5] =&nbsp;0xE9;&nbsp; &nbsp;&nbsp;// 写入偏移量&nbsp; &nbsp; *(DWORD*)(g_pTrampoline +&nbsp;6) = jmpBackOffset;
&nbsp; &nbsp;&nbsp;// 现在 g_pTrampoline 里的内容是:&nbsp; &nbsp;&nbsp;// [原函数前5字节] + [JMP 回原函数+5]
&nbsp; &nbsp;&nbsp;// --- 步骤 3: 实施 Hook (修改原函数) ---
&nbsp; &nbsp;&nbsp;// 3.1 修改内存保护属性 (代码段通常只读)&nbsp; &nbsp; DWORD oldProtect;&nbsp; &nbsp;&nbsp;VirtualProtect(g_pOrgMsgBox,&nbsp;5, PAGE_EXECUTE_READWRITE, &oldProtect);
&nbsp; &nbsp;&nbsp;// 3.2 计算去往 MyMessageBoxA 的偏移&nbsp; &nbsp;&nbsp;// 公式:目标地址 - 当前地址 - 5&nbsp; &nbsp; DWORD targetHookAddr = (DWORD)MyMessageBoxA;&nbsp; &nbsp; DWORD srcMsgBoxAddr = (DWORD)g_pOrgMsgBox;&nbsp; &nbsp; DWORD jmpToHookOffset = targetHookAddr - srcMsgBoxAddr -&nbsp;5;
&nbsp; &nbsp;&nbsp;// 3.3 写入 JMP 指令&nbsp; &nbsp;&nbsp;unsigned&nbsp;char* pByte = (unsigned&nbsp;char*)g_pOrgMsgBox;&nbsp; &nbsp; pByte[0] =&nbsp;0xE9; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// JMP 操作码&nbsp; &nbsp; *(DWORD*)(pByte +&nbsp;1) = jmpToHookOffset;&nbsp;// 偏移量
&nbsp; &nbsp;&nbsp;// 3.4 恢复内存保护 &nbsp;刷新缓存&nbsp; &nbsp;&nbsp;VirtualProtect(g_pOrgMsgBox,&nbsp;5, oldProtect, &oldProtect);&nbsp; &nbsp;&nbsp;FlushInstructionCache(GetCurrentProcess(), g_pOrgMsgBox,&nbsp;5);
&nbsp; &nbsp; std::cout <<&nbsp;"[+] Hook 安装完成 (32-bit JMP)"&nbsp;<< std::endl;}
// ==========================================// 4. 卸载 Hook// ==========================================void&nbsp;UninstallHook()&nbsp;{&nbsp; &nbsp;&nbsp;if&nbsp;(!g_pTrampoline || !g_pOrgMsgBox)&nbsp;return;
&nbsp; &nbsp; std::cout <<&nbsp;"[*] 正在卸载 Hook..."&nbsp;<< std::endl;
&nbsp; &nbsp;&nbsp;// 1. 恢复原函数头部的 5 字节&nbsp; &nbsp;&nbsp;// 从跳板里把备份的那 5 字节拷回去就行了&nbsp; &nbsp; DWORD oldProtect;&nbsp; &nbsp;&nbsp;VirtualProtect(g_pOrgMsgBox,&nbsp;5, PAGE_EXECUTE_READWRITE, &oldProtect);
&nbsp; &nbsp;&nbsp;memcpy(g_pOrgMsgBox, g_pTrampoline,&nbsp;5);
&nbsp; &nbsp;&nbsp;VirtualProtect(g_pOrgMsgBox,&nbsp;5, oldProtect, &oldProtect);&nbsp; &nbsp;&nbsp;FlushInstructionCache(GetCurrentProcess(), g_pOrgMsgBox,&nbsp;5);
&nbsp; &nbsp;&nbsp;// 2. 释放跳板内存&nbsp; &nbsp;&nbsp;VirtualFree(g_pTrampoline,&nbsp;0, MEM_RELEASE);&nbsp; &nbsp; g_pTrampoline =&nbsp;NULL;
&nbsp; &nbsp; std::cout <<&nbsp;"[+] Hook 已卸载,内存已释放"&nbsp;<< std::endl;}
int&nbsp;main()&nbsp;{&nbsp; &nbsp;&nbsp;// 1. 正常调用&nbsp; &nbsp; std::cout <<&nbsp;"--- 1. 正常调用 ---"&nbsp;<< std::endl;&nbsp; &nbsp;&nbsp;MessageBoxA(NULL,&nbsp;"我是正常弹窗",&nbsp;"Original", MB_OK);
&nbsp; &nbsp;&nbsp;// 2. 安装 Hook&nbsp; &nbsp;&nbsp;InstallHook();
&nbsp; &nbsp;&nbsp;// 3. Hook 后调用&nbsp; &nbsp; std::cout <<&nbsp;"--- 2. Hook 后调用 ---"&nbsp;<< std::endl;&nbsp; &nbsp;&nbsp;MessageBoxA(NULL,&nbsp;"我是正常弹窗",&nbsp;"Original", MB_OK);
&nbsp; &nbsp;&nbsp;// 4. 卸载 Hook&nbsp; &nbsp;&nbsp;UninstallHook();
&nbsp; &nbsp;&nbsp;// 5. 验证恢复&nbsp; &nbsp; std::cout <<&nbsp;"--- 3. 卸载后调用 ---"&nbsp;<< std::endl;&nbsp; &nbsp;&nbsp;MessageBoxA(NULL,&nbsp;"我是正常弹窗",&nbsp;"Original", MB_OK);
&nbsp; &nbsp;&nbsp;return&nbsp;0;}

运行结果如图所示:

Part3 x64 Inline Hook

  • ### 64 位的三大变化

| 特性 | 32位 (x86) | 64位 (x64) | 影响 | | — | — | — | — | | 指针大小 | 4 字节 | 8 字节 | 地址必须用 uintptr_t 存储。 | | 跳转指令 | E9 (相对) | FF 25 (绝对) | E9 只能跳 $\pm 2GB$,x64 内存太大,常用绝对跳转。 | | Hook 长度 | 5 字节 | 14 字节 | 需要破坏更多指令,风险增加。 |

  • ### 难点:14 字节绝对跳转 (Shellcode)

x64 无法使用简单的 JMP,必须构建一段 Stub 代码来实现长距离跳转。

x64 跳转机器码结构 (14 Bytes):

Opcode: FF&nbsp;25&nbsp;00&nbsp;00&nbsp;00&nbsp;00&nbsp;(JMP&nbsp;[RIP+0])Addr: &nbsp; 00 11 22 33 44 55 66 77 (8字节绝对地址)

完整项目代码:

#include&nbsp;<windows.h>#include&nbsp;<iostream>
// ==========================================// 1. 定义函数指针与全局变量// ==========================================
// 定义 MessageBoxA 的函数原型typedef&nbsp;int&nbsp;(WINAPI* PMessageBoxA)(HWND, LPCSTR, LPCSTR, UINT);
// 保存原函数地址void* g_pOrgMsgBox =&nbsp;NULL;
// 保存跳板的内存地址unsigned&nbsp;char* g_pTrampoline =&nbsp;NULL;
// 【变化1】x64 绝对跳转 Shellcode 模板 (14 字节)// 机器码含义: JMP [RIP+0] -> 读取紧随其后的 8 字节地址并跳转unsigned&nbsp;char&nbsp;g_JmpShellcode[14] = {&nbsp; &nbsp;&nbsp;0xFF,&nbsp;0x25,&nbsp;0x00,&nbsp;0x00,&nbsp;0x00,&nbsp;0x00,&nbsp;// [0-5] JMP [RIP+0]&nbsp; &nbsp;&nbsp;0x00,&nbsp;0x00,&nbsp;0x00,&nbsp;0x00,&nbsp;0x00,&nbsp;0x00,&nbsp;0x00,&nbsp;0x00&nbsp;// [6-13] 占位符:8字节绝对地址};
// ==========================================// 2. 我们的 Hook 函数 (Payload)// ==========================================int&nbsp;WINAPI&nbsp;MyMessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType)&nbsp;{&nbsp; &nbsp; std::cout <<&nbsp;"[!] [HOOK] 拦截成功!(x64 模式)"&nbsp;<< std::endl;
&nbsp; &nbsp;&nbsp;// 篡改参数&nbsp; &nbsp; LPCSTR newText =&nbsp;"恭喜!你成功完成了 x64 Inline Hook!";&nbsp; &nbsp; LPCSTR newCaption =&nbsp;"x64 Hook 演示";
&nbsp; &nbsp;&nbsp;// 调用跳板&nbsp; &nbsp;&nbsp;// 必须强转为函数指针&nbsp; &nbsp; PMessageBoxA pOriginal = (PMessageBoxA)g_pTrampoline;&nbsp; &nbsp;&nbsp;return&nbsp;pOriginal(hWnd, newText, newCaption, uType);}
// ==========================================// 3. 安装 Hook (x64 升级版逻辑)// ==========================================void&nbsp;InstallHook()&nbsp;{&nbsp; &nbsp; HMODULE hUser32 =&nbsp;LoadLibraryA("user32.dll");&nbsp; &nbsp; g_pOrgMsgBox = (void*)GetProcAddress(hUser32,&nbsp;"MessageBoxA");
&nbsp; &nbsp;&nbsp;if&nbsp;(!g_pOrgMsgBox) {&nbsp; &nbsp; &nbsp; &nbsp; std::cout <<&nbsp;"[-] 无法找到 MessageBoxA"&nbsp;<< std::endl;&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return;&nbsp; &nbsp; }&nbsp; &nbsp; std::cout <<&nbsp;"[*] MessageBoxA 地址: 0x"&nbsp;<< g_pOrgMsgBox << std::endl;
&nbsp; &nbsp;&nbsp;// --- 步骤 1: 准备跳板 (VirtualAlloc) ---
&nbsp; &nbsp;&nbsp;// 申请内存 (代码与 32 位完全一样,VirtualAlloc 自动处理 64 位地址)&nbsp; &nbsp; g_pTrampoline = (unsigned&nbsp;char*)VirtualAlloc(NULL,&nbsp;4096, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
&nbsp; &nbsp;&nbsp;if&nbsp;(!g_pTrampoline) {&nbsp; &nbsp; &nbsp; &nbsp; std::cout <<&nbsp;"[-] 内存申请失败"&nbsp;<< std::endl;&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return;&nbsp; &nbsp; }&nbsp; &nbsp; std::cout <<&nbsp;"[+] 跳板内存申请成功: 0x"&nbsp;<< (void*)g_pTrampoline << std::endl;
&nbsp; &nbsp;&nbsp;// --- 步骤 2: 填充跳板内容 ---
&nbsp; &nbsp;&nbsp;// 【变化2】备份字节数从 5 变成了 14&nbsp; &nbsp;&nbsp;// 因为我们需要覆盖 14 字节,所以必须备份 14 字节,否则原函数后半截会丢失&nbsp; &nbsp;&nbsp;memcpy(g_pTrampoline, g_pOrgMsgBox,&nbsp;14);
&nbsp; &nbsp;&nbsp;// 【变化3】构造回跳指令 (不再计算相对偏移,直接填绝对地址)&nbsp; &nbsp;&nbsp;// 目标:跳回原函数 +14 的位置&nbsp; &nbsp;&nbsp;unsigned&nbsp;char&nbsp;jumpBackCode[14];&nbsp; &nbsp;&nbsp;memcpy(jumpBackCode, g_JmpShellcode,&nbsp;14);
&nbsp; &nbsp;&nbsp;// 计算绝对地址:原函数地址 + 14&nbsp; &nbsp;&nbsp;// 注意:这里必须用 uintptr_t (unsigned long long) 来存 64 位地址&nbsp; &nbsp;&nbsp;uintptr_t&nbsp;targetAddrInOrg = (uintptr_t)g_pOrgMsgBox +&nbsp;14;
&nbsp; &nbsp;&nbsp;// 将 8 字节地址填入 Shellcode 的后半部分 ([6] 到 [13])&nbsp; &nbsp; *(uintptr_t*)(jumpBackCode +&nbsp;6) = targetAddrInOrg;
&nbsp; &nbsp;&nbsp;// 将构造好的 JMP 指令追加到跳板后面 (接在备份的 14 字节之后)&nbsp; &nbsp;&nbsp;memcpy(g_pTrampoline +&nbsp;14, jumpBackCode,&nbsp;14);
&nbsp; &nbsp;&nbsp;// --- 步骤 3: 实施 Hook (修改原函数) ---
&nbsp; &nbsp;&nbsp;// 3.1 修改内存保护属性&nbsp; &nbsp; DWORD oldProtect;&nbsp; &nbsp;&nbsp;VirtualProtect(g_pOrgMsgBox,&nbsp;14, PAGE_EXECUTE_READWRITE, &oldProtect);
&nbsp; &nbsp;&nbsp;// 3.2 构造去往 MyMessageBoxA 的跳转指令&nbsp; &nbsp;&nbsp;unsigned&nbsp;char&nbsp;jumpToHookCode[14];&nbsp; &nbsp;&nbsp;memcpy(jumpToHookCode, g_JmpShellcode,&nbsp;14);
&nbsp; &nbsp;&nbsp;// 填入我们的函数地址&nbsp; &nbsp;&nbsp;uintptr_t&nbsp;myFuncAddr = (uintptr_t)MyMessageBoxA;&nbsp; &nbsp; *(uintptr_t*)(jumpToHookCode +&nbsp;6) = myFuncAddr;
&nbsp; &nbsp;&nbsp;// 3.3 写入 JMP 指令 (覆盖原函数前 14 字节)&nbsp; &nbsp;&nbsp;memcpy(g_pOrgMsgBox, jumpToHookCode,&nbsp;14);
&nbsp; &nbsp;&nbsp;// 3.4 恢复内存保护 & 刷新缓存&nbsp; &nbsp;&nbsp;VirtualProtect(g_pOrgMsgBox,&nbsp;14, oldProtect, &oldProtect);&nbsp; &nbsp;&nbsp;FlushInstructionCache(GetCurrentProcess(), g_pOrgMsgBox,&nbsp;14);
&nbsp; &nbsp; std::cout <<&nbsp;"[+] Hook 安装完成 (x64 Absolute Jump)"&nbsp;<< std::endl;}
// ==========================================// 4. 卸载 Hook// ==========================================void&nbsp;UninstallHook()&nbsp;{&nbsp; &nbsp;&nbsp;if&nbsp;(!g_pTrampoline || !g_pOrgMsgBox)&nbsp;return;
&nbsp; &nbsp; std::cout <<&nbsp;"[*] 正在卸载 Hook..."&nbsp;<< std::endl;
&nbsp; &nbsp;&nbsp;// 恢复原函数头部的 14 字节&nbsp;&nbsp; &nbsp; DWORD oldProtect;&nbsp; &nbsp;&nbsp;VirtualProtect(g_pOrgMsgBox,&nbsp;14, PAGE_EXECUTE_READWRITE, &oldProtect);
&nbsp; &nbsp;&nbsp;memcpy(g_pOrgMsgBox, g_pTrampoline,&nbsp;14);
&nbsp; &nbsp;&nbsp;VirtualProtect(g_pOrgMsgBox,&nbsp;14, oldProtect, &oldProtect);&nbsp; &nbsp;&nbsp;FlushInstructionCache(GetCurrentProcess(), g_pOrgMsgBox,&nbsp;14);
&nbsp; &nbsp;&nbsp;// 释放跳板内存&nbsp; &nbsp;&nbsp;VirtualFree(g_pTrampoline,&nbsp;0, MEM_RELEASE);&nbsp; &nbsp; g_pTrampoline =&nbsp;NULL;
&nbsp; &nbsp; std::cout <<&nbsp;"[+] Hook 已卸载"&nbsp;<< std::endl;}
int&nbsp;main()&nbsp;{&nbsp; &nbsp;&nbsp;// 1. 正常调用&nbsp; &nbsp; std::cout <<&nbsp;"--- 1. 正常调用 ---"&nbsp;<< std::endl;&nbsp; &nbsp;&nbsp;MessageBoxA(NULL,&nbsp;"我是正常弹窗",&nbsp;"Original", MB_OK);
&nbsp; &nbsp;&nbsp;// 2. 安装 Hook&nbsp; &nbsp;&nbsp;InstallHook();
&nbsp; &nbsp;&nbsp;// 3. Hook 后调用&nbsp; &nbsp; std::cout <<&nbsp;"--- 2. Hook 后调用 ---"&nbsp;<< std::endl;&nbsp; &nbsp;&nbsp;MessageBoxA(NULL,&nbsp;"我是正常弹窗",&nbsp;"Original", MB_OK);
&nbsp; &nbsp;&nbsp;// 4. 卸载 Hook&nbsp; &nbsp;&nbsp;UninstallHook();
&nbsp; &nbsp;&nbsp;// 5. 验证恢复&nbsp; &nbsp; std::cout <<&nbsp;"--- 3. 卸载后调用 ---"&nbsp;<< std::endl;&nbsp; &nbsp;&nbsp;MessageBoxA(NULL,&nbsp;"我是正常弹窗",&nbsp;"Original", MB_OK);
&nbsp; &nbsp;&nbsp;return&nbsp;0;}
  • ### 原因:指令截断

执行,vs调试存在报错:

出现这种问题的原因:指令截断。假设MessageBoxA开头有15字节的指令。

CPU 尝试在 Trampoline 中执行指令时,遇到了第 3 条指令的那个“残缺字节”。 因为它不是一条完整的指令,CPU 抛出 Illegal Instruction 异常。

Part4 思考与总结

  • ### 为什么会发生截断?

答案:x64 指令长度不固定。当我们强行覆盖 14 字节时,第 3 条指令(假设 6 字节)的前 5 个字节被覆盖,剩下 1 个字节 孤零零地留在内存中。当 CPU 尝试执行这段被破坏的代码时,会因无法识别残缺的指令而抛出 非法指令异常 (Illegal Instruction),导致程序崩溃。

  • ### 解决方案 A:LDE (长度反汇编引擎)

原理:在 Hook 之前,先扫描函数开头的指令长度。

1.  如果前 N 条指令总长度 < 14,就继续加下一条。

2.  比如前 3 条指令加起来是 15 字节,那就备份 15 字节

3.  写入 14 字节 JMP,第 15 个字节用 NOP (0x90) 填充,防止留下垃圾代码。

  • ### 解决方案 B:中转跳板 (Relay Trampoline)

1.  申请内存:利用 VirtualAlloc 在原函数 2GB 范围内 申请一小块内存(Relay)。

2.  二级跳转

  • 第一跳(原函数 -> Relay):因为距离 < 2GB,可以使用短小的 5 字节E9 指令。这避免了 14 字节的大面积覆盖,大大降低截断风险。
  • 第二跳(Relay -> Payload):在 Relay 内存中,写入 14 字节 的 FF 25 绝对跳转指令,跳向任意远的恶意函数。

下一章,会利用方案A和方案B做进一步学习。

希水涵精选录是ABC_123运营的第2个公众号,专注于转载精心筛选的优质技术文章,同时也欢迎大家积极投稿。

Contact me: 2332887682#qq.com** OR 0day123abc#gmail.com**


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:希水涵精选录 八九 八九《精选10:Windows 安全 | Inline Hook 实战:从 x86 到 x64》

评论:0   参与:  0