乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      《windows核心編程系列》十八談談windows鉤子

       lpg2008 2013-12-11
          

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

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

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

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

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

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

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

            注意:使用鉤子會使系統(tǒng)變慢,因為它增加了系統(tǒng)對每個消息的處理量。所以要僅在必要的時候才安裝鉤子。不需要時要及時卸載。

        安裝鉤子:

        1:

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


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

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

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

           WH_KEYBOARD                 //從消息隊列中查詢WM_KEYUP或WM_KEYDOWN時。

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

           WH_MOUSE                      //查詢消息隊列中鼠標事件消息時。

           WH_MSGFILTER              //以下請參考MSDN。

           WH_SYSMSGFILTER

           WH_JORNALRECORD

           WHJORNALPLAYBACK

           WH_SHELL

           WH_CBT

           WH_FOREGROUNDIDLE

           WH_DEBUG

          2 :

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

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

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

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

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

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

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

         5:鉤子函數(shù)

         鉤子被安裝后,如果有相應的消息發(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,不對該消息進行處理,并返回CallNextHookEx的返回值。

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

          wParam和lParam的值依賴于具體的鉤子類型。請參考MSDN。 

          卸載鉤子。

           BOOL UnhookWindowsHookEx(HHOOK hhk);

           hhk為要卸載的鉤子句柄。

       

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

          程序外觀:

       

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

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

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

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

            PostMessage可以實現(xiàn)這個功能。

            看原型:

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


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

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

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

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

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

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

      建立和設置共享段的代碼:可以參考《windows核心編程》談談內(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>  


       

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

       

           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)傳入設置。</span>  
      1. <span style="font-size:18px;">                              int ThreadId)//要安裝鉤子的線程。  
      2.    {  
      3.     ::hWnd=hWnd;//將當前窗口句柄賦給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 消息。具體請參考MSDN.

      創(chuàng)建鉤子函數(shù)功能很簡單,僅僅安裝鉤子和設置共享段內(nèi)的數(shù)據(jù)。Thread為要安裝鉤子的線程。主程序在調(diào)用時傳入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)//只對WM_DOWN進行響應。  
      8.       {  
      9.         PostMessage(hWnd,WM_KEYDOWN,wParam,lParam);  
      10.       }      
      1. return CallNextHookEx(hHook,nCode,wParam,lParam);  

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

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

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

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

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

       

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

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

           原型為:

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

         實現(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日下午實現(xiàn)。

       

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

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

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

        0條評論

        發(fā)表

        請遵守用戶 評論公約

        類似文章 更多