Windows Hook编程

一、什么是HOOK?

  "hook"这个单词的意思是“钩子”,"Windows Hook"是Windows消息处理机制的一个重要扩展,
程序员可以通过它来 钩住(截获)感兴趣的消息,并用事先编好的一个函数(钩子过程)来处理这些消息!
当然,这个处理是在消息到达目标窗口之前进行的。
  钩子过程(hook procedure)实际上是一个用来处理消息的函数
,通过系统调用,程序员可以把它挂入系统或进程的钩子链中,让它成为一个钩子。每当系统中产生特定的消息时,钩子就能在第一时间钩住(截获)它,
也就是说钩子过程优先得到消息的控制权。这时钩子过程即可以加工、处理该消息,又可以不作任何改变而继续传递该消息,还可以强制结束这个消息的传递。


***********************************************************************************************************

二,Hook原理
每一个Hook都有一个与之相关联的指针列表,称之为钩子链表,由系统来维护。这个列表的指针指向指定的,应用程序定义的,被Hook子程调用的回调函数,也就是该钩子的各个处理子程。当与指定的Hook类型关联的消息发生时,系统就把这个消息传递到Hook子程。一些Hook子程可以只监视消息,或者修改消息,或者停止消息的前进,避免这些消息传递到下一个Hook子程或者目的窗口。最近安装的钩子放在链的开始,而最早安装的钩子放在最后,也就是后加入的先获得控制权。
Windows 并不要求钩子子程的卸载顺序一定得和安装顺序相反。每当有一个钩子被卸载,Windows 便释放其占用的内存,并更新整个Hook链表。如果程序安装了钩子,但是在尚未卸载钩子之前就结束了,那么系统会自动为它做卸载钩子的操作。钩子子程是一个应用程序定义的回调函数(CALLBACK Function),不能定义成某个类的成员函数,只能定义为普通的C函数。用以监视系统或某一特定类型的事件,这些事件可以是与某一特定线程关联的,也可以是系统中所有线程的事件。


************************************************************************************************

三、进程内钩子和全局钩子
  SetWindowsHookEx()函数(安装钩子)的最后一个参数决定了此钩子是进程内钩子还是全局钩子。
  进程内钩子用于监视指定线程的事件消息。它的钩子过程一般位于当前线程或当前线程创建的线程中。
  全局钩子监视系统中所有线程的消息。因为全局钩子会影响系统中所有的应用程序,所以钩子过程必须放在独立的动态链接库(DLL) 中。系统会自动将这个含有钩子过程(实质上是回调函数)的DLL映射到受钩子过程影响的所有进程的地址空间中,也就是将这个DLL注入所有进程。
  几点说明:
  1. 如果对于同一消息(如鼠标消息)既安装了进程内钩子又安装了全局钩子,那么系统会优先调用进程内钩子,然后调用全局钩子。
  2. 对于同一消息而言,可以安装多个钩子,消息被当前钩子的钩子过程处理完毕后应该把这个消息继续传递给下一个钩子。
  3. 钩子特别是全局钩子会降低消息处理效率,影响系统性能,因此只有在必要的时候才安装钩子,在使用完毕后应及时卸载。


****************************************************************************************************

四,HOOK编程中常用的函数
1,安装钩子
HHOOK SetWindowsHookEx(
int idHook, // type of hook to install
HOOKPROC lpfn, // address of hook procedure
HINSTANCE hMod, // handle to application instance
DWORD dwThreadId // identity of thread to install hook for
);
(1),idHook参数指定了所安装的钩子类型,即这个钩子将对哪种消息感兴趣。它的值是系统事先定义好的一些宏(具体参见MSDN),比如用户想要安装一个用来截获键盘消息的钩子,那么他应该将这个参数设置为WH_KEYBOARD,又如用户想要安装一个用来截获鼠标消息的钩子,那么他应该将这个参数设置为WH_MOUSE;
(2),lpfn参数是一个指向钩子过程的指针,根据hMod参数的不同,这个指针既可能指向一个DLL空间,也可能指向当前进程的代码空间。对于初学者而言,可以暂且把它理解成钩子过程的函数名;
(3),hMod参数用来指定一个DLL句柄,而这个DLL包含着lpfn参数所指向的钩子过程。当然,这个参数与dwThreadId的设置有关,如果该参数被设置为0,那么这个钩子将会是一个全局钩子,如此hMod参数必然要发挥其作用,但反之则必须将hMod设置为NULL;
(4),dwThreadId参数用来指定一个与钩子过程相关的线程ID,但如果这个参数被设置为0,那么这个钩子将与所有线程相关,即作为一个全局钩子。事实上,我们可以通过这个参数来确定生成一个进程内钩子还是一个全局钩子,从而为设置其他参数提供依据;
(5),最后,如果这个函数执行成功,它将返回被生成钩子的句柄,如果执行失败它将返回NULL,用户可以通过调用GetLastError()函数获知详情。


