小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

《windows核心編程系列》十八談?wù)剋indows鉤子

 lpg2008 2013-12-11
    

       windows應(yīng)用程序是基于消息驅(qū)動(dòng)的。各種應(yīng)用程序?qū)Ω鞣N消息作出響應(yīng)從而實(shí)現(xiàn)各種功能。

      windows鉤子是windows消息處理機(jī)制的一個(gè)監(jiān)視點(diǎn),通過安裝鉤子可以達(dá)到監(jiān)視指定窗口某種類型的消息的功能。所謂的指定窗口并不局限于當(dāng)前進(jìn)程的窗口,也可以是其他進(jìn)程的窗口。當(dāng)監(jiān)視的某一消息到達(dá)指定的窗口時(shí),在指定的窗口處理消息之前,鉤子函數(shù)將截獲此消息,鉤子函數(shù)既可以加工處理該消息,也可以不作任何處理繼續(xù)傳遞該消息。使用鉤子是實(shí)現(xiàn)dll注入的方法之一。其他常用的方法有:注冊(cè)表注入,遠(yuǎn)程線程注入。

       鉤子函數(shù)是一個(gè)處理消息的程序段。是在安裝鉤子的時(shí)候向系統(tǒng)注冊(cè)的

關(guān)于windows鉤子要清楚以下三點(diǎn):

     1:鉤子是用來截獲系統(tǒng)中的消息流的。利用鉤子可以處理任何我們感興趣的消息,當(dāng)然包括其他進(jìn)程的消息。

    2:截獲該消息后,用于處理該消息的程序叫做鉤子函數(shù)。它是自定義的函數(shù),在安裝鉤子時(shí)將此函數(shù)的地址告訴windows。

3:系統(tǒng)同一時(shí)間可能有多個(gè)進(jìn)程安裝鉤子,多個(gè)鉤子構(gòu)成鉤子鏈。所以截獲消息并處理后,應(yīng)該將此消息繼續(xù)傳遞下去,以便其他鉤子處理這一消息。

      注意:使用鉤子會(huì)使系統(tǒng)變慢,因?yàn)樗黾恿讼到y(tǒng)對(duì)每個(gè)消息的處理量。所以要僅在必要的時(shí)候才安裝鉤子。不需要時(shí)要及時(shí)卸載。

  安裝鉤子:

  1:

  1. SetWindowsHookEx(  
  2.   
  3.          int idHook,                  //要安裝的鉤子的類型。  
  4.   
  5.          HOOKPROC lpfn,                  //鉤子函數(shù)的地址。  
  6.   
  7.          HINSTANCE hMode,               //鉤子函數(shù)所在DLL在進(jìn)程內(nèi)的地址。  
  8.   
  9.          DWORD     dwThread,            //要安裝鉤子的線程。如為0,則為所有線程安裝鉤子。  
  10.   
  11.          );  


idHook指定要安裝鉤子的類型,他可以是下面的值:

     WH_CALLWNDPROC           //目標(biāo)線程調(diào)用SendMessage發(fā)送消息時(shí),鉤子函數(shù)被調(diào)用。

     WH_CALLWNDPROCRET   //當(dāng)SendMessage返回時(shí),鉤子函數(shù)被調(diào)用。

     WH_KEYBOARD                 //從消息隊(duì)列中查詢WM_KEYUP或WM_KEYDOWN時(shí)。

     WH_GETMESSAGE           //目標(biāo)線程調(diào)用GetMessage或PeekMessage時(shí)

     WH_MOUSE                      //查詢消息隊(duì)列中鼠標(biāo)事件消息時(shí)。

     WH_MSGFILTER              //以下請(qǐng)參考MSDN。

     WH_SYSMSGFILTER

     WH_JORNALRECORD

     WHJORNALPLAYBACK

     WH_SHELL

     WH_CBT

     WH_FOREGROUNDIDLE

     WH_DEBUG

    2 :

     lpfn是鉤子函數(shù)的地址。鉤子安裝后如果有相應(yīng)的消息發(fā)生,windows將調(diào)用此參數(shù)指向的函數(shù)。一般鉤子函數(shù)都是位于一個(gè)DLL中。當(dāng)為其他進(jìn)程內(nèi)的線程安裝鉤子時(shí),如果鉤子函數(shù)在DLL中,系統(tǒng)會(huì)把DLL映射到那個(gè)進(jìn)程內(nèi),使他能在該進(jìn)程內(nèi)被調(diào)用。

     注意:鉤子函數(shù)多是被其他進(jìn)程內(nèi)的線程調(diào)用,而不一定是安裝鉤子的線程。

