自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

網(wǎng)絡(luò)安全編程:Windows鉤子函數(shù)

安全
Windows下的窗口應(yīng)用程序是基于消息驅(qū)動(dòng)的,但是在某些情況下需要捕獲或者修改消息,從而完成一些特殊的功能。

[[401215]]

 Windows下的窗口應(yīng)用程序是基于消息驅(qū)動(dòng)的,但是在某些情況下需要捕獲或者修改消息,從而完成一些特殊的功能。對(duì)于捕獲消息而言,無(wú)法使用IAT或Inline Hook之類(lèi)的方式去進(jìn)行捕獲,不過(guò)Windows提供了專門(mén)用于處理消息的鉤子函數(shù)。

01 鉤子原理

Windows下的應(yīng)用程序大部分是基于消息模式機(jī)制的,一些CUI的程序不是基于消息的。Windows下的應(yīng)用程序都有一個(gè)消息過(guò)程函數(shù),根據(jù)不同的消息來(lái)完成不同的功能。Windows操作系統(tǒng)提供的鉤子機(jī)制的作用是用來(lái)截獲、監(jiān)視系統(tǒng)中的消息。Windows操作系統(tǒng)提供了很多不同種類(lèi)的鉤子,可以處理不同的消息。

Windows系統(tǒng)提供的鉤子按照掛鉤范圍分為局部鉤子和全局鉤子。局部鉤子是針對(duì)一個(gè)線程的,而全局鉤子則是針對(duì)整個(gè)操作系統(tǒng)內(nèi)基于消息機(jī)制的應(yīng)用程序的。全局鉤子需要使用DLL文件,DLL文件里存放了鉤子函數(shù)的代碼。

在操作系統(tǒng)中安裝全局鉤子以后,只要進(jìn)程接收到可以發(fā)出鉤子的消息后,全局鉤子的DLL文件會(huì)被操作系統(tǒng)自動(dòng)或強(qiáng)行地加載到該進(jìn)程中。由此可見(jiàn),設(shè)置消息鉤子也是一種可以進(jìn)行DLL注入的方法。

02 鉤子函數(shù)

鉤子函數(shù)主要有3個(gè),分別是SetWindowsHookEx()、CallNextHookEx()和UnhookWindowsHookEx()。下面介紹這些函數(shù)的使用方法。

SetWindowsHookEx()函數(shù)的定義如下: 

  1. HHOOK SetWindowsHookEx(  
  2.  int idHook,  
  3.  HOOKPROC lpfn,  
  4.  HINSTANCE hMod,  
  5.  DWORD dwThreadId  
  6. ); 

該函數(shù)的返回值為一個(gè)鉤子句柄。這個(gè)函數(shù)有4個(gè)參數(shù),下面分別進(jìn)行介紹。

lpfn:該參數(shù)指定為 Hook 函數(shù)的地址。如果 dwThreadId 參數(shù)被賦值為 0,或者被設(shè)置為一個(gè)其他進(jìn)程中的線程 ID,那么 lpfn 則屬于 DLL 中的函數(shù)過(guò)程。如果 dwThreadId 為當(dāng)前進(jìn)程中的線程 ID,那么 lpfn 可以是指向當(dāng)前進(jìn)程中的函數(shù)過(guò)程,也可以是屬于 DLL 中的函數(shù)過(guò)程。

hMod:該參數(shù)指定鉤子函數(shù)所在模塊的模塊句柄。該模塊句柄就是 lpfn 所在的模塊的句柄。如果 dwThreadId 為當(dāng)前進(jìn)程中的線程 ID,而且 lpfn 所指向的函數(shù)在當(dāng)前進(jìn)程中,那么 hMod 將被設(shè)置為 NULL。

dwThreadId:該參數(shù)設(shè)置為需要被掛鉤的線程的 ID 號(hào)。如果設(shè)置為 0,表示在所有的線程中掛鉤(這里的“所有的線程”表示基于消息機(jī)制的所有的線程)。如果指定為具體的線程的 ID 號(hào),表示要在指定的線程中進(jìn)行掛鉤。該參數(shù)影響上面兩個(gè)參數(shù)的取值。該參數(shù)的取值決定了該鉤子屬于全局鉤子,還是局部鉤子。

