封装Detours用于Python中x64函数hook
Detours
代码仓库: https://github.com/microsoft/Detours
x64写一个任意地址hook要比x86麻烦的多,所以这里直接封装框架来用于x64的hook。
Detours是微软发布的一个API hook框架,同时支持x86和x64,看文档说也支持ARM和ARM64的Windows。
编译文档
Detours翻了下github,并没有发现什么编译文档,就只有README里面有这么一段话:
大概意思是说打开visual studio
的命令行,然后切换到源码目录,执行nmake就能编译,测试除了一个小问题确实是可以编译成功。
visual studio
的命令行在开始菜单里打开,上面两个是x86的,下面两个是x64的,至于x64和x86_x64有啥区别我就不清楚了,因为测试编译的时候没什么不一样的,就懒得去搜了。
错误
编译的时候有一个错误: 'sn' 不是内部或外部命令,也不是可运行的程序
搜索发现这是一个秘钥管理和签名验证的工具,而visual studio
安装完后就已经有了,那就是没加入到环境变量里,用everything搜索一下,把这个路径添加到环境变量。我就临时用set命令来设置环境变量
开始编译
如果要编译64位就打开64位的命令行,
set path=C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8 Tools\x64;%path%
nmake
或者nmake all
(可以从cmake文件里看到,还有nmake clean
、nmake test
等)
这样就编译成功了,然后就会在bin.X64和lib.X64生成相应的文件,需要的是lib.X64下面的lib库
怎么编译Debug版本
这样编译出来的版本应该是Release版本,因为没有pdb文件,看了下cmake文件里的信息,指定Debug版本的变量是DETOURS_CONFIG
, 而指定是x86、x64、arm的变量是DETOURS_TARGET_PROCESSOR
!IF "$(DETOURS_CONFIG)" == "Debug"
DETOURS_DEBUG=1
!ELSE
DETOURS_DEBUG=0
!ENDIF
那么只需要在编译之前执行下set DETOURS_CONFIG=Debug
就可以编译成Debug版本的了,会生成一个lib.X64Debug
目录,里面编译出来的lib就有了pdb文件
编译成dll
Detours的使用很简单,几行代码就行了,srcFunc是hook的函数指针的指针,注意这是二级指针(PVOID就是void *
),具体为什么要定义二级指针看下面的解释。newFunc是新函数的函数指针,在c++里的话,可以直接传函数名。
#include "detours.h"
DWORD DetourHookFunction(PVOID* srcFunc, PVOID newFunc) {
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
DetourAttach(srcFunc, newFunc);
DetourTransactionCommit();
return 0;
}
DWORD DetourUnHookFunction(PVOID* srcFunc, PVOID newFunc) {
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
DetourDetach(srcFunc, newFunc);
DetourTransactionCommit();
return 0;
}
为了避免大家还不知道,我说一下怎么引入头文件和lib库
引入外部头文件
如果就想Detours就两个头文件,你可以直接添加到项目里,如果头文件比较多的话,放到dllmain.cpp目录下,然后在右键属性里,配置属性->C/C++->常规->附加包含目录添加这个头文件目录,我这里用$(ProjectDir)
来表示项目的目录, 你也可以添加绝对路径。
注意上面配置(C)
选所有配置,平台(P)
选所有平台,避免每个配置都要添加一遍
lib库则是在链接器->常规->附加库目录里添加
你可以在输入
里填lib库的名称(detours.lib)
不过我更喜欢在代码里用#pragma comment(lib, "detours.lib")
来引入lib库,因为这样可以根据宏来分别引入Debug还是Release的lib
#ifdef _DEBUG
#pragma comment(lib, "detours.lib")
#else
#pragma comment(lib, "detoursd.lib")
#endif
也可以判断是x86还是x64
#ifdef _WIN64
#pragma comment(lib, "detours64.lib")
#else
#pragma comment(lib, "detours32.lib")
#endif
也可以两个宏都判断一下。
hook函数为啥要定义成二级指针
我为啥要定义成二级指针?因为DetourAttach
函数就是传的二级指针,它的定义如下:
LONG WINAPI DetourAttach(_Inout_ PVOID *ppPointer,
_In_ PVOID pDetour);
为啥DetourAttach要把它定义成二级指针? 开始我也不理解这个问题,直到我想在Python里调用原函数,发生了无限递归的异常。
为啥C++写的代码不会触发无限递归,它同样是用的函数指针来调用,要想知道原因只能自己使用x64dbg调试看看。
经过漫长的调试发现,我传进去的ppPointer指针指向的值会被修改,它不在指向原函数(被hook函数),而是指向Detours新构建的一个函数指针,这也就能解释为啥不会无限递归了
这里再接上【Python微信机器人】第六七篇: 封装32位和64位Python hook框架实战打印微信日志
这篇文章留的一个坑: 如何在新函数里调用原函数。
c_log_addr就是我传给DetourAttach的第一个参数ppPointer,我通过调用Detours修改后的函数指针也就是c_log_addr.value
就可以避免无限递归的问题
你可以在hook前和hook后打印下c_log_addr.value
的值看一下,肯定是不一样的,
Python使用Detours
这个就看【Python微信机器人】第六七篇: 封装32位和64位Python hook框架实战打印微信日志
这篇文章了,里面说了怎么用Detours的dll hook日志