【Inline Hook基礎篇】框架搭建
阿新 • • 發佈:2018-11-09
- 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當中)。