鉤子函數(shù)被調(diào)用的過程

      當(dāng)進(jìn)程A一個(gè)線程準(zhǔn)備向一個(gè)窗口發(fā)送一個(gè)消息,系統(tǒng)檢查該線程是否被安裝了鉤子,如果該線程被安裝了鉤子且該消息與鉤子要截獲的消息類型一致,此消息將被截獲。系統(tǒng)檢查該鉤子的鉤子函數(shù)所在的DLL是否已經(jīng)被映射進(jìn)程A的地址空間中。如果尚未映射,系統(tǒng)會(huì)強(qiáng)制將該DLL映射到進(jìn)程A的地址空間。然后獲得鉤子函數(shù)在進(jìn)程A的虛擬地址,并調(diào)用鉤子函數(shù)。我們可以在鉤子函數(shù)內(nèi)定義我們對(duì)該消息處理的過程。

      注意:當(dāng)系統(tǒng)把鉤子函數(shù)所在的DLL映射到某個(gè)進(jìn)程地址空間時(shí),會(huì)映射整個(gè)DLL,而不僅僅是鉤子函數(shù),這也就說我們可以使用該DLL中的所有導(dǎo)出函數(shù)。

     3:hmod參數(shù)是鉤子函數(shù)所在dll的實(shí)例句柄,也是該dll在進(jìn)程內(nèi)的虛擬地址。如果鉤子函數(shù)在當(dāng)前進(jìn)程中,此參數(shù)應(yīng)被指定為NULL.

    4:dwThreadid指定要被安裝鉤子的線程的ID號(hào)。如果被設(shè)為0,就會(huì)為系統(tǒng)內(nèi)的所有GUI線程安裝鉤子。

   5:鉤子函數(shù)

   鉤子被安裝后,如果有相應(yīng)的消息發(fā)生,windows將調(diào)用鉤子函數(shù)。以下為鉤子函數(shù)的原型:

  

  1. LRESULT CALLBACK HookProc(int nCode,WPARAM wParam,LPARAM lParam)  
  2.   
  3.  {  
  4.   
  5.    //處理消息的代碼。  
  6.   
  7.     return CallNextHookEx(hHook,nCode,wParam,lParam);  
  8.   
  9.  }  


 

    HookProc為鉤子函數(shù)的名稱。

    nCode指定是否必須處理該消息。如果它為HC_ACTION,那么鉤子函數(shù)就必須處理該消息。如果小于0,鉤子函數(shù)就必須將該消息傳遞給CallNextHookEx,不對(duì)該消息進(jìn)行處理,并返回CallNextHookEx的返回值。

    CallNextHookEx用于把消息傳遞到鉤子鏈中下一個(gè)鉤子函數(shù)。

    wParam和lParam的值依賴于具體的鉤子類型。請(qǐng)參考MSDN。 

    卸載鉤子。

     BOOL UnhookWindowsHookEx(HHOOK hhk);

     hhk為要卸載的鉤子句柄。

 

     下面將要實(shí)現(xiàn)一個(gè)例子,實(shí)現(xiàn)對(duì)鍵盤按鍵的監(jiān)控。一旦有鍵盤被按下,就在主程序窗口顯示一條信息指示哪一個(gè)鍵被按下。

    程序外觀:

 

    首先要實(shí)現(xiàn)DLL:

     在dll內(nèi)實(shí)現(xiàn)鉤子函數(shù)這是毫無疑問的。而安裝鉤子和卸載鉤子的函數(shù)既可以寫在主程序內(nèi),也可以寫在DLL內(nèi)。寫在主程序內(nèi)時(shí)只可以在主程序內(nèi)安裝鉤子。而在dll內(nèi)實(shí)現(xiàn)則可以讓所有載入該dll的程序安裝鉤子。如當(dāng)某進(jìn)程將該DLL載入的時(shí)候,可以在DllMain中創(chuàng)建一個(gè)線程,讓他調(diào)用安裝鉤子的函數(shù),實(shí)現(xiàn)為此進(jìn)程內(nèi)的線程安裝鉤子的目的。為了拓展程序的功能,實(shí)現(xiàn)代碼重用,最好是將鉤子函數(shù)寫在DLL內(nèi)。另外這也可以實(shí)現(xiàn)模塊化。一旦需求發(fā)生更改可以只修改DLL內(nèi)的代碼,而不需要改變主程序。

      當(dāng)鉤子函數(shù)被調(diào)用的時(shí)候,也就是我們被攔截的消息已被觸發(fā),如何讓主程序得到這個(gè)通知呢 ?

      我們可以在其他進(jìn)程內(nèi)的鉤子函數(shù)內(nèi)給主程序的窗口發(fā)送消息。但如何發(fā)送呢?

      PostMessage可以實(shí)現(xiàn)這個(gè)功能。

      看原型:

  1. BOOL WINAPI PostMessage(HWND hWnd,UINT Msg,WPARAM wparam,LPARAM lParam);  


     hWnd即為要接受消息的窗口句柄。

     Msg為要發(fā)送的消息。

    wParam和lParam為消息的附加參數(shù)。

    雖然可以使用PostMessage實(shí)現(xiàn)向主程序的窗口發(fā)送消息,但是我們?nèi)绾潍@得主程序的窗口句柄呢?我們知道鉤子函數(shù)是在DLL內(nèi)實(shí)現(xiàn)的,而DLL會(huì)被加載到各個(gè)進(jìn)程內(nèi)。在其他進(jìn)程要想得到主程序的窗口句柄這是一個(gè)問題。

    在《windows核心編程系列》談?wù)剝?nèi)存映射文件中,我們談到了在可執(zhí)行文件內(nèi)使用共享段,可以實(shí)現(xiàn)同一個(gè)可執(zhí)行文件的多個(gè)實(shí)例共享共享段內(nèi)的數(shù)據(jù)的目的。那么在DLL使用共享段呢?哈哈,或許你已經(jīng)猜出來了,由于DLL被映射到了各個(gè)進(jìn)程,將數(shù)據(jù)放在DLL的共享段,可以實(shí)現(xiàn)在各個(gè)進(jìn)程內(nèi)共享DLL內(nèi)共享段數(shù)據(jù)的目的。

     我們的解決方法就是:在DLL內(nèi)建立共享段,將主程序的窗口句柄放在共享段中。在主程序調(diào)用安裝鉤子的函數(shù)時(shí)可以將共享段內(nèi)的窗口句柄賦為主程序的窗口句柄。從而達(dá)到在各個(gè)進(jìn)程內(nèi)共享數(shù)據(jù)的目的。到此,我們又學(xué)習(xí)一種在進(jìn)程間共享數(shù)據(jù)的方法,另一種方法是利用內(nèi)存映射文件。