idHook:該參數(shù)表示鉤子的類(lèi)型。由于鉤子的類(lèi)型非常多,因此放在所有的參數(shù)后面進(jìn)行介紹。下面介紹幾個(gè)常用到的鉤子,也可能是大家比較關(guān)心的幾個(gè)類(lèi)型。

1. WH_GETMESSAGE

安裝該鉤子的作用是監(jiān)視被投遞到消息隊(duì)列中的消息。也就是當(dāng)調(diào)用GetMessage()或PeekMessage()函數(shù)時(shí),函數(shù)從程序的消息隊(duì)列中獲取一個(gè)消息后調(diào)用該鉤子。

WH_GETMESSAGE鉤子函數(shù)的定義如下: 

  1. LRESULT CALLBACK GetMsgProc(  
  2.  int code, // 鉤子編碼  
  3.  WPARAM wParam, // 移除選項(xiàng)  
  4.  LPARAM lParam // 消息  
  5. ); 

2. WH_MOUSE

安裝該鉤子的作用是監(jiān)視鼠標(biāo)消息。該鉤子函數(shù)的定義如下: 

  1. LRESULT CALLBACK MouseProc(  
  2.  int nCode, // 鉤子編碼  
  3.  WPARAM wParam, // 消息標(biāo)識(shí)符  
  4.  LPARAM lParam // 鼠標(biāo)的坐標(biāo)  
  5. ); 

3. WH_KEYBOARD

安裝該鉤子的作用是監(jiān)視鍵盤(pán)消息。該鉤子函數(shù)的定義如下: 

  1. LRESULT CALLBACK KeyboardProc(  
  2.  int code, // 鉤子編碼  
  3.  WPARAM wParam, // 虛擬鍵編碼  
  4.  LPARAM lParam // 按鍵信息  
  5. ); 

4. WH_DEBUG

安裝該鉤子的作用是調(diào)試其他鉤子的鉤子函數(shù)。該鉤子函數(shù)的定義如下: 

  1. LRESULT CALLBACK DebugProc(  
  2.  int nCode, // 鉤子編碼  
  3.  WPARAM wParam, // 鉤子類(lèi)型  
  4.  LPARAM lParam // 調(diào)試信息  
  5. ); 

從上面的這些鉤子函數(shù)定義可以看出,每個(gè)鉤子函數(shù)的定義都是一樣的。每種類(lèi)型的鉤子監(jiān)視、截獲的消息不同。雖然它們的定義都是相同的,但是函數(shù)參數(shù)的意義是不同的。

接著介紹跟鉤子有關(guān)的另外一個(gè)函數(shù):UnhookWindowsHookEx(),其定義如下: 

  1. BOOL UnhookWindowsHookEx(  
  2.  HHOOK hhk // 鉤子函數(shù)的句柄  
  3. ); 

這個(gè)函數(shù)是用來(lái)移除先前用SetWindowsHookEx()安裝的鉤子。該函數(shù)只有一個(gè)參數(shù),是鉤子句柄,也就是調(diào)用該函數(shù)通過(guò)指定的鉤子句柄來(lái)移除與其相應(yīng)的鉤子。

在操作系統(tǒng)中,可以多次反復(fù)地使用SetWindowsHookEx()函數(shù)來(lái)安裝鉤子,而且可以安裝多個(gè)同樣類(lèi)型的鉤子。這樣,鉤子就會(huì)形成一條鉤子鏈,最后安裝的鉤子會(huì)首先截獲到消息。當(dāng)該鉤子對(duì)消息處理完畢以后,會(huì)選擇返回,或者選擇把消息繼續(xù)傳遞下去。在通常情況下,如果為了屏蔽消息,則直接在鉤子函數(shù)中返回一個(gè)非零值。比如要在自己程序中屏蔽鼠標(biāo)消息,則在安裝的鼠標(biāo)鉤子函數(shù)中直接返回非零值即可。如果為了消息在經(jīng)過(guò)鉤子函數(shù)后可以繼續(xù)傳達(dá)到目標(biāo)窗口,必須選擇將消息繼續(xù)傳遞。使消息能繼續(xù)傳遞的函數(shù)的定義如下: 

  1. LRESULT CallNextHookEx(  
  2.  HHOOK hhk, // 當(dāng)前鉤子的句柄  
  3.  int nCode, // 傳遞給釣子函數(shù)的釣子編碼  
  4.  WPARAM wParam, // 傳遞給釣子函數(shù)的值  
  5.  LPARAM lParam // 傳遞給釣子函數(shù)的值  
  6. ); 

