简单理解:在消息从OS移动到应用程序的时候,我们利用消息钩子在此期间偷看这些信息
钩链:多个相同的消息钩子,按照设置顺序依次调用这些钩子,组成的链条就被称为钩链
HHOOK SetWindowsHookEx( int idHook, // hook type HOOKpROC lpfn, // hook procedure HINSTANCE hMod, //hook procedure所属的DLL句柄 DWORD dwThreadId //需要挂钩的线程ID,为0时表示为全局钩子(Global Hook) );
hook proceduce是由操作系统调用的回调函数;安装消息钩子时,钩子过程需要存在于某个DLL内部,且该DLL的示例句柄即为hMod。
若其他进程(如图中所示)发生键盘输入事件,OS就会强制将KeyHook.dll加载到像一个进程的内存,然后调用KeyboardProc()函数。
KeyHook.dll中含有钩子API函数(KeyboardProc),通过HookMain.exe加载KeyHook.dll并使用(SetwindowsHookEx)安装键盘钩子(KeyboardProc)
LRESULT CALLBACK KeyboardProc( int code, // hook code WPARAM wParam, // virtual-key code LPARAM lParam // keystroke-message information );
HookMain程序的主要源代码如下所示:
#include "stdio.h" #include "conio.h" #include "windows.h" #define DEF_DLL_NAME "KeyHook.dll" #define DEF_HOOKSTART "HookStart" #define DEF_HOOKSTOP "HookStop" typedef void (*PFN_HOOKSTART)(); typedef void (*PFN_HOOKSTOP)(); void main() { HMODULE hDll = NULL; PFN_HOOKSTART HookStart = NULL; PFN_HOOKSTOP HookStop = NULL; char ch = 0; // 加载KeyHook.dll hDll = LoadLibraryA(DEF_DLL_NAME); if( hDll == NULL ) { printf("LoadLibrary(%s) failed!!! [%d]", DEF_DLL_NAME, GetLastError()); return; } // 获取导出函数地址 HookStart = (PFN_HOOKSTART)GetProcAddress(hDll, DEF_HOOKSTART); HookStop = (PFN_HOOKSTOP)GetProcAddress(hDll, DEF_HOOKSTOP); // 开始钩取 HookStart(); // 等待,直到用户输入“q” printf("press 'q' to quit!\n"); while( _getch() != 'q' ) ; // 终止钩子 HookStop(); // 卸载KeyHook.dll FreeLibrary(hDll); }
#include "stdio.h" #include "windows.h" #define DEF_PROCESS_NAME "notepad.exe" HINSTANCE g_hInstance = NULL; HHOOK g_hHook = NULL; HWND g_hWnd = NULL; BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpvReserved) { switch( dwReason ) { case DLL_PROCESS_ATTACH: g_hInstance = hinstDLL; break; case DLL_PROCESS_DETACH: break; } return TRUE; } LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) { char szPath[MAX_PATH] = {0,}; char *p = NULL; if( nCode >= 0 ) { // bit 31 : 0 => press, 1 => release if( !(lParam & 0x80000000) ) //释放键盘按键时 { GetModuleFileNameA(NULL, szPath, MAX_PATH); p = strrchr(szPath, '\\'); //比较当前进程名称是否为notepad.exe,成立则消息不传递给应用程 if( !_stricmp(p + 1, DEF_PROCESS_NAME) ) return 1; } } //如果不是notepad.exe,则调用CallNextHookEx()函数,将消息传递给应用程序 return CallNextHookEx(g_hHook, nCode, wParam, lParam); } #ifdef __cplusplus extern "C" { #endif __declspec(dllexport) void HookStart() { g_hHook = SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, g_hInstance, 0); } __declspec(dllexport) void HookStop() { if( g_hHook ) { UnhookWindowsHookEx(g_hHook); g_hHook = NULL; } } #ifdef __cplusplus } #endif
安装好键盘钩子后,无论在哪个进程中,只要发生了键盘输入事件,OS就会强制将KeyHook.dll注入到进程中,加载了KeyHook.dll的进程,发生键盘事件时会首先调用执行KeyHook.KetyboardProc()。
KetyboardProc()函数中发生键盘输入事件时,会比较当前进程的名称与“notepad.exe”是否相同,相同返回1,终止KetyboardProc()函数,意味着截获并删除了消息,这样键盘消息就不会传递到notepad.exe程序的消息队列。