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日誌