钩子原理     Windows下的应用程序大部分是基于消息模式机制的,一些CUI程序不基于消息。Windows下应用程序都有一个消息函数,根据不同的消息来完成不同的功能。Windows提供的钩子机制是用来截获监视系统中的消息。不同的钩子可以处理不同信息。
    钩子分为局部钩子和全局钩子。局部钩子是针对一个线程的,而全局钩子是针对整个操作系统内基于消息机制的应用程序的。全局钩子需要使用DLL文件,DLL文件里存放了钩子函数的代码。
    安装全局钩子后,只要进程接收到可以发出钩子的消息后,全局钩子的DLL文件会被操作系统自动或强行的加载到该进程中。由此可见,设置消息钩子也是一种可以进行DLL注入的方法。
钩子函数 钩子函数在系统消息触发时被系统调用 。在某个事件触发后,钩子函数捕获它并完成一些操作,是一段用以处理系统消息的程序
相关API函数 1 2 3 4 5 6 LRESULT CALLBACK HookProc   (   int  nCode,   WPARAM wParam,   LPARAM lParam, );
 
参数nCode:钩子代码,钩子子程通过该代码来决定执行什么动作。该值取决于钩子的类型,每种类型都拥有自己特有的钩子代码集合。
参数wParam和lParam 的值,都取决于钩子代码。但是一般都包含发送或者传递的消息的信息。
SetWindowsHookEx 返回一个钩子句柄
1 2 3 4 5 6 HHOOK WINAPI SetWindowsHookEx (    _In_  int  idHook,   _In_  HOOKPROC lpfn,   _In_  HINSTANCE hMod,   _In_  DWORD dwThreadId )  ;
 
lpfn:指定HOOK函数的地址。如果dwThread参数被设置为0或者被设置为一个进程中的线程ID,则该回调HOOK函数只能在DLL文件中。如果dwThread为当前进程中的线程ID,则这个回调函数可以在当前进程中也可以在DLL中。
hMod:钩子函数所在模块的句柄。lpfn所在的模块的句柄,如果dwThreadId为当前进程中的线程ID,而且lpfn所指向的函数在当前进程中,那么hMod被设置为NULL
dwThreadId:需要被挂钩的线程ID号(指定的话为局部钩子),如果设置为0表示在基于消息机制的所有的线程挂钩(全局钩子),如果指定为具体ID好,那么表示要在指定的线程中进行挂钩。这个参数影响上边两个参数的取值,决定了该钩子属于全局钩子还是局部钩子。
idHook:钩子的类型
UnhookWindowsEx 移除先前用SetWindowsHookEx安装的钩子,
1 2 3 BOOL UnhookWindowsHookEx (    HHOOK hhk             )  ;
 
可以多次反复安装钩子,而且可以安装多个同样类型的钩子。这样会形成一条钩子链,最后安装的钩子会首先截获到消息,当该钩子对消息处理完毕以后会选择返回,或者继续传递消息。通常情况下,为了消息可以传达到目标窗口,我们会选择将消息继续传递
CallNextHookEx 使消息继续传递,第一个参数为钩子句柄,后面三个为钩子函数的参数
1 2 3 4 5 6 LRESULT CallNextHookEx (    HHOOK  hhk,   int     nCode,   WPARAM wParam,   LPARAM lParam )  ;
 
GetKeyNameText GetKeyNameText函数检索表示键的名称的字符串。
1 2 3 4 5 int  GetKeyNameTextA (   LONG  lParam,   LPSTR lpString,   int    cchSize )  ;
 
键盘钩子实例 功能为用Messagebox显示按下键的字符。既然要截获键盘消息,那么肯定是截获系统范围内的键盘消息,因此需要安装全局钩子,这样就需要DLL文件支持。
首先新建DLL文件,定义两个导出函数和两个全局变量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 #include  "pch.h"  #include  <Windows.h>  extern  "C"  __declspec(dllexport) void  SetHookOn ()  ;extern  "C"  __declspec(dllexport) void  SetHookOff ()  ; HHOOK g_Hook = NULL ; HINSTANCE g_Inst = NULL ;LRESULT CALLBACK KeyboardProc (      int  code,     WPARAM wParam,     LPARAM lParam )  {     if  (code < 0 )     {         return  CallNextHookEx(g_Hook, code, wParam, lParam);     }     if  (code == HC_ACTION && lParam > 0 ) {         TCHAR szBuf[MAXBYTE] = { 0  };         GetKeyNameText(lParam, szBuf, MAXBYTE);         MessageBox(NULL , szBuf, NULL , MB_OK);     }     return  CallNextHookEx(g_Hook, code, wParam, lParam); }void  SetHookOn ()  {     g_Hook = SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, g_Inst, 0 ); }void  SetHookOff ()  {     UnhookWindowsHookEx(g_Hook); }BOOL APIENTRY DllMain ( HMODULE hModule,                         DWORD  ul_reason_for_call,                        LPVOID lpReserved                      )  {     g_Inst = (HMODULE)hModule;     switch  (ul_reason_for_call)     {     case  DLL_PROCESS_ATTACH:     case  DLL_THREAD_ATTACH:     case  DLL_THREAD_DETACH:     case  DLL_PROCESS_DETACH:         break ;     }     return  TRUE; }
 
