簡單實現了下SSDT SHADOW HOOK


介紹:
        SSDT SHADOW HOOK可用於安全軟件窗口保護、安全輸入、截屏保護等。例如:掛鈎NtUserFindWindowEx、NtUserGetForegroundWindow、NtUserBuildHwndList、NtUserQueryWindow、NtUserWindowFromPoint、NtUserSetParent、NtUserPostMessage、NtUserMessageCall、NtUserSetWindowLong、NtUserShowWindow、NtUserDestroyWindow、NtUserCallHwndParamLock用於窗口保護,掛鈎了NtUserSendInput、NtUserGetAsyncKeyState、NtUserOpenDesktop、NtUserTranslateMessage用於安全輸入,掛鈎NtGdiBitBlt、NtGdiStretchBlt用於截屏保護。

  窗口保護

惡意程序通過獲取安全軟件的窗口句柄,然后通過關閉、隱藏、禁用等手段破壞其正常工作,需要掛鈎這些函數來防止惡意程序的破壞。R3和R0函數對應關系如表1所示。

表1         R3-R0函數對於關系

R3

R0

作用

FindWindow

NtUserFindWindowEx

查找窗口獲取句柄

GetForegroundWindow

NtUserGetForegroundWindow

得到當前頂層窗口

EnumWindows

NtUserBuildHwndList

枚舉所有頂層窗口

GetWindowThreadProcessId

NtUserQueryWindow

獲取句柄對應的進程PID

WindowFromPoint

NtUserWindowFromPoint

獲取所在位置的窗口句柄

SetParent

NtUserSetParent

改變某個子窗口的父窗

PostMessage

NtUserPostMessage

發送消息

SendMessage

NtUserMessageCall

發送消息

SetWindowLong

NtUserSetWindowLong

改變窗口屬性

ShowWindow

NtUserShowWindow

改變窗口顯示狀態

DestroyWindow

NtUserDestroyWindow

銷毀窗口

EnableWindow

NtUserCallHwndParamLock

禁用、啟用窗口

 

  安全輸入

掛鈎NtUserSendInput、NtUserGetAsyncKeyState、NtUserOpenDesktop、NtUserTranslateMessage分別用於防止模擬按鍵、獲取鍵盤按鍵狀態、打開安全桌面、將虛假按鍵還原成真實的按鍵。

 

a)      NtUserSendInput

惡意程序可以通過調用SendInput來模擬按鍵干擾正常輸入,可以掛鈎NtUserSendInput防止惡意操作。當用戶正在輸入密碼等隱私信息的時候,禁止其他程序調用SendInput模擬鍵盤和鼠標操作。

b)      NtUserGetAsyncKeyState

惡意程序可能不停的調用NtUserGetAsyncKeyState來獲取鍵盤的按鍵狀態從而記錄鍵盤的輸入信息,可以掛鈎NtUserGetAsyncKeyState用來禁止此類鍵盤記錄行為。當用戶正在輸入密碼等隱私信息的時候,禁止其他程序調用NtUserGetAsyncKeyState,但是不會阻止當前受保護的進程調用(否則會影響正常的密碼輸入行為)。

c)      NtUserOpenDesktop

在Windows操作系統中,消息鈎子(通過SetWindowsHookEx設置)只會當前的桌面上的窗口有效,所以,可以建立一個安全桌面,用於運行需要嚴密保護的進程,這樣,非本桌面上運行的程序無法通過消息鈎子的方式來獲取需要保護進程窗口的信息,達到了保護目標進程窗口的目的(360保險箱和金山密保都有一個叫安全桌面的功能,正是這樣實現的)。

首先調用真實的NtUserOpenDesktop函數,然后獲取返回句柄的桌面名字,如果此桌面名字跟創建的安全桌面名字一樣,則關閉此桌面句柄,並返回一個NULL值,否則返回真實的句柄。達到保護安全桌面的目的,真正做到安全桌面不可滲透。

d)      NtUserTranslateMessage

在輸入密碼的時候,用戶程序一般調用TranslateMessage將消息轉化為具體的按鍵信息,利用此特點,可以構建一個DirectInput安全輸入通道,即盡可能少的通過Windows系統的鍵盤按鍵傳輸通道(此通道是極度危險的,惡意程序可以在任意位置添加HOOK截獲按鍵信息)。

對NtUserTranslateMessage的掛鈎用於修正虛擬鍵盤輸入的虛擬按鍵,首先需要判斷是否正在輸入密碼,虛擬鍵盤是否正在運行,是否需要修正按鍵,這三個參數都是運行於R3的控制程序傳遞進來給R0驅動的。然后判斷消息是否是鍵盤按鍵的消息,如果是,則進一步判斷此消息是否對應虛假按鍵,如果是,則修正為真實的按鍵。虛假的按鍵和真實的按鍵也是R3傳遞給R0驅動的。整個輸入通道完全自己構建,不通過Windows系統提供的任何通道,所有的類型的HOOK都無法在此期間截獲虛擬鍵盤輸入的密碼(很多安全軟件提供的虛擬鍵盤的思路大致如此吧,沒有去仔細調研)。

   截屏保護

很多截屏類的鍵盤記錄程序。當用戶在虛擬鍵盤上按下一個鍵時,惡意程序就截一次屏幕,這樣可以清楚地看到用戶輸入的密碼信息。掛鈎兩個函數NtGdiBitBlt、NtGdiStretchBlt可以用於防截屏。