建立和設(shè)置共享段的代碼:可以參考《windows核心編程》談?wù)剝?nèi)存映射文件。


 

  1. <span style="font-size:18px;"> #pragma data_seg("shared")  
  2.   HWND hWnd=NULL;  
  3.   HHOOK hHook=NULL;  
  4.   
  5.  #pragma data_seg()  
  6.   
  7. #pragma comment(linker,"/SECTION:shared,RWS")  
  8.   
  9. </span>  


 

     怎么多了個(gè)hHook,hHook是創(chuàng)建的鉤子的句柄。由于在鉤子函數(shù)中會(huì)調(diào)用CallNextHookEx將消息傳給鉤子鏈的下一結(jié)點(diǎn)。二者都是在其他進(jìn)程調(diào)用的,因此我們也必須把鉤子的句柄設(shè)為共享。

 

     DLL內(nèi)創(chuàng)建鉤子的代碼:

  1. <span style="font-size:18px;">    KEYHOOKDLL_API bool SetHook(</span>  
  1. <span style="font-size:18px;">                            bool IsInstall,//true表示安裝鉤子,false表示卸載鉤子。</span>  
  1. <span style="font-size:18px;">                            HWND hWnd,     //主程序窗口句柄,用于在主程序內(nèi)傳入設(shè)置。</span>  
  1. <span style="font-size:18px;">                              int ThreadId)//要安裝鉤子的線程。  
  2.    {  
  3.     ::hWnd=hWnd;//將當(dāng)前窗口句柄賦給DLL共線段內(nèi)的窗口句柄。  
  4.     if(IsInstall)  
  5.     {  
  6.         hHook=SetWindowsHookEx( WH_KEYBOARD,KeyHookProc,GetModuleHandle  </span>  
  1. <span style="font-size:18px;">                                           ("keyhookdll"),ThreadId);  
  2.         return true;  
  3.   
  4.     }  
  5.     else  
  6.     {  
  7.         UnhookWindowsHookEx(hHook);  
  8.         return true;  
  9.   
  10.     }  
  11.   
  12. }</span>  

      創(chuàng)建的鉤子類型為WH_KEYBOARD,他可以攔截WM_KEYDOWNWM_KEYUP 消息。具體請(qǐng)參考MSDN.

