微软Detours Hook库编译与使用

Detours 是微软开发的一个强大的Windows API钩子库,用于监视和拦截函数调用。它广泛应用于微软产品团队和众多独立软件开发中,旨在无需修改原始代码的情况下实现函数拦截和修改。Detours 在调试、监控、日志记录和性能分析等方面表现出色,已成为开发者的重要工具。本章将指导读者编译并使用Detours库,通过实现一个简单的弹窗替换功能,帮助读者熟悉该库的使用技巧。

Detours 是一个兼容多个Windows系列操作系统版本(包括 Windows XPWindows 11)的工具库。它现在在 MIT 开源许可证下发布,简化了开发者的使用许可流程,并允许社区利用开源工具和流程来支持其发展,目前该库的稳定版本为4.0.1读者可通过如下官方链接自行下载到本地。

下载文件后打开目录,其中src目录下存储的是Detours库的源代码,而samples则是一些使用案例,当准备就绪后读者需要打开Visual Studio开发者命令提示符,你可以从开始菜单中找到Visual Studio Tools工具菜单,并在其中找到VS20XX x86 本机工具命令提示Developer Command Prompt for VS 20XX字样,此处以x86为例,在命令提示符中跳转到Detours源代码目录,运行 nmake 命令执行编译。

如果一切顺利,这将会编译Detours库并生成所需的二进制文件,其中include保存有头文件信息,而lib.X86则包含有detours.lib库文件,如下图中所示。

接着我们打开Visual Studio工具,新建一个可执行控制台项目并配置包含引用目录及库目录,如下图所示;

接着我们来实现拦截并替换弹窗功能,在Windows中弹窗接口为MessageBoxW函数,首先需要定义OriginalMessageBoxW的函数指针,该指针用于指向原始的MessageBoxW函数地址。接着定义CustomMessageBoxW函数,在函数内首先将弹窗提示替换为自定义内容,并携带该参数调用OriginalMessageBoxW原函数地址,以此来实现替代弹窗功能。

c 复制代码
#include <iostream>
#include <Windows.h>
#include "detours.h"

#pragma comment(lib, "detours.lib")

// 定义指向原始 MessageBoxW 函数的指针
static int (WINAPI *OriginalMessageBoxW)(
	_In_opt_ HWND hWnd,
	_In_opt_ LPCWSTR lpText,
	_In_opt_ LPCWSTR lpCaption,
	_In_ UINT uType) = MessageBoxW;

// 自定义的 MessageBoxW 函数,用于替换原始函数
static int WINAPI CustomMessageBoxW(
	_In_opt_ HWND hWnd,
	_In_opt_ LPCWSTR lpText,
	_In_opt_ LPCWSTR lpCaption,
	_In_ UINT uType)
{
	// 调用原始的 MessageBoxW 函数,但修改了显示的文本
	return OriginalMessageBoxW(hWnd, L"hello lyshark", L"MsgBox", MB_OK);
}

接着就是对挂钩与摘钩的函数封装,分别定义这两个函数,其中InstallHook 函数通过Detours事务的方式,将原始的 MessageBoxW 函数指针替换为自定义的 CustomMessageBoxW 函数指针,从而拦截并修改该函数的行为。相反,RemoveHook 函数则通过类似的事务机制,将自定义的 CustomMessageBoxW 函数指针替换回原始的 MessageBoxW 函数指针,以恢复函数的原始行为。

c 复制代码
// 安装钩子
void InstallHook()
{
	// 开始一个 Detours 事务
	if (DetourTransactionBegin() == NO_ERROR)
	{
		// 更新当前线程以准备进行钩子操作
		if (DetourUpdateThread(GetCurrentThread()) == NO_ERROR)
		{
			// 将原始函数指针替换为自定义的函数指针
			if (DetourAttach(&(PVOID&)OriginalMessageBoxW, CustomMessageBoxW) == NO_ERROR)
			{
				// 提交事务,完成钩子安装
				if (DetourTransactionCommit() == NO_ERROR)
				{
					printf("钩子已成功安装。\n");
					return;
				}
			}
		}
		// 如果任何步骤失败,则中止事务
		DetourTransactionAbort();
	}
	printf("安装钩子失败。\n");
}