2,卸载钩子
BOOL UnhookWindowsHookEx(
HHOOK hhk // handle to hook procedure to remove
);
hhk参数是要被卸载的钩子的句柄,也就是SetWindowsHookEx()的返回值;
最后,如果这个函数执行成功会返回非零值,如果执行失败会返回零值,用户可以通过调用GetLastError()函数获知详情。

3,下一个钩子过程
LRESULT CallNextHookEx(
HHOOK hhk, // handle to current hook
int nCode, // hook code passed to hook procedure
WPARAM wParam, // value passed to hook procedure
LPARAM lParam // value passed to hook procedure
);
(1). hhk[可选]
说明:当前钩子的句柄
类型:HHOOK
此参数将被忽略。
(2). nCode [in]
说明:钩子代码; 就是给下一个钩子要交待的
类型:INT
钩传递给当前Hook过程的代码。下一个钩子程序使用此代码,以确定如何处理钩的信息。
(3). wParam[in]
说明:要传递的参数; 由钩子类型决定是什么参数
类型:WPARAM
wParam参数值传递给当前Hook过程。此参数的含义取决于当前的钩链与钩的类型。
(4). lParam[in]
说明:要传递的参数; 由钩子类型决定是什么参数
类型:LPARAM
lParam的值传递给当前Hook过程。此参数的含义取决于当前的钩链与钩的类型。

4,钩子子过程
钩子子程是一个应用程序定义的回调函数(CALLBACK Function),不能定义成某个类的成员函数,只能定义为普通的C函数。用以监视系统或某一特定类型的事件,这些事件可以是与某一特定线程关联的,也可以是系统中所有线程的事件。

函数语法:
LRESULT CALLBACK HookProc
(
int nCode,
WPARAM wParam,
LPARAM lParam
);
(1)HookProc是回调函数名。
(2)nCode参数是Hook代码,Hook子程使用这个参数来确定任务。这个参数的值依赖于Hook类型,每一种Hook都有自己的Hook代码特征字符集。
(3)wParam和lParam参数的值依赖于Hook代码,但是它们的典型值是包含了关于发送或者接收消息的信息。


*******************************************************************************************

五,HOOK编程1(本部分仅为进程内的钩子编程)
1,编程一(监视鼠标消息)
a,创建一个基于对话框的MFC工程,工程取名为InnerHook,删除上面的基本控件,保留两个按钮控件
b,定义钩子过程,
LRESULT CALLBACK MouseProc(int nCode,WPARAM wParam,LPARAM lParam)
{
return 1;
}
c,定义一个全局变量保存SetWindowsHookEx函数的返回值
HHOOK g_hMouse=NULL;
d,然后再CInnerHookDlg类中的OnInitDialog函数中调用SetWindowsHookEx函数创建钩子过程,即:
g_hMouse=SetWindowsHookEx(WH_MOUSE,MouseProc,NULL,GetCurrentThreadId())
e,至此鼠标已经无法在点击确定按钮了,但仍可通过键盘操作来点击对话框上面控件!

2,编程二(键盘钩子)
a,定义钩子过程,

LRESULT CALLBACK KeyboardProc(int nCode,WPARAM wParam,LPARAM lParam)
{
return 1;
}
b,定义一个全局变量保存SetWindowsHookEx函数的返回值
HHOOK g_hKeyboard=NULL;
c, 然后再CInnerHookDlg类中的OnInitDialog函数中调用SetWindowsHookEx函数创建钩子过程,即:
g_hKeyboard=SetWindowsHookEx(WH_KEYBOARD,KeyboardProc,NULL,GetCurrentThreadId())
d,至此,连键盘也无法操作了,所以运行后只能用任务管理器关闭,(不再展示)!

3,编程三(键盘钩子,仅屏蔽空格键)
只需在2的基础上将钩子过程修改为:
LRESULT CALLBACK KeyboardProc(int nCode,WPARAM wParam,LPARAM lParam)
{if(VK_SPACE==wParam) //如果输入的是空格
return 1; //则将被屏蔽
else
return CallNextHookEx(g_hKeyboard,nCode,wParam,lParam); //传递给下一个钩子过程
}
4,编程四(屏蔽ALT+F4组合键,原先可以调用这个组合键关闭程序,本例将不能)
只需在三的基础上将钩子过程修改为:
LRESULT CALLBACK KeyboardProc(int nCode,WPARAM wParam,LPARAM lParam)
{if(VK_F4==wParam&&(1==(lParam>>29&1))) //lParam参数各位意义参考百度
return 1;
else
return CallNextHookEx(g_hKeyboard,nCode,wParam,lParam);
}

六,HOOK编程2(本部分为全局钩子编程)

未完待续............

你可能感兴趣的:(Windows Hook编程)