創(chuàng)建鉤子函數(shù)功能很簡(jiǎn)單,僅僅安裝鉤子和設(shè)置共享段內(nèi)的數(shù)據(jù)。Thread為要安裝鉤子的線程。主程序在調(diào)用時(shí)傳入0,表示為所有線程安裝鉤子。

 

   再看鉤子函數(shù):

  1. LRESULT CALLBACK KeyHookProc(int nCode ,WPARAM wParam,LPARAM lParam)  
  2. {  
  3.     if(nCode<0||nCode==HC_NOREMOVE)  
  4.     {  
  5.         return CallNextHookEx(hHook,nCode,wParam,lParam);  
  6.     }  
  7.     if(lParam&0x40000000)//只對(duì)WM_DOWN進(jìn)行響應(yīng)。  
  8.       {  
  9.         PostMessage(hWnd,WM_KEYDOWN,wParam,lParam);  
  10.       }      
  1. return CallNextHookEx(hHook,nCode,wParam,lParam);  

   在鉤子函數(shù)中首先判斷nCode的值,當(dāng)他小于零時(shí)應(yīng)該直調(diào)用CallNextHookEx,除此之外它也可以有以下取值:

    ACTION:說明wParam和lParam包含按鍵消息的信息,可以處理。

    HC_NOREMOVE:說明wParam和lParam包含按鍵消息的信息,但該消息沒有被從消息隊(duì)列中移除。即程序是調(diào)用PeekMessage來查詢消息隊(duì)列內(nèi)的消息的。

     ( 與GetMessage的區(qū)別與聯(lián)系:他們都從消息隊(duì)列內(nèi)查詢消息,有消息時(shí)將此消息發(fā)送出去,GetMessage在消息隊(duì)列沒有消息時(shí)會(huì)一直等待,直到有消息到達(dá)時(shí)才返回。而PeekMessage無論消息隊(duì)列中是否有消息都立即返回。)

     因此當(dāng)檢測(cè)到nCode小于0或者為WH_NOREMOVE時(shí)不能對(duì)消息進(jìn)行處理而要直接調(diào)用CallNextHookEx。lParam的第30位為1時(shí)說明此時(shí)鍵被按下,為零時(shí)說明鍵被彈起。此處進(jìn)行了判斷,僅在鍵被按下時(shí)向窗口發(fā)送消息。防止消息每次擊鍵發(fā)送兩次消息。

 

     當(dāng)某消息到達(dá)時(shí)我們給主程序窗口發(fā)送的消息為用戶自定義消息:WM_KEY
     他被定義為#define WM_KEY  WM_USER+1

     在主程序內(nèi)我們必須自己實(shí)現(xiàn)相應(yīng)此消息的消息處理函數(shù)。

     原型為:

  1. afx_msg LRESULT OnKey(WPARAM wParam,LPARAM lParam);  

   實(shí)現(xiàn)

  1.      char keyname[100];  
  2. ::GetKeyNameText(lParam,keyname,100);//獲得按鍵的鍵名。  
  3. CString a;  
  4. a.Format("用戶按鍵:%s\r\n",keyname);  
  5. m_output+=a;  
  6. UpdateData(false);  
  7. ::MessageBeep(MB_OK);  
  8. CEdit *edit=(CEdit*)GetDlgItem(IDC_EDIT_OUTPUT);  
  9. edit->LineScroll(edit->GetLineCount());  
  10. return 0;  

    到此為止各主要函數(shù)都介紹完畢,剩下都是如何創(chuàng)建dll。此處不再介紹。例子程序2011年12月2日下午實(shí)現(xiàn)。

 

總結(jié):以上程序花了近三個(gè)小時(shí)實(shí)現(xiàn),此程序看似容易但一旦自己動(dòng)手實(shí)現(xiàn)各種問題接踵而至。所以以后要經(jīng)常動(dòng)手實(shí)現(xiàn)一些看似容易的程序,不要眼高手低。打這些字的時(shí)候鍵盤監(jiān)控程序仍在工作,顯示著我按下的每一個(gè)鍵。有明顯的電腦感覺速度比平常慢了不少,看來使用鉤子,尤其是系統(tǒng)范圍內(nèi)的鉤子會(huì)導(dǎo)致很大的overhead。

    windows核心編程中談到注入dll的幾種方式。其中介紹了使用windows鉤子,但是介紹的很簡(jiǎn)單。以上內(nèi)容參考自《windows核心編程》第五版,第四部分和《windows程序設(shè)計(jì)》第二版,王艷平著。如有錯(cuò)誤,請(qǐng)指正。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多