编译与使用微软DetoursHook库
Detours是微软开发的一个监听和拦截调用函数的强大的窗口接口勾挂库.为了无需修改原代码时拦截和修改函数,它广泛应用在微软产品团队和众多独立软件开发中.
Detours在调试,监听,记录日志和分析性能等方面表现出色,已变成程序员的重要工具.这里将指导读者编译并使用Detours库,实现简单弹窗替换功能,帮助读者熟悉该库的使用技巧.
Detours是一个兼容多个窗口系列系统版本(包括窗口XP到窗口11)的工具库.它现在在MIT开源许可下发布,简化了你的使用许可进程,并允许社区使用开源工具和进程来支持其发展,当前该库的稳定版本为4.0. 1读者可如下官方链接自行下载到本地.
Detours4.0.1:https://github.com/microsoft/Detours/releases,这里
下载文件后打开目录,其中源目录下保存的是Detours库的源,而示例则是一些用例,当准备好后读者需要打开VS你命令提示符,可从开始菜单中找到VSTools``工具菜单,并在其中找到VS20XXx86本地工具命令提示或DeveloperCommandPromptforVS20XX字样,此处以x86为例,在命令提示符中跳转到Detours``源目录,运行nmake命令执行编译.
如果一切顺利,这将会编译Detours库并生成要求的二进制文件,其中包含保存有头文件信息,而lib.X86则包含detours.lib``库文件.
接着打开VS工具,新建一个可执行控制台项并配置包含引用目录及库目录.
接着来实现拦截并替换弹窗功能,在窗口中弹窗接口为MessageBoxW函数,首先需要定义OriginalMessageBoxW的指向原的MessageBoxW``函数地址的函数指针.
接着定义CustomMessageBoxW函数,在函数内首先按自定义内容替换弹窗提示,并用该参数调用OriginalMessageBoxW的原函数地址,以此来实现替代弹窗功能.
cpp
#include <iostream>
#include <Windows.h>
#include "detours.h"
#pragma comment(lib, "detours.lib")
//定义指向原`MessageBoxW``函数指针`
static int (WINAPI *OriginalMessageBoxW)(
_In_opt_ HWND hWnd,
_In_opt_ LPCWSTR lpText,
_In_opt_ LPCWSTR lpCaption,
_In_ UINT uType) = MessageBoxW;
//`自定义`的`MessageBoxW`函数,用来替换原函数
static int WINAPI CustomMessageBoxW(
_In_opt_ HWND hWnd,
_In_opt_ LPCWSTR lpText,
_In_opt_ LPCWSTR lpCaption,
_In_ UINT uType)
{
//调用原的`MessageBoxW`函数,但修改了显示的文本
return OriginalMessageBoxW(hWnd, L"hello lyshark", L"MsgBox", MB_OK);
}
接着就是封装勾挂与解挂的函数,分别定义这两个函数,其中InstallHook函数Detours事务的方式,按自定义的CustomMessageBoxW``函数指针替换原的MessageBoxW``函数指针,从而拦截并修改该函数的行为.
相反,RemoveHook函数则类似事务机制,将自定义的CustomMessageBoxW``函数指针替换回原的MessageBoxW``函数指针,以恢复函数的原行为.
cpp
//`安装勾挂`
void InstallHook()
{
//开始一个`Detours`事务
if (DetourTransactionBegin() == NO_ERROR)
{
//更新`当前线程`以准备勾挂操作
if (DetourUpdateThread(GetCurrentThread()) == NO_ERROR)
{
//按`自定义`的`函数指针`替换原`函数指针`.
if (DetourAttach(&(PVOID&)OriginalMessageBoxW, CustomMessageBoxW) == NO_ERROR)
{
//提交事务,完成勾挂安装
if (DetourTransactionCommit() == NO_ERROR)
{
printf("勾挂已成功安装.\n");
return;
}
}
}
//如果任何步骤失败,则中止事务
DetourTransactionAbort();
}
printf("`安装勾挂`失败.\n");
}
//移除勾挂
void RemoveHook()
{
//开始一个`Detours`事务
if (DetourTransactionBegin() == NO_ERROR)
{
//更新`当前线程`以准备勾挂操作
if (DetourUpdateThread(GetCurrentThread()) == NO_ERROR)
{
//将`自定义`的`函数指针`替换回原`函数指针`
if (DetourDetach(&(PVOID&)OriginalMessageBoxW, CustomMessageBoxW) == NO_ERROR)
{
//提交事务,完成勾挂移除
if (DetourTransactionCommit() == NO_ERROR)
{
printf("勾挂已成功移除.\n");
return;
}
}
}
//如果任何步骤失败,则中止事务
DetourTransactionAbort();
}
printf("移除勾挂失败.\n");
}
在程序入口处,分三次调用MessageBoxW函数,其中第一次及最后调用依次调用均在未勾挂状态下,第二次调用前InstallHook()``安装勾挂,之后再调用MessageBoxW函数,并在调用结束后RemoveHook()移除勾挂,编译这段代码.
cpp
int main(int argc, char *argv[])
{
//显示原消息框
MessageBoxW(NULL, L"hello world", L"MsgBox", MB_OK);
//`安装勾挂`并显示修改后的消息框
InstallHook();
MessageBoxW(NULL, L"hello world", L"MsgBox", MB_OK);
//移除勾挂并按原消息框恢复
RemoveHook();
MessageBoxW(NULL, L"hello world", L"MsgBox", MB_OK);
system("pause");
return 0;
}
使用x64dbg``调试器加载运行代码,并查找到程序的入口处,因此处的入口处仅是一个main(intargc,char*argv[])所以,在汇编中可直接查找三个参数的关键变量位置,找到后即可定位到入口处,此时直接跟进去就可见主函数代码;
当执行0x00321314处后则勾挂生效,当勾挂生效后则按自定义勾挂地址替换底部0x00321347处的地址,此时在其之上的入栈操作数将会失效;
继续跟进0x00321347该地址,已按自定义的弹窗位置替换该地址中的入口处,此处是一个jmp``无条件跳转,预示着将要转向.
继续跟进该转向地址,则可见反汇编``指令集,这里的代码重新入栈了新的串变量,并在入栈后调用了原MessageBoxW函数,并依次实现替换函数弹窗中的内容.
此时,当继续调用原函数时,虽函数中的提示信息为你好世界但因为勾挂生效了则会按hellolyshark更改提示信息,以此来替换与更改函数功能.
在实际应用中,一般DLL注入方式使用Detours库,这样更好地替换或修改第三方的功能,如改变弹窗提示.
该方法可更高效地应用勾挂技术,实现对目标程序行为的控制和自定义.
如如下的这段代码,当使用注入器把它注入到第三方进程中时,首先执行DLL_PROCESS_ATTACH即开始勾挂,在勾挂函数中DetourFindFunction查找到MessageBoxW函数的入口地址.并在OriginalMessageBoxW指针中保存它,并用DetourAttach勾挂它.
cpp
#include <windows.h>
#include "detours.h"
#include "detver.h"
#pragma comment(lib, "detours.lib")
//定义`MessageBoxW``函数指针`类型
static int (WINAPI *OriginalMessageBoxW)(HWND hWnd, LPCWSTR lpText, LPCWSTR lpCaption, UINT uType) = NULL;
//`自定义`的`MessageBoxW`函数
int WINAPI MyMessageBoxW(HWND hWnd, LPCWSTR lpText, LPCWSTR lpCaption, UINT uType)
{
//可在此添加`自定义`逻辑
return OriginalMessageBoxW(hWnd, L"hello lyshark", lpCaption, uType);
}
//`安装勾挂`
void InstallHooks()
{
DetourRestoreAfterWith();
//开始事务
DetourTransactionBegin();
//更新`当前线程`
DetourUpdateThread(GetCurrentThread());
//查找并拦截`MessageBoxW`函数
OriginalMessageBoxW = (int (WINAPI *)(HWND, LPCWSTR, LPCWSTR, UINT))DetourFindFunction("User32.dll", "MessageBoxW");
if (OriginalMessageBoxW != NULL)
{
//开始勾挂
DetourAttach(&(PVOID&)OriginalMessageBoxW, MyMessageBoxW);
}
//提交事务
DetourTransactionCommit();
}
//卸载勾挂
void UninstallHooks()
{
//开始事务
DetourTransactionBegin();
//更新`当前线程`
DetourUpdateThread(GetCurrentThread());
//撤销拦截`MessageBoxW`函数
if (OriginalMessageBoxW != NULL)
{
//解挂
DetourDetach(&(PVOID&)OriginalMessageBoxW, MyMessageBoxW);
}
//提交事务
DetourTransactionCommit();
}
//`DLL`入口
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
//禁止线程库调用
DisableThreadLibraryCalls(hModule);
//`安装勾挂`
InstallHooks();
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
//卸载勾挂
UninstallHooks();
break;
}
return TRUE;
}
当勾挂成功后则进程中,按hellolyshark``提示替换任何调用弹窗的提示信息,而因未替换标题栏则依然会保持原状态.
勾挂在安全领域的应用相当广泛,如可监听和拦截指定的API调用,检测解析器的行为,拦截网络通信函数,监听传输数据,拦截文件操作和注册表访问等等.