------- 软件调试——还原 QQ 过滤驱动对关键内核设施所做的修改 -------

时间:2023-01-28 11:38:10

————————————————————————————————————————————————————————————————————————————————

在前一篇博文中,我们已经处理完最棘手的部分:杀掉 QQFrmMgr.sys 创建的系统线程。剩余的工作就轻松多了——移除 QQFrmMgr.sys 和 QQProtect.sys

安装的 SSDT(系统服务调度表)钩子与 SSDT Shadow 钩子、销毁它们注册的事件通知 callback,从而将系统恢复至干净状态。

在此之前,按照惯例,还是先来检查一下这两个 QQ 驱动是否 attach 到了其它设备栈中的设备上,因为 rootkit 或恶意软件通常会挂载到其它合法驱动创建的

设备上,以便拦截或修改途经的 IRP(I/O 请求包)中携带的敏感数据,比如用户的击键数据。

如果发现了任何挂载迹象,则可以通过前一篇介绍的 APC 机制结合 IoDetachDevice() 例程,把恶意设备从设备栈中清理掉。

由于这两个 QQ 驱动会向 Windows 对象管理器维护的全局名称空间中,注册相应的设备对象名,如下图所示:

------- 软件调试——还原 QQ 过滤驱动对关键内核设施所做的修改 -------

————————————————————————————————————————————————————————————————

Windows I/O 管理器导出的两个例程 IoCreateDevice() 与 IoCreateSymbolicLink() 普遍被驱动程序用来向 NT 名称空间中注册设备对象名,以及相应的符

号链接。用户态进程使用符号链接访问该对象;而在内核空间中,可以直接通过对象名进行访问,所以我们先用内核调试器的“!devstack”扩展命令,后接这

两个 QQ 设备对象的名称,查询它们是否挂载到了任何系统现存的设备栈上:

------- 软件调试——还原 QQ 过滤驱动对关键内核设施所做的修改 -------

如你所见,这两个设备对象各自所在的设备栈中,都只有它们自身——如果它们挂载到了任何其它设备上,“!devstack”的输出中就会含有那些“受害”的

设备。其中,“!DevObj”栏位下的 4 字节 16 进制数是该设备对象的“nt!_DEVICE_OBJECT”结构地址;“!DrvObj”栏位下的则是创建它们的驱动对象名

称。其实这两个 QQ 设备对象还算是“良性”的——某些 rootkit 创建设备对象时,根本不注册名字到 NT 名称空间(通过向 IoCreateDevice() 的第三个参

数传入 NULL,就可以做到这一点),对于此类“恶性”的匿名设备,需要获悉它的“nt!_DEVICE_OBJECT”结构地址,然后才能用“!devstack”遍历设备

栈,这个难度就不小了。

言归正传,接下来先检查系统的 SSDT,寻找有无被挂钩的系统服务,如下图,SSDT 的起始地址为 0x83c80f7c,一共有 0x191(401)个系统服务,其中一

部分已经被替换成 QQFrmMgr.sys 的钩子函数:

------- 软件调试——还原 QQ 过滤驱动对关键内核设施所做的修改 -------

------- 软件调试——还原 QQ 过滤驱动对关键内核设施所做的修改 -------

本来可以利用“!chkimg”扩展命令执行自动化检查,将 nt 模块(ntoskrnl.exe)的内存映像与磁盘文件比较,从而找出那些被修改了的部分,但不知为何我

的宿主机上 WinDbg 无法对 nt 模块实施检查,总是提示 ntkrpamp.exe/ntoskrnl.exe 的版本不匹配。(——还请成功执行“!chkimg”命令检查 nt 模块的

各位提供经验——)

一种最原始的方法就是先记录下受感染机器上 QQFrmMgr.sys 的钩子函数在 SSDT 中的位置,然后把它与另一个干净系统上的 SSDT 对比,以得知被 hook

