一、WidnowsHOOK分类

  1. 系统钩子(System Hooks):系统钩子是最常见的一类钩子,用于监视和修改系统级别的事件。系统钩子可以捕获和处理各种系统事件,如键盘输入、鼠标消息、窗口消息等。系统钩子可以全局范围内生效,可以对整个系统中的事件进行拦截和处理。

  2. 应用程序钩子(Application Hooks):应用程序钩子是针对特定应用程序的钩子,用于监视和修改特定应用程序的行为。应用程序钩子可以捕获和处理应用程序级别的事件,如窗口消息、菜单消息、定时器消息等。应用程序钩子只对指定的应用程序生效。

  3. 线程钩子(Thread Hooks):线程钩子是针对特定线程的钩子,用于监视和修改特定线程的行为。线程钩子可以捕获和处理特定线程的事件,如窗口消息、键盘输入等。线程钩子只对指定的线程生效。

  4. 子类钩子(Subclass Hooks):子类钩子是一种特殊的钩子,用于修改指定窗口类的消息处理函数。通过子类钩子,可以在不修改原始窗口类的情况下,对窗口消息进行自定义处理。

  5. WH_CALLWNDPROC钩子:这是一种特殊的系统钩子,用于监视和修改窗口消息的处理过程。WH_CALLWNDPROC钩子可以拦截和处理窗口消息的传递过程,允许对消息进行修改或拦截。

二、设键盘钩子钩子

1. SetWindowsHook可以HOOK类型
WH_KEYBOARD(键盘钩子):用于监视和处理键盘输入事件,可以拦截和修改键盘输入。

WH_MOUSE(鼠标钩子):用于监视和处理鼠标事件,包括鼠标移动、按下和释放鼠标按键等。

WH_KEYBOARD_LL(低级键盘钩子):与WH_KEYBOARD类似,但是是一个低级别的钩子,可以全局拦截键盘输入。

WH_MOUSE_LL(低级鼠标钩子):与WH_MOUSE类似,但是是一个低级别的钩子,可以全局监视鼠标事件。

WH_CALLWNDPROC(窗口消息钩子):用于监视和处理窗口消息,可以拦截和修改窗口消息的传递。

WH_GETMESSAGE(消息钩子):用于监视和处理消息队列中的消息,可以拦截和修改消息的传递。
2. 从零写一个键盘HOOK

首先需要的函数有

SetWindowsHookEx设置一个HOOK

HHOOK SetWindowsHookExA(
  [in] int       idHook,  //选择HOOK的目标
  [in] HOOKPROC  lpfn,    //回调函数
  [in] HINSTANCE hmod,    //过于抽象
  [in] DWORD     dwThreadId //选择一个线程0是全局
);

回调函数HOOK到之后需要执行的流程
RESULT CALLBACK是一种函数类型,它是Windows API中定义的一种回调函数类型。 参数是由SetWindowsHook给的

RESULT CALLBACK HookTest(
    int nCode, //接收事件状态
    WPARAM wParam, //wParam表示键盘事件的消息标识符 
    LPARAM lParam, //按下的键的虚拟键码、扫描码、扩展键标志等。 
);

GetMessage 用于从消息队列中获取一个消息

BOOL GetMessage(
  [out]          LPMSG lpMsg, //从线程队列接收消息
  [in, optional] HWND  hWnd, //NULL检测所有线程窗口, -1只接受当前线程消息
  [in]           UINT  wMsgFilterMin, //要检索的最低消息值的整数值 
  [in]           UINT  wMsgFilterMax  //要检索的最高消息值的整数值
);

PostQuitMessage 发送一个退出消息

void PostQuitMessage(
  [in] int nExitCode
);

UnhookWindowsHookEx 结束HOOK

BOOL UnhookWindowsHookEx(
  [in] HHOOK hhk  //要结束的HOOK
);

C++ 代码示例

#include <Windows.h>
#include <iostream> 
using namespace std;


