Detours Hook API 调用拦截技术
0x00 介绍
Detours 是 Microsoft Corporation 出品的应用程序 API 调用拦截技术项目,采用 MIT 开源协议。
Detours Github,请选中 Release 并下载源代码(好像 Release 停在 2018 不更新了,不过源代码还在更新,可以 Clone 仓库并找到需要的文件)。
0x01 找到需要的文件
先 克隆(Clone) 仓库,并使用终端软件例如 cmd 或 Windows 终端 来切换到目录,并使用 cmake 将项目转为源代码和解决方案文件,打开 .sln 文件进行编译(将在目录下产生几个文件夹,其中 LIB.X<编译平台类型> 是我们所需的文件)。
将 LIB.X* 中的所有文件复制到您需要编写的项目的目录中,并在代码头部加入以下文本:
#include "detours.h" // 您也可以将这行代码写到 "pch.h" 中。 #pragma comment(lib, "detours.lib") // 链接到 detours.lib。
0x02 开始编写代码!
0. 了解函数结构
请创建一个 Windows 动态链接库 (DLL)项目。首先您需要了解需要拦截的 API 的调用方法,比如 MessageBoxA (HWND, LPCSTR, LPCSTR, UINT)。
本文将使用 MessageBoxA 函数演示,拦截其他函数的方法大同小异(((
1. 定义结构体
了解后在代码中定义一个结构体:
// 定义原始的MessageBox函数类型 typedef int (WINAPI* MessageBoxA_t)(HWND, LPCSTR, LPCSTR, UINT); MessageBoxA_t OriginalMessageBoxA = NULL;
其中 MessageBoxA_t 是原始的 API 函数,使用它定义一个 OriginalMessageBoxA(原始的 MessageBoxA 函数)。
2. 获取原函数地址并替换为自己的函数地址
在 DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) 函数中,如果 ul_reason_for_call == DLL_PROCESS_ATTACH,那么执行以下代码:
OriginalMessageBoxA = (MessageBoxA_t)GetProcAddress(GetModuleHandle(L"user32.dll"), "MessageBoxA"); if (OriginalMessageBoxA) { DetourTransactionBegin(); DetourUpdateThread(GetCurrentThread()); DetourAttach(&(PVOID&)OriginalMessageBoxA, MyMessageBoxA); DetourTransactionCommit(); }
代码解析:
使用 GetProcAddress 获取到 MessageBoxA 的函数地址,将其备份到 OriginalMessageBoxA 中。注意需要使用“(MessageBoxA_t)”将其强制转换为 MessageBoxA_t 类型。
使用 DetourTransactionBegin() 和 DetourUpdateThread(GetCurrentThread()) 让 Detours 初始化并附着在当前线程。其中 MyMessageBoxA 是您替换的 MessageBoxA 的代码。
使用 DetourAttach(PVOID *ppPointer, PVOID pDetour) 替换掉需要的函数。之后调用 DetourTransactionCommit() 是让 Detour 函数起作用并检查函数的返回值判断是正确还是错误。
您可以参照以下的 MyMessageBoxA 代码:
int WINAPI MyMessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType) { if (strstr(lpText, "运行时出错!") != nullptr) if (OriginalMessageBoxA(hWnd, "很抱歉,程序运行时出现错误,需要终止进程。对您造成的不便,我们深感抱歉。\n\n是否跳转到产品官网?", "运行时出现错误", MB_ICONERROR | MB_YESNO) == IDYES) { system("start https://cnstlapy.cn/"); } return 0; } return OriginalMessageBoxA(hWnd, lpText, lpCaption, uType); }
代码解析:
当被注入应用程序调用 MessageBoxA(弹出信息框) 时,将会取消执行原来的 MessageBoxA 并调用我们的 MyMessageBoxA,并传入调用原函数的参数。
很显然,这是一个判断某个易语言是否运行时出错的函数代码。如果应用程序调用了 MessageBoxA(尝试弹出信息框)时,会先检查信息框的内容(lpText)是否包含“运行时出错!”的文本(代码使用的是查找文本,索引如果不是空指针的话就是找到了文本)。如果是,则弹出自己的信息框,询问用户是否跳转到官网反馈问题。如果用户点击了 是按钮(IDYES),则运行“start https://cnstlapy.cn/”,让系统调用默认浏览器打开网址。返回 0 后,易语言程序将会退出。
如果不包含“运行时出错”的文本,将会调用原始的 MessageBoxA 函数,将会正常地弹出信息框。
3. 编译 DLL
如图,在 Visual Studio 右上角选择发布类型和发布平台(发布类型选 Release),如果您需要将 DLL 注入到 32 位应用程序,那么请选择平台:x86,否则请选择 x64。
选择“▶ 本地 Windows 调试器”运行编译,编译成功后,可在文件夹中找到编译的 DLL。使用 VirtualAlloc,WriteProcessMemory 注入到进程中。此时 API 拦截程序 应当正常工作。(您可以试图触发易语言运行时错误,看看有没有弹出您的自定义信息框)
效果如下:
没有人发评论嘛www
打卡