分类: WINDOWS
2010-06-13 11:18:31
本文将试图以下面的顺序讲解HOOK的大部分内容:
1、 WINDOWS的消息机制
2、 HOOK介绍
3、 HOOK 链
4、 HOOK钩子的作用范围
5、 HOOK类型
6、 回调函数
7、 HOOK钩子的安装与卸载
+++++++++++++++++++
WINDOWS的消息机制
+++++++++++++++++++
Windows系统是以消息处理为其控制机制,系统
通过消息为窗口过程(windowsprocedure)传递输入。系统和应用两者都可以产生消息。对于每个输入事件,例如用户
按下了键盘上的某个键、移动了鼠标、单击了一个控件上的滚动条,等等,系统都将产生一系列消息。此外,对于应用带给系统的变化,如字体资源的
改变、应用本身窗口的改变,系统都将通过消息以响应这种变化。应用通过产生消息指示应用的窗口完成特定的任务或与其他应用的窗
口进行通信。
每个窗口都有一个处理Windows系统发送消息的处理程序,称为窗口程序。它是隐含在窗口背后的一段程序脚本,其
中包含对事件进行处理的代码。
Windows系统为每条消息指定了一个消息编号,例如当一个窗口变为活动窗口时,它事实上是收到
一条来自Windows系统的WM_ACTIVATE消息,该消息的编号为6,它对应于VB窗口的Activate事件。对于窗口来说,诸如
Open、Activate、MouseDown、Resize等事件,实际上对应的是窗口内部的消息处理程序,这些程序对于用户来讲是不可
见的。类似地,命令按钮也有消息处理程序,它的处理程序响应诸如WM_LBUTTONDOWN和WM_RBUTTONDOWN之
类的消息,即激活命令按钮的MouseDown事件。
WINDOWS的消息处理机制为了能在应用程序中监控系统的各种事件消息,提
供了挂接各种回调函数(HOOK)的功能。这种挂钩函数(HOOK)类似扩充中断驱动程序,挂钩上可以挂接多个反调函数构成一
个挂接函数链。系统产生的各种消息首先被送到各种挂接函数,挂接函数根据各自的功能对消息进行监视、修改和控制等,然后交还
控制权或将消息传递给下一个挂接函数以致最终达到窗口函数。WINDOW系统的
这种反调函数挂接方法虽然会略加影响到系统的运行效率,但在
很多场合下是非常有用的,通过合理有效地利用键盘事件的挂钩函数监控机制可以达到预想不到的良好效果。
+++++++++++
hook介绍
+++++++++++
Hook(钩子)是WINDOWS提供的一种消息处理机制平台,是指
在程序正常运行中接受信息之前预先启动的函数,用来检查和修改传给该程序的信息,(钩子)实际上是一个处理消息的程序段,通过系
统调用,把它挂入系统。每当特定的消息发出,在没有到达目的窗口前,钩子程序就先捕获该消息,亦即钩子函数先得到控制权。这时钩
子函数即可以加工处理(改变)该消息,也可以不作处理而继续传递该消息,还
可以强制结束消息的传递。
注意:安装钩子函数将会影响
系统的性能。监测“系统范围事件”的系统钩子特别明显。因为系统在处理所有的相关事件时都将调用您的钩子函数,这样您的系统将会
明显的减慢。所以应谨慎使用,用完后立即卸载。还有,由于您可以预先截获其它进程的消息,所以一旦您的钩子函数出了问题的话必将影响其它的进
程。记住:功能强大也意味着使用时要负责任。
+++++++++++++
HOOK链
+++++++++++++
WINDOWS提供了14种不同类型的HOOKS;不同的HOOK可以处理不同的消息。例如,WH_MOUSE
HOOK 用来监视鼠标消息。WINDOWS为这几种HOOKS维护着各自的HOOK链表。HOOK链表是一串由应用程序定义的回调函数
(CALLBACKFunction)队列,当某种类型的消息发生时,
WINDOWS向此种类型的HOOK链的第一个函数(HOOK链的顶部)发送该消息,在第一函数处理完该消息后由该函数向链表中的下一
个函数传递消息,依次向下。如果链中某个函数没有向下传送该消息,那么链表中后面的函数将得不到此消息。(对于某些类型的
HOOK,不管HOOK链中的函数是否向下传递消息,与此类型HOOK联系的所有HOOK函数都会收到系统发送的消息)一些Hook子过程可
以只监视消息,或者修改消息,或者停止消息的前进,避免这些消息传递到下一个Hook子过程或者目的窗口。最近安装的钩子放在链
的开始,而最早安装的钩子放在最后,也就是后加入的先获得控制权。
+++++++++++++++++
钩子的作用范围
++++++++++++++++
一共有两种范围(类型)的钩子:局部的和远程的。
一、
局部钩子仅钩挂您自己进程的事件。
二、远程的钩子还可以将钩挂其它进程发生的事件。
远程的钩子又有两种:
1、基于线
程的它将捕获其它进程中某一特定线程的事件。简言之,就是可以用来观察其它进程中的某一特定线程将发生的事件。
2、系统范围
的将捕捉系统中所有进程将发生的事件消息。
+++++++++++++
HOOK类型
+++++++++++++
Windows共有14种HOOKS,每一种类型的Hook可以使应用程序能够监视不同类型的系统消息处理机制。下面描
述所有可以利用的Hook类型的发生时机。(这些常数值均可以API浏览器里查到)
1、WH_CALLWNDPROC和
WH_CALLWNDPROCRET Hooks
WH_CALLWNDPROC和WH_CALLWNDPROCRET Hooks
使你可以监视发送到窗口过程的消息。系统在消息发送到接收窗口过程之前调用WH_CALLWNDPROC
Hook子过程,并且在
窗口过程处理完消息之后调用WH_CALLWNDPROCRET Hook子过程。
WH_CALLWNDPROCRET Hook
传递指针到CWPRETSTRUCT结构,再传递到Hook子过程。CWPRETSTRUCT结构包含了来自处理消息的窗口过程的返回值,
同样也包括了与这个消息关联的消息参数。
2、WH_CBT Hook
在以下事件之前,系统都会调
用WH_CBT Hook子过程,这些事件包括:
1. 激活,建立,销毁,最小化,最大化,移动,改变尺寸等窗口事件;
2. 完 成系统指令;
3. 来自系统消息队列中的移动鼠标,键盘事件;
4. 设置输入焦点事件;
5. 同步系统消息队
列事件。
Hook子过程的返回值确定系统是否允许或者防止这些操作中的一个。
3、 WH_DEBUG Hook
在系统调用系统中与其它Hook关联的Hook子过程之前,系统会调用WH_DEBUG Hook
子过程。你可以使用这个Hook来决定是否允许系统调用与其它Hook关联的Hook子过程。
4、 WH_FOREGROUNDIDLE Hook
当应用程序的前台线程处于空闲状态时,可以使用
WH_FOREGROUNDIDLEHook执行低优先级的任务。当应用程序的前台线程大概要变成空闲状态时,系统就会调用
WH_FOREGROUNDIDLE Hook子过程。
5、WH_GETMESSAGE Hook
应用程序使
用WH_GETMESSAGE Hook来监视从GetMessage or PeekMessage函数返回的消息。你可以使用
WH_GETMESSAGE Hook去监视鼠标和键盘输入,以及其它发送到消息队列中的消息。
6、 WH_JOURNALPLAYBACK Hook
WH_JOURNALPLAYBACK Hook使应用程序可以插入消息到系统消
息队列。可以使用这个Hook回放通过使用WH_JOURNALRECORD Hook记录下来的连续的鼠标和键盘事件。只要
WH_JOURNALPLAYBACK Hook已经安装,正常的鼠标和键盘事件就是无效的。
WH_JOURNALPLAYBACK Hook是全局Hook,它不能象线程特定Hook一样使用。
WH_JOURNALPLAYBACK Hook返回超时值,这个值告诉系统在处理来自回放Hook当前消息之前需要等待多长时间(毫秒)。
这就使Hook可以控制实时事件的回放。WH_JOURNALPLAYBACK是system-wide local hooks,它们不会
被注射到任何行程地址空间。
7、WH_JOURNALRECORD Hook
WH_JOURNALRECORD Hook用来监视和记录输入事件。典型的,可以使用这个Hook记录连续的鼠标和键盘事件,然后通
过使用WH_JOURNALPLAYBACK Hook来回放。WH_JOURNALRECORD Hook是全局Hook,它不能象线程特
定Hook一样使用。WH_JOURNALRECORD是system-wide local hooks,它们不会被注射到任何行程
地址空间。
8、WH_KEYBOARD Hook
在应用程序中,WH_KEYBOARD Hook用来监视
WM_KEYDOWN andWM_KEYUP消息,这些消息通过GetMessage or PeekMessage function返
回。可以使用这个Hook来监视输入到消息队列中的键盘消息。
9、WH_KEYBOARD_LL Hook
WH_KEYBOARD_LL Hook 监视输入到线程消息队列中的键盘消息。
10、WH_MOUSE Hook
WH_MOUSE Hook监视从
GetMessage 或者 PeekMessage 函数返回的鼠标消息。
使用这个Hook监视输入到消息队列中的鼠标消息。
11、 WH_MOUSE_LL Hook
WH_MOUSE_LL Hook监视输入到线程消息队列中的鼠标消息。
12、
WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks
WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks
使我们可以监视菜单,滚动条,消息框,对话框消息并且发现用户使用ALT+TAB or ALT+ESC 组合键切换窗口。
WH_MSGFILTER Hook
只能监视传递到菜单,滚动条,消息框的消息,以及传递到通过安装了Hook子过程的应用程序建立的对话框的消息。
WH_SYSMSGFILTER Hook 监视所有应用程序消息。
WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks
使我们可以在模式循环期间过滤消息,这等价于在主消息循环中过滤消息。
通过调用
CallMsgFilter function可以直接的调用WH_MSGFILTER Hook。通过使用这个函数,应用程序能够在模式循
环期间使用相同的代码去过滤消息,如同在主消息循环里一样。
13、WH_SHELL Hook
外
壳应用程序可以使用WH_SHELL Hook去接收重要的通知。当外壳应用程序是激活的并且当顶层窗口建立或者销毁时,系统调用
WH_SHELL Hook子过程。
WH_SHELL 共有5钟情况:
1. 只要有个top-level、unowned 窗
口被产生、起作用、或是被摧毁;
2. 当Taskbar需要重画某个按钮;
3. 当系统需要显示关于Taskbar的一个程序的
最小化形式;
4. 当目前的键盘布局状态改变;
5. 当使用者按Ctrl+Esc去执行Task Manager(或相同级别的
程序)。
按照惯例,外壳应用程序都不接收WH_SHELL消息。所以,在应用程序能够接收WH_SHELL消息之前,
应用程序必须调用SystemParametersInfo function注册它自己。
++++++++++++++++++++++++++
回调函数(HOOK处理子过程)
++++++++++++++++++++++++++
为了拦截和处理特定的
消息,你可以使用SetWindowsHookEx函数(下面将具体说明这些函数的声明及各种参数)在该类型的HOOK链中安装你自己的处理
HOOK的子过程(回调函数)。只要您安装的钩子的消息事件类型发生,WINDOWS就将调用钩子函数。譬如您安装的钩子是
WH_MOUSE类型,那么只要有一个鼠标事件发生时,该钩子函数就会被调用。不管您安装的是那一类型钩子,钩子函数的原型都时是一
样的,语法如下:
public function MyHook(ByVal nCode As Long, ByVal wParam As Long, ByVal lParam
As Long) as long
‘处理代码
end function
其中
MyHook可以随便命名,其它不能变。该函数必须放在模块段。
参数说明:
nCode指定HOOK传入的信息类型。Hook子过
程使用这个参数来确定任务。这个参数的值依赖于Hook类型,每一种Hook都有自己的Hook代码特征字符集。
wParam:
短整型参数。
lParam:长整型参数。
wParam,iParam的取值随nCode不同而不同,它代表了某种类型的HOOK
的某个特定的动作。它们的典型值是包含了关于发送或者接收消息的信息。
至于以上的几个参数及返回值的具体含义,各种类型的钩子都 不相同,所以您必
须查询WIN32 API 指南来得到不同类型钩子参数的详细定义以及它们返回值的意义。(如果哪位朋友有时间和兴趣的话,请整理一下
贴出来,顺便发给我一份,我的邮箱:wlclass163.com)
譬如:
WH_CALLWNDPROC
nCode 只
能是HC_ACTION,它代表有一个消息发送给了一个窗口
wParam 如果非0,代表正被发送的消息
lParam 指向
CWPSTRUCT型结构体变量的指针
return value: 未使用,返回0
WH_MOUSE
nCode 为
HC_ACTION 或 HC_NOREMOVE
wParam 包含鼠标的事件消息
lParam 指向
MOUSEHOOKSTRUCT型结构体变量的指针
return value: 如果不处理返回0,否则返回非0值
++++++++++++++++++++++++++
钩子的安装/卸载
++++++++++++++++++++++++++
现在我们知道了一些基本的理论,接下
来开始讲解如何安装和卸载一个钩子。
◆安装钩子
使用SetWindowsHookEx函数(API函
数),指定一个HOOK类型、自己的HOOK过程是全局还是局部HOOK,同时给出HOOK过程的进入点,就可以轻松的安装你自
己的HOOK过程。
SetWindowsHookEx总是将你的HOOK函数放置在HOOK链的顶端。你可以使用CallNextHookEx
函数将系统消息传递给HOOK链中的下一个函数。
[注意]对于某些类型的HOOK,系统将向该类的所有HOOK函数发送消息,这时,HOOK
函数中的CallNextHookEx语句将被忽略。
全局(远程钩子)HOOK函数可以拦截系统中所有线程的某个特定的消息,为了安装一个全局HOOK过程,必须在应用程序外建立一个DLL,并将该HOOK函数封装到其中,应用程序在安装全局HOOK过程时必
须先得到该DLL模块的句柄。将DLL名传递给LoadLibrary 函数,就会得到该DLL模块的句柄;得到该句柄 后,使用
GetProcAddress
函数可以得到HOOK过程的地址。最后,使用SetWindowsHookEx将HOOK过程的首址嵌入相应的HOOK链
中,SetWindowsHookEx传递一个模块句柄,它为HOOK过程的进入点,线程标识符置为0,指出:该HOOK过程同系统中的所有线程关联。如果是安装局部HOOK此时该HOOK函数可以放置在DLL中,也可以放置在应用程序的模块段。
建
议只在调试时使用全局HOOK函数。全局HOOK函数将降低系统效率,并且会同其它使用该类HOOK的应用程序产生冲突。
SetWindowsHookEx 函数的VB声明及参数解释:
Public Declare Function SetWindowsHookEx Lib "user32" Alias "SetWindowsHookExA"
(ByVal idHook As Long, ByVal lpfn As Long, ByVal hmod As Long, ByVal dwThreadId
As Long) As Long
SetWindowHookEx函数参数说明:
idHook:代表是何种
Hook(也就是上面讲的14种Hook)
lpfn:代表处理Hook的过程所在的Address,这是一个
CallBack Fucnction(也就是上面讲的回调函数),当挂上某个Hook时,我们便得定义一个Function来当作某个信息产生时,来处理它的Function。因这个参数是一个 Function的Address所以我们固定将
Hook Function放在.Bas中,并以AddressOf HookFunc传入
hmod:代表.DLL的
hInstance,如果是Local Hook,该值可以是Null(VB中可传0进去),而如果是Remote Hook,则可以使用
GetModuleHandle(".dll名称")来传入。
dwThreadId:代表执行这个Hook的ThreadId(线程
ID),如果不设定是那个Thread(线程)来做,则传0,而VB的Local Hook一般可传App.ThreadId进去
ThreadID是您安装该钩子函数后想监控的线程的ID号。该参数可以决定该钩子是局部的还是系统范围的。如果该值为
NULL,那么该钩子将被解释成系统范围内的,那它就可以监控所有的进程及它们的线程。如果您指定了您自己进程中的某个线程ID 号那
该钩子是一个局部的钩子。如果该线程ID是另一个进程中某个线程的ID,那该钩子是一个全局的远程钩子。这里有两个特殊情
况:WH_JOURNALRECORD 和WH_JOURNALPLAYBACK总是代表局部的系统范围的钩子,之所以说是局部,是因
为它们没有必要放到一个DLL中。WH_SYSMSGFILTER 总是一个系统范围内的远程钩子。其实它和WH_MSGFILTER钩子类
似,如果把参数ThreadID设成0的话,它们就完全一样了。
SetWindowHookEx函数回值: 如果
SetWindowsHookEx()成功,它会传回一个值,代表目前的Hook的Handle,否则返回NULL。您必须保存该句柄,因为
后面我们还要它来卸载钩子。
CallNextHookEx函数的VB声明及参数解释:
Declare Function CallNextHookEx Lib "user32" Alias "CallNextHookEx" (ByVal hHook
As Long, ByVal ncode As Long, ByVal wParam As Long,lParam As Any) As Long
hHook值是SetWindowsHookEx()的传回值,nCode, wParam, lParam则是回调函数中
的三个参数。
在钩子子程中调用得到控制权的钩子函数在完成对消息的处理后,如果想要该消息继续传递,那么它必须调用另外
一个API函数CallNextHookEx来传递它,以执行钩子链表所指的下一个钩子子过程。这个函数成功时返回钩子链中下一个钩子过程
的返回值,返回值的类型依赖于钩子的类型。
◆ 卸载钩子
要卸载一个钩子非常简
单,只需要使用UnhookWindowsHookEx函数来卸载创建的钩子。
函数声明:
Declare Function UnhookWindowsHookEx Lib "user32" Alias "UnhookWindowsHookEx"
(ByVal hHook As Long) As Long
参数说明:
hHook:是
SetWindowsHookEx()的传回值,上面告诉过你要记下来嘛。^_^
本文转贴自, 访问地址为