HOOK技术--键盘鼠标钩子实例
1.首先创建一个DLL项目,用以生成自定义的DLL文件,在DLL文件中对外有两个接口,安装钩子的函数SetHook和卸载钩子函数UnSetHook函数,对于
这些函数由于需要导出,所以在工程项目的.h文件中需要声明这些函数需要导出。
//MouseKeyboardHook.h
#ifdef MOUSEKEYBOARDHOOK_EXPORTS #define MOUSEKEYBOARDHOOK_API __declspec(dllexport) #else #define MOUSEKEYBOARDHOOK_API __declspec(dllimport) #endif extern "C" MOUSEKEYBOARDHOOK_API int SetHook( DWORD dwThreadId ); extern "C" MOUSEKEYBOARDHOOK_API int UnSetHook(void);
这里只有安装钩子函数有参数,该参数是线程ID号,这里需要注意,如果在调用DLL时发现找不到函数地址,那就需要加extern “C” 说明了,当时我就是没有加郁闷了很久才找到这个问题。我思考了一下,应该是DLL中我采用的是C编程语言,而我的调用该DLL的应用程序默认是C++,但是由于C和C++的OBJ的函数名格式不同,所以才会出现找不到函数地址的问题。是对于DLL的头文件按照这个格式就可以导出函数了。
//MouseKeyboardHook.cpp文件 #include "stdafx.h" #include "MouseKeyboardHook.h" #include "stdio.h" //共享内存变量 #pragma data_seg("MouseKeyboardHook") HHOOK g_hMouseHook = NULL; HHOOK g_hKeyboardHook = NULL; #pragma data_seg() // 这是导出变量的一个示例 MOUSEKEYBOARDHOOK_API int nMouseKeyboardHook=0; // 这是导出函数的一个示例。 int UnSetHook(void); int fnMouseKeyboardHook(void); int SetHook( DWORD dwThreadId ); //注意:钩子函数的格式必须是 LRESULT CALLBACK 函数名( int 钩子类型, WPARAM wParam, LPARAM lParam ); LRESULT CALLBACK MouseHookProc( int nCode, WPARAM wParam, LPARAM lParam );//处理鼠标的钩子函数 LRESULT CALLBACK KeyboardHookProc( int nCode, WPARAM wParam, LPARAM lParam );//处理键盘的钩子函数 HMODULE WINAPI ModuleFromAddress(PVOID pv) ;//这个是获取DLL的内存地址,可以重复使用,当做模版 MOUSEKEYBOARDHOOK_API int fnMouseKeyboardHook(void)//这个是项目自己生成的没多大用 { return 42; } LRESULT CALLBACK KeyboardHookProc( int nCode, WPARAM wParam, LPARAM lParam ) { if( wParam=='Q' && lParam>0 )//当只有按下Q键的时候才会退出钩子函数。其他其他的话都屏蔽 { MessageBox( NULL, "卸载钩子中...", "卸载钩子", MB_OK ); UnSetHook(); } else return TRUE; return ::CallNextHookEx(g_hKeyboardHook, nCode, wParam, lParam); } LRESULT CALLBACK MouseHookProc( int nCode, WPARAM wParam, LPARAM lParam )//我这里屏蔽所有鼠标消息 { return ::CallNextHookEx(g_hMouseHook, nCode, wParam, lParam); } // 这是已导出类的构造函数。 // 有关类定义的信息,请参阅 MouseKeyboardHook.h CMouseKeyboardHook::CMouseKeyboardHook() { return; } MOUSEKEYBOARDHOOK_API int SetHook( DWORD dwThreadId )//安装钩子函数 { g_hMouseHook = ::SetWindowsHookEx( WH_MOUSE, MouseHookProc, ModuleFromAddress(MouseHookProc), 0 ); g_hKeyboardHook = ::SetWindowsHookEx( WH_KEYBOARD, KeyboardHookProc, ModuleFromAddress(KeyboardHookProc), 0 ); return 0; } MOUSEKEYBOARDHOOK_API int UnSetHook(void)//卸载钩子函数,不过这个例子有问题,卸载不了,不知道为什么 { BOOL b1 = ::UnhookWindowsHookEx( g_hMouseHook ); BOOL b2 = ::UnhookWindowsHookEx( g_hKeyboardHook ); if( b1==FALSE || b2==FALSE ) MessageBox( NULL, "卸载钩子失败!", "", MB_OK ); return 0; } HMODULE WINAPI ModuleFromAddress(PVOID pv) { MEMORY_BASIC_INFORMATION mbi; if(::VirtualQuery(pv, &mbi, sizeof(mbi)) != 0) { return (HMODULE)mbi.AllocationBase; } else { return NULL; } }
这里要注意的地方是钩子函数的格式,其他的话应该问题不大。同时,因为在头文件中已经声明了导出函数,所以这里不需要再次声明了,按照正常函数写即可。
不过不知道为什么我的钩子卸载时失败了,真心不懂!
2.创建加载该DLL的应用程序
因为需要监视整个计算机,所以SetHook函数传参是0,代表监视整个计算机。
在本实例中,对于DLL的加载方式是动态加载的。
具体流程是:加载DLL文件-->获取导出函数地址-->调用钩子安装函数-->卸载钩子
程序主要源码如下:
BOOL bInstall = FALSE;//用来指示是否已经安装了钩子 // 用于应用程序“关于”菜单项的 CAboutDlg 对话框 //需要声明导出函数指针 typedef int ( *pSetHook)(DWORD * );//声明函数类型 typedef int ( *pUnsetHook)(void); void CKeyMouseHookAppDlg::OnBnClickedButton1()//程序是按下按钮动态加载DLL的 { HMODULE hModule = ::LoadLibrary( "MouseKeyboardHook.dll" );//加载DLL库 if( hModule!=NULL ) { if( bInstall==FALSE ) { pSetHook mpSetHook; mpSetHook = (pSetHook)::GetProcAddress( hModule, "SetHook" );//获取导出函数地址 if( mpSetHook!=NULL ) { mpSetHook( 0 );//安装全局钩子 bInstall = TRUE; GetDlgItem( IDC_BUTTON1 )->SetWindowText( "卸载DLL" ); } else MessageBox( "获取函数地址失败!" ); } else { pUnsetHook mpUnsetHook; mpUnsetHook = (pUnsetHook)::GetProcAddress( hModule, "UnSetHook" ); if( mpUnsetHook!=NULL ) { mpUnsetHook();//卸载钩子 bInstall = FALSE; GetDlgItem( IDC_BUTTON1 )->SetWindowText( "加载DLL" ); } else MessageBox( "获取函数地址失败!" ); ::FreeLibrary( hModule );//释放加载的DLL库 } } else MessageBox( "加载DLL失败!" ); }