基本概念
Detours定义了三个概念:
- Target 函数:要拦截的函数,通常为 Windows 的 API。
- Trampoline 函数:Target 函数的复制品。因为 Detours 将会改写 Target 函数,所以先把 Target 函数复制保存好,一方面仍然保存 Target 函数的过程调用语义,另一方面便于以后的恢复。
- Detour 函数:用来替代 Target 函数的函数。
拦截过程
拦截前:
targetFunction:
// targetFunction 入口,下面是常见的入口处指令,保存各个寄存器上下文环境
push ebp
mov ebp, esp
push eax
push ebx
// 后续为 targetFunction 继续部分
......
拦截后:
targetFunction:
// 此时 targetFunction 入口被修改,跳转到 detourFunction
jmp detourFunction
// 后续为 targetFunction 继续部分
......
trampolineFunction:
// trampolineFunction 函数入口,开头的 5 个字节与 targetFunction 相同
push ebp
mov ebp, esp
push eax
push ebx
// 跳回去继续执行 trampolineFunction
jmp targetFunction + 5
可以看出拦截前后相比,targetFunction 的入口处 5 个字节被修改了,改成了 jmp detourFunction(该指令也占 5 个字节),这样保证其它地方调用 targetFunction 时会走到我们定义的 detourFunction。其次 trampolineFunction 的入口会被设置为和原来 targetFunction 入口处 5 个字节相同的逻辑,然后会执行 jmp targetFunction + 5 的逻辑,这样在 detourFunction 中调用 trampolineFunction,经过 trampolineFunction 的重新路由,恢复了原来 targetFunction 的调用。
代码测试
测试代码和前一篇文章中一样:
#include <Windows.h>
#include "detours/detours.h"
#pragma comment(lib, "detours.lib")
static int (WINAPI* OLD_MessageBoxW)(HWND hWnd, LPCWSTR lpText, LPCWSTR lpCaption, UINT uType) = MessageBoxW;
int WINAPI New_MessageBoxW(HWND hWnd, LPCWSTR lpText, LPCWSTR lpCation, UINT uType)
{
int ret = OLD_MessageBoxW(hWnd, L"HOOK完成", L"[测试]", uType);
return ret;
}
void HOOK()
{
DetourRestoreAfterWith();
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
DetourAttach(&(PVOID&)OLD_MessageBoxW, New_MessageBoxW);
DetourTransactionCommit();
}
void UnHook()
{
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
DetourDetach(&(PVOID&)OLD_MessageBoxW, New_MessageBoxW);
DetourTransactionCommit();
}
int main()
{
::MessageBoxW(NULL, L"正常消息框", L"测试", MB_OK);
HOOK();
::MessageBoxW(NULL, L"正常消息框", L"测试", MB_OK);
UnHook();
return 0;
}
这里测试环境为 x64,可能与上面描述稍有不同。
图中是原始的 MessageBoxW 的汇编过程,也就是上面说的 targetFunction,这里第一二行可以认为是在进行寄存器上下文准备,第三行 00007FF82ACFA007 这个地址开始函数过程。
图中是替换后的 MessageBoxW 的汇编过程,可以看出第一行变成了 jump 到一个地址。
汇编单步调试发现最后 jump 到了 New_MessageBoxW 入口,也就是 detourFunction,在 New_MessageBoxW 中最后调用了 OLD_MessageBoxW,也就是上面说的 trampolineFunction。
在调用 OLD_MessageBoxW 过程中,首先进行了寄存器上下文的准备,这和原始 MessageBoxW 汇编调用第一二行是相同的,然后进行了跳转。
继续汇编调试,显示跳转的地址就是 00007FF82ACFA007,也就是原始 MessageBoxW 汇编开始函数过程的地方,这样就实现了恢复原始函数调用的目的。
关注公众号:C++学习与探索,有惊喜哦~