一、概述
Windows是非常流行的操作系统,其提供了非常多的API来实现各种功能。其中,hook是Windows系统中的一个重要机制,可以用来监视和影响系统中发生的事件。
通俗地说,hook就是一种机制,通过这种机制可以将自己的函数挂载到指定的Windows事件上,从而实现对这些事件的监视和控制。这样一来,当Windows系统发生特定事件时,挂载在该事件上的函数就会被自动调用。hook机制广泛应用于Windows系统中各种安全软件、GUI界面程序等,是Windows开发过程中非常重要的机制之一。
二、hook的类型
Windows中提供了不同类型的hook,主要分为以下三类:
1. 系统级hook
系统级hook也称为全局hook,可以监视系统中的所有进程。系统级hook需要使用Win32 API中的SetWindowsHookEx函数来实现。系统级hook分为以下三种类型:
- WH_KEYBOARD
WH_KEYBOARD hook允许你监视键盘消息。
HHOOK SetWindowsHookEx( int idHook, HOOKPROC lpfn, HINSTANCE hMod, DWORD dwThreadId );
- WH_MOUSE
WH_MOUSE hook允许你监视鼠标消息。
HHOOK SetWindowsHookEx( int idHook, HOOKPROC lpfn, HINSTANCE hMod, DWORD dwThreadId );
- WH_CALLWNDPROC
WH_CALLWNDPROC hook允许你监视指定的窗口。
HHOOK SetWindowsHookEx( int idHook, HOOKPROC lpfn, HINSTANCE hMod, DWORD dwThreadId );
2. 线程级hook
线程级hook也称为本地hook,只能监视指定线程的事件。线程级hook需要使用Win32 API中的SetWindowsHookEx函数来实现。
3. 子类化钩子
子类化钩子是一种特殊的hook,可以替换窗口中的原有消息处理函数。子类化钩子需要使用Win32 API中的SetWindowLongPtrA函数来实现。
三、hook的实现
1. WH_KEYBOARD hook的实现
WH_KEYBOARD hook可以用来监视键盘消息。
LRESULT CALLBACK KeyboardProc(int code, WPARAM wParam, LPARAM lParam) { if (code < 0) { return CallNextHookEx(NULL, code, wParam, lParam); } switch (wParam) { case VK_F1: //按下F1键,执行一些操作 break; case VK_F2: //按下F2键,执行一些操作 break; default: break; } return CallNextHookEx(NULL, code, wParam, lParam); } void SetKeyboardHook() { HHOOK hook = SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, NULL, GetCurrentThreadId()); }
2. WH_MOUSE hook的实现
WH_MOUSE hook可以用来监视鼠标消息。
LRESULT CALLBACK MouseProc(int code, WPARAM wParam, LPARAM lParam) { if (code < 0) { return CallNextHookEx(NULL, code, wParam, lParam); } switch (wParam) { case WM_LBUTTONDOWN: //鼠标左键按下,执行一些操作 break; case WM_RBUTTONDOWN: //鼠标右键按下,执行一些操作 break; default: break; } return CallNextHookEx(NULL, code, wParam, lParam); } void SetMouseHook() { HHOOK hook = SetWindowsHookEx(WH_MOUSE, MouseProc, NULL, GetCurrentThreadId()); }
3. WH_CALLWNDPROC hook的实现
WH_CALLWNDPROC hook可以用来监视指定窗口的消息。
LRESULT CALLBACK CallWndProc(int code, WPARAM wParam, LPARAM lParam) { if (code < 0) { return CallNextHookEx(NULL, code, wParam, lParam); } LPCWPSTRUCT lpCwpStruct = (LPCWPSTRUCT)lParam; HWND hwnd = lpCwpStruct->hwnd; switch (lpCwpStruct->message) { case WM_COMMAND: //窗口中的菜单被点击 break; case WM_CREATE: //窗口被创建 break; case WM_DESTROY: //窗口被销毁 break; default: break; } return CallNextHookEx(NULL, code, wParam, lParam); } void SetCallWndProcHook(HWND hwnd) { HHOOK hook = SetWindowsHookEx(WH_CALLWNDPROC, CallWndProc, NULL, GetCurrentThreadId()); }
4. 子类化钩子的实现
子类化钩子可以替换窗口中的原有消息处理函数。
LRESULT CALLBACK NewWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_COMMAND: //窗口中的菜单被点击 break; case WM_CREATE: //窗口被创建 break; case WM_DESTROY: //窗口被销毁 break; default: break; } return CallWindowProc(OldWndProc, hwnd, message, wParam, lParam); } void SubclassWnd(HWND hwnd) { OldWndProc = (WNDPROC)SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)NewWndProc); }
四、hook的注意事项
在使用hook的过程中,需要注意以下几个问题:
1. 钩子函数的实现
钩子函数需要使用LRESULT CALLBACK声明,它的返回值可以是true或false。
2. 钩子的顺序
当多个hook挂载在同一事件上时,它们的执行顺序与挂载的顺序有关。因此,在使用多个hook的时候,需要注意它们的调用顺序。
3. 钩子的释放
使用完毕的hook需要及时释放,否则可能会导致系统错误。
4. 钩子的安全性
hook机制可以用来实现各种功能,但是也具有一定的安全风险。因此,在使用hook的过程中需要注意安全性问题。
5. 钩子的范围
不同类型的hook的监视范围不同,需要根据具体需求进行选择。
五、参考资料
1. MSDN官方文档:https://docs.microsoft.com/en-us/windows/win32/winmsg/about-hooks
2. 《Windows核心编程》第五版,陈硕著,机械工业出版社