
系统钩子
钩子可以监视系统或进程中的各种事件讯息,截获发往目标视窗的讯息并进行处理。这样,我们就可以在系统中安装自定义的钩子,监视系统中特定事件的发生,完成特定的功能,比如截获键盘、滑鼠的输入,萤幕取词,日誌监视等等。
基本介绍
- 中文名:系统钩子
- 建立机制:事件驱动
- 含义:监视进程中的事件讯息并进行处理
- 用途:截获键盘、滑鼠的输入,萤幕取词
定义
其实Windows系统是建立在事件驱动的机制上的,说穿了就是整个系统都是通过讯息的传递来实现的。而钩子是Windows系统中非常重要的系统接口,用它可以截获并处理送给 其他应用程式的讯息,来完成普通应用程式难以实现的功能。
可见,利用钩子可以实现许多特殊而有用的功能。因此,对于高级编程人员来说,掌握钩子的编程方法是很有必要的。
钩子的种类很多,每种钩子可以截获并处理相应的讯息,如键盘钩子可以截获键盘讯息,外壳钩子可以截取、启动和关闭应用程式的讯息等。
如图所示是一全局钩子示意图
在实例程式中运用WH_GETMESSAGE钩子,这个钩子监视投递到讯息伫列中的Windows讯息。
钩子可以分为执行绪钩子和系统钩子, 执行绪钩子监视指定执行绪的事件讯息, 系统钩子监视系统中的所有执行绪的事件讯息。因为系统钩子会影响系统中所有的应用程式,所以钩子函式必须放在独立的动态程式库(DLL) 中。
其他信息
实现钩子机制的几个关键技术
1. windows的钩子程式,需要用到几个sdk中的api函式。下面列出这几个函式的原型及说明:
hhook setwindowshookex(int idhook,hook_proc lpfn,hinstance hmod,dword dwthreadid);
参数说明如下:
idhook:钩子的类型
lpfn:钩子处理函式地址
hmod:包含钩子函式的模组句柄
dwthreadid:钩子的监控执行绪
函式说明:函式将在系统中挂上一个由idhook指定类型的钩子,监控并处理相应的特定讯息。
bool unhookwindowshookex(hhook hhk);
函式说明:函式将撤销由hhk指定的钩子。
lresult callnexthookex( hhook hhk, int ncode,wparam wparam,lparam lparam );
函式说明:函式将讯息向下传递,下一个钩子处理将截获这一讯息。
2. 由于钩子的处理涉及到模组及进程间的数据地址问题,一般情况是把钩子整合到一个动态程式库(dll)中,VC中有三种形式的MFC DLL可供选择,即Regular statically linked to MFC DLL(标準静态连结MFC DLL)、Regular using the shared MFC DLL(标準动态连结MFC DLL)以及Extension MFC DLL(扩展MFC DLL)。第一种DLL在编译时把使用的MFC代码连结到DLL中,执行程式时不需要其他MFC动态连结类库的支持,但体积较大;第二种DLL在运行时动态连结到MFC类库,因而体积较小,但却依赖于MFC动态连结类库的支持;这两种DLL均可被MFC程式和Win32程式使用。第三种DLL的也是动态连线,但做为MFC类库的扩展,只能被MFC程式使用。
另外,要设立一个全局数据共享数据段,以存贮一些全局变数,保留上次钩子讯息事件发生时的状态。
3. Win32 DLL的入口和出口函式都是DLLMain。只要有进程或执行绪载入和卸载DLL时,都会调用该函式,其原型是:
BOOL WINAPI DllMain(HINSTANCE hinstDLL,DWORD fdwReason, LPVOID lpvReserved); 其中,第一个参数表示DLL的实例句柄;第三个参数系统保留;第二个参数指明了当前调用该动态连线库的状态,它有四个可能的值:DLL_PROCESS_ATTACH(进程载入)、DLL_THREAD_ATTACH(执行绪载入)、DLL_THREAD_DETACH(执行绪卸载)、DLL_PROCESS_DETACH(进程卸载)。在DLLMain函式中可以通过对传递进来的这个参数的值进行判别,根据不同的参数值对DLL进行必要的初始化或清理工作。由于在Win32环境下,所有进程的空间都是相互独立的,这减少了应用程式间的相互影响,但大大增加了编程的难度。当进程在动态载入DLL时,系统自动把DLL地址映射到该进程的私有空间,而且也複製该DLL的全局数据的一份拷贝到该进程空间,每个进程所拥有的相同的DLL的全局数据其值却并不一定是相同的。当DLL记忆体被映射到进程空间中,每个进程都有自己的全局记忆体拷贝,载入DLL的每一个新的进程都重新初始化这一记忆体区域,也就是说进程不能再共享DLL。因此,在Win32环境下要想在多个进程中共享数据,就必须进行必要的设定。一种方法便是把这些需要共享的数据单独分离出来,放置在一个独立的数据段里,并把该段的属性设定为共享,建立一个记忆体共享的DLL。
用钩子机制实现截获滑鼠左右键按压次数
建立钩子程式时需要把钩子处理整合到动态程式库中,所以例程中需要建立两个project。
1. 钩子处理动态程式库
(1) 选择mfc appwizard(dll)创建一个新project,命名为“spy”。
(2) 选择mfc extension dll类型。
(3) 创建一个新的头档案,命名为“hook.h”,修改它的代码如下:
extern "C" LRESULT CALLBACK mouseproc(int code,WPARAM wparam,LPARAM lparam); //钩子处理函式
extern "C" bool WINAPI starthook(); //启动钩子函式
extern "C" bool WINAPI stophook(); //撤销钩子函式
extern "C" int WINAPI getresultl(); //取得滑鼠左键单击次数的函式
extern "C" int WINAPI getresultr(); //取得滑鼠右键单击次数的函式
(4) 修改spy.cpp程式代码如下:
#include "hook.h" //包含头档案hook
#pragma data_seg("publicdata") //定义全局数据段
HHOOK hhook=NULL; //钩子句柄
HINSTANCE pinstance=NULL;//钩子模组句柄
UINT mouseclickl=0; //记录滑鼠左键单击次数的变数
UINT mouseclickr=0;//记录滑鼠右键单击次数
#pragma data_seg()
extern "C" int APIENTRY
DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
{ if (dwReason == DLL_PROCESS_ATTACH)
{…… //省略部分机器生成代码
new CDynLinkLibrary(SpyDLL);
pinstance=hInstance;//取得模组句柄
}
……;
}
extern "C" LRESULT CALLBACK mouseproc(int code, WPARAM wparam,LPARAM lparam)//钩子处理函
{
if (code<0) //若code〈0,则直接调用callnexthookex返回
return CallNextHookEx(hhook, code, wparam, lparam);
if(wparam==WM_LBUTTONDOWN)
{ mouseclickl++;//记录滑鼠左键单击次数 }
if(wparam==WM_RBUTTONDOWN)
{ mouseclickr++;//记录滑鼠右键单击次数 }
return CallNextHookEx(hhook, code, wparam,lparam);
}
extern "C" bool WINAPI starthook()//启动钩子函式
{
hhook=SetWindowsHookEx(WH_MOUSE,mouseproc,pinstance,0);//挂上钩子
if(hhook!=NULL)
return true;
else return false;
}
extern "C" bool WINAPI stophook() //撤销钩子函式
{ return UnhookWindowsHookEx(hhook); //撤销钩子}
extern "C" int WINAPI getresultl()//返回滑鼠左键单击次数
{ return mouseclickl;}
extern "C" int WINAPI getresultr()//返回滑鼠右键单击次数
{ return mouseclickr;}
(5) 修改spy.def程式代码如下:
exports
stophook @2
starthook @1
getresultl @3
getresultr @4
(6) 编译project,生成spy.dll档案和spy.lib档案。
在windows系统中用vc++实现钩子机制
2. 建立使用钩子的应用程式
(1) 生成一个单文档的执行档(exe)的project。
(2) 修改资源中的主选单,增加一个选单项“监控”,下有三个子选单项,分别为“启动”、“撤销”和“取出”。
(3) 在project中加入spy.lib档案。
(4) 分别修改“启动”、“撤销”和“取出”选单项的command回响函式如下:
#include "E:\DevStudio\MyProjects\spy\hook.h" //路径可不同
void CMainFrame::OnMenuitem32771() //“启动”选单项的回响函式
{ starthook(); }
void CMainFrame::OnMenuitem32772() //“撤销”选单项的回响函式
{ stophook();}
void CMainFrame::OnMenuitem32773() //“取出”选单项的回响函式
{ int resultl=getresultl();
int resultr=getresultr();
char buffer[80];
wsprintf(buffer,"在程式运行期间,你共单击滑鼠左键%d次,右键%d次!",resultl,resultr);
::MessageBox(this->m_hWnd,buffer,"message",MB_OK);
}
编译这个project,并把spy.dll放到生成的执行档目录下,便可运行程式。运行时,选择“监控”选单中的“启动”选单项,钩子便开始工作,监视滑鼠的活动情况;选择“撤销”选单项,系统便撤销钩子;选择“取出”选单项,程式便报告在监控期间,用户分别单击滑鼠左键和右键的次数。