0%
毅种循环

模块 59 - API Hooking - Detours 库

恶意软件开发课程 - API Hooking - Detours 库


模块 59 - API Hooking - Detours 库#

[!IMPORTANT] 本知识库声明

本知识库由本人整理自互联网 MalDev Academy 泄露资源,并由本人手动翻译为中文,过程中增加了大量关键技术提示实践心得

  1. 内容完整性:并未修改任何核心代码与技术逻辑,仅做汉化与注释加强。
  2. 版权归属:原始知识产权归原作者/官方所有。
  3. 支持正版:本仓库仅供内部学习交流,如果您有经济能力,请务必支持正版课程
  4. 权利申诉:如相关内容侵犯了您的权益,请联系我,我将立即核实并删除。

API Hooking - Detours 库#

简介#

Detours Hooking Library 是由微软研究院开发的一个软件库,用于在 Windows 中拦截和重定向函数调用。该库可以将特定函数的调用重定向到用户定义的替代函数,从而执行额外任务或修改原始函数的行为。Detours 通常用于 C/C++ 程序,并同时支持 32 位和 64 位应用程序。

该库的维基页面可以在 这里 找到。

事务 (Transactions)#

Detours 库通过将目标函数(即要被 Hook 的函数)的前几条指令替换为指向用户自定义函数(即要执行的 Hook 函数)的无条件跳转指令来实现重定向。这种无条件跳转也被称为“跳板”。

该库使用 事务 (Transactions) 系统来安装和卸载 Hook。事务允许将多个 Hook 操作编组为一个单一单元进行应用,这在需要同时修改多个程序行为时非常有用,且方便在出错时撤销所有更改。 一个完整的流程通常包括:开始事务 -> 添加 Hook -> 提交事务。只有在提交(Commit)后,更改才会正式生效。

使用 Detours 库#

要使用 Detours 库,首先需要从 GitHub 下载源码并编译,以获得所需的静态库文件(.lib)。此外,项目中必须包含 detours.h 头文件。详情可参考 Detours 维基中的 Using Detours 部分。

32 位与 64 位的兼容性#

为了在不同架构下自动包含正确的库文件,我们可以使用预处理指令:

// 如果编译为 64 位
#ifdef _M_X64
#pragma comment (lib, "detoursx64.lib")
#endif 

// 如果编译为 32 位
#ifdef _M_IX86
#pragma comment (lib, "detoursx86.lib")
#endif 
c

Detours 核心 API#

执行 Hook 的第一步始终是获取目标 WinAPI 函数的地址。在本模块中,我们将以 MessageBoxA 为例。以下是库提供的核心 API:

这些函数返回 LONG 类型值。执行成功返回 NO_ERROR (0),失败则返回非零值。

替换被挂钩的 API#

我们需要创建一个替代函数。该函数应具有与原始 API 相同的数据类型和参数列表。这允许我们检查或修改参数。

INT WINAPI MyMessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType) {
  // 我们可以在这里检查或修改参数:hWnd, lpText, lpCaption, uType
}
c

💡 知识扩展:参数限制

替代函数的参数个数可以少于原始函数(虽然不推荐,因为无法获取完整现场),但 绝对不能多于 原始函数。否则会导致堆栈访问越界,触发访问违规异常(Access Violation)。

无限循环问题#

这是 Hook 技术中最常见的一个坑:如果在我们的自定义函数内部又直接调用了原始的 MessageBoxA,由于它已经被挂钩,执行权会再次跳回到我们的函数中,从而形成 死循环,最终导致程序崩溃(栈溢出)。

// 错误示范:会导致死循环
INT WINAPI MyMessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType) {
  // 调用 MessageBoxA 时会再次触发 Hook 逻辑,陷入死循环
  return MessageBoxA(hWnd, "已被劫持内容", lpCaption, uType); 
}
c

解决方案 1:保存原始函数指针#

Detours 推荐的做法是:在 Hook 生效前,将原始函数的地址保存到一个全局变量中。在自定义函数内部,始终调用这个全局指针。

// 定义指向原始函数的全局指针
fnMessageBoxA g_pMessageBoxA = MessageBoxA;

INT WINAPI MyMessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType) {
  // 调用未经挂钩的“备份”指针,从而避免死循环
  return g_pMessageBoxA(hWnd, "修改后的内容", lpCaption, uType);
}
c

解决方案 2:使用功能相同的不同函数#

例如,Hook 了 MessageBoxA 之后,在内部调用 MessageBoxW

安装与卸载 Hook 流程#

演示#

  1. 调用第一次:正常弹出框。
  2. 安装 Hook。
  3. 调用第二次:内容被修改为 “different lpText”(Hook 生效)。
  4. 卸载 Hook。
  5. 调用第三次:恢复正常。

图片
图片


🎯 总结#

在本模块中,我们学习了:

  1. Detours 库入门:掌握了事务性 Hook 的操作流程。
  2. 安全性与健壮性:理解了“死循环风险”并学会了通过保存原始指针来规避它。
  3. 动态拦截:实现了在不修改二进制文件的情况下动态篡改 API 参数的能力。

💡 关键要点

  • 全局指针的重要性g_pMessageBoxA 既是 DetourAttach 的目标,也是你调用原始函数的唯一安全入口。
  • 事务一致性:记得每一个 Begin 都要对应一个 Commit

📚 下一步学习

下一个模块将介绍 API Hooking - Minhook Library。我们将学习另一个更为轻量且深受恶意软件作者喜爱的开源 Hook 库!