鉤子(Hook)

1、基本概念web

    鉤子(Hook)Windows消息處理機制的一個平臺,能夠在應用程序上面設置本身的消 息回調函數以監視指定窗口的某種消息,並且所監視的窗口能夠是其餘進程所建立的。ide

    當消息到達後,在目標窗口處理函數以前處理它。函數

    鉤子機制容許應用程序截獲(且或)處理window消息或特定事件。spa

    鉤子其實是一個處理消息的程序段,經過系統調用,把它掛入系統。每當特定的消息發出, 在沒有到達目的窗口前,鉤子程序就先捕獲該消息,即鉤子函數先獲得控制權。這時鉤子函數便可以加工處理(改變)該消息,也可 以不做處理而繼續傳遞該消息,還能夠強制結束消息的傳遞操作系統

2、運行機制線程

    1、鉤子鏈表和鉤子回調函數:指針

    每個Hook都 有一個與之相關聯的指針列表,稱之爲鉤子鏈表,由系統來維護。orm

    這個列表的每個節點中都有一個指針指向指定的,自定義的被系統回調的消息處理函數,也 就是該種類型鉤子的各個處理函數。xml

    當與指定的類型鉤子有關聯的消息發生時,系統就把這個消息傳遞到鉤子消息處理函數。對象

    最後安裝的鉤子放在鏈表的開始,而最早安裝的鉤子放在最後,也就是後加入的先得到控制 權。

    Windows 並不要求鉤子消息處理函數的卸載順序必定得和安裝順序相 反。每當有一個鉤子被卸載,Windows 便釋放其佔用的內存,並更新整個鉤子鏈表。若是程序安裝了鉤子,可是在還沒有卸載鉤子以前就結束了,那麼系統會自動爲它作卸載鉤子的操做。

    鉤子消息處理函數是一個自定義的回調函數(CALLBACK Function)不能定義成某個類的成員函數, 只能定義爲普通的C函數。用以監視系統或某一特定類型的事件, 這些事件能夠是與某一特定線程關聯的,也能夠是系統中全部線程的事件。

    鉤子回調函數必須按照如下的語法:

 LRESULT CALLBACK HookProc(int nCode, WPARAM wParam, LPARAM lParam); 1)HookProc是自定義的名字。 2)nCode參數是Hook代 碼,Hook子程使用這個參數來肯定任務。 這個參數的值依賴於Hook類型,每一種Hook都 有本身的Hook代碼特徵字符集。   3)wParamlParam參數的值依賴於nCode,可是它們的典型值 是包含了關於發送或者接收消息的信息。

  2、 鉤子的安裝與釋放:

使用API函數SetWindowsHookEx()把一個應用程序定義的鉤子回調函數安裝到鉤子鏈表中的開頭。當指定類 型的Hook監視的事件發生時,系統就調用與這個Hook關聯的Hook鏈的開頭的Hook子 程。每個Hook鏈中的Hook子程都決定是否把這個事件傳遞到下一個Hook子程。

Hook子程傳遞事件到下一個Hook子程須要調用API函數CallNextHookEx()

HHOOK SetWindowsHookEx(int idHook, // 鉤子的類型,即它處理的消息類型 HOOKPROC lpfn, // 鉤子回調函數地址,                  // 即當鉤到了指定類型消息後回調這個函數。 HINSTANCEhMod, // 系統鉤子則爲DLL的 實例句柄, // 線程鉤子乃爲NULL DWORD dwThreadId); // 系統鉤子則爲 零,線程鉤子爲指定線程ID 函數成功則返回鉤子的句柄,失敗返回NULL

  發送給(線 程鉤子是指定的線程,系統鉤子則是所有線程)線程的消息被鉤子 回調函數先處理。

在鉤子回調函數中完成對消息的處理後,若是想要該消息繼續傳 遞,那麼它必須調用另一個SDK中的API函數CallNextHookEx來傳遞它,以執行鉤子鏈表所指的下一個鉤子回調函數。這個函數成功時返回鉤子鏈中 下一個鉤子過程的返回值,返回值的類型依賴於鉤子的類型。這個函數的原型以下:

LRESULT CallNextHookEx(HHOOK hhk, // SetWindowsHookEx()函數返回的鉤子句柄。 int nCode, // 傳給鉤子過程的事件代碼。 WPARAM wParam, // 分別是傳給鉤子回調函數的參數值, LPARAM lParam); // 其具體含義與鉤子類型有關。

鉤子函數也能夠經過直接返回TRUE來丟棄該消息,並阻止該消息的傳遞,其餘安裝了鉤子的應用程序將不 會接收到鉤子的通知並且還有可能產生不正確的結果。

鉤子在使用完以後須要用UnHookWindowsHookEx()卸載,不然會形成麻煩。

BOOL UnhookWindowsHookEx(HHOOK hhk); // SetWindowsHookEx()函數返回的鉤子句柄。

函數成功返回TRUE, 不然返回FALSE

3、一些運行機制:

Win16環境中,DLL的 全局數據對每一個載入它的進程來講都是相同的;

而在Win32環境中,狀況卻發生了變化,DLL函數中的代碼所建立的任何對象與變量都歸調用它的進程全部。當進程在載入DLL時,操做系統自動把DLL映射到該進程的私有空間,也就是進程的虛擬地址空間,並且也複製該DLL的全局變量的一份拷貝到該進程空間。也就是說每一個進程所擁有相同的DLL全局變量,它們的名稱相同,但其值卻並不必定是相同的,並且是互不 干涉的。

所以,在Win32環境下要想在多個進程中共享數據,就必須進行必要的設置。在訪問同一個Dll的各進程之間共享存儲器是經過存儲器映射文件技術實現的。 也能夠把這些須要共享的數據分離出來,放置在一個獨立的數據段裏,並把該段的屬性設置爲共享。必須給這些變量賦初值,不然編譯器會把沒有賦初始值的變量放在一個叫未被初始化的數據段中。

#pragma data_seg預處理指令用於設置共享數據段。例如:

// 告訴編譯器MyData數 據段要可讀可寫可共享

#pragma comment(linker, "/section:MyData,rws") #pragma data_seg("MyData") int g_iProNum = -1;#pragma data_seg()

全部對這些數據的操做都針對同一個實例的,而不是在每一個進程 的地址空間中都有一份。

當進程隱式或顯式調用一個動態庫裏的函數時,系統都要把這個 動態庫映射到這個進程的虛擬地址空間裏(如下簡稱"地址空間")。這使得DLL成 爲進程的一部分,以這個進程的身份執行,使用這個進程的堆棧。

相關文章
相關標籤/搜索