封裝Detours用于Python中x64函數(shù)hook詳解
Detours
代碼倉(cāng)庫(kù): https://github.com/microsoft/Detours
x64寫(xiě)一個(gè)任意地址hook要比x86麻煩的多,所以這里直接封裝框架來(lái)用于x64的hook。
Detours是微軟發(fā)布的一個(gè)API hook框架,同時(shí)支持x86和x64,看文檔說(shuō)也支持ARM和ARM64的Windows。
編譯文檔
Detours翻了下github,并沒(méi)有發(fā)現(xiàn)什么編譯文檔,就只有README里面有這么一段話:

大概意思是說(shuō)打開(kāi)visual studio的命令行,然后切換到源碼目錄,執(zhí)行nmake就能編譯,測(cè)試除了一個(gè)小問(wèn)題確實(shí)是可以編譯成功。

visual studio的命令行在開(kāi)始菜單里打開(kāi),上面兩個(gè)是x86的,下面兩個(gè)是x64的,至于x64和x86_x64有啥區(qū)別我就不清楚了,因?yàn)闇y(cè)試編譯的時(shí)候沒(méi)什么不一樣的,就懶得去搜了。
錯(cuò)誤
編譯的時(shí)候有一個(gè)錯(cuò)誤: 'sn' 不是內(nèi)部或外部命令,也不是可運(yùn)行的程序

搜索發(fā)現(xiàn)這是一個(gè)秘鑰管理和簽名驗(yàn)證的工具,而visual studio安裝完后就已經(jīng)有了,那就是沒(méi)加入到環(huán)境變量里,用everything搜索一下,把這個(gè)路徑添加到環(huán)境變量。我就臨時(shí)用set命令來(lái)設(shè)置環(huán)境變量

開(kāi)始編譯
如果要編譯64位就打開(kāi)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等)

這樣就編譯成功了,然后就會(huì)在bin.X64和lib.X64生成相應(yīng)的文件,需要的是lib.X64下面的lib庫(kù)
怎么編譯Debug版本
這樣編譯出來(lái)的版本應(yīng)該是Release版本,因?yàn)闆](méi)有pdb文件,看了下cmake文件里的信息,指定Debug版本的變量是DETOURS_CONFIG, 而指定是x86、x64、arm的變量是DETOURS_TARGET_PROCESSOR
!IF "$(DETOURS_CONFIG)" == "Debug" DETOURS_DEBUG=1 !ELSE DETOURS_DEBUG=0 !ENDIF
那么只需要在編譯之前執(zhí)行下set DETOURS_CONFIG=Debug就可以編譯成Debug版本的了,會(huì)生成一個(gè)lib.X64Debug目錄,里面編譯出來(lái)的lib就有了pdb文件
編譯成dll
Detours的使用很簡(jiǎn)單,幾行代碼就行了,srcFunc是hook的函數(shù)指針的指針,注意這是二級(jí)指針(PVOID就是void *),具體為什么要定義二級(jí)指針看下面的解釋。newFunc是新函數(shù)的函數(shù)指針,在c++里的話,可以直接傳函數(shù)名。
#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;
}為了避免大家還不知道,我說(shuō)一下怎么引入頭文件和lib庫(kù)
引入外部頭文件
如果就想Detours就兩個(gè)頭文件,你可以直接添加到項(xiàng)目里,如果頭文件比較多的話,放到dllmain.cpp目錄下,然后在右鍵屬性里,配置屬性->C/C++->常規(guī)->附加包含目錄添加這個(gè)頭文件目錄,我這里用$(ProjectDir)來(lái)表示項(xiàng)目的目錄, 你也可以添加絕對(duì)路徑。
注意上面配置(C)選所有配置,平臺(tái)(P)選所有平臺(tái),避免每個(gè)配置都要添加一遍

lib庫(kù)則是在鏈接器->常規(guī)->附加庫(kù)目錄里添加

你可以在輸入里填lib庫(kù)的名稱(detours.lib)

