Detours 作为微软研发并开源的API HOOK框架,在微软内部以及业界有着广泛的使用,正如它的官方说明一样:
Detours is a software package for re-routing Win32 APIs underneath applications. For almost twenty years, has been licensed by hundreds of ISVs and used by nearly every product team at Microsoft.
首先要介绍该框架用到的几个概念:
Source Function:用户编写的源代码
Target Function:被调用的函数(比如Windows API)
Detour Function: 自定义函数,用来替换Target Function的函数
Trampoline:从Detour Function 跳转回 Target Function 所需的指令
下面我们看一下HOOK之前的正常调用流程:
再看一下HOOK之后的流程:
为了方便理解,我们看一下伪代码示意图:
上面就是 Detours Hook API的基本原理了,下面我们在看一下源代码,是具体如何实现以上部分的。
在代码里面,如果我们想hook 一个api一般编写如下代码:
其中Old_ReadProcessMemory 与 My_ReadProcessMemory 分别为 Target Function 以及 Detour Function
看到上面的代码我们可以思考一下,函数最后为什么调用Old_ReadProcessMemory 而不是直接调用 ReadProcessMemory呢?
接下来的源码分析可以解答这个问题。
我们先分析DetourTransactionBegin这个函数
可以看到,这个函数的主要作用是修改之前已经分配的Trampoline内存的属性,确保它们是可以写入的,为之后的操作进行准备。
DetourUpdateThread函数的主要作用就是去让线程进入暂停状态,为后面的替换操作做准备。
接下来分析 DetourAttach函数
DetourAttach函数内部只有一句代码就是调用 DetourAttachEx函数。
由于DetourAttachEx函数特别长,下面的话就只指出几个关键片段
复制Target Function开始处的机器码到Trampoline
在复制到Trampoline的机器码结尾处填充跳转指令(跳转到Target Function没有被覆盖部分)
另外一个要注意的点就是 函数用数据结构保存了第一个参数
DetourAttach函数的主要作用就是把必要的信息都计算好保存起来,并没有执行真正的Hook动作
真正的Hook动作是在DetourTransactionCommit(DetourTransactionCommitEx)函数中完成的
LONG WINAPI DetourTransactionCommit()
{
return DetourTransactionCommitEx(NULL);
}
以下是关键代码片段:
将Target Function 开始处的机器码替换为跳转到Detour Function的跳转指令,同时将DetourAttach传入的第一个参数指向的地址改为Trampoline中备份的机器码
DetourAttach函数的第一个参数就传出了一个新的函数地址。
这样我们就可以回答前文的那个问题:为什么调用用Old_ReadProcessMemory 而不是直接调用 ReadProcessMemory呢?
因为Old_ReadProcessMemory 指向了Trampoline中备份的机器码,运行效果等同于被Hook前的Target Function
而ReadProcessMemory开始处的机器码已经被替换成了跳转到Detour Function的指令,如果Detour Function函数中再使用ReadProcessMemory的话就变成了死循环了~
参考文献:黑客防线2008.12《Detours源码探索》