Windows - Hook键盘和鼠标消息

基本概念

从功能层面上讲,钩子(Hook)是Windows消息处理机制的一个分支,在消息到达窗口处理函数之前,截获任何窗口的消息或特定事件,能完成一般程序无法完成的功能。

从代码层面上讲,钩子是一个处理消息的代码函数,通过系统调用该函数,成功挂入系统。当消息发出时,在没有到达目的窗口前,钩子函数先捕获该消息,就是先得到控制权。此时,钩子函数可改变该消息,可以不改变该消息而继续传递,还可以强制结束该消息的传递

运行原理

钩子链表和回调函数(即钩子子程)

每个钩子都对应一个结构体指针,这些指针相互连接,形成钩子链表,由系统维护。结构体中的数据为指向回调函数的地址。当与钩子关联的消息发生时,系统把该消息传递给它。回调函数一般做三种消息处理:监视、修改和停止。最近安装的钩子在链首,也就是说后加入的先获得消息控制权。


钩子被卸载时,系统释放资源,更新钩子链表。

钩子子程是应用程序定义的回调函数,约定格式,不能是类的普通成员函数,只能是类的静态成员函数或普通C函数。可监控系统中所有线程或某一线程的消息或特定事件。

约定格式为:LRESULT CALLBACK HookProc(int nCode, WPARAM wParam, LPARAMlParam); 其中:
HookProc   应用程序自定义名字
nCode      是Hook代码,Hook子程使用这个参数来确定任务。这个参数的值依赖于Hook类型,每一种Hook都有自己的Hook代码特征字符集。(对于这点,还不是很清楚)

wParam和lParam   依赖于Hook类型,一般是发送或者接收消息的信息。

钩子安装

使用SetWindowsHookEx()把回调函数安装到钩子链表的链首。
HHOOK SetWindowsHookEx(  
     intidHook,         //钩子的类型,即它处理的消息类型  
     HOOKPROC lpfn,  //钩子子程的地址指针。系统钩子,dwThreadId为0或是一个由别的进程创建的线程标识,lpfn必须指向DLL中的钩子子程;线程钩子,lpfn指向当前进程的一段钩子子程代码。当钩子钩到任何消息后就调用该钩子子程。  
     HINSTANCE hMod,  //应用程序实例的句柄。标识包含lpfn所指的子程的DLL。如果dwThreadId 标识当前进程创建的一个线程,而且子程代码位于当前进程,hMod必须为NULL。可很简单的设定其为本应用程序的实例句柄。  
     DWORD dwThreadId //与安装的钩子子程相关联的线程的标识符。若为0,钩子子程与所有的线程关联,即为全局钩子。  
      );  

函数成功则返回钩子子程的句柄,失败返回NULL。

钩子传递

用CallNextHookEx()把消息传递到下一个钩子函数。
LRESULT CallNextHookEx( 
    HHOOKhhk;     //为当前钩子的句柄,由SetWindowsHookEx()函数返回;
    intnCode;     //为传给钩子过程的事件代码;
    WPARAMwParam;  // 为传给钩子子程的wParam;
    LPARAMlParam;    //为传给钩子子程的lParam;   
    );

虽然钩子函数也可通过直接返回TRUE来丢弃该消息以阻止该消息的传递,但是这样的话,其它安装了钩子的应用程序将不会接收到钩子通知,有可能产生不正确的结果。

钩子卸载

用UnHookWindowsHookEx()来完成释放钩子。

UnHookWindowsHookEx(
  HHOOK hhk; //为当前钩子的句柄,由SetWindowsHookEx()函数返回;
  );

函数成功返回TRUE,否则返回FALSE

运行环境

在Win32环境中,每个进程各种加载DLL,虽拥有相同的DLL全局数据变量名称,但值不同,互不干涉。有两种方法可以共享数据:内存映射和独立数据段。

独立数据段中的变量必须赋初值,#pragma data_seg预处理指令用于设置共享数据段。例如:
#pragma data_seg("SharedDataName")
HHOOK hHook=NULL;
#pragma data_seg()
在#pragma data_seg("SharedDataName")和#pragmadata_seg()之间的所有变量,将被访问该DLL的所有进程看到和共享。

系统钩子和线程钩子

SetWindowsHookEx()函数的最后一个参数决定了此钩子是系统钩子还是线程钩子。

线程钩子,监视指定线程的事件消息,在当前线程或者当前线程派生的线程内。

系统钩子,监视系统中的所有线程的事件消息,会影响系统中所有应用程序,须放在独立DLL中。系统自动将包含"钩子回调函数"的DLL映射到受钩子函数影响的所有进程的地址空间中,即将这个DLL注入了那些进程。

对同一事件消息(如鼠标消息)如果既安装了线程钩子又安装了系统钩子,那么系统会自动先调用线程钩子,后调用系统钩子。
对同一事件消息可安装多个钩子处理过程,这些钩子处理过程形成了钩子链。当前钩子处理结束后应把钩子信息传递给下一个钩子函数。
钩子,特别是系统钩子会消耗消息处理时间,降低系统性能。只有在必要的时候才安装钩子,使用完毕后要及时卸载。

钩子类型

每一种类型的钩子可以使应用程序监视不同类型的系统消息

WH_CALLWNDPROC和WH_CALLWNDPROCRET Hooks