該函數(shù)有4個(gè)參數(shù)。第一個(gè)參數(shù)是鉤子句柄,就是調(diào)用SetWindowsHookEx()函數(shù)的返回值;后面3個(gè)參數(shù)是鉤子函數(shù)的參數(shù),直接依次抄過(guò)來(lái)即可。例如: 

  1. HHOOK g_Hook = SetWindowsHook(……);  
  2. LRESULT CALLBACK GetMsgProc(  
  3.  int code, // 鉤子編碼  
  4.  WPARAM wParam, // 移除選項(xiàng)  
  5.  LPARAM lParam // 消息  
  6.  
  7.  
  8.  return CallNextHookEx(g_Hook, code, wParam, lParam); 

03 鉤子實(shí)例

Windows鉤子的應(yīng)用比較廣。無(wú)論是安全產(chǎn)品還是惡意軟件,甚至是常規(guī)軟件,都會(huì)用到Windows提供的鉤子功能。

1. 全局鍵盤(pán)鉤子

下面來(lái)寫(xiě)一個(gè)可以截獲鍵盤(pán)消息的鉤子程序,其功能非常簡(jiǎn)單,就是把按下的鍵對(duì)應(yīng)的字符顯示出來(lái)。既然要截獲鍵盤(pán)消息,那么肯定是截獲系統(tǒng)范圍內(nèi)的鍵盤(pán)消息,因此需要安裝全局鉤子,這樣就需要DLL文件的支持。先來(lái)新建一個(gè)DLL文件,在該DLL文件中需要定義兩個(gè)導(dǎo)出函數(shù)和兩個(gè)全局變量,定義如下: 

  1. extern "C" __declspec(dllexport) VOID SetHookOn();  
  2. extern "C" __declspec(dllexport) VOID SetHookOff();  
  3. // 鉤子句柄  
  4. HHOOK g_Hook = NULL 
  5. // DLL 模塊句柄  
  6. HINSTANCE g_Inst = NULL

在DllMain()函數(shù)中,需要保存該DLL模塊的句柄,以方便安裝全局鉤子。代碼如下: 

  1. BOOL APIENTRY DllMain( HANDLE hModule,  
  2.  DWORD ul_reason_for_call, 
  3.  LPVOID lpReserved  
  4.  )  
  5.  
  6.  // 保存 DLL 的模塊句柄  
  7.  g_Inst = (HINSTANCE)hModule;  
  8.  return TRUE;  

安裝與卸載鉤子的函數(shù)如下: 

  1. VOID SetHookOn()  
  2.  
  3.  // 安裝鉤子  
  4.  g_Hook = SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, g_Inst, 0);  
  5. VOID SetHookOff()  
  6.  
  7.  // 卸載鉤子  
  8.  UnhookWindowsHookEx(g_Hook);  