//钩子函数
LRESULT CALLBACK KeyboardHookCallback(int nCode, WPARAM wParam, LPARAM lParam)
{	
	if (nCode >= 0) 
	{
		KBDLLHOOKSTRUCT* pKeyboardHookStruct = (KBDLLHOOKSTRUCT*)lParam; //获取按下的键的虚拟键码、扫描码、扩展键标志等。 
		if (wParam == WM_KEYDOWN) //键盘按下
		{
			printf("%s\n", "你按下了");
			BYTE keyState[256]; //存放整数的大小
			wchar_t buffer[2]; //宽字节变量
			buffer[1] = '\0'; //设置终止符
			if (GetKeyboardState(keyState)) //获取按键状态 返回布尔 keyState其中的265键
			{	
				//判断返回是不是大于0
				//ToUnicode 将指定的虚拟键代码和键盘状态转换为相应的 Unicode 字符。 ToUnicode 参数 1. 要转换的虚拟密钥代码。 2. 要转换的密钥的硬件 扫描代码 。 3. keyState 前键盘状态的 256 字节数组的指针。 数组中 (字节) 的每个元素都包含一个键的状态。 4. 已接收的按键 5,接收按键的缓冲区大小 6,函数行为 ‘0’ 不受理ALT+数字键组合  
				if (ToUnicode(pKeyboardHookStruct->vkCode, pKeyboardHookStruct->scanCode, keyState, (LPWSTR)&buffer, 1, 0) > 0)
				{ 
					wcout << "按下:" << buffer << endl; // wcout宽字节输出  
					if (97 == buffer[0])  //判断符合条件退出
					{
						PostQuitMessage(0);
					}
					
				}
			}
		}
		else if(wParam == WM_KEYUP) //键盘松开
		{
			printf("%s\n", "你松开了");
		}
		else
		{
			printf("%s\n", "键盘处于按下与松开的叠加状态????");
		}
	}
}


int main()
{    
	//安装HOOK  
	HHOOK hKeyHook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardHookCallback, NULL, 0);
	//判断是否HOOK成功
	if (hKeyHook == NULL)
	{
		printf("%s\n", "获取失败");
	}
	//消息循环
	//GetMessage 调用线程的 消息队列 里取得一个消息并将其放于指定的结构
	MSG msg;
	while (GetMessage(&msg, NULL, 0, 0))
	{
		//将虚拟密钥消息转换为字符消息 , 人话:把键盘的输入流转换为字符串
		TranslateMessage(&msg);
		//DispatchMessageW把消息发往窗口 
		DispatchMessageW(&msg);
	}

	//卸载HOOK
	UnhookWindowsHookEx(hKeyHook);
	return 0;
}
3. 逆向HOOK

源码

	//安装HOOK  
	HHOOK hKeyHook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardHookCallback, NULL, 0);
	//判断是否HOOK成功
	if (hKeyHook == NULL)
	{
		printf("%s\n", "获取失败");
	}

逆向

.text:00414E10                 push    ebp             ; Main函数
.text:00414E11                 mov     ebp, esp
.text:00414E13                 sub     esp, 0F4h       ; 申请栈空间
.text:00414E19                 push    ebx             ; 保存栈指指针装状态
.text:00414E1A                 push    esi
.text:00414E1B                 push    edi
.text:00414E1C                 lea     edi, [ebp+var_34]
.text:00414E1F                 mov     ecx, 0Dh
.text:00414E24                 mov     eax, 0CCCCCCCCh
.text:00414E29                 rep stosd
.text:00414E2B                 mov     eax, ___security_cookie
.text:00414E30                 xor     eax, ebp
.text:00414E32                 mov     [ebp+var_4], eax
.text:00414E35                 mov     esi, esp
.text:00414E37                 push    0               ; dwThreadId
.text:00414E39                 push    0               ; hmod
.text:00414E3B                 push    offset fn       ; lpfn 回调函数 指向虚函数表
.text:00414E40                 push    0Dh             ; idHook
.text:00414E42                 call    ds:SetWindowsHookExW
.text:00414E48                 cmp     esi, esp
.text:00414E4A                 call    j___RTC_CheckEsp ; 检查溢出
.text:00414E4F                 mov     [ebp+hhk], eax  ; SetWindowsHook返回值
.text:00414E52                 cmp     [ebp+hhk], 0    ; if 返回值是否是0
.text:00414E56                 jnz     short loc_414E6A
.text:00414E58                 push    offset unk_41DCC4 ; char
.text:00414E5D                 push    offset aS       ; "%s\n"
.text:00414E62                 call    printf
.text:00414E67                 add     esp, 8          ; 堆栈平衡

回调函数是什么时候被调用的,在debug 中查看 在调用SetWIndowsHook中传入了push Hook技术.0041119A,跟如 查看SetWindowsHook Call中

00414E37    6A 00           push 0x0
00414E39    6A 00           push 0x0
00414E3B    68 9A114100     push Hook技术.0041119A                                                   ; 回调函数
00414E40    6A 0D           push 0xD
00414E42    FF15 5C114200   call dword ptr ds:[<&USER32.SetWindowsHookExW>]                        ; apphelp.6E2B1AC0
00414E48    3BF4            cmp esi,esp
00414E4A    E8 EFC4FFFF     call Hook技术.0041133E

