• 本文作者: 漏洞应急响应中心
  • |
  • 2016年9月23日
  • |
  • APT攻防研究
  • |

攻防对抗之杀软穿透驱动揭秘

天融信阿尔法实验室 李明政

1 穿透驱动的引出定义和历史

2 穿透驱动的实现原理简介

3 对抗杀软的驱动防御

1.1 穿透驱动的引出定义和历史

最早国内的杀软市场还是瑞星、金山、江民三分天下,借助PC时代红利的爆发。最鼎盛时瑞星一年靠软件能卖7亿元,个人级产品几乎垄断当时的市场。没有金刚钻不揽瓷器活,作为杀毒软件在与病毒木马后门搏斗的过程中打的过对方的保证便是率先加载于系统之中的底层驱动,这些底层驱动中有一个通用的驱动组件叫穿透驱动。

杀毒软件为了达到清理恶意软件的目的也需要对进程文件注册表进程做相对应的强制性的操作。这些对应的强制性的操作一般通过Ring3的dll组件暴露出的API接口通过调用DeviceIoControl下发对应的IoCode然后调用底层的Ring0驱动完成的。完成强制操作的这个底层驱动我们称之为穿透驱动。

我们先从国外的杀毒软件开始说起。杀软穿透驱动的诞生演化来源于最初的魔高一尺道高一丈的与windows下的rootkit的对抗。

Cmd有一个ver命令。输入ver之后回显大概是这样的。

1

我们现在使用的windows系统的内核叫windows nt。6.1代表的是你所使用的windows系统的内核版本号。每一次新的windows操作系统的发布,新的windows系统的nt的内核版本号都会比原来大一些。很多人已经升级win10了。Windows10输入ver命令之后会显示为10.0。但是nt的第一个内核版本不是1.1而是3.1。

因为杀毒软件的工作机制严重依赖于操作系统提供的底层接口。可能杀毒软件从XP兼容到最新的windows10的时候改动的时候就是某几个驱动组件的代码的改动,但是当年从DOS时代走向NT3.1的时候杀毒软件的变动是整个架构的变动。NT3.1作为NT内核的鼻祖于93年7月27日发布算起来已经走过了23年岁月。有一本书叫<< Show  stopper >> ,底层调试的张银奎老师曾经完整的翻译了这本书。<<观止——微软创建NT和未来的夺命狂奔>>完整的记录了当年NT内核的领导设计者David Cutler领导开发NT内核的种种细节和NT3.1的艰难诞生。

而现代杀毒软件的所有的底层驱动的组件的运行机制都能从最原始的NT3.1的身上找到影子。

Windows提供了几个标准的删除文件结束进程删除注册表的API分别是DeleteFile/TerminateProcess/RegDeleteKey。VS2008版本的MSDN上面会有这样的显示。

2

可以看到这些通用的API从比较早的windows系统便开始支持。最初的杀毒软件一开始也是用这些通用的API删除病毒和木马的注册表启动项,结束进程,删除病毒的母体文件。由于早期的windows操作系统对于Ring0管控的不完善。同在一个起跑线上的恶意软件很快就发现了一些捷径。他们可以很方便的以系统驱动的形式进入到windows的内核当中。有一些名词可能都会有听过dll inject/ring 3 inline hook/ssdt hook/inline hook,这都是恶意组件常用的手段。一般进入到内核的恶意组件我们称之为rootkit。Windows rootkit鼎盛时期有安全人员专门建立了一个网站www.rootkit.com,网站随着历史车轮的碾过已经无法打开,当然早期windows系统杀毒软件的厂商很快也发现要对付这些钻进系统内核的恶意组件,杀软自身也需要一个强力的能够操作文件注册和进程的anti rootkit的组件这就慢慢衍生出了今天的穿透驱动。

穿透驱动很早就被应用于国外的杀毒软件当中。

Avast的avastark.sys,AVG的avgarcln.sys,麦咖啡的mcafeeark.sys,趋势的TrendMicroArkDrv.sys。

一般都是Ring3通过调用清理的EXE组件加载相应的穿透驱动dll,穿透驱动dll通过ring3的DeviceIoControl下发相应的IoCode实现对底层的函数调用nt!ZwDeleteFile/nt!ZwSetValueKey/nt!ZwDeleteKey,用来达到删除文件清理注册表的功能。

说一下国内的杀毒软件的穿透驱动的情况。

