Windows Hook之原理篇

这篇博客介绍了Windows系统的HOOK机制,包括其概念、使用步骤和不同类型钩子的用途。HOOK用于截获系统消息流,通过安装钩子函数来处理消息。文章详细讲解了如何定义钩子函数、安装与卸载钩子,以及各种类型的钩子如键盘、鼠标、外壳和窗口过程钩子。还提到了HOOK API技术,用于拦截特定API的调用。最后,概述了API钩子系统的一般框架。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Windows Hook之原理篇


1. Windows系统中HOOK机制的概念

        Windows系统是建立在消息驱动机制上的,整个系统都是通过消息的传递来实现的。而HOOK( 钩子)是Windows系统中非常重要的系统接口,用它可以截获并处理送给其他应用程序的消息,然后完成普通应用程序难以实现的功能。钩子可以监视系统或进程中的各种事件消息,截获并发往目标窗口的消息并进行处理。因此,就可以通过在系统中安装自定义的钩子,监视系统中特定事件的产生,完成特定的功能,比如截获键盘/鼠标的输入、屏幕取词、日志监视等。

        首先我们应该掌握以下几点关于钩子的内容:

        (1)钩子是用来截获系统中的消息流的。利用钩子,可以处理任何消息,包括其他进程的消息;

        (2)截获消息后,用于处理消息的子程序叫做钩子函数,它是应用程序自定义的一个函数,在安装钩子时要把这个函数的地址告诉Windows;

        (3)系统中同一时间可能有多个进程安装了钩子,多个钩子函数一起组成钩子链,该钩子链是由Windows系统维护的。在处理截获到的消息时,应该把消息事件传递下去,以便其他钩子也有机会处理这一消息。而且最新安装的钩子放在链的开始,而最早安装的钩子放在最后,也就是后加入的先获得控制权;

        (4)钩子函数有很多类型。按事件类型分类,主要有键盘钩子、鼠标钩子、外壳钩子、日志钩子和窗口过程钩子等,这些钩子的用途会在后面详细阐述;按使用范围分类,主要有线程钩子和系统钩子。线程钩子监视制定线程的事件消息,系统钩子监视系统中的所有线程的事件消息。因为系统钩子会影响系统中所有的应用程序,所以钩子函数必须放在独立的动态链接库(DLL)中。如果对于同一事件既安装了线程钩子又安装了系统钩子,那么系统会自动调用线程钩子,然后调用系统钩子;

        (5)钩子会使系统变慢,因为它增加了系统对每个消息的处理量。


2. Windows系统中HOOK机制的使用步骤

        使用HOOK机制可以简单地分成定义钩子函数、安装钩子和卸载钩子三步:

        (1)定义钩子函数

           钩子函数式一种特殊的回调函数,有关回调函数在这里先不做撰述。钩子监视的特定事件发生后,系统会调用钩子函数进行处理。不同事件的钩子函数的形式各不相同,我们以鼠标钩子函数举例说明钩子函数的原型:

           LRESULT CALLBACK HookProc( int nCode, WPARAM wParam, LPARAM IParam );

           参数wParam和IParam包含所钩消息的信息,比如鼠标位置/状态、键盘按键等。nCode包含有关消息本身的信息,比如是否从消息队列中移出。该函数的具体功能是先从钩子函数中实现自定义的功能,然后调用函数CallNextHookEx,把钩子信息传递给钩子链的下一个钩子函数。

        (2)安装钩子

           在程序初始化的时候,调用函数SetWindowsHookEx安装钩子。其函数原型为:

           HHook SetWindowsHookEx( int idHook, HOOKPROC lpfn, HINSTANCE hMod, DWORD dwThreadId );

           参数idHook表示钩子类型,和钩子函数类型对应。比如WH_KEYBOARD表示键盘钩子等。lpfn是钩子函数所在的地址,也就是(1)中HookProc函数的地址。hMod是钩子函数所在的实例的句柄,对于系统钩子,该参数为钩子函数所在的DLL句柄。dwThreadId指定钩子所监视的线程的线程号。对于系统钩子,该参数为NULL。             

           SetWindowsHookEx返回所安装的钩子句柄。

        (3)卸载钩子

           当不再使用钩子时,必须及时卸载。简单地调用函数BOOL UnhookWindowsHookEx( HHook hhk ); 

           参数hhk就是(2)中SetWindowsHookEx返回所安装的钩子句柄。