进入SetWindowsHook Call查看内部执行流程 走到基址6E2B1ADC的时候回调函数又入栈了,如何调用?

EPB的值加上0xC的时候就是0019 FDD8 然后再取ss取值就是0041119A在调用SetWindowsHook之前存放回调函数的位置,然后压入Call中这是产生关联不会执行。

要是说回调函数被那个函数调用了只能说:回调函数是由操作系统在特定事件发生时主动调用的,而不是由用户代码直接调用的。是在GetMessage之后被调用的

	MSG msg;
	while (GetMessage(&msg, NULL, 0, 0))
	{
		//将虚拟密钥消息转换为字符消息 , 人话:把键盘的输入流转换为字符串
		TranslateMessage(&msg);
		//DispatchMessageW把消息发往窗口 
		DispatchMessageW(&msg);
	}

	//卸载HOOK
	UnhookWindowsHookEx(hKeyHook);
	return 0;
.text:00414E6A                 mov     esi, esp
.text:00414E6C                 push    0               ; wMsgFilterMax  四个参数
.text:00414E6E                 push    0               ; wMsgFilterMin
.text:00414E70                 push    0               ; hWnd
.text:00414E72                 lea     eax, [ebp+Msg]  ; 取内存地址
.text:00414E75                 push    eax             ; lpMsg 压入指针
.text:00414E76                 call    ds:GetMessageW  ; 四个参数压入GetMessagW
.text:00414E7C                 cmp     esi, esp        ; 检查溢出
.text:00414E7E                 call    j___RTC_CheckEsp
.text:00414E83                 test    eax, eax        ; 条件永远为真
.text:00414E85                 jz      short loc_414EAF ; ZF为1跳转
.text:00414E87                 mov     esi, esp        ; 保存栈指针
.text:00414E89                 lea     eax, [ebp+Msg]
.text:00414E8C                 push    eax             ; lpMsg
.text:00414E8D                 call    ds:TranslateMessage
.text:00414E93                 cmp     esi, esp        ; 检查溢出
.text:00414E95                 call    j___RTC_CheckEsp
.text:00414E9A                 mov     esi, esp
.text:00414E9C                 lea     eax, [ebp+Msg]
.text:00414E9F                 push    eax             ; lpMsg
.text:00414EA0                 call    ds:DispatchMessageW
.text:00414EA6                 cmp     esi, esp
.text:00414EA8                 call    j___RTC_CheckEsp
.text:00414EAD                 jmp     short loc_414E6A ; 这是一个while(True) 循环因为 只有 00414E85 能跳出 jmp 但是永远为真True
.text:00414EAF ; ---------------------------------------------------------------------------
.text:00414EAF
.text:00414EAF loc_414EAF:                             ; CODE XREF: _main_0+75↑j
.text:00414EAF                 mov     esi, esp
.text:00414EB1                 mov     eax, [ebp+hhk]
.text:00414EB4                 push    eax             ; hhk 压入SetWindowsHook 的句柄
.text:00414EB5                 call    ds:UnhookWindowsHookEx ; 退出Hook函数

KeyboardHookCallback (回调函数)

