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
长按识别二维码关注我们