游戏逆向-2.2VEH+软件/硬件断点实现hook
在上一篇文章中概述了几种常见的inline hook,这章将会讲述通过Windows异常机制实现hook的方法。
实现异常断点hook的步骤:
1.注册veh异常处理函数
2.设置硬件断点
3.通过veh异常函数处理后实现hook的整个流程
首先说说注册veh异常处理函数
在使用veh之前,读者应自行补充Windows异常机制,这里博主不再赘述,只大概讲述流程,
Windows接收到异常后首先检查发出异常的进程是否处在调试器的调试状态下,如果没有调试器就会将异常发送到VEH异常处理函数中,如果没有经过VEH处理后查看是否发往下一个异常处理函数也就是SEH,然后再给UEH,最后再给VCH。
总的来说:调试器>VEH>SEH>UEH>VCH
在这里我们只讲述VEH就行了,其他的异常读者自行补充...
注册VEH异常处理函数很简单,通过使用微软的AddVectoredExceptionHandler()函数就完成,此API有两个参数,第一个参数如果不是0,那么自定义的异常处理函数放到最先执行;如果是0,那么自定义的异常处理函数放到最后执行,第二个参数是指向用户自定义的异常处理函数。
设置异常断点
在异常断点前需要读者了解一个Windows非常重要的结构体 PCONTEXT
读者可以通过VS查看微软对其的定义
此结构体中保存着所有寄存器的状态信息,我们可以通过图中展示的Dr0-Dr3设置硬件断点达到无痕hook的效果,Dr7用来控制Dr0-Dr3寄存器的开启状态。
这里我们可以写一个veh类来实现hook
//veh.hclass Veh
{
private:public:__int64 mdr1 = 0;//断点地址1__int64 mdr2 = 0;//断点地址2__int64 mdr3 = 0;//断点地址3__int64 mdr4 = 0;//断点地址4//软件断点地址__int64 software_breakpoint1 = 0;__int64 software_breakpoint2 = 0;__int64 software_breakpoint3 = 0;__int64 software_breakpoint4 = 0;public:Veh();~Veh();//设置异常处理函数void SetVectoredExceptionHandler(PVECTORED_EXCEPTION_HANDLER vectored_handler_pointer);//卸载异常处理函数ULONG UnSetVectoredExceptionHandler(PVECTORED_EXCEPTION_HANDLER vectored_handler_pointer);//0 创建快照失败,1 成功,2 模块句柄获取失败,3 获得线程上下文失败,4 设置线程上下文失败int SetHardWareBreakPoint(const wchar_t* main_modulename, DWORD64 dr7_statu, __int64 br1, __int64 br2, __int64 br3, __int64 br4);};
//veh.cppVeh::Veh()
{}Veh::~Veh()
{}void Veh::SetVectoredExceptionHandler(PVECTORED_EXCEPTION_HANDLER vectored_handler_pointer)
{//添加VEH 参数1=1表示插入Veh链的头部,=0表示插入到VEH链的尾部AddVectoredExceptionHandler(1, vectored_handler_pointer);
}ULONG Veh::UnSetVectoredExceptionHandler(PVECTORED_EXCEPTION_HANDLER vectored_handler_pointer)
{return RemoveVectoredExceptionHandler(vectored_handler_pointer);
}int Veh::SetHardWareBreakPoint(const wchar_t* main_modulename, DWORD64 dr7_statu, __int64 br1, __int64 br2, __int64 br3, __int64 br4)
{this->mdr1 = br1;this->mdr2 = br2;this->mdr3 = br3;this->mdr4 = br4;//遍历线程 通过openthread获取到线程环境后设置硬件断点HANDLE hTool32 = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);if (hTool32 != INVALID_HANDLE_VALUE){//线程环境结构体THREADENTRY32 thread_entry32;thread_entry32.dwSize = sizeof(THREADENTRY32);HANDLE h_hook_thread = NULL;//模块信息MODULEINFO module_info = { 0 };HANDLE target_modulehandle = GetModuleHandleW(main_modulename);if (target_modulehandle != 0){//获取模块结束地址GetModuleInformation(GetCurrentProcess(), (HMODULE)target_modulehandle, &module_info, sizeof(MODULEINFO));__int64 target_modulehandle_endaddress = ((__int64)module_info.lpBaseOfDll + module_info.SizeOfImage);//遍历线程if (Thread32First(hTool32, &thread_entry32)){do{//如果线程父进程ID为当前进程IDif (thread_entry32.th32OwnerProcessID == GetCurrentProcessId()){h_hook_thread = OpenThread(THREAD_SET_CONTEXT | THREAD_GET_CONTEXT | THREAD_QUERY_INFORMATION, FALSE, thread_entry32.th32ThreadID);// 从 ntdll.dll 中取出 ZwQueryInformationThread(FARPROC&)ZwQueryInformationThread = ::GetProcAddress(GetModuleHandle(L"ntdll"), "ZwQueryInformationThread");// 获取线程入口地址PVOID startaddr;//用来接收线程入口地址ZwQueryInformationThread(h_hook_thread, ThreadQuerySetWin32StartAddress, &startaddr, sizeof(startaddr), NULL);if (((__int64)startaddr >= (__int64)target_modulehandle) && ((__int64)startaddr <= target_modulehandle_endaddress)){OutputDebugStringA_1Param("[GN]:veh->线程起始地址:%p", startaddr);//暂停线程SuspendThread(h_hook_thread);//设置硬件断点CONTEXT thread_context = { CONTEXT_DEBUG_REGISTERS };thread_context.ContextFlags = CONTEXT_ALL;//得到指定线程的环境(上下文)if (!GetThreadContext(h_hook_thread, &thread_context)){OutputDebugStringA("[GN]:veh->获得线程上下文失败!");return 3;}thread_context.Dr0 = br1;thread_context.Dr1 = br2;thread_context.Dr2 = br3;thread_context.Dr3 = br4;thread_context.Dr7 = dr7_statu;if (!SetThreadContext(h_hook_thread, &thread_context)){OutputDebugStringA("[GN]:veh->设置线程上下文失败!");return 4;}//恢复线程ResumeThread(h_hook_thread);}CloseHandle(h_hook_thread);}} while (Thread32Next(hTool32, &thread_entry32));}CloseHandle(hTool32);OutputDebugStringA("[GN]:veh->it's over!");return true;}elsereturn 2;//模块句柄获取失败}return 0;
}
//dllmain.cppVeh* veh = nullptr;void InitVeh()
{veh = new Veh();veh->Veh::SetVectoredExceptionHandler(VectoredHandler);int ret = veh->Veh::SetHardWareBreakPoint(L"demo.exe", 0x455,GetModuleHandleA("demo.exe") + 0x1685F,GetModuleHandleA("demo.exe") + 0x500F,GetModuleHandleA("demo.exe") + 0x6EA0A,GetModuleHandleA("demo.exe") + 0x4E60);if (ret != 1){MessageBoxA(NULL, "VEH异常处理设置失败!", "警告", MB_OK);exit(-1);}
}BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{switch (ul_reason_for_call){case DLL_PROCESS_ATTACH:{CreateThread(0, 0, (LPTHREAD_START_ROUTINE)InitVeh, 0, 0, 0);break;}case DLL_THREAD_ATTACH:break;case DLL_THREAD_DETACH:break;case DLL_PROCESS_DETACH:break;default:break;}return TRUE;
}
这里设置4个硬件断点后,还可以通过int 3实现软件断点,软件断点的优点在于只破坏一个字节,同时理论上可以下无限多个hook,而硬件断点只限制在四个,当然除了int 3等,还可以通过设置Page_NoAccess达到异常hook,文章中就不再讲述另外的异常hook,如读者有兴趣了解更多可以自行补充,另外上一章的inline hook和软件断点将会留在2.3章实战中演示具体操作。
经过注册VEH异常处理函数和设置硬件断点后,基本大功告成,然后来处理异常。
通过veh异常函数处理后实现hook的整个流程
//CallBacks.cppLONG WINAPI VectoredHandler(PEXCEPTION_POINTERS ExceptionInfo)
{//硬件断点if (ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_SINGLE_STEP){if (ExceptionInfo->ExceptionRecord->ExceptionAddress == (PVOID64)veh->mdr1){ExceptionInfo->ContextRecord->Rip = veh->mdr1 + 0x05;//Rip设置异常hook跳回地址 模拟inline hook中的jmp跳回return EXCEPTION_CONTINUE_EXECUTION;}else if (ExceptionInfo->ExceptionRecord->ExceptionAddress == (PVOID64)veh->mdr2){ExceptionInfo->ContextRecord->R9 = 0;ExceptionInfo->ContextRecord->R8 = 0;ExceptionInfo->ContextRecord->Rdx = 0;ExceptionInfo->ContextRecord->Rip = veh->mdr2 + 0x08;//Rip设置异常hook跳回地址 模拟inline hook中的jmp跳回return EXCEPTION_CONTINUE_EXECUTION;}else if (ExceptionInfo->ExceptionRecord->ExceptionAddress == (PVOID64)veh->mdr3){*(__int64*)(ExceptionInfo->ContextRecord->Rsp + 0x18) = ExceptionInfo->ContextRecord->R8;*(__int64*)(ExceptionInfo->ContextRecord->Rsp + 0x10) = ExceptionInfo->ContextRecord->Rdx;*(__int64*)(ExceptionInfo->ContextRecord->Rsp + 0x08) = ExceptionInfo->ContextRecord->Rcx;ExceptionInfo->ContextRecord->Rip = veh->mdr3 + 0x0F;//Rip设置异常hook跳回地址 模拟inline hook中的jmp跳回return EXCEPTION_CONTINUE_EXECUTION;}else if (ExceptionInfo->ExceptionRecord->ExceptionAddress == (PVOID64)veh->mdr4){ExceptionInfo->ContextRecord->Rip = veh->mdr4 + 0x03;//Rip设置异常hook跳回地址 模拟inline hook中的jmp跳回return EXCEPTION_CONTINUE_EXECUTION;}}//软件断点else if (ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_BREAKPOINT){if (ExceptionInfo->ExceptionRecord->ExceptionAddress == (PVOID64)veh->software_breakpoint1){ExceptionInfo->ContextRecord->Rip = veh->software_breakpoint1 + 0x57;//Rip设置异常hook跳回地址 模拟inline hook中的jmp跳回return EXCEPTION_CONTINUE_EXECUTION;}}//设置硬件断点防止丢失else{ExceptionInfo->ContextRecord->Dr0 = veh->mdr1;ExceptionInfo->ContextRecord->Dr1 = veh->mdr2;ExceptionInfo->ContextRecord->Dr2 = veh->mdr3;ExceptionInfo->ContextRecord->Dr3 = veh->mdr4;return EXCEPTION_CONTINUE_SEARCH;}return EXCEPTION_CONTINUE_SEARCH;
}
上面代码展示了VEH接收到异常消息后,通过判断veh类中的dr四个寄存器是否为我们想hook的地址,如果是就进入异常处理,然后修改Rip使得进程恢复正常执行,如果不是则判断是否为软件断点进入处理,最后返回继续获取进程异常。
到这里无痕hook的大致流程基本完成,另外讲讲博主在使用VEH初期遇到的坑吧。
最大的问题是明明调用Veh::SetHardWareBreakPoint()返回true,但进程执行后发现VEH异常处理函数并没有收到四个硬件断点发来的异常,也就是说明硬件断点没有被设置成功,经过在各大论坛查找大神们的回复总结后解决了此问题,可以分为:
1.遍历当前进程中的所有线程,对每个线程设置一次Context,并排除自身线程:
拿到线程地址->
SuspendThread()//暂停线程->
GetThreadContext()//获取线程上下文->
设置Dr0-Dr3->
SetThreadContext()//恢复线程
2.遍历得到当前进程的主线程,设置一次Context
经过无数次的测试对比博主选择了第二个方案,也就是上面代码展示的方案,具体原因这里就不再赘述,因为实际使用的情况很复杂,需要各位读者自行测试。
到此VEH的讲解基本结束了,后面大致就是通过实战和大家讲解!最后谢谢各位读者的观看!
游戏逆向-2.2VEH+软件/硬件断点实现hook相关推荐
- 小甲鱼 OllyDbg 教程系列 (四) : 逆向 VisualSite Designer 之 硬件断点
去掉程序开始之前的界面:https://www.bilibili.com/video/av6889190?p=9 去掉关闭程序后的广告:https://www.bilibili.com/video/a ...
- 通过硬件断点对抗hook检测
前言 我们知道常见的注入方式有IAT hook.SSDT hook.Inline hook等,但其实大体上可以分为两类,一类是基于修改函数地址的hook,一类则是基于修改函数代码的hook.而基于修改 ...
- [系统安全] 二十四.逆向分析之OllyDbg调试INT3断点、反调试、硬件断点与内存断点
您可能之前看到过我写的类似文章,为什么还要重复撰写呢?只是想更好地帮助初学者了解病毒逆向分析和系统安全,更加成体系且不破坏之前的系列.因此,我重新开设了这个专栏,准备系统整理和深入学习系统安全.逆向分 ...
- 【Windows 逆向】OD 调试器工具 ( 分析 OD 硬件断点处的关键代码 | 添加硬件断点 | 关键代码 | MOV 指令 | EAX 寄存器值分析 | 使用命令查看 esi+0cc 地址 )
文章目录 一.添加硬件断点 二.关键代码 三.MOV 汇编指令格式 四.EAX 寄存器值分析 五.使用命令查看 esi+0cc 地址 一.添加硬件断点 在上一篇博客中 , 在子弹个数数据内存地址 07 ...
- 【Windows 逆向】OD 调试器工具 ( OD 调试数据时硬件断点对应的关键代码 | 删除硬件端点恢复运行 )
文章目录 前言 一.OD 调试数据时硬件断点对应的关键代码 二.删除硬件端点恢复运行 前言 在 [Windows 逆向]OD 调试器工具 ( CE 中获取子弹动态地址前置操作 | OD 中调试指定地址 ...
- 软件调试学习笔记(六)—— 硬件断点
软件调试学习笔记(六)-- 硬件断点 硬件断点 设置硬件断点 触发硬件断点 处理硬件断点 实验:硬件断点的设置与处理 硬件断点 描述: 与软件断点与内存断点不同,硬件断点不依赖被调试程序,而是依赖于C ...
- X86逆向教程10:学会使用硬件断点
本节课我们将学习硬件断点的使用技巧,硬件断点是由硬件提供给我们的一组寄存器,我们可以对这些硬件寄存器设置相应的值,然后让硬件帮我们断在需要下断点的地址上面,这就是硬件断点,硬件断点依赖于寄存器,这些寄 ...
- OD 快捷键使用大全。非常详细( 游戏逆向分析必看 )+ OD 断点 使用大全
From:https://www.cnblogs.com/YiShen/p/9742872.html OllyDBG 快捷键 OllyDbg 窗口通用快捷键 快捷键 功能 Ctrl + F2 重启程序 ...
- 调试器工作原理--CPU软件断点/硬件断点/单步执行标识
断点和单步执行是两个经常使用的调试功能,也是调试器的核心功能. 断点是调试器的最常用技术之一.其基本思想是在某一个位置设置一个陷阱,当CPU执行到此位置时,中断到调试器中,让调试者分析和调试,之后恢复 ...
最新文章
- mysql热备份还原_利用xtrabackup完成mysql的热备份与还原
- BZOJ 3170: [Tjoi 2013]松鼠聚会 切比雪夫距离
- LevelDb实现原理
- 利用NSight进行交叉编译
- 【AIX】AIX 开机自动挂载NFS共享
- WebSphere的管理员界面
- Python(五):list、tuple
- 网易音乐链接html代码,爬取网易云音乐MP3链接脚本
- 史上最全人工智能英文原版PDF教材1.03G资源包Artificial Intelligence
- 阿里大鱼短信接口PHP版,精简版阿里大鱼短信SMS发送接口PHP实例
- 支付宝接口http请求及sign加密
- 各运营商虚拟主机对比_满足您需求的最佳虚拟主机提供商
- 【拼多多】六一儿童节
- 【老王读Spring AOP-3】Spring AOP 执行 Pointcut 对应的 Advice 的过程
- 初探强化学习(14)DQN类算法的值高估问题的笔记
- linux将多个文件打包成一个文件,将多个文件打包成一个shell
- Math.sin() 与 Math.cos() 用法
- OpenCV 找出图像中最小值最大值函数minMaxLoc的使用
- [TEST] 此文用以测试样式中的各种标签
- Jasper(2)——简单使用导出PDF报表