微軟Detours Hook庫(kù)編譯與使用教程
Detours 是微軟開(kāi)發(fā)的一個(gè)強(qiáng)大的Windows API鉤子庫(kù),用于監(jiān)視和攔截函數(shù)調(diào)用。它廣泛應(yīng)用于微軟產(chǎn)品團(tuán)隊(duì)和眾多獨(dú)立軟件開(kāi)發(fā)中,旨在無(wú)需修改原始代碼的情況下實(shí)現(xiàn)函數(shù)攔截和修改。Detours 在調(diào)試、監(jiān)控、日志記錄和性能分析等方面表現(xiàn)出色,已成為開(kāi)發(fā)者的重要工具。本章將指導(dǎo)讀者編譯并使用Detours庫(kù),通過(guò)實(shí)現(xiàn)一個(gè)簡(jiǎn)單的彈窗替換功能,幫助讀者熟悉該庫(kù)的使用技巧。
Detours 是一個(gè)兼容多個(gè)Windows
系列操作系統(tǒng)版本(包括 Windows XP
到 Windows 11
)的工具庫(kù)。它現(xiàn)在在 MIT 開(kāi)源許可證下發(fā)布,簡(jiǎn)化了開(kāi)發(fā)者的使用許可流程,并允許社區(qū)利用開(kāi)源工具和流程來(lái)支持其發(fā)展,目前該庫(kù)的穩(wěn)定版本為4.0.1
讀者可通過(guò)如下官方鏈接自行下載到本地。
Detours 4.0.1:https://github.com/microsoft/Detours/releases
下載文件后打開(kāi)目錄,其中src
目錄下存儲(chǔ)的是Detours
庫(kù)的源代碼,而samples
則是一些使用案例,當(dāng)準(zhǔn)備就緒后讀者需要打開(kāi)Visual Studio
開(kāi)發(fā)者命令提示符,你可以從開(kāi)始菜單中找到Visual Studio Tools
工具菜單,并在其中找到VS20XX x86 本機(jī)工具命令提示
或Developer Command Prompt for VS 20XX
字樣,此處以x86
為例,在命令提示符中跳轉(zhuǎn)到Detours
源代碼目錄,運(yùn)行 nmake
命令執(zhí)行編譯。
如果一切順利,這將會(huì)編譯Detours
庫(kù)并生成所需的二進(jìn)制文件,其中include
保存有頭文件信息,而lib.X86
則包含有detours.lib
庫(kù)文件,如下圖中所示。
接著我們打開(kāi)Visual Studio
工具,新建一個(gè)可執(zhí)行控制臺(tái)項(xiàng)目并配置包含引用目錄及庫(kù)目錄,如下圖所示;
接著我們來(lái)實(shí)現(xiàn)攔截并替換彈窗功能,在Windows
中彈窗接口為MessageBoxW
函數(shù),首先需要定義OriginalMessageBoxW
的函數(shù)指針,該指針用于指向原始的MessageBoxW
函數(shù)地址。接著定義CustomMessageBoxW
函數(shù),在函數(shù)內(nèi)首先將彈窗提示替換為自定義內(nèi)容,并攜帶該參數(shù)調(diào)用OriginalMessageBoxW
原函數(shù)地址,以此來(lái)實(shí)現(xiàn)替代彈窗功能。
#include <iostream> #include <Windows.h> #include "detours.h" #pragma comment(lib, "detours.lib") // 定義指向原始 MessageBoxW 函數(shù)的指針 static int (WINAPI *OriginalMessageBoxW)( _In_opt_ HWND hWnd, _In_opt_ LPCWSTR lpText, _In_opt_ LPCWSTR lpCaption, _In_ UINT uType) = MessageBoxW; // 自定義的 MessageBoxW 函數(shù),用于替換原始函數(shù) static int WINAPI CustomMessageBoxW( _In_opt_ HWND hWnd, _In_opt_ LPCWSTR lpText, _In_opt_ LPCWSTR lpCaption, _In_ UINT uType) { // 調(diào)用原始的 MessageBoxW 函數(shù),但修改了顯示的文本 return OriginalMessageBoxW(hWnd, L"hello lyshark", L"MsgBox", MB_OK); }
接著就是對(duì)掛鉤與摘鉤的函數(shù)封裝,分別定義這兩個(gè)函數(shù),其中InstallHook
函數(shù)通過(guò)Detours
事務(wù)的方式,將原始的 MessageBoxW
函數(shù)指針替換為自定義的 CustomMessageBoxW
函數(shù)指針,從而攔截并修改該函數(shù)的行為。相反,RemoveHook
函數(shù)則通過(guò)類(lèi)似的事務(wù)機(jī)制,將自定義的 CustomMessageBoxW
函數(shù)指針替換回原始的 MessageBoxW
函數(shù)指針,以恢復(fù)函數(shù)的原始行為。
// 安裝鉤子 void InstallHook() { // 開(kāi)始一個(gè) Detours 事務(wù) if (DetourTransactionBegin() == NO_ERROR) { // 更新當(dāng)前線(xiàn)程以準(zhǔn)備進(jìn)行鉤子操作 if (DetourUpdateThread(GetCurrentThread()) == NO_ERROR) { // 將原始函數(shù)指針替換為自定義的函數(shù)指針 if (DetourAttach(&(PVOID&)OriginalMessageBoxW, CustomMessageBoxW) == NO_ERROR) { // 提交事務(wù),完成鉤子安裝 if (DetourTransactionCommit() == NO_ERROR) { printf("鉤子已成功安裝。\n"); return; } } } // 如果任何步驟失敗,則中止事務(wù) DetourTransactionAbort(); } printf("安裝鉤子失敗。\n"); } // 移除鉤子 void RemoveHook() { // 開(kāi)始一個(gè) Detours 事務(wù) if (DetourTransactionBegin() == NO_ERROR) { // 更新當(dāng)前線(xiàn)程以準(zhǔn)備進(jìn)行鉤子操作 if (DetourUpdateThread(GetCurrentThread()) == NO_ERROR) { // 將自定義的函數(shù)指針替換回原始函數(shù)指針 if (DetourDetach(&(PVOID&)OriginalMessageBoxW, CustomMessageBoxW) == NO_ERROR) { // 提交事務(wù),完成鉤子移除 if (DetourTransactionCommit() == NO_ERROR) { printf("鉤子已成功移除。\n"); return; } } } // 如果任何步驟失敗,則中止事務(wù) DetourTransactionAbort(); } printf("移除鉤子失敗。\n"); }
在程序入口處,我們分三次調(diào)用MessageBoxW
函數(shù),其中第一次調(diào)用及最后依次調(diào)用均在未掛鉤狀態(tài)下進(jìn)行,第二次調(diào)用之前通過(guò)InstallHook()
安裝鉤子,之后再調(diào)用MessageBoxW
函數(shù),并在調(diào)用結(jié)束后通過(guò)RemoveHook()
移除鉤子,編譯這段代碼。
int main(int argc, char *argv[]) { // 顯示原始的消息框 MessageBoxW(NULL, L"hello world", L"MsgBox", MB_OK); // 安裝鉤子并顯示修改后的消息框 InstallHook(); MessageBoxW(NULL, L"hello world", L"MsgBox", MB_OK); // 移除鉤子并恢復(fù)為原始的消息框 RemoveHook(); MessageBoxW(NULL, L"hello world", L"MsgBox", MB_OK); system("pause"); return 0; }
使用x64dbg
調(diào)試器加載運(yùn)行代碼,并尋找到程序的入口處,由于此處的入口處僅僅是一個(gè)main(int argc, char *argv[])
所以,在匯編中我們可以直接尋找三個(gè)參數(shù)的關(guān)鍵變量位置,找到后即可定位到入口處,此時(shí)直接跟進(jìn)去就可以看到主函數(shù)代碼;
如下圖中所示,當(dāng)0x00321314
處被執(zhí)行后則鉤子生效,當(dāng)鉤子生效后則底部0x00321347
處的地址將被替換為自定義鉤子地址,此時(shí)在其之上的入棧操作數(shù)將會(huì)失效;
繼續(xù)跟進(jìn)0x00321347
這個(gè)地址,如下圖所示該地址中的入口處已被替換為我們自定義的彈窗位置,此處是一個(gè)jmp
無(wú)條件跳轉(zhuǎn),預(yù)示著將要轉(zhuǎn)向。
我們繼續(xù)跟進(jìn)這個(gè)轉(zhuǎn)向地址,則可看到如下圖所示的反匯編指令集,這里的代碼重新入棧了新的字符串變量,并在入棧后調(diào)用了原始MessageBoxW
函數(shù),并依次來(lái)實(shí)現(xiàn)替換函數(shù)彈窗中的內(nèi)容。
此時(shí),當(dāng)繼續(xù)調(diào)用原始函數(shù)時(shí),雖函數(shù)中的提示信息為hello world
但由于掛鉤生效了則提示信息會(huì)被變更為hello lyshark
,以此來(lái)實(shí)現(xiàn)對(duì)函數(shù)功能的替換與更正。
在實(shí)際應(yīng)用中,我們通常通過(guò) DLL 注入的方式使用 Detours 庫(kù),以便更好地實(shí)現(xiàn)對(duì)第三方程序的功能替換或修改,例如改變彈窗提示。這種方法能夠更高效地應(yīng)用 Hook 技術(shù),實(shí)現(xiàn)對(duì)目標(biāo)程序行為的控制和定制。
例如如下所示的這段代碼,當(dāng)使用注入器將其注入到第三方進(jìn)程中時(shí),首先DLL_PROCESS_ATTACH
將被執(zhí)行也就是開(kāi)始掛鉤,在掛鉤函數(shù)中通過(guò)DetourFindFunction
尋找到MessageBoxW
函數(shù)的入口地址,并將其存儲(chǔ)到OriginalMessageBoxW
指針中,并通過(guò)DetourAttach
對(duì)其進(jìn)行掛鉤。
#include <windows.h> #include "detours.h" #include "detver.h" #pragma comment(lib, "detours.lib") // 定義 MessageBoxW 函數(shù)指針類(lèi)型 static int (WINAPI *OriginalMessageBoxW)(HWND hWnd, LPCWSTR lpText, LPCWSTR lpCaption, UINT uType) = NULL; // 自定義的 MessageBoxW 函數(shù) int WINAPI MyMessageBoxW(HWND hWnd, LPCWSTR lpText, LPCWSTR lpCaption, UINT uType) { // 可以在這里添加自定義邏輯 return OriginalMessageBoxW(hWnd, L"hello lyshark", lpCaption, uType); } // 安裝鉤子 void InstallHooks() { DetourRestoreAfterWith(); // 開(kāi)始事務(wù) DetourTransactionBegin(); // 更新當(dāng)前線(xiàn)程 DetourUpdateThread(GetCurrentThread()); // 查找并攔截 MessageBoxW 函數(shù) OriginalMessageBoxW = (int (WINAPI *)(HWND, LPCWSTR, LPCWSTR, UINT))DetourFindFunction("User32.dll", "MessageBoxW"); if (OriginalMessageBoxW != NULL) { // 開(kāi)始掛鉤 DetourAttach(&(PVOID&)OriginalMessageBoxW, MyMessageBoxW); } // 提交事務(wù) DetourTransactionCommit(); } // 卸載鉤子 void UninstallHooks() { // 開(kāi)始事務(wù) DetourTransactionBegin(); // 更新當(dāng)前線(xiàn)程 DetourUpdateThread(GetCurrentThread()); // 撤銷(xiāo)攔截 MessageBoxW 函數(shù) if (OriginalMessageBoxW != NULL) { // 摘除鉤子 DetourDetach(&(PVOID&)OriginalMessageBoxW, MyMessageBoxW); } // 提交事務(wù) DetourTransactionCommit(); } // DLL 入口點(diǎn) BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: // 禁用線(xiàn)程庫(kù)調(diào)用 DisableThreadLibraryCalls(hModule); // 安裝鉤子 InstallHooks(); break; case DLL_THREAD_ATTACH: break; case DLL_THREAD_DETACH: break; case DLL_PROCESS_DETACH: // 卸載鉤子 UninstallHooks(); break; } return TRUE; }
當(dāng)掛鉤成功后則進(jìn)程中任何調(diào)用彈窗的提示信息都將被替換成hello lyshark
提示,而標(biāo)題欄因未被替換則依然會(huì)保持原始狀態(tài),如下圖是注入之前與注入之后的提示變化;
至此本章內(nèi)容結(jié)束,其實(shí)Hook在安全領(lǐng)域的應(yīng)用相當(dāng)廣泛,例如可以監(jiān)控和攔截指定的API調(diào)用,檢測(cè)分析程序的行為,攔截網(wǎng)絡(luò)通信函數(shù),監(jiān)控?cái)?shù)據(jù)傳輸,攔截文件操作和注冊(cè)表訪(fǎng)問(wèn)等等,本文也只是拋磚引玉讓讀者能認(rèn)識(shí)Detours
庫(kù)。更多有用的案例可自行參考samples
目錄下的內(nèi)容學(xué)習(xí)。
到此這篇關(guān)于微軟Detours Hook庫(kù)編譯與使用的文章就介紹到這了,更多相關(guān)微軟Detours Hook庫(kù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++模擬Linux Shell編寫(xiě)一個(gè)自定義命令
這篇文章主要介紹了C++如何模擬Linux Shell實(shí)現(xiàn)編寫(xiě)一個(gè)自定義命令,本文通過(guò)實(shí)例代碼進(jìn)行命令行解析,代碼簡(jiǎn)單易懂,對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-12-12簡(jiǎn)單總結(jié)C++中指針常量與常量指針的區(qū)別
這里我們來(lái)簡(jiǎn)單總結(jié)C++中指針常量與常量指針的區(qū)別,包括如何聲明和使用常量指針以及指針常量,需要的朋友可以參考下2016-06-06C++類(lèi)的靜態(tài)成員變量與靜態(tài)成員函數(shù)詳解
下面小編就為大家?guī)?lái)一篇C++類(lèi)的靜態(tài)成員變量與靜態(tài)成員函數(shù)的文章。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2021-11-11詳解C語(yǔ)言中二級(jí)指針與鏈表的應(yīng)用
對(duì)于初學(xué)者而言,有很多地方肯定是費(fèi)解的。比如函數(shù)的參數(shù)列表的多樣化,動(dòng)態(tài)分配內(nèi)存空間函數(shù)malloc等,其實(shí)這些知識(shí)和指針聯(lián)系緊密,尤其是二級(jí)指針,快跟隨小編來(lái)學(xué)習(xí)一下吧2022-07-07用Visual Studio2017寫(xiě)C++靜態(tài)庫(kù)圖文詳解
這篇文章主要介紹了用Visual Studio2017寫(xiě)C++靜態(tài)庫(kù)的圖文教程,需要的朋友可以參考下2017-04-04C++實(shí)現(xiàn)希爾排序(ShellSort)
這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)希爾排序,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-04-04C++中靜態(tài)成員函數(shù)訪(fǎng)問(wèn)非靜態(tài)成員的實(shí)例
這篇文章主要介紹了C++中靜態(tài)成員函數(shù)訪(fǎng)問(wèn)非靜態(tài)成員的實(shí)例的相關(guān)資料,需要的朋友可以參考下2017-07-07