不過(guò)我更喜歡在代碼里用#pragma comment(lib, "detours.lib")來(lái)引入lib庫(kù),因?yàn)檫@樣可以根據(jù)宏來(lái)分別引入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
也可以兩個(gè)宏都判斷一下。
hook函數(shù)為啥要定義成二級(jí)指針
我為啥要定義成二級(jí)指針?因?yàn)?code>DetourAttach函數(shù)就是傳的二級(jí)指針,它的定義如下:
LONG WINAPI DetourAttach(_Inout_ PVOID *ppPointer,
_In_ PVOID pDetour);為啥DetourAttach要把它定義成二級(jí)指針? 開(kāi)始我也不理解這個(gè)問(wèn)題,直到我想在Python里調(diào)用原函數(shù),發(fā)生了無(wú)限遞歸的異常。
為啥C++寫(xiě)的代碼不會(huì)觸發(fā)無(wú)限遞歸,它同樣是用的函數(shù)指針來(lái)調(diào)用,要想知道原因只能自己使用x64dbg調(diào)試看看。
經(jīng)過(guò)漫長(zhǎng)的調(diào)試發(fā)現(xiàn),我傳進(jìn)去的ppPointer指針指向的值會(huì)被修改,它不在指向原函數(shù)(被hook函數(shù)),而是指向Detours新構(gòu)建的一個(gè)函數(shù)指針,這也就能解釋為啥不會(huì)無(wú)限遞歸了
這里再接上【Python微信機(jī)器人】第六七篇: 封裝32位和64位Python hook框架實(shí)戰(zhàn)打印微信日志這篇文章留的一個(gè)坑: 如何在新函數(shù)里調(diào)用原函數(shù)。

c_log_addr就是我傳給DetourAttach的第一個(gè)參數(shù)ppPointer,我通過(guò)調(diào)用Detours修改后的函數(shù)指針也就是c_log_addr.value就可以避免無(wú)限遞歸的問(wèn)題
你可以在hook前和hook后打印下c_log_addr.value的值看一下,肯定是不一樣的,
Python使用Detours
這個(gè)就看【Python微信機(jī)器人】第六七篇: 封裝32位和64位Python hook框架實(shí)戰(zhàn)打印微信日志這篇文章了,里面說(shuō)了怎么用Detours的dll hook日志
- 5 分鐘讀懂Python 中的 Hook 鉤子函數(shù)
- python hook監(jiān)聽(tīng)事件詳解
- python學(xué)習(xí)之hook鉤子的原理和使用
- Python實(shí)現(xiàn)監(jiān)控鍵盤(pán)鼠標(biāo)操作示例【基于pyHook與pythoncom模塊】
- 詳解Python開(kāi)發(fā)中如何使用Hook技巧
- Python利用pyHook實(shí)現(xiàn)監(jiān)聽(tīng)用戶鼠標(biāo)與鍵盤(pán)事件
- Python中使用PyHook監(jiān)聽(tīng)鼠標(biāo)和鍵盤(pán)事件實(shí)例
相關(guān)文章
Python+SeaTable實(shí)現(xiàn)計(jì)算兩個(gè)日期間的工作日天數(shù)
在實(shí)際的項(xiàng)目管理、任務(wù)管理、工作計(jì)劃等場(chǎng)景中,某些時(shí)間段會(huì)涉及雙休日、法定節(jié)假日,甚至還有公司自定義的工作時(shí)間安排,所以就需要計(jì)算出兩個(gè)日期間的實(shí)際工作日天數(shù)。本文用Python+SeaTable實(shí)現(xiàn)這一需求,需要的可以參考一下2022-07-07
PyQt5實(shí)現(xiàn)簡(jiǎn)單的計(jì)算器
這篇文章主要為大家詳細(xì)介紹了PyQt5實(shí)現(xiàn)簡(jiǎn)單的計(jì)算器,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-05-05
Python selenium文件上傳下載功能代碼實(shí)例
這篇文章主要介紹了Python selenium文件上傳下載功能代碼實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-04-04
Python實(shí)現(xiàn)端口流量轉(zhuǎn)發(fā)的示例代碼
端口流量轉(zhuǎn)發(fā)(Port Forwarding)是一種網(wǎng)絡(luò)通信技術(shù),用于將特定的網(wǎng)絡(luò)流量從一個(gè)端口或網(wǎng)絡(luò)地址轉(zhuǎn)發(fā)到另一個(gè)端口或地址,它在網(wǎng)絡(luò)中扮演著一個(gè)非常重要的角色,在Python語(yǔ)言中實(shí)現(xiàn)端口轉(zhuǎn)發(fā)非常容易,文中有相關(guān)的代碼示例,需要的朋友可以參考下2023-11-11
Django中文件上傳和文件訪問(wèn)微項(xiàng)目的方法
這篇文章主要介紹了Django中文件上傳和文件訪問(wèn)微項(xiàng)目的方法,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-04-04
Pycharm配置遠(yuǎn)程調(diào)試的方法步驟
這篇文章主要介紹了Pycharm配置遠(yuǎn)程調(diào)試的方法步驟,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-12-12
CNN卷積函數(shù)Conv2D()各參數(shù)的含義及用法解讀
這篇文章主要介紹了CNN卷積函數(shù)Conv2D()各參數(shù)的含義及用法解讀,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-02-02

