Windows Hook链
本文是Windows hook系列的一文。
引入
hook是Windows操作系统消息处理机制的一个平台,应用程序可以通过设置Hook对某个进程或窗口进行监视,即对特定事件“挂钩”;
一旦预定义特定事件发生,Windows操作系统即会向钩子hook发送通知消息,这时,应用程序可进行响应。
HOOK的处理主要有以下三个阶段:
阶段1:定义Hook;
阶段2:在Hook链表中传递Hook;
阶段3:卸载Hook。
今天主要学习第二阶段的 HOOK链
HOOK链
我一直以来的方法论,就是学习一个东西,要搞清楚五件事情:
1.它是什么?
2.它的作用是什么?
3.它怎么使用?
Hook链是什么?
每一个Hook都有一个与之相关联的指针列表,称之为钩子链表,由系统来维护。
这个列表的指针指向指定的,应用程序定义的。
Hook链的作用
被Hook子程调用的回调函数,也就是该钩子的各个处理子程。当与指定的Hook类型关联的消息发生时,系统就把这个消息传递到Hook子程。一些Hook子程可以只监视消息,或者修改消息,或者停止消息的前进,避免这些消息传递到下一个Hook子程或者目的窗口。最近安装的钩子放在链的开始,而最早安装的钩子放在最后,也就是后加入的先获得控制权。
Hook链怎么使用
Windows 并不要求钩子子程的卸载顺序一定得和安装顺序相反。每当有一个钩子被卸载,Windows 便释放其占用的内存,并更新整个Hook链表。如果程序安装了钩子,但是在尚未卸载钩子之前就结束了,那么系统会自动为它做卸载钩子的操作。
hook链并不单独使用,需要hook的安装与释放
hook安装
使用API函数SetWindowsHookEx()把一个应用程序定义的钩子子程安装到钩子链表中。SetWindowsHookEx函数总是在Hook链的开头安装Hook子程。当指定类型的Hook监视的事件发生时,系统就调用与这个Hook关联的Hook链的开头的Hook子程。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | HHOOK SetWindowsHookEx(
int idHook, / / 钩子的类型,即它处理的消息类型
HOOKPROC lpfn, / / 钩子子程的地址指针。如果dwThreadId参数为 0
/ / 或是一个由别的进程创建的线程的标识,
/ / lpfn必须指向DLL中的钩子子程。
/ / 除此以外,lpfn可以指向当前进程的一段钩子子程代码。
/ / 钩子函数的入口地址,当钩子钩到任何消息后便调用这个函数。
HINSTANCE hMod, / / 应用程序实例的句柄。标识包含lpfn所指的子程的DLL。
/ / 如果dwThreadId 标识当前进程创建的一个线程,
/ / 而且子程代码位于当前进程,hMod必须为NULL。
/ / 可以很简单的设定其为本应用程序的实例句柄。
DWORD dwThreadId / / 与安装的钩子子程相关联的线程的标识符。
/ / 如果为 0 ,钩子子程与所有的线程关联,即为全局钩子。
);
|
CallNextHookEx()
实现传递Hook,具体解释如下:(MSDN定义链接:CallNextHookEx function)
在钩子子程中调用得到控制权的钩子函数在完成对消息的处理后,如果想要该消息继续传递,那么它必须调用另外一个 SDK中的API函数CallNextHookEx来传递它,以执行钩子链表所指的下一个钩子子程。这个函数成功时返回钩子链中下一个钩子过程的返回值, 返回值的类型依赖于钩子的类型。
1 2 3 4 5 6 7 | LRESULT CallNextHookEx
(
HHOOK hhk;
int nCode;
WPARAM wParam;
LPARAM lParam;
);
|
- hhk为当前钩子的句柄,由SetWindowsHookEx()函数返回。
- NCode为传给钩子过程的事件代码。
- wParam和lParam 分别是传给钩子子程的wParam值,其具体含义与钩子类型有关。
这里留一个问题:如果没有CallNextHookEx(),hook机制会怎么样?
卸载Hook
钩子在使用完之后需要用UnHookWindowsHookEx()卸载,否则会造成麻烦。释放钩子比较简单,UnHookWindowsHookEx()只有一个参数。函数原型如下:
1 2 3 4 | UnHookWindowsHookEx
(
HHOOK hhk;
);
|
函数成功返回TRUE,否则返回FALSE。
系统钩子和线程钩子
SetWindowsHookEx()函数的最后一个参数决定了此钩子是系统钩子还是线程钩子。
线程勾子用于监视指定线程的事件消息。线程勾子一般在当前线程或者当前线程派生的线程内。
系统勾子监视系统中的所有线程的事件消息。因为系统勾子会影响系统中所有的应用程序,所以勾子函数必须放在独立的动态链接库(DLL) 中。系统自动将包含"钩子回调函数"的DLL映射到受钩子函数影响的所有进程的地址空间中,即将这个DLL注入了那些进程。
几点说明:
(1)如果对于同一事件(如鼠标消息)既安装了线程勾子又安装了系统勾子,那么系统会自动先调用线程勾子,然后调用系统勾子。
(2)对同一事件消息可安装多个勾子处理过程,这些勾子处理过程形成了勾子链。当前勾子处理结束后应把勾子信息传递给下一个勾子函数。
(3)勾子特别是系统勾子会消耗消息处理时间,降低系统性能。只有在必要的时候才安装勾子,在使用完毕后要及时卸载。
思考题
是否可以根据Hook链的先后顺序,来实现AntiHook,欢迎交流。
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)