Windows Hook案例分析与技术探索

1. Windows Hook基本介绍

        Win Hook——Windows中提供的一种用以替换DOS下“中断“的系统机制,中文译为“挂钩”或“钩子”。在对特定的系统事件进行Hook后,一旦发生Hook事件,对该事件进行Hook的程序就会收到系统的通知, 这时程序就可以在第一时间对该事件做出响应。
         钩子实际上是一个处理消息的程序段,通过系统调用,把它挂入系统。每当特定的消息发出,在没有到达目的程序前,钩子程序就先捕获该消息,亦即钩子函数先得到控制权。这时钩子函数即可以加工处理(改变)该消息,也可以不作处理而继续传递该消息,还可以强制结束消息的传递。
        Hook 技术按照实现原理来分的话可以分为API Hook和消息Hook;按照作用范围来分可以分为全局Hook和局部Hook;按照权限来分可以分为应用层(Ring3)Hook和内核层(Ring0)Hook。如下图1所示。
      

        
        应用层Hook适用于x86和x64,而内核层Hook一般仅在x86平台适用,因为从Windows Vista的64版 本开始引入的Patch Guard (一种Windows内核保护机制,防止非授权的第三方应用篡改Windows内核) 技术极大地限制了Windows x64内核挂钩的使用。

2. Windows Hook实现原理

        Hook技术被广泛应用于安全的多个领域,比如杀毒软件的主动防御功能,涉及到对一些敏感API的监控,就需要对这些API进行Hook;窃取密码的木马病毒,为了接收键盘的输入,需要Hook键盘消息;甚至是操作系统及一些应用程序,在打补丁时也是通过Hook技术。接下来,我们就来了解下Hook技术的原理。举个例子:一般对于线上App出现bug的时候,有一种快速解决的方案——热修复,底层机制就是下面要说的HotFix Hook。
        提到Hook,不得不提一下DLL注入——为了达到某种目的,我们通常需要将一个DLL注入到另外一个进程的地址空间中去, 一旦注入成功,就可以在这个进程中随心所欲,肆意妄为了。所谓逆向指的是在没有别人的源代码的情况下去破解。那么在Windows下面DLL注入很自然的成为一种破解手段。
        DLL注入跟Hook有什么关系呢? 很显然,Hook是原理,是实现DLL注入技术手段的一种方式。而DLL注入跟逆向、破解殊途同归。
        我们知道,系统函数都是以DLL封装起来的,应用程序应用到系统函数时,首先把该DLL加载到当前的进程空间中,调用的系统函数的入口地址,可以通过 GetProcAddress函数进行获取。当系统函数进行调用的时候,首先把所必要的信息保存下来(包括参数和返回地址,等一些别的信息),然后就跳转到函数的入口地址,继续执行。其实函数地址,就是系统函数“可执行代码”的开始地址。那怎么才能让函数首先执行我们的函数呢? 很显然,把开始的那段可执行代码替换为我们自己定制的一小段可执行代码,这样系统函数调用时,不就按我们的意图执行了吗?
        通常我们这么干:把系统函数的入口地方的内容替换为一条Jmp指令,目的就是跳到我们的函数来执行。而Jmp后面要求的是相对偏移,也就是我们的函数入口地址到系统函数入口地址之间的差异,再减去这条指令的大小。用公式表达如下:
        
DWORD nLen = UserFuncAddr – SysFuncAddr - 指令大小; 
Jmp nLen;
        函数里做完必要的处理后,通常要回调原来的系统函数,然后返回,因为此时已经达到了目的。调用原来系统函数之前必须先把原来修改的系统函数入口地方给恢复,这样对用户无感知。否则,每次触发调用我们的Hook函数,不但有可能被用户感知到进程异常,而且容易被杀毒捕捉到行为异常。
          最终,一次完整的执行流是这样的:
        (1)我们的dll "注入" 外部进程
        (2)保存系统函数入口处的代码
        (3)替换掉进程中的系统函数入口指向我们的函数,等待执行流
        (4)当系统函数被调用,立即跳转到我们的函数
        (5)我们函数进行处理
        (6)恢复系统函数入口的代码
        (7)调用原来的系统函数
        (8)返回
        注意,我们的核心目的只是需要让被注入进程载入我们的dll就可以了,我们可以在dll实例化的时候进行API的Hook。举个例子:鼠标钩子,键盘钩子。我们可以给系统装一个鼠标钩子,然后所有响应到鼠标事件的进程,就会“自动”(其实是系统处理了)载入我们的dll然后设置相应的钩子函数。刚刚上面讲了DLL注入跟Hook的关系,接下来我们看看DLL注入有哪些实现方式。如图2所示。

