基于 Detour 实现简单的 HOOK
2023-5-15 00:0:36 Author: 白帽子(查看原文) 阅读量:19 收藏

STATEMENT

声明

由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,雷神众测及文章作者不为此承担任何责任。

雷神众测拥有对此文章的修改和解释权。如欲转载或传播此文章,必须保证此文章的完整性,包括版权声明等全部内容。未经雷神众测允许,不得任意修改或者增减此文章内容,不得以任何方式将其用于商业目的。

Detour

官方介绍 Detours 的作用是一个软件包,用于监视和检测 Windows 上的 API 调用。说人话就是通过 API Hook 技术对 API 调用过程进行中间处理,也就是监控参数和返回。

项目地址:microsoft/Detours

Detout的基本使用

零七零八

如果从GitHub上使用这个库的话,第一件事情是编译这个库,直接下载下来,然后在解压之后的目录中可以看到 vc 目录,这里面有 sln 文件,可以直接用 Microsoft Visual Studio 编译,记得由于实际使用的版本不同,编译不同的版本。其中还会编译测试用的exe文件,不过好像作用不大。

使用的时候,需要的是 include 目录中的 h 文件和编译好的 lib 文件,可以按照个人的想法,放到 Microsoft Visual Studio 的响应文件夹下,或者直接类似我下方的代码用相对路径引用。

但是都用上 Microsoft Visual Studio了,不如直接使用 NuGet 来管理依赖库,可以直接下载,使用上也可以直接 # include <detours.h>

测试代码

# include <Windows.h># include "../include/detours.h"
# if _X64# pragma comment(lib,"../lib.X64/detours.lib")# else# pragma comment(lib,"../lib.X86/detours.lib")# endif
int (WINAPI* OldMesssageBoxA)(HWND hWnd,LPCSTR lpText,LPCSTR lpCaption,UINT uType) = MessageBoxA;
int WINAPI MyFunction0(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType){ return OldMesssageBoxA(NULL, "Hooking MessageBoxA!", "Success", MB_OKCANCEL);}
void HookOn(){ //开始事务 DetourTransactionBegin(); //更新线程信息 DetourUpdateThread(GetCurrentThread()); //将拦截的函数附加到原函数的地址上,这里可以拦截多个函数。 DetourAttach(&(PVOID&)OldMesssageBoxA, MyFunction0); //结束事务 DetourTransactionCommit();}
void HookOff(){ //开始事务 DetourTransactionBegin(); //更新线程信息 DetourUpdateThread(GetCurrentThread()); //将拦截的函数从原函数的地址上解除,这里可以解除多个函数。 DetourDetach(&(PVOID&)OldMesssageBoxA, MyFunction0); //结束事务 DetourTransactionCommit();}
int main() { MessageBoxA(NULL, "Hook Me!", "Test.01", MB_OK); HookOn(); MessageBoxA(NULL, "Hook Me!", "Test.02", MB_OK); HookOff(); MessageBoxA(NULL, "Hook Me!", "Test.03", MB_OK); return 0;}

可以看到运行之后的结果如下,在HookOn()之后,我们劫持了MessageBoxA的运行流程,并且按照自己的想法进行修改。在HookOff()之后恢复了本身的函数逻辑。(这里拦截的弹窗窗口标题忘记修改了,不过不是非常影响)

接下来可以从汇编的机器码角度看 Detour 是如何实现的 Hook 的功能。

在 Hook API 之前的时候,user32.dll中的汇编代码如下

然后正常执行到修改之后,可以看到

可以跳转过去,发现劫持之后的程序流程直接返回到主函数

在这里的程序执行流程是这样的,之前的图是随机基址,每次调试地址都不一样。。下面采用的地址为固定基址。。

通过 DLL 进行 Hook(本以为是错误的演示)

上面的测试代码忘记是从哪里抄的了,稍微改了一下就直接用了。~~但是这样的代码直接照搬到 DLL 中可以直接用嘛?问题就在于上面图中的程序执行流程中关于API函数那一块,我们修改的是同一块内存嘛?~~根据操作系统的规则,加载的 DLL 用到的函数会直接从主程序所加载的 DLL 空间中获取。

在上面的代码中稍加改动,进行测试

// DLL 中的主要代码extern "C" __declspec(dllexport) void TestFuction(){   MessageBoxA(NULL, "DLL.Step.3", "DLL Run", MB_OK);   return;}
BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved){ switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: MessageBoxA(NULL, "DLL.Step.1", "DLL load", MB_OK); HookOn(); MessageBoxA(NULL, "DLL.Step.2", "Hook On", MB_OK); break; case DLL_THREAD_ATTACH: break; case DLL_THREAD_DETACH: break; case DLL_PROCESS_DETACH: HookOff(); MessageBoxA(NULL, "DLL.Step.4", "DLL Unload", MB_OK); break; } return TRUE;}// EXE 中的主要代码int main(){ MessageBoxA(NULL, "Start!", "Test.01", MB_OK); HMODULE SelfDllHandle = LoadLibraryA("DLL1.dll"); PVOID DLLExportFunction = GetProcAddress(SelfDllHandle, "TestFuction"); ((void(*)())DLLExportFunction)(); MessageBoxA(NULL, "Self!", "Test.02", MB_OK); FreeLibrary(SelfDllHandle); MessageBoxA(NULL, "End!", "Test.02", MB_OK); return 0;}

在运行之前,我们不妨先猜想一下运行逻辑。如果成功的话,应该是先正常弹窗(Test.01),然后加载我们的 Hook 用 DLL,此时先正常弹窗(DLL.Step.1)然后被 Hook 的弹窗(DLL.Step.2),对 Api 进行劫持之后主函数调用的两个弹窗(Test.02,DLL.Step.3)都被修改,然后卸载 DLL,触发 HookOff 函数,结束弹窗(DLL.Step.4),最后程序结束前的弹窗(Test.03)。实际结果与我们的猜想类似。

不过在我反复测试之后,情况出现了,在 GetProcAddress之后,执行自身的函数时,运行流进入了销毁函数。。原来一直出BUG的原因是我 switch 的代码不规范,导致程序流走到了另一块区域。。。

修正之后同样的可以用图像示意

可以清晰看到,从 HookOn 之后,API 就被我们接管,直到 HookOff 。在此期间,只要是Call到 user32.dll 中 MessageBoxA 函数的全部被劫持到自定义函数中。

安恒信息

杭州亚运会网络安全服务官方合作伙伴

成都大运会网络信息安全类官方赞助商

武汉军运会、北京一带一路峰会

青岛上合峰会、上海进博会

厦门金砖峰会、G20杭州峰会

支撑单位北京奥运会等近百场国家级

重大活动网络安保支撑单位

END

长按识别二维码关注我们


文章来源: http://mp.weixin.qq.com/s?__biz=MzAwMDQwNTE5MA==&mid=2650246697&idx=1&sn=0c1707f85e7b451b94611271d7703a49&chksm=82ea5580b59ddc96ba998974ddc5100709915adfe27f19d458713bfa0c5efad3fa30f059a143#rd
如有侵权请联系:admin#unsafe.sh