的是哪个系统服务——前面那张截图就是用这种脏累的体力活实现的。

注意,系统每次初始化时,SSDT 的基地址,以及其中的系统服务入口点地址都是随机变化的,因而我们不能记录它们的内核地址,而是要记录函数名,在复

原前用反汇编指令“u”,即可强制解析原始系统服务在本次启动时分配到的内核地址,然后以“eb”指令编辑还原被 hook 的表项——言词苍白无力,还是

看图有真相吧:

------- 软件调试——还原 QQ 过滤驱动对关键内核设施所做的修改 -------

------- 软件调试——还原 QQ 过滤驱动对关键内核设施所做的修改 -------

注意,Intel x86/x64 体系结构的微处理器采用“小端字节序”在内存中放置数据,换言之,一个“双字”(DWORD)数据的最低有效字节位于连续的四字节

存储空间中的最小地址处;最高有效位则存放在最大地址处。以上图为例,系统服务“nt!NtCreateSection”的入口点地址——83e5583b,其中最低有效字

节是“3b”,所以我们在编辑时把它放在最前面(最小地址处)。

这个游戏规则是处理器硬件厂商规定的,如果不遵守它来办事就无法正确地恢复被挂钩的系统函数!

此外,通过分析我们还得知:QQFrmMgr.sys 利用“inline hook”技术硬挂钩了 KeUserModeCallback() 内核例程中的正常函数调用

,由于我机器上的“!chkimg”不能工作,无法依赖它检测处挂钩前的原始函数调用,但是我们可以用 IDA PRO 逆向 ntkrpamp.exe/ntoskrnl.exe 的磁盘文

件,定位到 KeUserModeCallback() 中的原始函数调用——这种不修改函数指针数组(如 SSDT,一般位于 .data section),而是修改特定函数(一般位于

.text section)中的调用逻辑,就称为“inline hook”。

如下两张截图,手工比较“ntkrnlpa.exe”的磁盘文件与内存映像,两者的 KeUserModeCallback() 初始逻辑(机器指令序列)基本相同,

除了内存映像中的首条 call 指令目标被替换成了“QQFrmMgr+0x503e”之外。。。。。

------- 软件调试——还原 QQ 过滤驱动对关键内核设施所做的修改 -------

------- 软件调试——还原 QQ 过滤驱动对关键内核设施所做的修改 -------

击败“inline hook”的方法也是用前面介绍过的“eb”内存编辑指令,根据上图 IDA PRO 中的原始信息来还原。同样需要留心字节序的问题!

(事实上,“!chkimg”有一个开关选项为“-f”,能够把二进制文件的内存映像中所有被篡改或损坏之处还原成与磁盘文件上的一致,

“一键还原所有 hook”,无需前面介绍的繁杂手工操作;但既然该指令用在 nt 模块上如此令人蛋疼,这里也就无法演示了,请各位自行测试!)

——————————————————————————————————————————————————————————————————————————————————————

清理完 SSDT 中的病毒后,让我们来关注 SSDT Shadow——内核全局变量“KeServiceDescriptorTableShadow”持有该表的基地址,

该表由 Win32 子系统的内核模式部分—— Win32k.sys 填写,负责实现 GUI 线程请求的所有涉及窗口绘图,鼠标指针,以及其它图形操作。

由于多数正规应用程序为了与用户交互,都会请求图形操作,因此该表也成了 rootkit 们重点 hook 的系统数据结构之一。

如下图所示,我们在检查 SSDT Shadow 中的函数时遇到了无效的内存地址(显示为问号),这是因为断入调试器的当前线程不是一个“GUI 线程”,所以它

的 SSDT Shadow 起始虚拟地址(图中的 9a94c000)没有映射到物理内存中的实际 SSDT Shadow(您可以看到

描述该虚拟地址的页表条目是非法的):

------- 软件调试——还原 QQ 过滤驱动对关键内核设施所做的修改 -------