在DllMain函数中,需要保存该DLL模块的句柄,以方便安装全局钩子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 #include  "pch.h"  #include  "framework.h"  #include  "MFCKeyboardHook.h"  #include  "MFCKeyboardHookDlg.h"  #include  "afxdialogex.h"  #pragma  comment(lib,"KeyboardHook" )   extern  "C"  void  SetHookOn ()  ;extern  "C"  void  SetHookOff ()  ;#ifdef  _DEBUG #define  new DEBUG_NEW #endif   CMFCKeyboardHookDlg::CMFCKeyboardHookDlg(CWnd* pParent ) 	: CDialog(IDD_MFCKEYBOARDHOOK_DIALOG, pParent) { 	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); }void  CMFCKeyboardHookDlg::DoDataExchange (CDataExchange* pDX)  { 	CDialog::DoDataExchange(pDX); } BEGIN_MESSAGE_MAP(CMFCKeyboardHookDlg, CDialog) 	ON_WM_PAINT() 	ON_WM_QUERYDRAGICON() 	ON_BN_CLICKED(IDC_BUTTON2, &CMFCKeyboardHookDlg::OnBnClickedButton2) 	ON_BN_CLICKED(IDC_BUTTON1, &CMFCKeyboardHookDlg::OnBnClickedButton1) END_MESSAGE_MAP()BOOL CMFCKeyboardHookDlg::OnInitDialog ()   { 	CDialog::OnInitDialog(); 	 	 	SetIcon(m_hIcon, TRUE);			 	SetIcon(m_hIcon, FALSE);		 	 	return  TRUE;   }void  CMFCKeyboardHookDlg::OnPaint ()  { 	if  (IsIconic()) 	{ 		CPaintDC dc (this )  ;  		SendMessage(WM_ICONERASEBKGND, reinterpret_cast <WPARAM>(dc.GetSafeHdc()), 0 ); 		 		int  cxIcon = GetSystemMetrics(SM_CXICON); 		int  cyIcon = GetSystemMetrics(SM_CYICON); 		CRect rect; 		GetClientRect(&rect); 		int  x = (rect.Width() - cxIcon + 1 ) / 2 ; 		int  y = (rect.Height() - cyIcon + 1 ) / 2 ; 		 		dc.DrawIcon(x, y, m_hIcon); 	} 	else  	{ 		CDialog::OnPaint(); 	} }HCURSOR CMFCKeyboardHookDlg::OnQueryDragIcon ()   { 	return  static_cast <HCURSOR>(m_hIcon); }void  CMFCKeyboardHookDlg::OnBnClickedButton2 ()  { 	 	SetHookOn(); }void  CMFCKeyboardHookDlg::OnBnClickedButton1 ()  { 	 	SetHookOff(); }
 
全局钩子DLL注入 WH_GETMESSAGE:该钩子作用是监视被投递到消息队列的消息。也就是在调用GetMessage()或PeekMessage()函数时,函数从消息队列中获取一个消息后调用该钩子。
利用WH_GETMESSAGE可以将DLL文件注入到所有的基于消息机制的程序中,在需要DLL大范围注入到基于消息的进程中时可以使用这种方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 #include  "pch.h"  #include  <Windows.h>  #include  <tchar.h>  #pragma  warning (disable:4996) #pragma  data_seg("mydata" )  HHOOK g_Hook = NULL ;                             #pragma  data_seg() #pragma  comment(linker,"/SECTION:mydata,RWS" )      extern  "C"  __declspec(dllexport) void  SetHookOn ()  ;extern  "C"  __declspec(dllexport) void  SetHookOff ()  ; HINSTANCE g_hInst;LRESULT CALLBACK GetMessageProc (      int  nCode,     WPARAM wParam,     LPARAM lParam )  {     MessageBox(NULL , L"Hooked" , L"提示" , MB_ICONWARNING | MB_OKCANCEL);     return  CallNextHookEx(g_Hook, nCode, wParam, lParam); }void  SetHookOn ()  {     g_Hook=SetWindowsHookEx(WH_GETMESSAGE, GetMessageProc, g_hInst, 0 ); }void  SetHookOff ()  {     UnhookWindowsHookEx(g_Hook); }BOOL APIENTRY DllMain ( HMODULE hModule,                         DWORD  ul_reason_for_call,                        LPVOID lpReserved                      )  {     switch  (ul_reason_for_call)     {     case  DLL_PROCESS_ATTACH:     {         g_hInst = hModule;     }     case  DLL_THREAD_ATTACH:     case  DLL_THREAD_DETACH:     case  DLL_PROCESS_DETACH:         break ;     }     return  TRUE; }