Detours中文帮助

来源:互联网 发布:淘宝双12活动力度 编辑:程序博客网 时间:2024/04/20 15:26

Detours概述

(翻译: liutao_free@sohu.com)

Microsoft Research Detours包概述

Detours是一个字x86机器上拦截任意Win32二进制函数的库。拦截代码在运行时动态的注入。Detours使用用户提供的拦截函数将一个无条件跳转指令替换目标函数起始的少数指令。拦截代码将目标函数替换为跳板函数。跳板函数的地址被放在目标函数指针中。拦截函数能够替换目标函数,也可以在跳板函数中通过目标函数指针作为子调用来执行目标函数,从而扩展目标函数的语义。

Detours在执行的时候被插入。目标函数的代码在内存中被修改,而不是磁盘,因此使其在一个非常精细的粒度中拦截二进制函数。例如,一个可执行程序中的某个DLL中的处理过程被拦截,而同时在其它的可执行程序中的这个DLL中的这个处理过程不会被拦截。不像DLL重链接或静态定向,Detours库中的拦截技术,不论函数是在应用程序中还是系统中,都可以有保障的定位目标函数。

除了基本的拦截功能外,Detours也包含编辑任何库的DLL导入表、附加任意的数据节到已经存在的二进制程序以及为为新进程加载一个DLL的功能。一旦被加载到进程中,被加载的DLL可以拦截这个进程中的任何函数,无论是应用程序库还是像Windows API这样的系统库函数。

Detours技术概述被分为三章:

  • 二进制函数的拦截
  • Detours的使用
  • 有效负载和DLL导入编辑

Detours的开发新手会发现完整的阅读上述三章以及简单实例是很有帮助的。

这个文档也包含下面的信息:

  • Detours API参考
  • 使用Detours API的编程实例
  • 频繁被问到的问题(FAQ)

 

二进制函数的拦截

Detours库能够拦截函数的调用。拦截代码在运行时动态的注入。Detours使用一个无条件跳转指令jmp替换目标函数的起始的少数指令,跳转指令跳转到用户指定的拦截函数。目标函数的指令被保存在跳板函数中。跳板由从目标函数中移除的指令和跳转到目标函数中剩余部分的跳转指令组成。

当程序执行到目标函数时,直接跳转到用户提供的拦截函数。拦截函数偷偷的进行任意的预处理。拦截函数可以返回到调用它的原函数,也可以不做拦截而调用跳板函数,跳板函数会执行目标函数。目标函数执行完成,它会返回到拦截函数。拦截函数执行适当的后置处理,并返回到调用它的原函数。图表1显示了进行和不进行拦截的逻辑流程。

f1
  图表1. 进行和不进行拦截的逻辑流程

Detours库通过重写它们进程内部的二进制映像来拦截目标函数。对于每一个目标函数,Detours实际上重写了两个函数,目标函数和与之相匹配的跳板函数,以及一个函数指针,指向目标。跳板函数由Detours动态的分配。在插入拦截代码前,跳板只包含一个跳转指令跳转到目标函数。插入拦截代码后,跳板保存目标函数的初始指令和跳转到目标函数剩余部分指令的跳转指令。

指向目标函数的指针由用户初始化为指向目标函数。在拦截代码附加到目标函数后,目标函数指针被修改为指向跳板函数。拦截代码从目标函数卸载后,目标函数指针又指回原始的目标函数。

f2  
图表2. 跳板和目标函数,在插入拦截代码之前(左面)和之后(右面)

图表2展示了拦截代码的插入。拦截一个目标函数,Detours首先为动态的跳板函数分配内存(如果没有静态的跳板函数),并使其对目标函数和跳板函数都有写权限。从第一个指令开始,Detours从目标函数拷贝5字节(足够容纳一个无条件跳转指令)的指令到跳板函数。如果目标函数小于5字节,Detours终止并返回一个错误代码。

为了拷贝指令,Detours使用一个简单的表驱动的反汇编器。Detours从跳板函数的末尾添加一个跳转指令,该指令负责从跳板函数的末尾跳转到目标函数中第一个没有被拷贝的指令。最后,Detours存储目标函数和跳板函数原始页的权限,并调用FlushInstructionCache API来刷新CPU指令缓存。

 

Detours的使用

