本文讲下Windows API HOOK(钩子),主要和DLL注入联系在一起.
Windows系统把每个WindowsAPI函数功能封装到了每一个DLL库文件中,从而提高程序的编译运行效率,而在应用程序运行期间动态库时,必然存在程序获取DLL中的函数地址.于是通过注入DLL来截取信息.
消息HOOK
HOOK(挂钩)技术,可分为 内核层(R0)HOOK , 应用层(R3)HOOK ,这两种技术实现起来虽然略相似,但是 内核层HOOK 比起 应用层HOOK 难度要高得多,而且编写的代码也要保证正确(异常机制),不然万一来个赋值给一个空指针的代码,蓝屏就呵呵了…本文主要讲解 应用层hook
主要用到的WindowsAPI
头文件 Winuser.h (include Windows.h)
库文件 User32.lib
DLL文件 User32.dll
SetWindowsHookEx
HHOOK WINAPI SetWindowsHookEx ( _In_ int idHook, _In_ HOOKPROC lpfn, _In_ HINSTANCE hMod, _In_ DWORD dwThreadId ) ;
其中 idHook 可选的值有很多种, 更多选项可以到 MSDN https://msdn.microsoft.com/en-us/library/windows/desktop/ms644990(v=vs.85).aspx 查看
本文用到其中的 WH_GETMESSAGE 监视发送到消息队列的消息
GetMsgProc
其 _In_ HOOKPROC lpfn,
对应的回调函数如下
LRESULT CALLBACK GetMsgProc ( _In_ int code, _In_ WPARAM wParam, _In_ LPARAM lParam ) ;
lParam 参数可以转换为 **MSG***结构体
MSG* pMsg = (MSG*)lParam;
CallNextHookEx
这个函数起到传递下一个钩子函数的作用,这样的话,其他钩子才可以继续执行
LRESULT WINAPI CallNextHookEx ( _In_opt_ HHOOK hhk, _In_ int nCode, _In_ WPARAM wParam, _In_ LPARAM lParam ) ;
nCode,wParam,lParam 一般都是 GetMsgProc 中的参数
,而 hhk 则为 SetWindowsHookEx 返回的钩子句柄.
UnhookWindowsHookEx
卸载钩子,与 SetWindowsHookEx
成对使用
BOOL WINAPI UnhookWindowsHookEx ( _In_ HHOOK hhk ) ;
下面以一个简单例子说明(MFC )
Source.def文件,用于导出DLL函数
LIBRARY hookFun.dll ;dll名 EXPORTS ;导出函数 SetHook @1 UnsetHook @2
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 #include <windows.h> #define DLL_EXPORT extern "C" __declspec(dllexport) DLL_EXPORT void _stdcall SetHook (DWORD pid) ;DLL_EXPORT void _stdcall UnsetHook () ; HHOOK hHook_g = NULL ; HINSTANCE hInstance_g = NULL ;BOOL APIENTRY DllMain (_In_ void * _DllHandle, _In_ unsigned long _Reason, _In_opt_ void * _Reserved) { if (_Reason==DLL_PROCESS_ATTACH) { hInstance_g = (HINSTANCE)_DllHandle; } return TRUE; }#define WM_HOOKMSG WM_USER+100 LRESULT CALLBACK GetMsgProc (_In_ int code,_In_ WPARAM wParam,_In_ LPARAM lParam) { MSG* pMsg = (MSG*)lParam; if (pMsg->message== WM_HOOKMSG) { MessageBoxA (NULL , "HOOK MSG" , NULL , 0 ); } return CallNextHookEx (hHook_g, code, wParam, lParam); }DLL_EXPORT void _stdcall SetHook (DWORD tid) { hHook_g = SetWindowsHookExW (WH_GETMESSAGE, GetMsgProc, hInstance_g, tid); }DLL_EXPORT void _stdcall UnsetHook () { UnhookWindowsHookEx (hHook_g); }
调用进程主要代码如下
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 #define WM_HOOKMSG WM_USER+100 #define DLL_PATH L"C:\\hookFun.dll" typedef void (_stdcall* LPFNSETHOOK) (DWORD) ;typedef void (_stdcall* LPFNUNSETHOOK) () ; HMODULE hMo = NULL ;void ChookMfcDlg::OnBnClickedButton1 () { HWND hwndMain = ::FindWindow (NULL , L"剪贴簿查看器 - [剪贴板]" ); DWORD PID; char szMsg[50 ]={ 0 }; DWORD TID= GetWindowThreadProcessId (hwndMain, &PID); sprintf (szMsg, "Thread ID:%08X" , TID); MessageBoxA (NULL , szMsg, szMsg, MB_ICONINFORMATION); hMo= LoadLibrary (DLL_PATH); LPFNSETHOOK lpSethook = (LPFNSETHOOK)GetProcAddress (hMo, "SetHook" ); lpSethook (TID); PostThreadMessageW (TID, WM_HOOKMSG, 0 , 0 ); LPFNUNSETHOOK lpUnSethook = (LPFNUNSETHOOK)GetProcAddress (hMo, "UnsetHook" ); }void ChookMfcDlg::OnBnClickedButton2 () { LPFNUNSETHOOK lpUnSethook = (LPFNUNSETHOOK)GetProcAddress (hMo, "UnsetHook" ); lpUnSethook (); FreeLibrary (hMo); }
一般来说,注入DLL这项技术确实很老了,注入时可能会失败.so …
键盘HOOK
这个例子没有用到DLL,直接勾住当前程序
KeyboardProc —- WH_KEYBOARD(2)
LowLevelKeyboardProc —- WH_KEYBOARD_LL(13)
注意 上面的 KeyboardProc 和 LowLevelKeyboardProc 函数,前者是普通 的钩子函数,而后者是一个低级 的钩子函数,这两个函数的参数不同,所以要获取的值也不同!!
wParam [in]
Type: WPARAM
The virtual-key code of the key thatgenerated the keystroke message.
lParam [in]
Type: LPARAM
The repeat count, scan code, extended-key flag, contextcode, previous key-state flag, and transition-state flag. For more informationabout thelParam parameter, seeKeystroke Message Flags. The following table describes the bits of this value.
由此可看出 ,wparam主要是键盘的虚拟键代码,lparam主要是:
The lParam parameter of a keystroke message containsadditional information about the keystroke that generated the message. Thisinformation includes therepeat count, the scan code, the extended-key flag, thecontext code, the previous key-state flag, and the transition-state flag.The following illustration shows the locations of these flags and values in thelParam parameter.
当 SetWindowsHookEx() 参数 idHOOK 为 WH_KEYBORAD_LL 时,这个函数的参数中的 wparam 为 键盘消息 ,如WM_KEYDOMN… 那么 lparam 就是一个 LPKBDLLHOOKSTRUCT结构体 了!看这个结构体的名字就知道这是一个用于 低级键盘钩子的,“LL”->”LowLevel“,呵呵~这个结构体中 包括了 虚拟键代码 和 扫描码 !
别忘了还有那个 SetWindowsHookEx() 函数,现在在回忆一下那个函数原型:
HHOOK WINAPI SetWindowsHookEx ( _In_ int idHook, _In_ HOOKPROC lpfn, _In_ HINSTANCE hMod, _In_ DWORD dwThreadId ) ;
代码如下:
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 HHOOK hHook2;LRESULT CALLBACK KeyBoradProc (intcode,WPARAMwp,LPARAMlp) { if (code<0 ) { return CallNextHookEx (hHook2,code,wp, lp); } WCHAR szkeyValue[20 ]={ 0 }; GetKeyNameText (lp,szkeyValue, 50 ); AfxGetMainWnd ()->SetDlgItemText (IDC_EDIT1,szkeyValue); return CallNextHookEx (hHook2,code,wp, lp); } hHook2 = SetWindowsHookEx (WH_KEYBOARD,KeyBoradProc,AfxGetInstanceHandle (),GetCurrentThreadId ()); UnhookWindowsHookEx (hHook2); 下面是关于低级键盘钩子的代码; hHook= ::SetWindowsHookEx (WH_KEYBOARD_LL,myLowLevelKeyboardProc,AfxGetInstanceHandle (),0 ); UnhookWindowsHookEx (hHook)
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 LRESULT CALLBACK LowLevelKeyboardProc (_In_int code,_In_WPARAMwParam,_In_LPARAMlParam) { if (code < 0 ) { returnCallNextHookEx (hHook,code,wParam, lParam); } if (code ==HC_ACTION&&wParam==WM_KEYDOWN) { LPKBDLLHOOKSTRUCTpKbs = (LPKBDLLHOOKSTRUCT)lParam; WCHARszlMsg[100 ] = { 0 }; wsprintf (szMsg,L"vkCode:%c-scanCode:%02X" ,pKbs->vkCode,pKbs->scanCode); if (pKbs->scanCode==0x01 ) { returnTRUE; } } return CallNextHookEx (hHook,code,wParam, lParam); }