如果当前线程的“nt!_KTHREAD”结构的“Win32Thread”字段为“NULL”,那么它就不是一个 GUI 线程,从而它的页表中,描述

SSDT Shadow 虚拟地址的那一项 PTE 就是无效的,没法用来转译物理地址:

------- 软件调试——还原 QQ 过滤驱动对关键内核设施所做的修改 -------

为了“看见”Win32k.sys 实现的 SSDT Shadow,我们可以在绝大多数用户、内核模式线程都频繁调用的例程—— nt!NtWaitForSingleObject()

下一个条件断点,仅在调用线程的“Win32Thread”字段不为空时,才断入内核调试器,这样我们就能够窥视 SSDT Shadow 了!

如下一系列截图所示:

------- 软件调试——还原 QQ 过滤驱动对关键内核设施所做的修改 -------

------- 软件调试——还原 QQ 过滤驱动对关键内核设施所做的修改 -------

------- 软件调试——还原 QQ 过滤驱动对关键内核设施所做的修改 -------

------- 软件调试——还原 QQ 过滤驱动对关键内核设施所做的修改 -------

综上截图所述,QQFrmMgr.sys 在 SSDT Shadow 中仅安装了一枚钩子,我们计算出该钩子例程在整个 Win32k.sys 内存映像中的偏移量为

0x20c630——该信息相当关键,它用于在 Win32k.sys 磁盘文件中定位原始的 SSDT Shadow 函数入口地址。

再次以 IDA PRO 打开 Win32k.sys,从该模块的描述信息可知,它的基地址为 0xBF800000,把这个值加上 0x20c630 得出

0xBFA0C630——也就是持有“受害”函数入口点的位置,于是我们在 IDA PRO 中执行“Jump to address”菜单选项,跳转到该地址,

真相大白,被 hook 的就是“win32k!NtUserFindWindowEx”,请注意它的前后两个例程与内存映像中钩子例程的前后两个完全一致。

反汇编“win32k!NtUserFindWindowEx”来获取它在本次引导实例中的内核地址,然后用“eb”移除掉可恶的 QQ 钩子函数,至此大功告成!

------- 软件调试——还原 QQ 过滤驱动对关键内核设施所做的修改 -------

------- 软件调试——还原 QQ 过滤驱动对关键内核设施所做的修改 -------

------- 软件调试——还原 QQ 过滤驱动对关键内核设施所做的修改 -------

—————————————————————————————————————————————————————————————————————————————————————————

小结:本篇博文演示了如何通过调试手段,将被恶意修改的关键系统设施还原成初始状态。

限于篇幅,最后一部分内容——销毁 QQ 驱动注册的事件通知回调函数——将放在下一篇博文中介绍,有任何疑问或者建议请在下方评论处反馈。

—————————————————————————————————————————————————————————————————————————————————————————

