Windows向用户提供GUI,以事件驱动的方式工作.
常规的Windows消息流:
发生键盘输入事件时,WM_KeyDown消息被添加到OS消息队列.
OS判断哪个应用程序发生了事件,然后从OS消息队列重取出消息,添加到应用程序的消息队列中.
应用程序监视自身的消息队列,发现新添加的消息后,调用相应的事件处理程序处理.
SetWindowsHookEx()可以实现消息钩取.
第一个参数是钩子类型,例如可以为 WH_KeyBoard ,键盘钩子.
第二个参数是钩子过程,是由操作系统调用的回调函数,说白了就是处理程序.
第三个参数是钩子过程所属的DLL句柄,因为钩子过程需要存在于某个DLL内部.
第四个参数代表想要影响的进程,若为0则影响所有进程.
可执行程序加载并安装钩子 -> 其它进程发生硬盘输入事件 -> OS将Dll文件加载到相应得进程内存 -> 调用回调函数
重点看一下HookMain.exe文件的源代码.
#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)(); //声明一个返回值为NULL的函数指针 void main() { HMODULE hDll = NULL; PFN_HOOKSTART HookStart = NULL; //定义函数指针 PFN_HOOKSTOP HookStop = NULL; //函数指针赋值为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();
printf("press 'q' to quit! "); 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) //此处猜测是覆写了KeyBoardProc()函数 { 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, '\'); //返回字符串中从右侧搜索首次出现字符 \ 的地址。 if( !_stricmp(p + 1, DEF_PROCESS_NAME) ) //字符串相等时,返回值为0 return 1; //程序为notepad.exe时,直接返回1即不将键盘按键数据传到应用程序 } } // 若当前进程名称不是notepad.exe则调用 CallNextHookEx() 函数,将消息传递给应用程序或下一个钩子 return CallNextHookEx(g_hHook, nCode, wParam, lParam); } #ifdef __cplusplus extern "C" { #endif __declspec(dllexport) void HookStart() //declspec是针对编译器的关键字,指出相应函数为导出函数 { 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.KeyboardProc()
Printf()函数看着简单,其实挺复杂的.