對(duì)于Windows鉤子來(lái)說(shuō),上面的這些步驟基本上都是必需的,或者是差別不大,關(guān)鍵在于鉤子函數(shù)的實(shí)現(xiàn)。這里是為了獲取鍵盤(pán)按下的鍵,鉤子函數(shù)如下: 

  1. // 鉤子函數(shù)  
  2. LRESULT CALLBACK KeyboardProc(  
  3.   int code, // 鉤子編碼  
  4.   WPARAM wParam, // 虛擬鍵編碼  
  5.   LPARAM lParam // 按建信息  
  6.  
  7.  
  8.   if ( code < 0 )  
  9.   {  
  10.     return CallNextHookEx(g_Hook, code, wParam, lParam);  
  11.   }  
  12.   if ( code == HC_ACTION && lParam > 0 )  
  13.   {  
  14.     char szBuf[MAXBYTE] = { 0 };  
  15.     GetKeyNameText(lParam, szBuf, MAXBYTE);  
  16.     MessageBox(NULL, szBuf, NULL, MB_OK);  
  17.   } 
  18.   return CallNextHookEx(g_Hook, code, wParam, lParam);  

關(guān)于鉤子函數(shù),這里簡(jiǎn)單地解釋一下,首先是進(jìn)入鉤子函數(shù)的第一個(gè)判斷。 

  1. if ( code < 0 )  
  2.  
  3.  return CallNextHookEx(g_Hook, code, wParam, lParam);  

如果code的值小于0,則必須調(diào)用CallNextHookEx(),將消息繼續(xù)傳遞下去,不對(duì)該消息進(jìn)行處理,并返回CallNextHookEx()函數(shù)的返回值。這一點(diǎn)是MSDN上要求這么做的。 

  1. if ( code == HC_ACTION && lParam > 0 )  
  2.  
  3.  char szBuf[MAXBYTE] = { 0 };  
  4.  GetKeyNameText(lParam, szBuf, MAXBYTE); 
  5.   MessageBox(NULL, szBuf, NULL, MB_OK); 

如果code等于HC_ACTION,表示消息中包含按鍵消息;如果為WM_KEYDOWN,則顯示按鍵對(duì)應(yīng)的文本。

將該DLL文件編譯連接。為了測(cè)試該DLL文件,新建一個(gè)MFC的Dialog工程,添加兩個(gè)按鈕,如圖1所示。

圖1  鍵盤(pán)鉤子的測(cè)試程序

分別對(duì)這兩個(gè)按鈕添加代碼,具體如下: 

  1. void CHookTestDlg::OnButton1()  
  2.  
  3.  // 在這里添加控制通知的處理程序  
  4.  SetHookOn();  
  5.  
  6. void CHookTestDlg::OnButton2()  
  7.  
  8.  // 在這里添加控制通知的處理程序  
  9.  SetHookOff();  

直接調(diào)用DLL文件導(dǎo)出這兩個(gè)函數(shù),不過(guò)在使用之前要先對(duì)這兩個(gè)函數(shù)進(jìn)行聲明,否則編譯器因無(wú)法找到這兩個(gè)函數(shù)的原型而導(dǎo)致連接失敗。定義如下: 

  1. extern "C" VOID SetHookOn();  
  2. extern "C" VOID SetHookOff(); 

進(jìn)行編譯連接,提示出錯(cuò),內(nèi)容如下: 

  1. Linking...  
  2. HookTestDlg.obj : error LNK2001: unresolved external symbol _SetHookOn  
  3. HookTestDlg.obj : error LNK2001: unresolved external symbol _SetHookOff  
  4. Debug/HookTest.exe : fatal error LNK1120: 2 unresolved externals  
  5. Error executing link.exe.  
  6. HookTest.exe - 3 error(s), 0 warning(s) 

從給出的提示可以看出是連接錯(cuò)誤,找不到外部的符號(hào)。將DLL編譯連接后生成的DLL文件和LIB文件都復(fù)制到測(cè)試工程的目錄下,并將LIB文件添加到工程中。在代碼中添加如下語(yǔ)句: 

  1. #pragma comment (lib, KeyBoradHookTest) 

再次連接,成功!

運(yùn)行測(cè)試程序,并單擊“HookOn”按鈕,隨便按下鍵盤(pán)上的任意一個(gè)鍵,會(huì)出現(xiàn)提示對(duì)話框,如圖2所示。

圖2  截獲到的鍵盤(pán)輸入

從圖2中可以看出,當(dāng)按下鍵盤(pán)上的按鍵時(shí),程序?qū)⒉东@到按鍵。到此,鍵盤(pán)鉤子的例子程序就完成了。

2. 低級(jí)鍵盤(pán)鉤子