WH_CALLWNDPROC和WH_CALLWNDPROCRETHooks使你可以监视发送到窗口过程的消息。系统在消息发送到接收窗口的过程函数之前调用WH_CALLWNDPROC钩子子程,并在窗口过程函数处理完消息之后调WH_CALLWNDPROCRET钩子子程。

WH_CALLWNDPROCRET钩子传递指针到CWPRETSTRUCT结构,再传递到钩子子程。CWPRETSTRUCT结构包含了来自处理消息的窗口过程的返回值,同样也包括了与这个消息关联的消息参数。

WH_CBT Hook

在以下事件之前,系统都会调用WH_CBT Hook子程,这些事件包括:
(1)激活,建立,销毁,最小化,最大化,移动,改变尺寸等窗口事件;
(2)完成系统指令;
(3)来自系统消息队列中的移动鼠标,键盘事件;
(4)设置输入焦点事件;
(5)同步系统消息队列事件。
Hook子程的返回值确定系统是否允许或者防止这些操作中的一个。

WH_DEBUG Hook

在系统调用系统中与其他Hook关联的Hook子程之前,系统会调用WH_DEBUGHook子程。你可以使用这个Hook来决定是否允许系统调用与其他Hook关联的Hook子程。

4.4 WH_FOREGROUNDIDLE Hook

当应用程序的前台线程处于空闲状态时,可以使用WH_FOREGROUNDIDLEHook执行低优先级的任务。当应用程序的前台线程大概要变成空闲状态时,系统就会调用WH_FOREGROUNDIDLEHook子程。

WH_GETMESSAGE Hook

应用程序使用WH_GETMESSAGE Hook来监视从GetMessage orPeekMessage函数返回的消息。你可以使用WH_GETMESSAGEHook去监视鼠标和键盘输入,以及其他发送到消息队列中的消息。

WH_JOURNALPLAYBACK Hook

WH_JOURNALPLAYBACKHook使应用程序可以插入消息到系统消息队列。可以使用这个Hook回放通过使用WH_JOURNALRECORDHook记录下来的连续的鼠标和键盘事件。只要WH_JOURNALPLAYBACKHook已经安装,正常的鼠标和键盘事件就是无效的。WH_JOURNALPLAYBACKHook是全局Hook,它不能象线程特定Hook一样使用。WH_JOURNALPLAYBACKHook返回超时值,这个值告诉系统在处理来自回放Hook当前消息之前需要等待多长时间(毫秒)。这就使Hook可以控制实时事件的回放。WH_JOURNALPLAYBACK是system-widelocal hooks,它们不会被注入到任何进程的地址空间中。

WH_JOURNALRECORD Hook

WH_JOURNALRECORDHook用来监视和记录输入事件。典型的,可以使用这个Hook记录连续的鼠标和键盘事件,然后通过使用WH_JOURNALPLAYBACKHook来回放。WH_JOURNALRECORDHook是全局Hook,它不能象线程特定Hook一样使用。WH_JOURNALRECORD是system-wide localhooks,它们不会被注入到任何进程的地址空间中。

WH_KEYBOARD Hook

在应用程序中,WH_KEYBOARD Hook用来监视WM_KEYDOWN andWM_KEYUP消息,这些消息通过GetMessage or PeekMessagefunction返回。可以使用这个Hook来监视输入到消息队列中的键盘消息。

WH_KEYBOARD_LL Hook

WH_KEYBOARD_LL Hook监视输入到线程消息队列中的键盘消息

WH_MOUSE Hook

WH_MOUSE Hook监视从GetMessage 或者 PeekMessage函数返回的鼠标消息。使用这个Hook监视输入到消息队列中的鼠标消息。

WH_MOUSE_LL Hook

WH_MOUSE_LL Hook监视输入到线程消息队列中的鼠标消息。

WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks

WH_MSGFILTER 和 WH_SYSMSGFILTERHooks使我们可以监视菜单,滚动条,消息框,对话框消息并且发现用户使用ALT+TAB or ALT+ESC组合键切换窗口。

WH_MSGFILTERHook只能监视传递到菜单,滚动条,消息框的消息,以及传递到通过安装了Hook子程的应用程序建立的对话框的消息。WH_SYSMSGFILTERHook监视所有应用程序消息。WH_MSGFILTER 和 WH_SYSMSGFILTERHooks使我们可以在模式循环期间过滤消息,这等价于在主消息循环中过滤消息。
   
通过调用CallMsgFilter function可以直接的调用WH_MSGFILTERHook。通过使用这个函数,应用程序能够在模式循环期间使用相同的代码去过滤消息,如同在主消息循环里一样。

WH_SHELL Hook

外壳应用程序可以使用WH_SHELLHook去接收重要的通知。当外壳应用程序是激活的并且当顶层窗口建立或者销毁时,系统调用WH_SHELL Hook子程。WH_SHELL共有5钟情況:
(1)只要有个top-level、unowned 窗口被产生、起作用、或是被摧毁;
(2)当Taskbar需要重画某个按钮;
(3)当系统需要显示关于Taskbar的一个程序的最小化形式;
(4)当目前的键盘布局状态改变;
(5)当使用者按Ctrl+Esc去执行Task Manager(或相同级别的程序)。
按照惯例,外壳应用程序都不接收WH_SHELL消息。所以,在应用程序能够接收WH_SHELL消息之前,应用程序必须调用SystemParametersInfofunction注册它自己。

猜你喜欢

转载自blog.csdn.net/gwzz1228/article/details/53817979