2014-07-24
基本概念[1]
Windows消息机制[5]
Windows操作系统是建立在事件驱动机制之上的,系统各部分之间的沟通也都是通过消息的相互传递而实现的。但在通常情况下,应用程序只能处理来自进程内部的消息或是从其他进程发过来的消息(借助进程间通信技术,如剪贴板,管道等)。
图1 Windows应用程序消息处理机制
(1)操作系统接收到应用程序的窗口消息,将消息投递到该应用程序的消息队列中。[5]
(2)应用程序在消息循环中调用GetMessage 函数从消息队列中取出一条一条的消息。取出消息后,应用程序可以对消息进行一些预处理,例如,放弃对某些消息的响应,或者调用TranslateMessage 产生新的消息。
(3)应用程序调用DispatchMessage,将消息回传给操作系统。消息是由MSG 结构体对象来表示的,其中就包含了接收消息的窗口的句柄。因此,DispatchMessage 函数总能进行正确的传递。DispatchMessage 函数分派一个消息到窗口过程,由窗口过程函数对消息进行处理。DispachMessage 实际上是将消息回传给操作系统,由操作系统调用窗口过程函数对消息进行处理(响应)。”
(4)系统利用WNDCLASS 结构体的lpfnWndProc 成员保存的窗口过程函数的指针调用窗口过程,对消息进行处理(即“系统给应用程序发送了消息”)。”
Hook(钩子)
如果需要对在进程外传递的消息进行拦截处理就必须采取一种称为HOOK的技术。HOOK作为Windows操作系统中非常重要的一种系统接口,用它可以轻松截获并处理在其他应用程序之间传递的消息,并由此可以完成一些普通应用程序难以实现的特殊功能。
Hook就是在图1中的第(2)步和第(4)步之间进行的额外工作(个人猜想,有待考证)。
运行机制[1]
钩子实际上是一个处理消息的程序段,通过系统调用,把它挂入系统。
每一个HOOK实质上都由系统维护着一个指针列表,其指针指向HOOK的各个处理函数,我们称之为HOOK子程。当与指定的Hook类型关联的消息发生时,系统就把这个消息传递到Hook子程。一些Hook子程可以只监视消息,或者修改消息,或者停止消息的前进,避免这些消息传递到下一个Hook子程或者目的窗口。
Hook子程是一个应用程序定义的回调函数(CALLBACK Function),不能定义成某个类的成员函数,只能定义为普通的C函数。用以监视系统或某一特定类型的事件,这些事件可以是与某一特定线程关联的,也可以是系统中所有线程的事件。
最近安装的Hook放在链的开始,而最早安装的钩子放在最后,也就是后加入的先获得控制权。
Windows 并不要求Hook子程的卸载顺序一定得和安装顺序相反。每当有一个钩子被卸载,Windows 便释放其占用的内存,并更新整个Hook链表。如果程序安装了钩子,但是在尚未卸载钩子之前就结束了,那么系统会自动为它做卸载钩子的操作。
在使用钩子时根据其监视范围的不同可以将其分为全局钩子和线程钩子两大类,其中线程钩子指定某个线程ID(可以是当前线程),只能监视该线程;全局钩子可以对同一个窗口下的所有线程进行监视。这里的全局HOOK的本质还是由触发HOOK机制的线程调用自身进程空间中的代码进行处理,所以我们的HOOK子程代码必须映射进该线程所在的进程的地址空间,即通过DLL的方式实现。下面描述实现过程:
首先我们编写HOOK驱动器,将HOOK.dll映射进内存中,安装好HOOK后,进行HOOK监视:
核心函数[1]
使用Windows HOOK所需要的核心函数不多,只有四个:
- SetWindowsHookEx():安装一个HOOK
- HOOK 子程:HOOK的处理函数,如GetMsgProc, KeyboardProc等
- CallNextHookEx():调用HOOK链的下一个HOOK子程
- UnhookWindowsHookEx():卸载一个HOOK
函数SetWindowsHookEx:
1 HHOOK SetWindowsHookEx( 2 //idHook用来标识HOOK类型,比如鼠标信息用WH_MOUSE,键盘消息用WH_KEYBOARD等 3 int idHook, 4 //lpfn指向一个具体的HOOK子程,用于实际处理拦截的消息 5 HOOKPROC lpfn, 6 //hMod用来标识HOOK子程所在的模块,\ 7 //如果是一个全局HOOK,则是一个载入内存的DLL句柄(使用GetModuleHandle得到);如果是一个内部线程HOOK,则为NULL即可。 8 HINSTANCE hMod, 9 //dwThread指明HOOK的范围, 10 //如果是0则表示监视运行在同一个窗口下的所有线程,否则指定一个具体的线程ID即可。 11 DWORD dwThreadId 12 );