【C++ Hook钩子技术(上)——Detour框架】如何玩转高端hook钩子技术,一个框架解决你的烦恼!便捷开发从此走上进阶之路


前言

你是否已经垂涎HOOK(钩子)技术已久,但是由于还怕它涉及系统底层而不敢轻易尝试。那么今天我将带你以一个HOOK框架入门C++初级进阶之路,从此让程序听你的话不再是梦。
本篇内容介绍如何使用Detour库来搭建Hook Windows系统API的框架,并对常用API MessageBoxW进行挂钩,改变弹窗标题和弹窗内容。并使用xdbg通过逆向查看Detour Hook在代码层面对原始目标挂钩程序流程的改变。
并非新技术和创新,大佬娱乐即可。
相互学习,共同进步,欢迎大家留言讨论。


一、Hook(钩子)技术

钩子技术(Hook)是一种在计算机编程中常用的技术,它允许程序拦截、修改或者扩展特定的操作或事件。在操作系统或应用程序中,钩子技术可以用于监视和干预系统级别或应用程序级别的事件流,比如键盘输入、鼠标操作、窗口消息等。

钩子技术通常分为两种类型:系统级钩子和应用程序级钩子。系统级钩子可以监控系统范围内的事件,而应用程序级钩子则只针对特定的应用程序。钩子技术在实际应用中有着广泛的用途,比如实现键盘记录器、窗口管理工具、安全软件等。

总的来说,钩子技术通过拦截系统或应用程序的事件,实现了对系统或应用程序行为的监控、修改和扩展,为软件开发和系统管理提供了便利。

二、HOOK与CallBack

钩子技术(Hook)和回调函数(Callback)在编程中都是用于实现程序的扩展和灵活性,但它们有着不同的作用和实现方式。

1.相同点

  • 事件响应: 钩子技术和回调函数都用于实现对特定事件的响应和处理。
  • 灵活性: 两者都可以增强程序的灵活性和扩展性,使得程序能够更好地处理不同的情况和需求。

2.差异性

  1. 作用:
  • 钩子技术主要用于拦截、监视和修改系统级别或应用程序级别的事件,比如键盘输入、鼠标操作、窗口消息等。
  • 回调函数主要用于在特定事件发生时被调用,通常用于异步编程或事件驱动编程中,比如网络请求完成后的回调函数、定时器触发后的回调函数等。
  1. 实现方式:
  • 钩子技术通常是通过操作系统提供的接口或库函数来实现的,可以在系统级别或应用程序级别进行钩子的安装和管理。
  • 回调函数是一种编程模式,通过将函数指针或函数引用传递给其他函数,以便在特定事件发生时被调用。
  1. 应用场景:
  • 钩子技术适用于需要拦截、监视和修改事件流的情况,比如实现键盘记录器、窗口管理工具等。
  • 回调函数适用于需要异步处理或事件驱动的情况,比如处理异步任务完成后的结果、处理定时器事件等。

综上所述,钩子技术和回调函数虽然都与事件响应相关,但它们的作用、实现方式和应用场景有着明显的区别。钩子技术更专注于事件的拦截和修改,而回调函数更专注于事件的响应和异步处理。

三、Detour Hook框架

Detours 是一种在 Windows 平台上实现函数钩子的库,通常称为 Detours 库。Detours 被广泛用于软件开发和系统编程中,特别是在进行程序注入、调试、监视和修改等方面。Detours 库允许开发者在运行时动态地修改已编译的二进制可执行文件中的函数调用,以便拦截并重定向到用户自定义的代码。

1.Detour 事务函数

DetourRestoreAfterWith:Detours 库提供的一个函数修饰符,用于指定在使用 Detours 库进行函数钩取后是否需要在每次函数调用后恢复钩取前的函数状态。它的作用是指定是否在每次函数调用后自动还原钩取前的状态。
DetourTransactionBegin:用于开始一个钩取事务。钩取事务是对目标函数进行钩取操作的一种逻辑单元,通过使用事务可以确保一系列钩取操作的原子性和一致性。
DetourUpdateThread:用于通知 Detours 库当前线程的状态发生了变化。在进行函数钩取后,为了确保钩取能够正确地应用到当前线程,需要使用 DetourUpdateThread 来通知 Detours 库当前线程的状态已经发生了变化【更新线程状态、确保钩取生效、解决线程同步问题】。
DetourAttach/DetourDetach:用于将目标函数进行钩取(attach)和取消钩取(detach)操作。
DetourTransactionCommit:用于提交函数钩取事务,确认钩取操作的应用。

2.Detour 使用流程

  1. 调用 DetourTransactionBegin 开始一个钩取事务。
  2. 调用 DetourUpdateThread 更新当前线程的状态,确保钩取能够及时生效。
  3. 使用 DetourAttach 或 DetourDetach 等函数将目标函数进行钩取/取消。
  4. 调用 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库获取戳我

在 `ent` 框架中,可以在 `schema` 目录下的实体定义文件中定义钩子方法。下面是一个示例: ```go package schema import ( "context" "fmt" "crypto/md5" "entgo.io/ent" "entgo.io/ent/schema/field" "entgo.io/ent/schema/mixin" ) // UserMixin 定义一个 User 实体的混合类型 type UserMixin struct { mixin.Schema } // Fields 定义 UserMixin 的字段 func (UserMixin) Fields() []ent.Field { return []ent.Field{ field.String("name").Unique(), field.String("password"), } } // Hooks 定义 UserMixin 的钩子 func (um UserMixin) Hooks() []ent.Hook { return []ent.Hook{ // 在创建或更新 User 时,对密码进行 MD5 加密 um.EncryptPassword(), } } // EncryptPassword 是一个钩子函数,用于对 User 实体的密码字段进行 MD5 加密 func (UserMixin) EncryptPassword() ent.Hook { return func(next ent.Mutator) ent.Mutator { return ent.MutateFunc(func(ctx context.Context, m ent.Mutation) (ent.Value, error) { if password, ok := m.Field("password"); ok { // 如果密码字段不为空,则进行 MD5 加密 if p, ok := password.(string); ok && p != "" { mp := fmt.Sprintf("%x", md5.Sum([]byte(p))) m.SetField("password", mp) } } return next.Mutate(ctx, m) }) } } ``` 上面的示例中,定义了一个 `UserMixin` 混合类型,它包含了一个名为 `EncryptPassword` 的钩子函数。这个钩子函数用于对 `User` 实体的 `password` 字段进行 MD5 加密。 在 `Hooks()` 方法中,使用 `um.EncryptPassword()` 方法来注册这个钩子函数,表示在创建或更新 `User` 实体时,会自动调用这个钩子函数。 这个钩子函数的具体逻辑是,当执行 `Mutation` 的 `Field()` 方法时,如果字段名为 `password`,且字段值不为空,则对字段值进行 MD5 加密,并使用 `Mutation` 的 `SetField()` 方法来设置加密后的值。最后,调用 `next.Mutate()` 方法来继续执行下一个钩子函数或实体操作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值