// 移除钩子
void RemoveHook()
{
	// 开始一个 Detours 事务
	if (DetourTransactionBegin() == NO_ERROR)
	{
		// 更新当前线程以准备进行钩子操作
		if (DetourUpdateThread(GetCurrentThread()) == NO_ERROR)
		{
			// 将自定义的函数指针替换回原始函数指针
			if (DetourDetach(&(PVOID&)OriginalMessageBoxW, CustomMessageBoxW) == NO_ERROR)
			{
				// 提交事务,完成钩子移除
				if (DetourTransactionCommit() == NO_ERROR)
				{
					printf("钩子已成功移除。\n");
					return;
				}
			}
		}
		// 如果任何步骤失败,则中止事务
		DetourTransactionAbort();
	}
	printf("移除钩子失败。\n");
}

在程序入口处,我们分三次调用MessageBoxW函数,其中第一次调用及最后依次调用均在未挂钩状态下进行,第二次调用之前通过InstallHook()安装钩子,之后再调用MessageBoxW函数,并在调用结束后通过RemoveHook()移除钩子,编译这段代码。

c 复制代码
int main(int argc, char *argv[])
{
	// 显示原始的消息框
	MessageBoxW(NULL, L"hello world", L"MsgBox", MB_OK);

	// 安装钩子并显示修改后的消息框
	InstallHook();
	MessageBoxW(NULL, L"hello world", L"MsgBox", MB_OK);

	// 移除钩子并恢复为原始的消息框
	RemoveHook();
	MessageBoxW(NULL, L"hello world", L"MsgBox", MB_OK);

	system("pause");
	return 0;
}

使用x64dbg调试器加载运行代码,并寻找到程序的入口处,由于此处的入口处仅仅是一个main(int argc, char *argv[])所以,在汇编中我们可以直接寻找三个参数的关键变量位置,找到后即可定位到入口处,此时直接跟进去就可以看到主函数代码;

如下图中所示,当0x00321314处被执行后则钩子生效,当钩子生效后则底部0x00321347处的地址将被替换为自定义钩子地址,此时在其之上的入栈操作数将会失效;

继续跟进0x00321347这个地址,如下图所示该地址中的入口处已被替换为我们自定义的弹窗位置,此处是一个jmp无条件跳转,预示着将要转向。

我们继续跟进这个转向地址,则可看到如下图所示的反汇编指令集,这里的代码重新入栈了新的字符串变量,并在入栈后调用了原始MessageBoxW函数,并依次来实现替换函数弹窗中的内容。

此时,当继续调用原始函数时,虽函数中的提示信息为hello world但由于挂钩生效了则提示信息会被变更为hello lyshark,以此来实现对函数功能的替换与更正。

在实际应用中,我们通常通过 DLL 注入的方式使用 Detours 库,以便更好地实现对第三方程序的功能替换或修改,例如改变弹窗提示。这种方法能够更高效地应用 Hook 技术,实现对目标程序行为的控制和定制。

例如如下所示的这段代码,当使用注入器将其注入到第三方进程中时,首先DLL_PROCESS_ATTACH将被执行也就是开始挂钩,在挂钩函数中通过DetourFindFunction寻找到MessageBoxW函数的入口地址,并将其存储到OriginalMessageBoxW指针中,并通过DetourAttach对其进行挂钩。

c 复制代码
#include <windows.h>
#include "detours.h"
#include "detver.h"

#pragma comment(lib, "detours.lib")

// 定义 MessageBoxW 函数指针类型
static int (WINAPI *OriginalMessageBoxW)(HWND hWnd, LPCWSTR lpText, LPCWSTR lpCaption, UINT uType) = NULL;

