運用Detours庫hook API

一、Detours庫的來歷及下載:        Detours庫類似於WTL的來歷,是由Galen Hunt and Doug Brubacher自己開發出來,於99年7月發表在一篇名爲《Detours: Binary Interception of Win32 Functions.》的論文中。基本原理是改寫函數的頭5個字節(因爲一般函數開頭都是保存堆棧環境的三條指令共5個字節:8B FF 55 8B EC)爲一條跳轉指令,直接跳轉到自己的函數開頭,從而實現API攔截的。後來得到MS的支持並在其網站上提供下載空間:

http://research.microsoft.com/research/downloads/Details/d36340fb-4d3c-4ddd-bf5b-1db25d03713d/Details.aspx

        目前最新的版本是:Detours Express 2.1。

二、Detours的使用準備:

        Detours庫是以源碼形式提供的,這給我們的使用帶來極大的方便。你可以選擇把它編譯成庫、也可以直接把源碼加入工程……形式使用。農夫採取的方法是編譯成庫後使用的。編譯庫的方法很簡單,下載包中已經製作好了makefile,我們只須直接用vc下的nmake工具直接編譯即可。鑑於前段時間在網上看見有部分朋友對此也存疑惑,農夫在此“浪費”一下空間,詳細解說一下編譯的過程(括號中爲本人的例子):

1、運行你下載的安裝包,把文件解壓到磁盤上

       此處建議您把解壓後的src文件夾拷貝到VC的安裝目錄的VC98子目錄下(D:\SDK\6.0\VC98)。對於像我一樣使用庫的方式會有好處,稍後即講:)。

2、編譯並設置開發環境

        在你剛纔拷貝過去的src文件夾下建立一個*.bat文件,裏面填上“..\bin\nmake”內容後保存即可。運行該批處理文件,恭喜您:庫已經編譯完成,唯一要做的是要把..\bin\Detoured.dll拷貝到您的系統目錄下。現在您的工程裏面包含如下文件即可運用Detours庫來進行開發了:

#include <detours.h>

#pragma comment(lib, "detours.lib")
#pragma comment(lib, "detoured.lib")

         對於沒有把src文件拷貝過來的朋友,也不用着急。bat文件中把路徑用全路徑即可進行編譯。不過你除了拷貝.dll文件外,還得要把.lib、.h文件拷貝到VC目錄下,或者在你的VC環境中設置這些文件的包含路徑,纔可正常使用VC進行開發。看,是不是要麻煩些?

        另外的建議:在Detours.h文件中定義一個函數指針類型,到用的時候您就知道會很方便了:

         typedef LONG (WINAPI* Detour)(PVOID*, PVOID);

三、幾個重要的函數:

1、DetourAttach & DetourDetach

        這兩個函數就是實際實現API掛鉤的(改寫頭5個字節爲一個跳轉指令),前一個實現API攔截,後一個爲在不需要的時候恢復原來的API。

        第一個參數爲自己定義的一個用於保存原來系統API的函數,該函數就相當於您沒掛鉤時的API。農夫習慣於在原有API的基礎上加以“Sys”前綴來命名;

        第二個參數爲自己攔截API的功能函數,您想幹什麼“壞事”都是在這個函數中實現的。農夫習慣於在原有API的基礎上加以“Hook”前綴來命名。

2、DetourCreateProcessWithDll

       該函數是在以DLL注入方式攔截API時使用的,它其實就是封裝“CreateProcess”,以“CREATE_SUSPEND”方式創建進程,然後修改IAT,把Detoured.dll和您的*.dll插入到它的導入表,然後再啓動進程。所以它的參數就是在普通的CreateProcess基礎上增加了兩個DLL的路徑參數,最後一個參數爲創建進程的函數指針,默認爲CreateProcessA!這點要特別注意:如果您的程序攔截了該函數,而在HookCreateProcessA中又調用DetourCreateProcessWithDll函數的話,一定要在此傳入SysCreateProcessA,否則您的程序就會遞歸調用了,當心您的程序崩潰!至於爲何要這麼調用?呵呵,問我幹嘛?:)

        當然其它的API也很有用,不過對於開發者來說,這三個最重要!

四、開發實例:

         隨源碼有一個幫助文檔,上面列舉了很多例子:包含普通的API、類成員函數、COM接口、DLL方式注入……。包含了我們的大多應用領域。

         下面舉個攔截CreateFileA函數的例子。該例子將所有創建的文件移動到指定目錄下(暈,第一次使用,怎麼不能插入C++代碼?):

1、定義保存系統原來函數的函數SysCreateProcessA:(聽起來有點拗口)

static HANDLE (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、編寫自己的功能函數:

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 ) // 放過設備
{
// 創建的普通文件全部轉到D盤去,這裏沒考慮是“讀”訪問
char *p = strrchr(lpFileName, &apos;\\&apos;);
if( p++ == NULL )
{
   p = lpFileName;
}

sprintf(chDestFile, "D:\\%s", p);
}

// 創建文件,注意這裏不可以再調用CreateFileA,否則您就遞歸調用了!取而代之的應該是SysCreateFileA!!!!
return SysCreateFileA(chDestFile, .....); // 後面的參數照搬下來就可以了
}

3、掛鉤,用自己的函數替換系統API:

DetourAttach(&(PVOID&)SysCreateFileA, HookCreateFileA);
恢復時用DetourDetach即可。

這下運行您的程序,發現所有用CreateFileA創建的新文件都被轉移到D盤下了。

五、幾個問題:

1、字節編碼:

       很多Win32函數都有多字節A版和寬字符W版,之所以平時沒有附加A或W後綴,那是因爲編譯器已經幫我們做了這個工作。但在彙編惜字節如金的條件下,如果有兩種版本,請務必明確指出,不要用CreateFile這種函數;

2、應用對象:

       該庫適合於初學者在RING3下對大多數API的攔截。對於那些逆向高手來說,這個簡直不值一提;

3、發佈:

       由於該庫並沒有包含在MS 的SDK中,所以要隨程序一塊打包Detoured.dll。當然如果您是直接用源碼加入工程編譯的則可免去這個文件;

4、攔截DLL參數設置:

        拿上面CreateFile函數來說,假如我是想在文件名稱符合特定條件下才將其轉移到D盤上,比如當文件類型爲TXT文件時才轉移。我們可以把盤符“D”及文件類型“TXT”直接寫死在程序裏面。這樣的話,假如有其它程序是把“PDF”文件放在F盤不是又要重寫一個?

        不要以爲加一個接口就可以解決。如是,祝賀您步入了農夫當初的歧途:)!其實要傳這個參數進去的實質是進程間通信問題。本人採取的是FileMapping,改寫了一下Detours的源碼。當然其它進程間通信方式也是可以的。

5、攔截DLL必須導出一個函數

          一般搞個空函數就可以了。如果沒有任何導出函數,您辛苦編寫的DLL將無法工作,還可能爲此花上一大堆時間去排查。像如下聲明一個就行了:

////////////////////////////////////////////////////////////////////////////////
// Must at least ONE export function:
__declspec(dllexport) void ExportFunc(void)


{
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章