卡巴斯基反病毒軟件率先推出截屏保護,即當虛擬鍵盤運行的時候,阻止程序進行截屏操作,在一定程度上可以阻止此類鍵盤記錄工具的工作。但是卡巴斯基有一個很大的缺陷,它只能阻止全屏的截圖,不能阻止部分屏幕的截圖。



簡單實現:
首先定位到SSDT SHADOW的地址
使用的硬編碼,根據相同版本下與SSDT地址存在的偏移獲取的SSDT SHADOW的地址
windbg下
kd> ?KeServiceDescriptorTable-KeServiceDescriptorTableShadow
Evaluate expression: 64 = 00000040
我只裝了XP的虛擬機 [cpp] view plain copy
  1. switch (dwVersion)  
  2.     {  
  3.     case VERSION_2K:  
  4.         KeServiceDescriptorTableShadow=(DWORD)KeServiceDescriptorTable+0xE0;  
  5.         break;  
  6.     case VERSION_2K3:     
  7.         break;  
  8.     case VERSION_XP:  
  9.         KeServiceDescriptorTableShadow=(DWORD)KeServiceDescriptorTable-0x40;  
  10.         break;  
  11.     case VERSION_VISTA:  
  12.         break;  
  13.     case VERSION_WIN7:  
  14.         break;  
  15.     }  

方法其實好多,參考 http://bbs.pediy.com/showthread.php?t=56955
然后就是HOOK了
[cpp] view plain copy
  1. typedef BOOL (__stdcall *PNtUserDestroyWindow)(HWND hwnd);  
  2. //定義原來和當前的函數地址  
  3. PNtUserDestroyWindow Old_NtUserDestroyWindow,Cur_NtUserDestroyWindow;  
  4.   
  5. BOOL __stdcall MyNtUserDestroyWindow(HWND hwnd)  
  6. {   
  7.     if (hwnd==hProctecHwnd)   
  8.     {  
  9.         KdPrint(("被保護窗口hwnd=%d \n",hwnd));  
  10.         return FALSE;//如果是被保護窗口的句柄直接返回錯誤  
  11.     }  
  12.     else      
  13.         return Old_NtUserDestroyWindow(hwnd);//調用原來的函數  
  14. }  
  15.   
  16. //SSDT SHADOW HOOK  
  17. VOID HookNtUserDestroyWindow()  
  18. {  
  19.     DWORD SSDTShadowBaseAddr=GetSSDTShadowAddr()+0x10;//表基址所在地址  
  20.     DWORD TableCount=SSDTShadowBaseAddr+0x8;//函數數量所在地址  
  21.     DWORD dwCount=*((PDWORD)TableCount);  
  22.     PDWORD Fun_Addr=(PDWORD)(*((PDWORD)SSDTShadowBaseAddr));  
  23.     PDWORD NtUserDestroyWindowAddr=Fun_Addr+355;  
  24.     //保存原函數地址,SSDT HOOK是根據ZW函數地址硬編碼得出的索引得到的函數地址  
  25.     Old_NtUserDestroyWindow=(PNtUserDestroyWindow)(*NtUserDestroyWindowAddr);  
  26.     KdPrint(("ssdt shadow addr:0x%X",SSDTShadowBaseAddr));  
  27.     KdPrint(("數量是:%d",dwCount));  
  28.     KdPrint(("原函數地址:%X\n",*NtUserDestroyWindowAddr));  
  29.     KdPrint(("新函數地址:%X\n",MyNtUserDestroyWindow));  
  30.     __asm //去掉頁面保護  
  31.     {  
  32.         cli  
  33.             mov eax,cr0  
  34.             and eax,not 10000h //and eax,0FFFEFFFFh  
  35.             mov cr0,eax  
  36.   
  37.     }  
  38.   
  39.     *NtUserDestroyWindowAddr=MyNtUserDestroyWindow;     //指向HOOK地址  
  40.     __asm   
  41.     {   
  42.         mov  eax, cr0   
  43.         or   eax, 10000h   
  44.         mov  cr0, eax   
  45.         sti   
  46.     }   
  47.     KdPrint(("hook ok"));  
  48. }  
  49.   
  50. VOID UNHOOKNtUserDestroyWindow() //還原HOOK  
  51. {   
  52.     DWORD SSDTShadowBaseAddr=GetSSDTShadowAddr()+0x10;    
  53.     PDWORD Fun_Addr=(PDWORD)(*((PDWORD)SSDTShadowBaseAddr));  
  54.     PDWORD NtUserDestroyWindowAddr=Fun_Addr+355;  
  55.       
  56.     __asm //去掉頁面保護  
  57.     {  
  58.         cli  
  59.             mov eax,cr0  
  60.             and eax,not 10000h //and eax,0FFFEFFFFh  
  61.             mov cr0,eax  
  62.   
  63.     }  
  64.     *NtUserDestroyWindowAddr=Old_NtUserDestroyWindow;  
  65.   
  66.     __asm   
  67.     {   
  68.         mov  eax, cr0   
  69.             or   eax, 10000h   
  70.             mov  cr0, eax   
  71.             sti   
  72.     }   
  73.     KdPrint(("unhook..."));  
  74. }  

HOOK了一個NtUserDestroyWindow,讓某個窗口點擊右上角的叉號關閉不了

在實現驅動與應用程序通信,控制台接收漢字輸入的時候竟然卡了一會。。。

注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com