LRESULT CALLBACK KeyboardHookCallback(int nCode, WPARAM wParam, LPARAM lParam)
{	
	if (nCode >= 0) 
	{
		KBDLLHOOKSTRUCT* pKeyboardHookStruct = (KBDLLHOOKSTRUCT*)lParam; //获取按下的键的虚拟键码、扫描码、扩展键标志等。 
		if (wParam == WM_KEYDOWN) //键盘按下3
		{
			printf("%s\n", "你按下了");
			BYTE keyState[256]; //存放整数的大小
			wchar_t buffer[2]; //宽字节变量 2 给字节
 			buffer[1] = '\0'; //设置终止符
			if (GetKeyboardState(keyState)) //获取按键状态 返回布尔 keyState其中的265键
.text:004147D3                 xor     eax, ebp
.text:004147D5                 mov     [ebp+var_4], eax ; if (nCode >= 0)
.text:004147D8                 cmp     [ebp+arg_0], 0
.text:004147DC                 jl      loc_414912      ; 回调函数结束(KeyboardHookCallback)
.text:004147E2                 mov     eax, [ebp+arg_8] ; KBDLLHOOKSTRUCT* pKeyboardHookStruct = (KBDLLHOOKSTRUCT*)lParam;
.text:004147E5                 mov     [ebp+var_C], eax
.text:004147E8                 cmp     [ebp+arg_4], 100h ; if (wParam == WM_KEYDOWN(0x100)) //键盘按下
.text:004147EF                 jnz     loc_4148E3
.text:004147F5                 push    offset unk_41DC74 ; char 字符串:你按下了
.text:004147FA                 push    offset aS       ; "%s\n"
.text:004147FF                 call    printf
.text:00414804                 add     esp, 8          ; printf堆栈平衡
.text:00414807                 mov     [ebp+var_1EC], 2
.text:00414811                 cmp     [ebp+var_1EC], 4
.text:00414818                 jnb     short loc_41481C ; 检查堆栈溢出的Call
.text:0041481A                 jmp     short loc_414821
.text:0041481C ; ---------------------------------------------------------------------------
.text:0041481C
.text:0041481C loc_41481C:                             ; CODE XREF: sub_4147B0+68↑j
.text:0041481C                 call    j____report_rangecheckfailure ; 检查堆栈溢出的Call
.text:00414821
.text:00414821 loc_414821:                             ; CODE XREF: sub_4147B0+6A↑j
.text:00414821                 xor     eax, eax
.text:00414823                 mov     ecx, [ebp+var_1EC] ; wchar_t buffer[2];
.text:00414829                 mov     [ebp+ecx+pwszBuff], ax ;  buffer[1] = '\0';
.text:00414831                 mov     esi, esp
.text:00414833                 lea     eax, [ebp+KeyState] ; BYTE keyState[256];
.text:00414839                 push    eax             ; lpKeyState
.text:0041483A                 call    ds:GetKeyboardState ; if (GetKeyboardState(keyState))
if (ToUnicode(pKeyboardHookStruct->vkCode, pKeyboardHookStruct->scanCode, keyState, (LPWSTR)&buffer, 1, 0) > 0)
				{ 
					wcout << "按下:" << buffer << endl; // wcout宽字节输出  
.text:0041487D                 test    eax, eax        ; if (ToUnicode(pKeyboardHookStruct->vkCode, pKeyboardHookStruct->scanCode, keyState, (LPWSTR)&buffer, 1, 0) > 0)
.text:0041487F                 jle     short loc_4148E1 ; 跳到函数结束
.text:00414881                 mov     esi, esp
.text:00414883                 push    offset sub_411505
.text:00414888                 lea     eax, [ebp+pwszBuff]
.text:0041488E                 push    eax             ; String buffer
.text:0041488F                 push    offset Str      ; Str "按下"
.text:00414894                 mov     ecx, ds:?wcout@std@@3V?$basic_ostream@_WU?$char_traits@_W@std@@@1@A ; std::wostream std::wcout
.text:0041489A                 push    ecx             ; int
.text:0041489B                 call    sub_4111D6      ; cout << "按下:" << endl;
.text:004148A0                 add     esp, 8
.text:004148A3                 push    eax             ; int
.text:004148A4                 call    sub_4112E4      ; wcout << buffer << endl
.text:004148A9                 add     esp, 8
.text:004148AC                 mov     ecx, eax
.text:004148AE                 call    ds:??6?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@P6AAAV01@AAV01@@Z@Z ;  我也不清楚这是什么
if (97 == buffer[0])  //判断符合条件退出
					{
						PostQuitMessage(0);
					}
					
.text:004148C0                 imul    ecx, eax, 0
.text:004148C3                 movzx   edx, [ebp+ecx+pwszBuff]
.text:004148CB                 cmp     edx, 61h ; 'a'  ; if (97 == buffer[0])
.text:004148CE                 jnz     short loc_4148E1
.text:004148D0                 mov     esi, esp
.text:004148D2                 push    0               ; nExitCode
.text:004148D4                 call    ds:PostQuitMessage ; 退出消息队列
 if(wParam == WM_KEYUP) //键盘松开
		{
			printf("%s\n", "你松开了");
		}
.text:004148E3
.text:004148E3 loc_4148E3:                             ; CODE XREF: sub_4147B0+3F↑j
.text:004148E3                 cmp     [ebp+arg_4], 101h ; else if(wParam == WM_KEYUP) //键盘松开
.text:004148EA                 jnz     short loc_414900
.text:004148EC                 push    offset unk_41DC8C ; 字符串:你松开了
.text:004148F1                 push    offset aS       ; "%s\n"
.text:004148F6                 call    printf
.text:004148FB                 add     esp, 8
.text:004148FE                 jmp     short loc_414912 ; 回调函数结束(KeyboardHookCallback)