3. HOOK API技术

       HOOK API是指截获特定进程或系统对某个API函数的调用,使得API的执行流程转向指定的代码。Windows下的应用程序都有自己的地址空间,它们只能调用自己地址空间中的函数,所以在HOOK API之前,必须将一个可以代替API执行的自定义函数(一般称为代理函数)的执行代码注入到目标进程,然后再想办法将目标进程对该API的调用改为对注入到目标进程中的自定义函数的调用,这样即可实现拦截API函数。Windows下的应用程序都建立在API函数之上,所以截获API是一项相当有用的技术,使得用户有机会干预其他应用程序的程序流程。

        下面先简单介绍一种系统钩子注入DLL的方法:

        对系统钩子而言,当安装钩子的函数SetWindowsHookEx调用成功后,Windows在系统内部对系统中的所有进程自动调用LoadLibrary函数,强迫它们加载钩子函数执行代码的模块,这就是这些进程能够访问钩子函数的原因。我们可以创建一个系统钩子,并将包含钩子函数执行代码的模块编译成DLL(该DLL中包含代码函数的代码),就可以实现DLL的自动注入。

        使用Windows钩子注入特定DLL到其他进程,一般都安装WH_GETMESSAGE钩子,这是因为Windows下的应用程序大部分都需要调用GetMessage或PeekMessage函数从消息队列中获取消息,所以它们都会加载钩子函数所在的DLL。

        由于在此处安装WH_GETMESSAGE钩子的目的仅仅是让其他进程加载够子函数所在的DLL,所以一般仅在钩子函数总调用CallNextHookEx函数即可,如代码所示:

        LRESULT WINAPI GetMsgPro( int code, WPARAM wParam, LPARAM lParam )

        {

                   return  ::CallNextHookEx( g_hHook, code, wParam, lParam );

        }

       如果要将DLL注入到特定进程中,一般是将该进程中主线程的线程ID传递给SetWindowsHookEx函数;

       而如果要将DLL注入到所有进程中,安装一个系统范围内的钩子即可(将0作为线程ID传递给SetWindowsHookEx函数)。


4. Windows消息钩子类型

        按事件分类,有如下的几种常用类型

        (1)键盘钩子和低级键盘钩子可以监视各种键盘消息。

        (2)鼠标钩子和低级鼠标钩子可以监视各种鼠标消息。

        (3)外壳钩子可以监视各种Shell事件消息。比如启动和关闭应用程序。

        (4)日志钩子可以记录从系统消息队列中取出的各种事件消息。

        (5)窗口过程钩子监视所有从系统消息队列发往目标窗口的消息。

        下面具体描述一下上述各类钩子的用途:

          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_FOREGROUNDIDLE Hook执行低优先级的任务。当应用程序的前台线程大概要变成空闲状态时,系统就会调用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 and WM_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(或相同级别的程序)。 

5. API HOOK 框架

         API 钩子系统一般框架通常,我们把拦截API的调用的这个过程称为是安装一个API钩子(API Hook)。一个API钩子至少有两个模块组成:一个是钩子服务器(Hook Server)模块,一般为EXE的形式;一个是钩子驱动器(Hook Driver)模块,一般为DLL的形式。

        服务器主要负责向目标进程注入驱动器,使得驱动器工作在目标进程的地址空间中,这是关键的第一步。驱动器则负责实际的API拦截工作,以便在我们所关心的API函数调用的前后能做一些我们需要的工作。

        API HOOK 框架图如下所示:


        

        原理篇就讲到这里了,请继续关注《Windows Hook之实现篇》。


        


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值