// 自定义的 MessageBoxW 函数
int WINAPI MyMessageBoxW(HWND hWnd, LPCWSTR lpText, LPCWSTR lpCaption, UINT uType)
{
	// 可以在这里添加自定义逻辑
	return OriginalMessageBoxW(hWnd, L"hello lyshark", lpCaption, uType);
}

// 安装钩子
void InstallHooks()
{
	DetourRestoreAfterWith();

	// 开始事务
	DetourTransactionBegin();

	// 更新当前线程
	DetourUpdateThread(GetCurrentThread());

	// 查找并拦截 MessageBoxW 函数
	OriginalMessageBoxW = (int (WINAPI *)(HWND, LPCWSTR, LPCWSTR, UINT))DetourFindFunction("User32.dll", "MessageBoxW");
	if (OriginalMessageBoxW != NULL)
	{
		// 开始挂钩
		DetourAttach(&(PVOID&)OriginalMessageBoxW, MyMessageBoxW);
	}

	// 提交事务
	DetourTransactionCommit();
}

// 卸载钩子
void UninstallHooks()
{
	// 开始事务
	DetourTransactionBegin();

	// 更新当前线程
	DetourUpdateThread(GetCurrentThread());

	// 撤销拦截 MessageBoxW 函数
	if (OriginalMessageBoxW != NULL)
	{
		// 摘除钩子
		DetourDetach(&(PVOID&)OriginalMessageBoxW, MyMessageBoxW);
	}

	// 提交事务
	DetourTransactionCommit();
}

// DLL 入口点
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
	switch (ul_reason_for_call)
	{
	case DLL_PROCESS_ATTACH:
		// 禁用线程库调用
		DisableThreadLibraryCalls(hModule);
		// 安装钩子
		InstallHooks();
		break;
	case DLL_THREAD_ATTACH:
		break;
	case DLL_THREAD_DETACH:
		break;
	case DLL_PROCESS_DETACH:
		// 卸载钩子
		UninstallHooks();
		break;
	}
	return TRUE;
}

当挂钩成功后则进程中任何调用弹窗的提示信息都将被替换成hello lyshark提示,而标题栏因未被替换则依然会保持原始状态,如下图是注入之前与注入之后的提示变化;

至此本章内容结束,其实Hook在安全领域的应用相当广泛,例如可以监控和拦截指定的API调用,检测分析程序的行为,拦截网络通信函数,监控数据传输,拦截文件操作和注册表访问等等,本文也只是抛砖引玉让读者能认识Detours库。更多有用的案例可自行参考samples目录下的内容学习。

相关推荐
捕鲸叉2 小时前
创建线程时传递参数给线程
开发语言·c++·算法
A charmer2 小时前
【C++】vector 类深度解析:探索动态数组的奥秘
开发语言·c++·算法
Peter_chq2 小时前
【操作系统】基于环形队列的生产消费模型
linux·c语言·开发语言·c++·后端
青花瓷4 小时前
C++__XCode工程中Debug版本库向Release版本库的切换
c++·xcode
幺零九零零5 小时前
【C++】socket套接字编程
linux·服务器·网络·c++
捕鲸叉5 小时前
MVC(Model-View-Controller)模式概述
开发语言·c++·设计模式
徒步僧6 小时前
ThingsBoard规则链节点:RPC Call Reply节点详解
qt·microsoft·rpc
Dola_Pan6 小时前
C++算法和竞赛:哈希算法、动态规划DP算法、贪心算法、博弈算法
c++·算法·哈希算法
yanlou2336 小时前
KMP算法,next数组详解(c++)
开发语言·c++·kmp算法
小林熬夜学编程6 小时前
【Linux系统编程】第四十一弹---线程深度解析:从地址空间到多线程实践
linux·c语言·开发语言·c++·算法