數(shù)據(jù)防泄露軟件通常會(huì)禁止PrintScreen鍵,防止通過(guò)截屏將數(shù)據(jù)保存為圖片而導(dǎo)致泄密。這類(lèi)軟件想要實(shí)現(xiàn)是比較簡(jiǎn)單的,但是想要將功能做得強(qiáng)大些,還是需要下功夫的。數(shù)據(jù)防泄露的軟件除了要有兼容性好的底層驅(qū)動(dòng)的設(shè)計(jì),還要有完善的規(guī)則設(shè)置。此外就是需要軟件安全人員對(duì)其進(jìn)行各種各樣的攻擊,避免數(shù)據(jù)防泄露軟件因?yàn)楦鞣N各樣的原因而被心懷惡意者突破。

這里介紹如何禁止PrintScreen鍵。其實(shí)很簡(jiǎn)單,只要安裝低級(jí)鍵盤(pán)鉤子(WH_KEYBO ARD_LL)就可以搞定。普通的鍵盤(pán)鉤子(WH_KEYBOARD)是無(wú)法過(guò)濾一些系統(tǒng)按鍵的。在低級(jí)鍵盤(pán)鉤子的回調(diào)函數(shù)中,判斷是否為PrintScreen鍵,如果是,則直接返回TRUE(前面提到,如果想屏蔽某個(gè)消息的話,那么在鉤子函數(shù)中對(duì)該消息進(jìn)行處理后,直接返回一個(gè)非零值),如果不是,則傳遞給鉤子鏈的下一處。

代碼如下: 

  1. extern "C" __declspec(dllexport) BOOL SetHookOn()  
  2.  
  3.   if ( g_hHook != NULL ) 
  4.   {  
  5.     return FALSE;  
  6.   }  
  7.   g_hHook = SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, g_hIns, NULL);  
  8.   if ( NULL == g_hHook )  
  9.   {  
  10.     MessageBox(NULL, "安裝鉤子出錯(cuò) !", "error", MB_ICONSTOP);  
  11.     return FALSE; 
  12.   }  
  13.   return TRUE;  
  14.  
  15. extern "C" __declspec(dllexport) BOOL SetHookOff()  
  16.  
  17.   if ( g_hHook == NULL )  
  18.   {  
  19.     return FALSE;  
  20.   }  
  21.   UnhookWindowsHookEx(g_hHook);  
  22.   g_hHook = NULL 
  23.   return TRUE;  
  24.  
  25. LRESULT CALLBACK LowLevelKeyboardProc(  
  26.   int nCode, WPARAM wParam, LPARAM lParam)  
  27.  
  28.   KBDLLHOOKSTRUCT *Key_Info = (KBDLLHOOKSTRUCT*)lParam; 
  29.   if ( HC_ACTION == nCode )  
  30.   {  
  31.     if ( WM_KEYDOWN == wParam || WM_SYSKEYDOWN == wParam )  
  32.     {  
  33.       if ( Key_Info->vkCode == VK_SNAPSHOT )  
  34.       {  
  35.         return TRUE;  
  36.       }  
  37.     }  
  38.   }  
  39.   return CallNextHookEx(g_hHook, nCode, wParam, lParam);  

代碼量非常短,然而,就是這短短的代碼阻止了數(shù)據(jù)的泄露。當(dāng)然,對(duì)于一個(gè)攻擊者來(lái)說(shuō),這個(gè)代碼無(wú)法保護(hù)數(shù)據(jù),這種保護(hù)也就很脆弱了。任何的保護(hù)都有突破的辦法,攻擊無(wú)處不在,攻擊者會(huì)嘗試任何手段突破所有的保護(hù)。

3. 使用鉤子進(jìn)行DLL注入

Windows提供的鉤子類(lèi)型非常多,其中一種類(lèi)型的鉤子非常實(shí)用,那就是WH_GETME SSAGE鉤子。它可以很方便地將DLL文件注入到所有的基于消息機(jī)制的程序中。

