1. 程式人生 > >【Inline Hook基礎篇】框架搭建

【Inline Hook基礎篇】框架搭建


  • Windows程式設計師對於HOOK技術應該都很熟悉,HOOK俗稱:鉤子。即將自己想實現的功能,掛鉤到系統的函式上,達到呼叫系統的函式時能自動執行我們實現的功能。
  • 對於HOOK,也分為:訊息鉤子,API鉤子,核心鉤子。訊息鉤子和API鉤子都是在應用層(Ring3)上實現,核心鉤子則是在核心層(Ring0)上實現的。此次開篇,逐重討論下Inline Hook的實現方式,Inline Hook也是API鉤子的一種實現方式。
  • 搭建這個Hook框架時,我們需要先簡單瞭解下訊息鉤子,因為訊息鉤子可以讓我們編寫的DLL輕鬆的注入到各個窗體當中。訊息鉤子安裝涉及到一個API:

HHOOK WINAPI SetWindowsHookEx(
In int idHook,
In HOOKPROC lpfn,
In HINSTANCE hMod,
In DWORD dwThreadId
);

HHOOK SetWindowsHookEx(   
    int idHook,      // 鉤子的型別,即它處理的訊息型別   
    HOOKPROC lpfn,   // 鉤子子程的地址指標。如果dwThreadId引數為0   
                     // 或是一個由別的程序建立的執行緒的標識,   
                     // lpfn必須指向DLL中的鉤子子程。   
// 除此以外,lpfn可以指向當前程序的一段鉤子子程程式碼。 // 鉤子函式的入口地址,當鉤子鉤到任何訊息後便呼叫這個函式。 HINSTANCE hMod, // 應用程式例項的控制代碼。標識包含lpfn所指的子程的DLL。 // 如果dwThreadId 標識當前程序建立的一個執行緒, // 而且子程程式碼位於當前程序,hMod必須為NULL。 // 可以很簡單的設定其為本應用程式的例項控制代碼。
DWORD dwThreadId // 與安裝的鉤子子程相關聯的執行緒的識別符號。 // 如果為0,鉤子子程與所有的執行緒關聯,即為全域性鉤子。 ); //鉤子型別 idHook 選項: WH_MSGFILTER = -1; {執行緒級; 截獲使用者與控制元件互動的訊息} WH_JOURNALRECORD = 0; {系統級; 記錄所有訊息佇列從訊息佇列送出的輸入訊息, 在訊息從佇列中清除時發生; 可用於巨集記錄} WH_JOURNALPLAYBACK = 1; {系統級; 回放由 WH_JOURNALRECORD 記錄的訊息, 也就是將這些訊息重新送入訊息佇列} WH_KEYBOARD = 2; {系統級或執行緒級; 截獲鍵盤訊息} WH_GETMESSAGE = 3; {系統級或執行緒級; 截獲從訊息佇列送出的訊息} WH_CALLWNDPROC = 4; {系統級或執行緒級; 截獲傳送到目標視窗的訊息, 在 SendMessage 呼叫時發生} WH_CBT = 5; {系統級或執行緒級; 截獲系統基本訊息, 譬如: 視窗的建立、啟用、關閉、最大最小化、移動等等} WH_SYSMSGFILTER = 6; {系統級; 截獲系統範圍內使用者與控制元件互動的訊息} WH_MOUSE = 7; {系統級或執行緒級; 截獲滑鼠訊息} WH_HARDWARE = 8; {系統級或執行緒級; 截獲非標準硬體(非滑鼠、鍵盤)的訊息} WH_DEBUG = 9; {系統級或執行緒級; 在其他鉤子呼叫前呼叫, 用於除錯鉤子} WH_SHELL = 10; {系統級或執行緒級; 截獲發向外殼應用程式的訊息} WH_FOREGROUNDIDLE = 11; {系統級或執行緒級; 在程式前臺執行緒空閒時呼叫} WH_CALLWNDPROCRET = 12; {系統級或執行緒級; 截獲目標視窗處理完畢的訊息, 在 SendMessage 呼叫後發生}
  • 訊息鉤子解除安裝API:

BOOL WINAPI UnhookWindowsHookEx( In HHOOK hhk );//函式成功返回TRUE,否則返回FALSE。

  • 系統鉤子與執行緒鉤子:
    SetWindowsHookEx()函式的最後一個引數決定了此鉤子是系統鉤子還是執行緒鉤子。 0為系統鉤子
    執行緒勾子用於監視指定執行緒的事件訊息。執行緒勾子一般在當前執行緒或者當前執行緒派生的執行緒內。
    系統勾子監視系統中的所有執行緒的事件訊息。因為系統勾子會影響系統中所有的應用程式,所以勾子函式必須放在獨立的動態連結庫(DLL) 中。系統自動將包含”鉤子回撥函式”的DLL對映到受鉤子函式影響的所有程序的地址空間中,即將這個DLL注入了那些程序。
  • 幾點說明:
    (1)如果對於同一事件(如滑鼠訊息)既安裝了執行緒勾子又安裝了系統勾子,那麼系統會自動先呼叫執行緒勾子,然後呼叫系統勾子。
    (2)對同一事件訊息可安裝多個勾子處理過程,這些勾子處理過程形成了勾子鏈。當前勾子處理結束後應把勾子資訊傳遞給下一個勾子函式。
    (3)勾子特別是系統勾子會消耗訊息處理時間,降低系統性能。只有在必要的時候才安裝勾子,在使用完畢後要及時解除安裝。
  • 介紹完訊息,我們初步框架的實現方式也發現了:利用掛鉤系統級訊息,安裝一個全域性鉤子,達到注入各個程序的目的。
  • 我們直接編寫一個DLL(VS2008,選擇MFC DLL專案),並開放:註冊和釋放兩個介面。
HINSTANCE       g_hInstance = NULL;    // 模組例項控制代碼

//鉤子回撥,預設不處理直接傳遞給系統的下一個鉤子
LRESULT WINAPI HookProc( int nCode, WPARAM wParam, LPARAM lParam ) 
{
    // 傳給系統中的下一個鉤子
    return CallNextHookEx( g_hHook, nCode, wParam, lParam ); 
}

/********************************************************************/
/* 安裝鉤子              */
/* 引數: (無)             */
/* 返回值: TRUE 成功, FALSE 失敗         */
/********************************************************************/
BOOL WINAPI StartWork()
{
    // 如果已經安裝鉤子則返回 FALSE
    if (g_hHook != NULL) return FALSE;
    // 安裝鉤子
    g_hHook = SetWindowsHookEx( WH_CALLWNDPROC, HookProc, g_hInstance, NULL);
    if (g_hHook == NULL) 
    {
        return FALSE;
    }

    return TRUE;

}

/********************************************************************/
/* 釋放鉤子              */
/* 引數: (無)             */
/* 返回值: (無)         */
/********************************************************************/
void WINAPI StopWork()
{
    if (NULL != g_hHook)
    {
        UnhookWindowsHookEx(g_hHook);
        g_hHook = NULL;
    }
}
  • 在DLL被載入到記憶體的時候,進行一次g_hInstance的值設定。
BOOL CFileTransferApp::InitInstance() 
{
    g_hInstance = this->m_hInstance;
    return CWinApp::InitInstance();
}

int CFileTransferApp::ExitInstance() 
{
    return CWinApp::ExitInstance();
}

到此,全域性訊息鉤子注入已經完成。看似很簡單,但是這是我們進行API HOOK的基礎,達到了能注入各個程序,我們就可以在InitInstance函式裡面,進行API的掛鉤操作。下一章節,我們將實現API掛鉤(修改系統API前幾個位元組,實現JMP到我們自定義的API當中)。