使用Detours
为了达到拦截和截获绕过目标函数,有两件事是必要的:一个包含目标函数地址的目标指针和一个detour函数。为了正确拦截目标函数、detour函数和目标指针,必须具有完全相同的调用签名,包括参数数量和调用约定。使用相同的调用约定可以确保正确地保存寄存器,并确保在detour函数和目标函数之间正确地对齐堆栈
下面的代码片段说明了Detours库的用法。用户代码必须包含detours.h头文件并链接到detours.lib库。
#include <windows.h> #include <detours.h> static LONG dwSlept = 0; // Target pointer for the uninstrumented Sleep API. 静态长dwSlept =0; //未检测的Sleep API的目标指针。 // static VOID (WINAPI * TrueSleep)(DWORD dwMilliseconds) = Sleep; // Detour function that replaces the Sleep API. //用来替换Sleep API的Detour函数。 // VOID WINAPI TimedSleep(DWORD dwMilliseconds) { // Save the before and after times around calling the Sleep API. //保存调用Sleep API前后的次数。 DWORD dwBeg = GetTickCount(); TrueSleep(dwMilliseconds); DWORD dwEnd = GetTickCount(); InterlockedExchangeAdd(&dwSlept, dwEnd - dwBeg); //DllMain函数将TimedSleep绕道附加和分离到
} // DllMain function attaches and detaches the TimedSleep detour to the // Sleep target function. The Sleep target function is referred to // through the TrueSleep target pointer. //
//睡眠目标函数。Sleep目标函数被引用 //通过truessleep目标指针。 //
BOOL WINAPI DllMain(HINSTANCE hinst, DWORD dwReason, LPVOID reserved) { if (DetourIsHelperProcess()) { return TRUE; } if (dwReason == DLL_PROCESS_ATTACH) { DetourRestoreAfterWith(); DetourTransactionBegin(); DetourUpdateThread(GetCurrentThread()); DetourAttach(&(PVOID&)TrueSleep, TimedSleep); DetourTransactionCommit(); } else if (dwReason == DLL_PROCESS_DETACH) { DetourTransactionBegin(); DetourUpdateThread(GetCurrentThread()); DetourDetach(&(PVOID&)TrueSleep, TimedSleep); DetourTransactionCommit(); } return TRUE; }
简单的修改Windows Sleep API。
通过在一个DetourAttach事务中调用DetourAttach API来启用目标函数的拦截。通过调用DetourTransactionBegin API和DetourTransactionCommit API来标记一个detourtransaction。DetourAttach API接受两个参数:目标指针的地址和DetourAttach函数的指针。目标函数没有作为实参给出,因为它必须已经存储在目标指针中。
DetourUpdateThread API在事务中登记线程,以便在事务提交时适当地更新它们的指令指针。
DetourAttach API分配并准备一个蹦床来调用目标函数。当detour事务提交时,重写目标函数和trampoline,并更新目标指针以指向trampoline函数。
一旦绕过目标函数,对目标函数的任何调用都将通过detour函数重新路由。当通过蹦床调用目标函数时,由detour函数负责复制参数。这是很直观的,因为目标函数变成了一个简单的由detour函数调用的子例程。
通过在DetourDetach事务中调用DetourDetach API来删除对目标函数的拦截。与DetourAttach API一样,DetourDetach API也接受两个参数:目标指针的地址和指向detourfunction的指针。当detour事务提交时,目标函数被重写并恢复到原始代码,trampoline函数被删除,目标指针被恢复到指向原始目标函数。
如果需要在没有源代码访问的情况下将detour函数插入到现有的应用程序中,则应该将detour函数打包到DLL中。可以在创建时使用DetourCreateProcessWithDllEx或DetourCreateProcessWithDlls api将DLL加载到新进程中。如果使用DetourCreateProcessWithDllEx或DetourCreateProcessWithDlls插入DLL, DllMain函数必须调用DetourRestoreAfterWith API。如果DLL可以在32位和64位的混合环境中使用,那么DllMain函数必须调用DetourIsHelperProcess API。DLL必须将DetourFinishHelperProcess API导出为export Ordinal 1, rundll32.exe将调用该API来执行helper任务。
注意:微软绝不保证或支持任何通过迂回或任何其他机制更改的微软或第三方代码。
Detours包中包含的withdll.exe程序使用detourcreateprocesswithdll API来启动一个新的具有DLL的进程。