標題: Detours 使用方法 HOOK API [打印本頁] 作者: liuyuxi 時間: 2015-1-11 00:03 標題: Detours 使用方法 HOOK API 一、Detours庫的來歷及下載:
Detours庫類似于WTL的來歷,是由Galen Huntand Doug Brubacher自己開發(fā)出來,于99年7月發(fā)表在一篇名為《Detours: Binary Interception of Win32 Functions.》的論文中;驹硎歉膶懞瘮(shù)的頭5個字節(jié)(因為一般函數(shù)開頭都是保存堆棧環(huán)境的三條指令共5個字節(jié):8B FF 55 8B EC)為一條跳轉(zhuǎn)指令,直接跳轉(zhuǎn)到自己的函數(shù)開頭,從而實現(xiàn)API攔截的。后來得到MS的支持并在其網(wǎng)站上提供下載空間: http://research.microsoft.com/re ... 03713d/Details.aspx
目前最新的版本是:Detours Express 2.1。
二、Detours的使用準備:
Detours庫是以源碼形式提供的,這給我們的使用帶來極大的方便。你可以選擇把它編譯成庫、也可以直接把源碼加入工程……形式使用。農(nóng)夫采取的方法是編譯成庫后使用的。編譯庫的方法很簡單,下載包中已經(jīng)制作好了makefile,我們只須直接用vc下的nmake工具直接編譯即可。鑒于前段時間在網(wǎng)上看見有部分朋友對此也存疑惑,農(nóng)夫在此“浪費”一下空間,詳細解說一下編譯的過程(括號中為本人的例子):
1、運行你下載的安裝包,把文件解壓到磁盤上
此處建議您把解壓后的src文件夾拷貝到VC的安裝目錄的VC98子目錄下(D:/SDK/6.0/VC98)。對于像我一樣使用庫的方式會有好處,稍后即講:)。
2、編譯并設置開發(fā)環(huán)境
在你剛才拷貝過去的src文件夾下建立一個*.bat文件,里面填上“../bin/nmake”內(nèi)容后保存即可。運行該批處理文件,恭喜您:庫已經(jīng)編譯完成,唯一要做的是要把../bin/Detoured.dll拷貝到您的系統(tǒng)目錄下,F(xiàn)在您的工程里面包含如下文件即可運用Detours庫來進行開發(fā)了:
#include <detours.h>
#pragma comment(lib, "detours.lib")
#pragma comment(lib, "detoured.lib")
對于沒有把src文件拷貝過來的朋友,也不用著急。bat文件中把路徑用全路徑即可進行編譯。不過你除了拷貝.dll文件外,還得要把.lib、.h文件拷貝到VC目錄下,或者在你的VC環(huán)境中設置這些文件的包含路徑,才可正常使用VC進行開發(fā)?,是不是要麻煩些?
另外的建議:在Detours.h文件中定義一個函數(shù)指針類型,到用的時候您就知道會很方便了:
typedef LONG (WINAPI* Detour)(PVOID*, PVOID);
三、幾個重要的函數(shù):
1、DetourAttach & DetourDetach
這兩個函數(shù)就是實際實現(xiàn)API掛鉤的(改寫頭5個字節(jié)為一個跳轉(zhuǎn)指令),前一個實現(xiàn)API攔截,后一個為在不需要的時候恢復原來的API。
第一個參數(shù)為自己定義的一個用于保存原來系統(tǒng)API的函數(shù),該函數(shù)就相當于您沒掛鉤時的API。農(nóng)夫習慣于在原有API的基礎上加以“Sys”前綴來命名;
第二個參數(shù)為自己攔截API的功能函數(shù),您想干什么“壞事”都是在這個函數(shù)中實現(xiàn)的。農(nóng)夫習慣于在原有API的基礎上加以“Hook”前綴來命名。
2、DetourCreateProcessWithDll
該函數(shù)是在以DLL注入方式攔截API時使用的,它其實就是封裝“CreateProcess”,以“CREATE_SUSPEND”方式創(chuàng)建進程,然后修改IAT,把Detoured.dll和您的*.dll插入到它的導入表,然后再啟動進程。所以它的參數(shù)就是在普通的CreateProcess基礎上增加了兩個DLL的路徑參數(shù),最后一個參數(shù)為創(chuàng)建進程的函數(shù)指針,默認為CreateProcessA!這點要特別注意:如果您的程序攔截了該函數(shù),而在HookCreateProcessA中又調(diào)用DetourCreateProcessWithDll函數(shù)的話,一定要在此傳入SysCreateProcessA,否則您的程序就會遞歸調(diào)用了,當心您的程序崩潰!至于為何要這么調(diào)用?呵呵,問我干嘛?:)
當然其它的API也很有用,不過對于開發(fā)者來說,這三個最重要!
四、開發(fā)實例:
隨源碼有一個幫助文檔,上面列舉了很多例子:包含普通的API、類成員函數(shù)、COM接口、DLL方式注入……。包含了我們的大多應用領域。
下面舉個攔截CreateFileA函數(shù)的例子。該例子將所有創(chuàng)建的文件移動到指定目錄下(暈,第一次使用,怎么不能插入C++代碼?):
1、定義保存系統(tǒng)原來函數(shù)的函數(shù)SysCreateProcessA:(聽起來有點拗口)
staticHANDLE (WINAPI* SysCreateFileA)( LPCTSTR lpFileName, // pointer to name of the file
DWORD dwDesiredAccess, // access (read-write) mode
DWORD dwShareMode, // share mode
LPSECURITY_ATTRIBUTES lpSecurityAttributes, // pointer to security attributes
DWORD dwCreationDisposition, // how to create
DWORD dwFlagsAndAttributes, // file attributes
HANDLE hTemplateFile // handle to file with attributes to copy
) = CreateFileA;
2、編寫自己的功能函數(shù):
HANDLE WINAPI HookCreateFileA( LPCTSTR lpFileName, // pointer to name of the file
DWORD dwDesiredAccess, // access (read-write) mode
DWORD dwShareMode, // share mode
LPSECURITY_ATTRIBUTES lpSecurityAttributes, // pointer to security attributes
DWORD dwCreationDisposition, // how to create
DWORD dwFlagsAndAttributes, // file attributes
HANDLE hTemplateFile // handle to file with attributes to copy
)
{
char chDestFile[256];
strcpy(chDestFile, lpFileName);
if( strstr(lpFileName, "////.//") != NULL ) // 放過設備
{
// 創(chuàng)建的普通文件全部轉(zhuǎn)到D盤去,這里沒考慮是“讀”訪問
char *p = strrchr(lpFileName, '//');
if( p++ == NULL )
{
p = lpFileName;
}
sprintf(chDestFile, "D://%s", p);
}
// 創(chuàng)建文件,注意這里不可以再調(diào)用CreateFileA,否則您就遞歸調(diào)用了!取而代之的應該是SysCreateFileA!。!
return SysCreateFileA(chDestFile, .....); //后面的參數(shù)照搬下來就可以了
}
3、掛鉤,用自己的函數(shù)替換系統(tǒng)API:
DetourAttach(&(PVOID&)SysCreateFileA, HookCreateFileA);
恢復時用DetourDetach即可。
這下運行您的程序,發(fā)現(xiàn)所有用CreateFileA創(chuàng)建的新文件都被轉(zhuǎn)移到D盤下了。
五、幾個問題:
1、字節(jié)編碼:
很多Win32函數(shù)都有多字節(jié)A版和寬字符W版,之所以平時沒有附加A或W后綴,那是因為編譯器已經(jīng)幫我們做了這個工作。但在匯編惜字節(jié)如金的條件下,如果有兩種版本,請務必明確指出,不要用CreateFile這種函數(shù);
2、應用對象:
該庫適合于初學者在RING3下對大多數(shù)API的攔截。對于那些逆向高手來說,這個簡直不值一提;
3、發(fā)布:
由于該庫并沒有包含在MS的SDK中,所以要隨程序一塊打包Detoured.dll。當然如果您是直接用源碼加入工程編譯的則可免去這個文件;
4、攔截DLL參數(shù)設置:
拿上面CreateFile函數(shù)來說,假如我是想在文件名稱符合特定條件下才將其轉(zhuǎn)移到D盤上,比如當文件類型為TXT文件時才轉(zhuǎn)移。我們可以把盤符“D”及文件類型“TXT”直接寫死在程序里面。這樣的話,假如有其它程序是把“PDF”文件放在F盤不是又要重寫一個?
不要以為加一個接口就可以解決。如是,祝賀您步入了農(nóng)夫當初的歧途:)!其實要傳這個參數(shù)進去的實質(zhì)是進程間通信問題。本人采取的是FileMapping,改寫了一下Detours的源碼。當然其它進程間通信方式也是可以的。
5、攔截DLL必須導出一個函數(shù)
一般搞個空函數(shù)就可以了。如果沒有任何導出函數(shù),您辛苦編寫的DLL將無法工作,還可能為此花上一大堆時間去排查。像如下聲明一個就行了:
////////////////////////////////////////////////////////////////////////////////
// Must at least ONE export function:
__declspec(dllexport) void ExportFunc(void)
{
}
六、后記
鄉(xiāng)村野夫,恍惚于世。本應扶犁,貿(mào)入IT。
三十而立,一事無成?畾憵q逝,輾轉(zhuǎn)反側(cè)。
寫文靜心,閑以思遠。悠悠我祖,自愛陶潛。
1 介紹
Api hook包括兩部分:api調(diào)用的截取和api函數(shù)的重定向。通過api hook可以修改函數(shù)的參數(shù)和返回值。關于原理的詳細內(nèi)容參見《windows核心編程》第19章和第22章。
2 Detours API hook
"Detours is a library for intercepting arbitrary Win32 binary functions on x86 machines. Interception code is applied dynamically at runtime. Detours replaces the first few instructions of the target function with an unconditional jump to the user-provided detour function. Instructions from the target function are placed in a trampoline. The address of the trampoline is placed in a target pointer. The detour function can either replace the target function, or extend its semantics by invoking the target function as a subroutine through the target pointer to the trampoline."
在Detours庫中,驅(qū)動detours執(zhí)行的是函數(shù) DetourAttach(…).
LONG DetourAttach(
PVOID * ppPointer,
PVOID pDetour
);
這個函數(shù)的職責是掛接目標API,函數(shù)的第一個參數(shù)是一個指向?qū)⒁粧旖雍瘮?shù)地址的函數(shù)指針,第二個參數(shù)是指向?qū)嶋H運行的函數(shù)的指針,一般來說是我們定義的替代函數(shù)的地址。但是,在掛接開始之前,還有以下幾件事需要完成:
需要對detours進行初始化.
需要更新進行detours的線程.
這些可以調(diào)用以下函數(shù)很容的做到:
DetourTransactionBegin()
DetourUpdateThread(GetCurrentThread())
在這兩件事做完以后,detour函數(shù)才是真正地附著到目標函數(shù)上。在此之后,調(diào)用DetourTransactionCommit()是detour函數(shù)起作用并檢查函數(shù)的返回值判斷是正確還是錯誤。
2.1 hook DLL 中的函數(shù)
在這個例子中,將要hook winsock中的函數(shù) send(…)和recv(…).在這些函數(shù)中,我將會在真正調(diào)用send或者recv函數(shù)前,把真正說要發(fā)送或者接收的消息寫到一個日志文件中去。注意:我們自定義的替代函式一定要與被hook的函數(shù)具有相同的參數(shù)和返回值。例如,send函數(shù)的定義是這樣的:
int send(
__in SOCKET s,
__in const char *buf,
__in int len,
__in int flags
);
因此,指向這個函數(shù)的指針看起來應該是這樣的:
int (WINAPI *pSend)(SOCKET, const char*, int, int) = send;
把函數(shù)指針初始化成真正的函數(shù)地址是ok的;另外還有一種方式是把函數(shù)指針初始化為NULL,然后用函數(shù)DetourFindFunction(…)指向真正的函式地址.把send(…) 和 recv(…)初始化:
int (WINAPI *pSend)(SOCKET s, const char* buf, int len, int flags) = send;
int WINAPI MySend(SOCKET s, const char* buf, int len, int flags);
int (WINAPI *pRecv)(SOCKET s, char* buf, int len, int flags) = recv;
int WINAPI MyRecv(SOCKET s, char* buf, int len, int flags);
現(xiàn)在,需要hook的函數(shù)和重定向到的函數(shù)已經(jīng)定義好了。這里使用 WINAPI 是因為這些函數(shù)是用 __stdcall 返回值的導出函數(shù),現(xiàn)在開始hook:
view sourceprint?INTAPIENTRY DllMain(HMODULEhDLL, DWORDReason, LPVOIDReserved)