国内的杀毒软件的穿透驱动用到的技术大体是一致的。伴随着免费的杀毒加之在一个很大的空白期中推广得力。360在windows底层的开发技术和人员储备随着软件用户数量的增长也大幅度成长。

360比较早期的穿透驱动的开发人员是潘剑锋(PJF,冰刃作者),王宇(小型山地熊,现Fireeye实验室研究员),郑文斌(MJ0011)。第一个版本的产品总是不完美的,产品总是在迭代中更新和进步。早期360在傅盛的带领下还只是一个很小的小组得到的资源并不多,Ring3的程序员都很少更不用说windows底层的驱动开发人员了。跌跌撞撞慢慢聚集齐了最早的一批底层开发人员。

360早期产品的驱动的功能也是比较分散的。后来随着产品迭代逐渐形成了Bfsdll.dll/Bfsdrv.sys  Bregdll.dll/Bregdrv.sys这两个穿透驱动。分别封装了对文件和注册表操作的底层接口。大概是在10年年初的时候这两个穿透驱动的组件被暴露出来有漏洞,也就是瑞星当时宣扬的360后门事件。

3

问题并不在于调用了未公开的CmXxxKey函数,而是在于任何的EXE可以在未验证(没有有效的数字签名)的情况下都可以公开调用360提供的dll文件暴露出的操作注册表和文件的接口,这种操作方式直接可以绕过杀毒软件底层驱动的监控。

360很快就修正了这个问题,并在后来的版本迭代的过程中将这两个穿透驱动文件合并为了一个。统一为bapi.dll/bapidrv.sys。后来又推出了清理恶意软件兼职打架的穿透驱动dsark.sys.。

截图分别是当时360为注册表穿透驱动和文件穿透驱动申请的专利,以及穿透驱动的windows内核底层开发人员。

4

5

 

毒霸和管家跟360类似,不过毒霸和管家的ring3组件全都是导出了一个类,而不是单个的ring3 api的接口。所以在穿透驱动的dll中看不到单个的导出函数。

6

 

7

 

2.1 穿透驱动的实现原理简介

我们先来看一下正常的系统调用分别以OpenFile和RegDeleteValue为例

8

 

Kernel32!OpenFileàntdll!NtOpenFileàwindows内核进行严格的参数检查并切入到Ring0ànt!NtOpenFile

(未公开不导出)

而删除注册表键值也是如此。

9

 

Kernel32!RegDeleteValueW->ntdll!NtDeleteValueKeyà windows内核进行严格的参数检查并切入到

Ring0ànt!NtDeleteValueKey(未公开不导出)ànt!CmDeleteValueKey(未公开不导出)

而我们再来看一下穿透驱动导出的BRegDeleteValueW是如何工作的。

Bapi!BRegDeleteValueWàkernel32!DeviceIoControlàntdll!ZwDeviceIoControlFileànt!NtDeviceIoControlFileànt!CmDeleteValueKey(未公开不导出)

可以看到直接通过调用ring3的dll组件暴露出的api接口,直接绕过了操作系统的ring3切入ring0时候的系统检查机制,直接调用到windows最底层的api从而实现了一些很底层的低级别的操作。这种效果是通过调用windows MSDN中提供的常规的Ring3函数RegDeleteValueW所达不到的。

有一个网页大概网络上已经找不到了,但是笔者还是做了镜像留存,便是当时360的穿透驱动被暴露出ring3调用校验不严格之后360早期的穿透驱动的开发人员MJ做出的一些解释。后来数字的这批很早期的底层开发人员在pwn2own上借助于将近10年的底层调试功底用他们发现的windows内核提权漏洞穿越chrome沙箱大放光彩,当然这已然是后话。

10

 

逆向国内杀毒软件的注册表方面的穿透驱动可以简单使用这几条命令便可找到他们对应的底层函数的位置。

11

 

3 对抗杀软的驱动防御

       3.1 ring3下与杀软的盾做对抗

杀毒软件是有两个属性的,一个是攻的属性,攻的属性的体现就是前面介绍的穿透驱动,这个就是杀毒软件与恶意组件做斗争的矛,。而杀毒软件还需要有另外一个属性便是防御(盾)。防御恶意组件加载进入内核(加载驱动),防御恶意组件感染可执行文件(捕获操作文件的行为作特征比对和行为防御)。

杀毒软件的盾在32位和64位系统上的实现原理是不同的。下面我们以windows 7为例讲一下如何与杀毒软件的盾做斗争。