在有些情況下,需要DLL文件完成一些功能,但是完成功能時(shí)需要DLL在目標(biāo)進(jìn)程的空間中。這時(shí),就需要使用WH_GETMESSAGE消息把DLL注入到目標(biāo)的進(jìn)程中。代碼非常簡(jiǎn)單,這里直接給出DLL文件的代碼,具體如下: 

  1. #include <windows.h>  
  2. extern "C" __declspec(dllexport) VOID SetHookOn();  
  3. extern "C" __declspec(dllexport) VOID SetHookOff();  
  4. HHOOK g_HHook = NULL 
  5. HINSTANCE g_hInst = NULL 
  6. VOID DoSomeThing()  
  7.  
  8.   /*  
  9.   ……  
  10.   自己要實(shí)現(xiàn)功能的代碼  
  11.   ……  
  12.   */  
  13.  
  14. BOOL WINAPI DllMain(  
  15.   HINSTANCE hinstDLL, // DLL 模塊的句柄  
  16.   DWORD fdwReason, // 調(diào)用函數(shù)的原因  
  17.   LPVOID lpvReserved // 保留的  
  18.  
  19.  
  20.   switch ( fdwReason )  
  21.   {  
  22.   case DLL_PROCESS_ATTACH:  
  23.     {  
  24.       g_hInst = hinstDLL 
  25.       DoSomeThing();  
  26.       break;  
  27.     }  
  28.   }  
  29.   return TRUE;  
  30.  
  31. LRESULT CALLBACK GetMsgProc(  
  32.   int code, // 鉤子編碼  
  33.   WPARAM wParam, // 移除選項(xiàng)  
  34.   LPARAM lParam // 消息  
  35.  
  36.  
  37.   return CallNextHookEx(g_HHook, code, wParam, lParam);  
  38.  
  39. VOID SetHookOn()  
  40.  
  41.   g_HHook = SetWindowsHookEx(WH_GETMESSAGE, GetMsgProc, g_hInst, 0); 
  42.  
  43. VOID SetHookOff()  
  44.  
  45.   UnhookWindowsHookEx(g_HHook);  

整個(gè)代碼就是這樣。只要知道,在需要DLL大范圍地注入到基于消息的進(jìn)程中時(shí),可以使用這種方法。 

 

責(zé)任編輯:龐桂玉 來(lái)源: 計(jì)算機(jī)與網(wǎng)絡(luò)安全
相關(guān)推薦

2023-06-19 08:23:28

kubernetes容器

2021-01-18 10:35:18

網(wǎng)絡(luò)安全Windows代碼

2021-03-03 12:20:42

網(wǎng)絡(luò)安全DLL編程

2021-02-07 10:55:01

網(wǎng)絡(luò)安全文件API

2011-05-20 17:59:06

回調(diào)函數(shù)

2021-01-26 13:45:03

網(wǎng)絡(luò)安全Winsock編程

2021-03-05 13:46:56

網(wǎng)絡(luò)安全遠(yuǎn)程線程

2021-05-08 11:50:59

網(wǎng)絡(luò)安全API函數(shù)代碼

2021-03-19 10:23:45

網(wǎng)絡(luò)安全內(nèi)核文件

2021-04-14 15:53:58

網(wǎng)絡(luò)安全C語(yǔ)言wcslen

2021-02-21 18:19:43

網(wǎng)絡(luò)安全網(wǎng)絡(luò)安全編程創(chuàng)建進(jìn)程

2021-02-23 10:20:07

網(wǎng)絡(luò)安全進(jìn)程代碼

2021-04-06 11:04:54

網(wǎng)絡(luò)安全C語(yǔ)言代碼

2016-10-10 00:18:27

2021-02-15 15:23:03

網(wǎng)絡(luò)安全注冊(cè)表API

2021-06-18 09:55:09

網(wǎng)絡(luò)安全目錄監(jiān)控

2021-03-01 11:20:13

網(wǎng)絡(luò)安全多線程代碼

2011-03-17 13:32:45

2021-06-15 11:16:24

網(wǎng)絡(luò)安全U盤(pán)軟件

2021-04-19 10:26:41

網(wǎng)絡(luò)安全PE文件
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)