C++ Hook钩子技术·上——Detour框架
前言
你是否已经垂涎HOOK(钩子)技术已久,但是由于还怕它涉及系统底层而不敢轻易尝试。那么今天我将带你以一个HOOK框架入门C++初级进阶之路,从此让程序听你的话不再是梦。
本篇内容介绍如何使用Detour库来搭建Hook Windows系统API的框架,并对常用API MessageBoxW进行挂钩,改变弹窗标题和弹窗内容。并使用xdbg通过逆向查看Detour Hook在代码层面对原始目标挂钩程序流程的改变。
并非新技术和创新,大佬娱乐即可。
相互学习,共同进步,欢迎大家留言讨论。
一、Hook(钩子)技术
钩子技术(Hook)是一种在计算机编程中常用的技术,它允许程序拦截、修改或者扩展特定的操作或事件。在操作系统或应用程序中,钩子技术可以用于监视和干预系统级别或应用程序级别的事件流,比如键盘输入、鼠标操作、窗口消息等。
钩子技术通常分为两种类型:系统级钩子和应用程序级钩子。系统级钩子可以监控系统范围内的事件,而应用程序级钩子则只针对特定的应用程序。钩子技术在实际应用中有着广泛的用途,比如实现键盘记录器、窗口管理工具、安全软件等。
总的来说,钩子技术通过拦截系统或应用程序的事件,实现了对系统或应用程序行为的监控、修改和扩展,为软件开发和系统管理提供了便利。
二、HOOK与CallBack
钩子技术(Hook)和回调函数(Callback)在编程中都是用于实现程序的扩展和灵活性,但它们有着不同的作用和实现方式。
1.相同点
- 事件响应: 钩子技术和回调函数都用于实现对特定事件的响应和处理。
- 灵活性: 两者都可以增强程序的灵活性和扩展性,使得程序能够更好地处理不同的情况和需求。
2.差异性
- 作用:
- 钩子技术主要用于拦截、监视和修改系统级别或应用程序级别的事件,比如键盘输入、鼠标操作、窗口消息等。
- 回调函数主要用于在特定事件发生时被调用,通常用于异步编程或事件驱动编程中,比如网络请求完成后的回调函数、定时器触发后的回调函数等。
- 实现方式:
- 钩子技术通常是通过操作系统提供的接口或库函数来实现的,可以在系统级别或应用程序级别进行钩子的安装和管理。
- 回调函数是一种编程模式,通过将函数指针或函数引用传递给其他函数,以便在特定事件发生时被调用。
- 应用场景:
- 钩子技术适用于需要拦截、监视和修改事件流的情况,比如实现键盘记录器、窗口管理工具等。
- 回调函数适用于需要异步处理或事件驱动的情况,比如处理异步任务完成后的结果、处理定时器事件等。
综上所述,钩子技术和回调函数虽然都与事件响应相关,但它们的作用、实现方式和应用场景有着明显的区别。钩子技术更专注于事件的拦截和修改,而回调函数更专注于事件的响应和异步处理。
三、Detour Hook框架
Detours 是一种在 Windows 平台上实现函数钩子的库,通常称为 Detours 库。Detours 被广泛用于软件开发和系统编程中,特别是在进行程序注入、调试、监视和修改等方面。Detours 库允许开发者在运行时动态地修改已编译的二进制可执行文件中的函数调用,以便拦截并重定向到用户自定义的代码。
1.Detour 事务函数
DetourRestoreAfterWith:Detours 库提供的一个函数修饰符,用于指定在使用 Detours 库进行函数钩取后是否需要在每次函数调用后恢复钩取前的函数状态。它的作用是指定是否在每次函数调用后自动还原钩取前的状态。
DetourTransactionBegin:用于开始一个钩取事务。钩取事务是对目标函数进行钩取操作的一种逻辑单元,通过使用事务可以确保一系列钩取操作的原子性和一致性。
DetourUpdateThread:用于通知 Detours 库当前线程的状态发生了变化。在进行函数钩取后,为了确保钩取能够正确地应用到当前线程,需要使用 DetourUpdateThread 来通知 Detours 库当前线程的状态已经发生了变化【更新线程状态、确保钩取生效、解决线程同步问题】。
DetourAttach/DetourDetach:用于将目标函数进行钩取(attach)和取消钩取(detach)操作。
DetourTransactionCommit:用于提交函数钩取事务,确认钩取操作的应用。
2.Detour 使用流程
- 调用 DetourTransactionBegin 开始一个钩取事务。
- 调用 DetourUpdateThread 更新当前线程的状态,确保钩取能够及时生效。
- 使用 DetourAttach 或 DetourDetach 等函数将目标函数进行钩取/取消。
- 调用 DetourTransactionCommit 提交钩取事务,确认钩取操作的应用。
3.Detour Hook 框架
本篇文章拟使用对Windows API——MessageBoxW函数进行挂钩操作。
int
WINAPI
MessageBoxW(
_In_opt_ HWND hWnd,
_In_opt_ LPCWSTR lpText,
_In_opt_ LPCWSTR lpCaption,
_In_ UINT uType);
编译环境为VS2019 ,x86 release版本,新建动态库项目。
/*挂钩框架*/
//1.保存detour的事务
DetourRestoreAfterWith();
//2.开始处理detour的事务
DetourTransactionBegin();
//3.更新线程信息-执行事务的线程
DetourUpdateThread(GetCurrentThread());
//4.设置需要拦截的代理函数
//第一个参数二级函数指针-原函数地址
//第二个参数函数地址-代理函数的地址
DetourAttach((PVOID *)&RealMessageBox, MyMessageBox);//msg
//5.提交事务
DetourTransactionCommit();
/*卸载框架*/
//1.开始处理detour的事务
DetourTransactionBegin();
//2.更新线程信息-执行事务的线程
DetourUpdateThread(GetCurrentThread());
//3.撤销hook
//第一个参数二级函数指针-原函数地址
//第二个参数函数地址-代理函数的地址
DetourDetach((PVOID*)&RealMessageBox, MyMessageBox);
//4.提交事务
DetourTransactionCommit();
下面将根据以上内容进行实习
四、代码实现
1.Detour HOOK 框架Code
以下是Detour框架
#include "pch.h"
#include "detours.h"
#pragma comment(lib,"./libX86/detours.lib")
//msg
int(WINAPI* RealMessageBox)(HWND, LPCTSTR, LPCTSTR, UINT) = MessageBox;
int WINAPI MyMessageBox(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType)
{
return RealMessageBox(hWnd, L"你已经被Hook了!", L"Hooked", uType);
}
void StartDetourHook()
{
//1.保存detour的事务
DetourRestoreAfterWith();
//2.开始处理detour的事务
DetourTransactionBegin();
//3.更新线程信息-执行事务的线程
DetourUpdateThread(GetCurrentThread());
//4.设置需要拦截的代理函数
//第一个参数二级函数指针-原函数地址
//第二个参数函数地址-代理函数的地址
DetourAttach((PVOID *)&RealMessageBox, MyMessageBox);//msg
//5.提交事务
DetourTransactionCommit();
}
void ExitDetourHook()
{
//1.开始处理detour的事务
DetourTransactionBegin();
//2.更新线程信息-执行事务的线程
DetourUpdateThread(GetCurrentThread());
//3.撤销hook
//第一个参数二级函数指针-原函数地址
//第二个参数函数地址-代理函数的地址
DetourDetach((PVOID*)&RealMessageBox, MyMessageBox);
//4.提交事务
DetourTransactionCommit();
}
以下是dllMain内容
#include "pch.h"
void StartDetourHook();
void ExitDetourHook();
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
{
StartDetourHook();
break;
}
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
{
ExitDetourHook();
break;
}
}
return TRUE;
}
2.靶子程序(TestMFC)Code
这里仅展示部分代码,如何实现简易的MFC戳我
void CTestMFCDlg::OnBnClickedButton1()
{
// TODO: 在此添加控件通知处理程序代码
::MessageBoxW(NULL, L"弹窗出来了!", L"Infor", MB_OK);
}
五、xdbg反编译查看Detour实现原理
这里我就不展开说了,因为我也是反编译的彩笔,实现原理很简单,看了下面两张对比图就应该很明白了。
挂钩前的MessageBoxW
Detour挂钩后
总结
本篇文章介绍了使用Detour HOOK库便捷搭建HOOK框架,简单的API HOOK并非难题,其实更重要的是你要能够判断程序到底使用的那个API函数,以下是在使用HOOK需要注意的几点,很重要!
- Detour的库需要区分64 和 x86,32为dll只能注入到32位程序这个应该都是常识了吧
- Detour必须是在Release模式下才其作用。
- API函数的修饰符很重要,涉及到程序调用后的平栈问题。一定要和文档中的申明保持一致。【修饰符类似WINAPI、WSAAPI】
- 挂钩MessageBoxW并不能拦截MessageBoxA,也许是因为我是win10系统,按理讲MessageBoxA是封装了一层MessageBoxW,实际并没有。可能是封装的MessageBoxExW,我猜的,没有逆向去跟。
视频教程和detour库获取戳我