常规的Ring3下的方式便是躲猫猫,躲猫猫的形式有两种。一种是dll2shellcode另外一种形式是shellcode backdoor。这两种都直接是二进制形式的后门,现今为止基于脚本的后门如powershell或者Jscript的后门由于变种快,容易免杀也十分流行。

12

13

 

上图展示了经过改造后的shellcode类型的后门poison ivy的shellcode可以流畅的跑在win7和win8 win8.1系统上面。由于是shellcode很容易就达成免杀。经过多态之后的shellcode更是不容易被杀软所查杀。

Ring3下比较头疼的便是启动项问题,很多人除了写Run便不知道其他的地方了。

有一个工具叫Autoruns,现在已经归属于微软的Sysinternals出品的,它可以将windows中的犄角旮旯的启动

项完整无缺的列出来。你只需要找到一个犄角旮旯并符合这个犄角旮旯的文件格式设计藏好就可以。

14

3.2.1 ring0下与杀软的盾做对抗 (32位)

要在Ring0下与杀软做对抗了解杀软的防御驱动在内核中的工作原理是必不可少的,实际上杀软的盾(防御驱动)在32位系统和64位系统由于系统的底层机制的变化工作原理区别还是很大的。下面的讲解都是以windows7为例。

32位系统上有一个很微妙的点这个点就是nt!KiFastCallEntry。所有Ring3函数调用nt!模块的Ring0函数的调用都要经过这个函数。可以看到红色剪头处的nt! KiFastCallEntry的执行流程都跳转到了杀毒软件的防御驱动的代码当中。通过这种方式,可以截获到windows的进程、模块加载、注册表修改、文件操作等绝大多数重要的信息。配合额外的策略驱动接受策略过滤从而对恶意的行为做出反馈弹窗提示用户、直接阻止亦或是放行。

我们其实所需要做的就是恢复nt!中的杀毒软件的这几个字节的Hook。但是面临的困境也很明显如果你的驱动不是白驱动或者带签名的驱动很容易加载的时候就被杀软拦截阻止了。

我们的策略便是加载一个存在任意地址写任意数据漏洞的正规驱动。通过Ring3程序与这个漏洞组件进行通讯从而解锁杀软在nt! KiFastCallEntry的Hook,这个Hook一旦解锁,杀软在32位系统下面的防御便土崩瓦解。

15

32位下的存在任意地址写任意数据的正规驱动还是很容易找到的。比如你可以在谷歌搜索

2010-01-23  RsNTGdi.sys

还有一种绕过杀软监控的加载驱动的方式便是重启加驱。重启加驱在32位下面有一种通用的方式。加载一个带有签名的白驱动,加载的路径指向自身的文件夹即可,然后使用MoveFileEx在关机的时刻移动我们的恶意驱动替换掉刚加载的白驱动即可,这个过程只有两个行为,加载一个合法的驱动和删除并写入一个新文件,这两个行为均不会被杀毒软件拦截,重启之后你便得到了一个绕过杀软驱动加载拦截加载在系统内核中的Ring0组件。

3.2.2 ring0下与杀软的盾做对抗 (64位)

       由于微软在64位系统下面的系统限制DSE(阻止非签名驱动的加载)和PatchGuard(阻止patch内核代码)使得Ring0下的64位低技术含量的恶意程序大幅度减少。杀软在64位下面的工作机制也仅限于 进程/线程/模块加载/注册表回调配合 文件过滤驱动/Minifilter 实现驱动防御。

64位下面有这样几个比较关键的点简单介绍一下。

nt!PspCreateProcessNotifyRoutineExCount (记录了扩展进程创建回调的个数)

nt!PspCreateProcessNotifyRoutineCount       (记录了进程创建回调的个数)

nt!PspCreateThreadNotifyRoutineCount          (记录了线程创建回调的个数)

nt!PspLoadImageNotifyRoutineCount              (记录了模块加载回调的个数)

nt!CmpCallBackCount                          (记录了注册表回调的个数)

ci!g_CiOptions                                       (标记着是否关闭DSE)

虽然patchguard不允许篡改内核当中的代码,但是内核中的这些变量是可以被修改的。上述的count被修改为0之后杀毒软件在64位上的系统防御就会失效,篡改了DSE的标记之后便可以在64位系统上面加载没有签名的驱动了。

同32位系统上面的思路是一致的加载一个存在任意地址写任意数据的漏洞驱动。(如驱动人生的LDrvPro64.sys)

篡改上述关键位置便可以达到关闭64位的杀软保护和系统自身的DSE防护。

 

 

Written by 漏洞应急响应中心