为了拦截一个目标函数需要两个东西:一个包含目标函数地址的目标函数指针和一个拦截函数。为了正确的拦截目标函数,拦截函数和目标函数指针必须有相同的调用签名,这包括参数个数和调用约定。相同的调用约定确保寄存器被正确的维持,以及栈在拦截函数和目标函数之间被正确的平衡。

在图表5中的代码片段说明了Detours库的使用方法。用户代码必须包含detours.h头文件,链接detours.lib库,并在运行时可以访问动态链接库detoured.dll。

// 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.
BOOL WINAPI DllMain(HINSTANCE hinst, DWORD dwReason, LPVOID reserved)
{
    if (dwReason == DLL_PROCESS_ATTACH) {
        DetourTransactionBegin();
        DetourUpdateThread(GetCurrentThread());
        DetourAttach(&(PVOID&)TrueSleep, TimedSleep);
        DetourTransactionCommit();
    }
    else if (dwReason == DLL_PROCESS_DETACH) {
        DetourTransactionBegin();
        DetourUpdateThread(GetCurrentThread());
        DetourDetach(&(PVOID&)TrueSleep, TimedSleep);
        DetourTransactionCommit();
    }
    return TRUE;
}

图表5. 简单的修改Windows Sleep API的实例

与一个拦截事务一起执行DetourAttach使拦截目标函数功能生效。拦截事务通过调用DetourTransactionBegin和DetourTransactionCommit来标记。DetourAttach带有两个参数:目标函数指针的地址和指向拦截函数的指针。目标函数没有作为参数,因为它必须已经被存储在目标函数指针中。

DetourUpdatethread登记事务中的线程,使在事务提交时,它们的指令指针被正确的更新。

DetourAttach分配并且准备一个用于调用目标函数的跳板函数。这时,提交拦截事务,目标函数和跳板函数被重写并且目标函数指针被更新成指向跳板函数。

一旦目标函数被拦截,任何对目标函数的调用都会被重新定位到拦截函数。当在跳板函数中执行目标函数时,由拦截函数负责拷贝参数。为了直观,拦截函数简单的调用了目标函数。

在事务中调用DetourDetach来移除对目标函数的拦截。像DetourAttach一样,DetourDetach带有两个参数:目标函数指针的地址和指向拦截函数的指针。当拦截事务提交时,目标函数被重写为存储的原始的代码,跳板函数被删除,并且目标函数指针被设置为指向原始目标函数。

假设拦截函数需要插入到一个已经存在的没有源代码的应用程序,拦截函数应该被打包在一个DLL中。使用DetourCreateProcessWithDll在新进程创建时加载这个DLL。

注意:微软不承认或支持任何使用拦截或者其它机制的微软的或第三方的代码代码。

在Detours包中的withdll.exe程序使用DetourCreateProcessWithDll开启一个新进程并加载一个指定名称的DLL。

 

有效负载和DLL导入编辑

除了附加和卸载拦截函数的API以外,Detours包也包含被称为有效负载的API,这些API附加任意数据节到Windows二进制文件并编辑DLL导入表。Detours中二进制编辑的API都是完全可逆的;Detours使用二进制存储要恢复的信息,可以在将来任何时候移除掉所作的编辑。

f3
 图表3. 一个Windows PE二进制文件的格式

图表3显示了Window可移植的(PE)二进制文件的基本结构。Windows二进制的PE格式,是COFF(通用对象文件格式)的扩展。Windows二进制文件包含一个DOS的兼容头,一个PE头,一个包含程序代码的text节,一个包含初始数据的data节,一个列出被导入的DLL和函数的导入表,一个列出被代码导出的函数的导出表,以及调试符号。除了两个头例外,文件的其它节都是可选的,并且在给定的二进制文件中可能不存在。

f4
 图表4. Detours修改后的二进制文件格式

为了修改一个Windows二进制文件,Detours像图表4中显示的那样,在导出表和调试符号之间创建一个新的.detours节。注意,调试符号必须位于一个Windows二进制文件的末尾。新的节包含一个detour头记录和一个原始PE头的拷贝。如果修改了导入表,Detours创建一个新的导入表,并把它附加值PE头拷贝的后面,这时修改原始PE头,使它指向新的导入表。最后,Detours在.detours写入用户负载的信息并将调试符号附加到文件末尾。Detours可以通过从.detours节恢复原始PE头和移除.detours节来撤销对Windows二进制文件的修改。图表4展示了Detours修改后的Windows二进制文件的格式。