------- 软件调试——还原 QQ 过滤驱动对关键内核设施所做的修改 -------的更多相关文章

  1. ------- 软件调试——注销 QQ 过滤驱动设置的事件通知 CallBack (完)-------

    ---------------------------------------------------------------------------------- 本系列的最后一篇演示如何通过调试手 ...

  2. ------- 软件调试——挫败 QQ.exe 的内核模式保护机制 -------

    ------------------------------------------------------------------------ QQ 是一款热门的即时通信(IM)类工具,在安装时刻会 ...

  3. (转)支持 PS/2 与 USB 的键盘过滤驱动(可卸载)

    Author:  sinisterEmail:   sinister@whitecell.orgHomepage:http://www.whitecell.org Date:    2007-02-2 ...

  4. 软件调试——IA-32 保护模式下寄存器一览

    最近在看张银奎先生的<调试软件>一书,想将关键的技术记录下来,以便日后查阅,也分享给想看之人吧. 1 通用寄存器 EAX,EBX,ECX,EDX:用于运算的通用寄存器,可以使用AX,BX等 ...

  5. &lt&semi;读书笔记&gt&semi;软件调试之道 :问题的核心-如何修复缺陷

    声明:本文档的内容主要来源于书籍<软件调试修炼之道>作者Paul Butcher,属于读书笔记.欢迎转载! 修复缺陷 对于一个好的修复来说,不仅仅是让软件运行正确,还需要为将来奠定基础.一 ...

  6. &lt&semi;读书笔记&gt&semi;软件调试之道 :问题的核心-重现问题

    声明:本文档的内容主要来源于书籍<软件调试修炼之道>作者Paul Butcher,属于读书笔记. 重现第一,提问第二 问题重现是实证过程的最强大武器,如果不能重现问题,你也无法证明修复了它 ...

  7. Windbg对过滤驱动DriverEntry函数下断点技巧

    方法1: 1> 先用DeviceTree.exe查看指定的过滤驱动的Load Address(加载地址) 2> 再用LordPE.EXE查看指定过滤驱动文件的入口点地址 3> 计算过 ...

  8. File System Minifilter Drivers(文件系统微型过滤驱动)入门

    问题: 公司之前有一套文件过滤驱动,但是在实施过程中经常出现问题,现在交由我维护.于是在边看代码的过程中,一边查看官方资料,进行整理. 这套文件过滤驱动的目的只要是根据应用层下发的策略来控制对某些特定 ...

  9. Windbg在软件调试中的应用

    Windbg在软件调试中的应用 Windbg是微软提供的一款免费的,专门针对Windows应用程序的调试工具.借助于Windbg, 我们常见的软件问题:软件异常,死锁,内存泄漏等,就可以进行高效的排查 ...

随机推荐

  1. SQLServer的数据存储结构01 文件与文件组

    在SQLServer中,每当新建一个数据库时,则会有一组相应的SQLServer文件被创建,这些单独的SQLServer文件构成的总体称为文件组. 一个数据库对应着一个文件组,在这个文件组里,会包括三 ...

  2. 【poj1061】 青蛙的约会

    http://poj.org/problem?id=1061 (题目链接) 题意 两只青蛙在周长为L的球上沿一条直线向一个方向跳,每只每次分别跳m,n米,它们一开始分别在X,Y处,问跳几次两青蛙可以在 ...

  3. LeetCode Game of Life

    原题链接在这里:https://leetcode.com/problems/game-of-life/ 题目: According to the Wikipedia's article: " ...

  4. linux nginx安装(转载)

    1.linux 下面安装 1.下载 pcre-8.10.tar.gz  nginx-1.1.1.tar.gz 2.安装 pcre 让nginx支持rewrite pcre-8.10.tar.gz  上 ...

  5. BZOJ 3893 Cow Jog

    Description The cows are out exercising their hooves again! There are \(N\) cows jogging on an infin ...

  6. visio移动形状 上下左右键 移动滚动条

    今天在用visio写作业,想微移visio的形状,于是按上下左右方向键,结果移动的是滚动条. 那么如何让visio按上下左右移动的是形状,其实按Scroll键就好了.

  7. java 连接 postgresql

    最近公司用postgresql这个数据库,看网上说这个数据库还算好用,自己就用了一下,我就是用java连接了一下数据库. 其实每个数据库的连接方式大致相同,只是用到的驱动不同,用不同数据库只需要换不同 ...

  8. spring boot 学习&lpar;三&rpar;API注解记录及测试

    spring boot API注解记录及测试 部分注解解析 @Controller : 修饰创建处理 http 处理对象,一般用于页面渲染时使用. @RestController : Json数据交互 ...

  9. HDU5363&colon;Key Set

    Problem Description soda has a set S with n integers {1,2,-,n}. A set is called key set if the sum o ...

  10. CF-798C

    C. Mike and gcd problem time limit per test 2 seconds memory limit per test 256 megabytes input stan ...