3. Windows Hook 案例分析

3.1 消息 Hook

3.1.1 原理
        先来了解下Windows消息过程:
      (1)发生键盘输入事件时,WM_KEYDOWN消息被添加到操作系统消息队列。
      (2)OS判断哪个应用程序中发生了事件,然后从操作系统消息队列取出消息,添加到相应应用序的消息队列中。
      (3)应用程序监视自身的消息队列,发现新的WM_KEYDOWN消息后,调用相应的事件处理程序处理。
        所以,我们只需在操作系统消息队列和应用程序消息队列之间安装钩子即可窃取键盘消息,并实现恶意操作。那么我们该如何安装这个消息钩子呢?很简单,Windows提供了一个官方函数
SetWindowsHookEx()用于设置消息Hook,编程时只要调用该API就能简单地实现Hook。 消息Hook常被窃密木马用来监听用户的键盘输入,程序里只需写入如下代码就能对键盘消息进行 Hook。
1 SetWindowsHookEx( 
2 WH_KEYBOARD,  // 键盘消息
3 KeyboardProc, // 钩子函数(处理键盘输入的函数)
4 hInstance,    // 钩子函数所在DLL的Handle 
5 0             // 该参数用于设定要Hook的线程ID,为0时表示监视所有线程
6 )
        下面,看一个案例。此案例使用了窗口挂钩将一个DLL注入到Explorer.exe的地址空间中。
3.1.2 案例
      (1)背景。
        很多公司平常开周会通常使用自己的电脑来接投影仪,自己电脑最舒适的是x*y的分辨率,具体以电脑属性为准。而大多数投影仪只支持较低的分辨率。那么在投影开始到结束这个过程中,屏幕的分辨率变化过程:x*y->x1*y1->x*y。
        于是,引发了一个问题,再更改显示器分辨率的时候有一件事情让我非常不喜欢:桌面上的
图标记不住原来的位置。改了两次分辨率,图标的位置都不是原来的位置了。很烦人! 这里,
也将通过这个案例来说明消息Hook的实现过程。
        方案是通过消息Hook的方式操作注册表(这需要对注册表原理有一定的了解)。实现了一
个MsgHook.exe跟一个DllInject.DLL,当进程启动的时候会触发Save事件,创建下面的注册表 项:
\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Des ktop Item Position Saver
        MsgHook会为每个图标保存一个位置。比如周会接投影的时候设法触发Save事件保存之前
的图标,开完会断开投影的时候触发Restore事件,直接恢复原有图标。
        大多数的公共控件的窗口消息不能够跨进程(Windows的内建控件可以,微软做了检查,通
过内存映射支持了),为了让MsgHook能够按照上面方案运行,我们必须将代码注入到Explorer
进程中,因为只有它才能成功的将LVM_GETITEM和LVM_GETITEMPOSITION消息发送到桌面
的ListView控件。核心还是Explorer进程打开注册表项找到那些保存过位置的图标,并将它们的
位置恢复到执行Save事件时它们所在的位置。
        测试机器:Win10 x64 测试软件x64
      (2)实现。
         第一步,找到桌面的ListView控件窗口我们通过Spy++查看当前系统ListView控件列表如下,红框里面的是我们需要的窗口,如图3所示。  
        如上图,获取类别为ProgMan的窗口,即使程序管理器(Program Manager)没有运行,
Windows Shell 仍然会创建一个类别为ProgMan的窗口。其子窗口SHELLDLL_DefView-
>SysListView32。这个SysListView32窗口就是桌面的List View控件窗口。
// 获取类别(class)为ProgMan的窗口并校验
HWND hWnd = GetFirstChild(GetFirstChild(FindWindow(TEXT("ProgMan"), NULL))); 
chASSERT(IsWindow(hWnd));
        第二步,DLl注入以及隐藏窗口创建。有了ListView控件窗口,就可以通过
GetWindowThreadProcessId来确定创建该窗口的的线程的标识符。然后把线程id传给
SetMsgHook函数(DLL内部实现)。
        
<
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ปรัชญา แค้วคำมูล

你的鼓励将是我创作的最大动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值