创建一个新的导入表有两个目的。第一,它维持原始导入表以免程序员需要恢复对Windows文件的修改。第二,新的导入表可以包含重命名的导入DLL和函数或完全新的DLL和函数。例如,Detours包里包含的setdll.exe程序,为用户的DLL插入一个初始项到目标二进制应用程序中。它是应用程序导入表的第项,用户的DLL总是第一个运行在应用程序的地址空间中。

Detours支持编辑导入表(DetourBinaryEditImports),增加负载(DetourBinarySetPayload),枚举负载(DetourBinaryEnumeratePayloads),和移除负载(DetourBinaryPurgePayloads)的API。Detours也支持枚举被映射到一个地址空间的二进制文件(DetourEnumerateModules)和在被映射的二进制文件中定位负载(DetourFindPayload)的API。每一个负载由一个128位的全局唯一标识符(GUID)标识。负载可以被用于附加针对应用程序的配置信息到应用程序的二进制文件中。

负载能够通过使用DetourCopyPayloadToProcess被直接拷贝到一个目标进程。

Detours频繁被问到的问题(FAQ)

这一页包含一个列表,列出了关于Detours频繁被问到的问题。这些问题根据题目和相关内容进行了分组。

兼容性

我可以在Windows95、Windows98或Windows ME中使用Detours吗?

不可以。Detours只兼容Windows NT家族的操作系统:例如,Windows NT、Windows XP,和Windows Server 2003。Detours不能工作在Windows 9x家族的操作系统上,因为它们使用原始的虚拟内存系统。

与Detours代码一起编译

我如何使用Detours做X?

注意查看Detours实例。Detours实例是非常全面。您想要实现的事情很可能已经包含在某个实例中了。

我在哪里可以找到detours.lib和detours.h?

您需要使用您的C/C++编译器在detours或detours/src目录下,输入nmake编译一个版本的detours.lib。

运行Detours

为什么我在我的拦截函数中看不到任何对malloc的调用?

可能因为目标程序没有使用您拦截的malloc函数。

像malloc这样的标准库函数既可以从一个libc*.lib静态链接也可以从一个msvcrt*.dll动态链接到程序中。当静态链接到程序时,一个程序得到它自己私有的版本的标准库函数。当动态链接时,一个程序共享DLL中版本的静态库。如果您拦截您支持的版本的函数,或者如果目标程序使用它自己私有版本的函数,您的拦截函数将不会被调用。

为什么Detours作为静态库(detours.lib)打包,而不是作为动态库(像detours.dll)打包?

作为静态库打包Detours可以最小化当您要拦截一个函数时,Detours包自身的需要和减少版本问题。注意,当静态链接到您的代码中时,Detours仅占用大概16KB。

为什么我需要包含detoured.dll?

detoured.dll文件是一个标记,它引导微软技术支持人员和工具,就像Windows OCA,帮助他们快速的检测到一个进程已经被Detours包修改。微软对第三方对微软二进制文件文件的修改不负法律责任也不作任何支持。这包括使用Detours包进行的内存内的修改。

我如何调试我的拦截DLL的启动

Windbg可以单步或中断在进程启动后的异常。Windbg在“Windows 调试工具集”(Debugging Tools for Windows)中,可以在www.microsoft.com下载它。例如,您可以使用命令行:

windbg –o withdll.exe –d:mydll.dll myexe.exe

许可证

Detours可以被使用在商业应用程序中吗?

可以。Detours有一个商业许可证。要查询Detours包的商业许可证信息,请发送email到微软的知识产权和许可证发放组iplg@microsoft.com。请在标题包含“DETOURS LICENSE REQUEST”文本字样。

Bug报告

我如何报告一个bug?

请发送bug的详细报告到detours@microsoft.com。bug报告可能被用于在Detours包的后期版本中修复bug。请在标题包含“DETOURS BUG REPORT”。在您的信息的正文,请包含README.TXT文件的第一行,它包含对您当前使用的Detours版本的描述。

在提交一个bug报告前,请您努力保证您的问题不是您代码的错误,或者错误的使用了Detours。用户通常会出现的错误被包含在这个FAQ中了。

detours@microsoft.com邮件地址只被用于bug报告,它不是产品支持热线。

 

(翻译: liutao_free@sohu.com)

原创粉丝点击