键盘HOOK学习以及逆向

键盘HOOK学习以及逆向

一、WidnowsHOOK分类#

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

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

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

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

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

#include

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)

#

💡 关键要点

一、WidnowsHOOK分类# 系统钩子(System Hooks):系统钩子是最常见的一类钩子,用于监视和修改系统级别的事件。系统钩子可以捕获和处理各种系统

更多疯狂内容

在After Effects 中加快渲染速度的15个技巧总结
现代牌录音笔的性能及使用评测(解析现代牌录音笔的特点、功能和用户体验)
“老日”是什么字,耆怎么读,不读lǎo,也不读shì,耆字什么意思?