【技术分享】解密一个反杀毒恶意驱动

阅读量136475

|

发布时间 : 2017-01-17 11:16:48

x
译文声明

本文是翻译文章,文章来源:securityintelligence.com

原文地址:https://securityintelligence.com/exposing-av-disabling-drivers-just-in-time-for-lunch/

译文仅供参考,具体内容表达以及含义原文为准。

http://p4.qhimg.com/t01f71a50a1ff67ec44.png

翻译:胖胖秦

预估稿费:200RMB

投稿方式:发送邮件至linwei#360.cn,或登陆网页版在线投稿


前言

IBM X-Force安全研究团队在调查一起针对巴西银行的远程恶意软件攻击事件中,发现了一个恶意的反杀毒驱动,它作为恶意金融软件的一部分。而这个恶意软件是用来盗取受害者的银行帐户。


解码反杀毒驱动

当试图了解未知软件的形为时,主要有两种方法:动态调试,我们执行代码并使用sysinternals工具和调试器来观察它的形为;静态分析,我们使用反汇编或反编译来检测它的代码。

在这种情况下,恶意软件作为真实攻击的一部分,时间是最重要的。我的目标是尽可能快的了解这个恶意驱动的行为,并最终减小检测和响应的时间。由于这个特殊的驱动不包含大量的代码,所以我决定静态分析它。


反汇编

http://p9.qhimg.com/t01db797967727db91a.png

我已经打开一个IDA的实例来分析一段从来没有见过的代码。

我注意到的第一个问题就是函数比较少,只有12个需要分析。

http://p9.qhimg.com/t01211b4b9b2db04725.png

查看导入表和字符串来推测恶意软件的功能是一个好办法,但是在这种情况下,查看字符串似乎不太可能。

http://p1.qhimg.com/t01371e3324d8692202.png

但是,导入表却恰恰相反

http://p5.qhimg.com/t013d4a027967a82cdd.png

现在,我发现了字符串创建和转换函数,有趣的是还有注册表设置函数。恶意软件的作者可能使用多种方法来隐藏导入表,因此我们必须考虑上面截图中没有出现的其他功能。

因为代码不多,所以我决定从入口点DriverEntry开始分析。

http://p8.qhimg.com/t0177ce734aa7370b96.png

第一个函数是由编译器生成的,与安全cookie有关。我们对此不感兴趣,略过它,继续往下看。

http://p4.qhimg.com/t01b18ddb9aeecbb665.png

从上图可知,函数多次调用sub_4011EA,每次调用都使用不同的参数,参数的范围在1至5之间。

让我们花点时间检查sub_4011EA的函数调用图:

http://p6.qhimg.com/t012e66cb314556519a.png

我们看到sub_401160使用了注册表设置函数,让我们检查一下写入了什么。并将这个函数重命名成Write_To_Registry。

汇编代码往往很长,难以理解,所以我使用IDA的Hex-Rays插件,它能将汇编代码转换成c代码。这能明显减少代码量的显示。但请牢记,生成的代码并不是准确的。

http://p1.qhimg.com/t01c153add776ff8b08.png

如上所示,Hex-Rays近乎完美的翻译了代码。现在我们在MSDN文档的帮助下可以轻松了解发生了什么。反杀毒驱动试图访问一个由参数指定的注册表键。如果该键存在,反杀毒驱动会尝试向它写入值,IDA标识为ValueName。从前一张图可知,将要写入的数据是“4”。

但是为什么只是近乎完美呢?请注意,ZwOpenKey接收的第一个参数应该是指向HANDLE的指针,但在第一行代码中却被强制转换成Unicode字符串指针。对一个有经验的研究员,这是小事一桩。通过快速查看MSDN的ObjectAttributes说明,我们发现ObjectName的参数类型的确是字符串指针。从另一方面来看,HANDLE是一个数值又是一个字符串,那么这里发生了什么?应该是IDA对变量类型识别有误。


往下看

现在,有两个关键的问题:

1. ObjectName从何而来

2. ValueName从何而来

为了回答第一个问题,我们往回看,检查谁调用了Write_To_Registry函数。

http://p2.qhimg.com/t01425d1c33b875e169.png

从上图可知,我们感兴趣的UnicodeString参数是由sub_4010EA传入的。而ValueName参数则是由sub_4010EA确定。现在我们只需要检查一个函数就可以回答上述问题。

sub_4010EA函数有两个参数:a1和a2。a2是一个指向字符串的指针,根据前面看到的对这个函数的调用,可以知道它实际上是一个输出参数。

http://p6.qhimg.com/t01bf3998ca89a48a78.png

函数的第一部分是一个循环,循环0x1A7次,将byte_403000缓冲区的值依次跟0x8进行异或,结果填充byte_4031B0缓冲区,这是简单的解密。Byte_403357是解密标志,代表是否已解密,以保证不会进行重复的XOR解密操作。


二进制Blob

我提取二进制数据并且解密它。这是结果:

http://p2.qhimg.com/t01c9035c5589e3d2d3.png

数据似乎包含两部分。蓝色部分看起来像是二进制数据。红色部分看起来像是已加密的数据。

仔细观察sub_4010EA,发现解密后的缓冲区传递到sub_401000中,同时还传入了参数a1。sub_401000的返回值会传递过v3。而v3最终会转换成输出参数a2,幸运的是,这是我们感兴趣的值。

在这一点上,我很想改变我的方法。我可以在虚拟机中执行驱动,连接内核调试器并检查sub_401000的返回值。但是我不知道哪种方法更快,所以我决定坚持使用静态分析,至少现在是这样。


Sub_401000

下一个函数sub_401000更加复杂。首先,让我们看看它是如何被调用的:

http://p1.qhimg.com/t011a53ad0717a40cfc.png

在此次调用中,它接收三个参数:a1,已解密的缓冲区以及常数1

http://p4.qhimg.com/t011ec3782dc63904a2.png

上面标记的代码表示在循环中匹配a2参数,每次循环的索引加7。在这一点上,我不想进一步的分析二进制数据的解析过程,因为它可能耗时太多。那么我们看看解析过程的下一步是什么?

在第一个循环内,v9是对应a2参数的位置索引。而在这个函数的尾部,我们看到对两个额外函数的调用,其中第二个函数的参数取决于XORed缓冲区的偏移,偏移由v9决定。


合理的推测

我推测sub_4010EA和sub_401000这两个函数都涉及到加密或解密。

让我们来看第一个:

http://p9.qhimg.com/t016a1e9a9205f6f003.png

头部标记的部分是初始化一个长度为256(0x100)的数组,数组的每个成员都等于其索引的值。等同于a [i] = i。据此推测,我强烈怀疑这个调用可能是RC4加密。

看了下时钟,已经是11:30。如果推测是错误的,我可能需要花更多的时间去分析,同时我将考虑进行动态调试。但是我决定再试试静态分析。我假设这个函数与RC4算法有关,为了得到解密的数据和解密的密钥,我重新检查了sub_401000函数。

根据RC4算法,我修改这个函数的变量名:

http://p8.qhimg.com/t01bbdce8f42cbd630b.png

密钥的长度是8个字节。我找到了保存密钥的地址和密钥生成部分:

http://p0.qhimg.com/t01ad32a8e6b3954bc8.png

上述代码遍历XORed缓冲区的前8个字节,将每个字节与0x8进行异或,最终生成RC4密钥,注意,这里的密钥长度是8,这符合我们之前的推测。

剩下的就是找到需要解密的是什么数据。同样,要准确回答这一问题需要一段时间。目前我们所知的,或者通过推测,需要解密的数据可能是XORed_buff的某些部分。


一个捷径

为了节省时间,我决定不去关心哪些数据需要被解密。相反,我尝试去解密一切数据。RC4是流算法,因此每个字符的解密取决于算法的当前状态。要解密所有内容,我必须先从头开始开始解密缓冲区,直到0x1A7。

伪代码应该是这样的

http://p4.qhimg.com/t013f0b76ce1f3970d3.png

外层循环从密钥的尾部开始,密钥位于XORed缓冲区的头部,依次处理缓冲区的其余部分。内部循环执行RC4解密。

正如所料,解密后的结果包含大量的垃圾数据,但是也含有许多的有价值的数据:

http://p1.qhimg.com/t01aa6d0316883c409c.png

事实上,有五个这样的字符串; 每个字符串对应着不同的杀毒软件。字符串的路径表示杀毒软件驱动的服务路径。

 

进一退二

让我们回过头来看看第一个函数overwrite_AV_reg_service,它被调用5次,参数的范围是1-5:

http://p1.qhimg.com/t012d125962337c3954.png

函数接收参数,使用get_string函数从加密缓冲区中解密相应参数的字符串,并使用set_registry_value函数将数据写入已解密的注册表键中。


拼图

现在我们知道驱动的不同函数都做了什么,我们可以查看函数调用图:

http://p4.qhimg.com/t019af18500f5c9eb76.png

main_func中的第一个函数被调用5次,参数为1-5。对于每个参数,get_string函数解密不同的反病毒软件相关路径。然后调用set_registry_if_key_exists覆写反病毒驱动的注册表路径,并阻止它加载到系统中。


禁用杀软,重启加载

我们还注意到,系统在加载完恶意驱动之后会重新启动。导致在系统重启之后不加载相关的反病毒软件,使得恶意软件能够畅通无阻的执行。

由于反病毒软件的自我保护功能,用户模式不能覆写反病毒软件的注册表项,所以才使用驱动;执行驱动可以在更高的权限下执行更多的恶意操作,所以防止这种恶意操作是相当困难的。

就是这样。我们现在知道驱动都做什么,我甚至还有几分钟的时间来吃午饭!

最终免责声明

如果要在动态调试还是静态分析中作出选择,我选择了后一种方法。因为它可以让我理解代码内部是如何工作的。通过动态调试虽然可以直接获取字符串,但是不知道它是怎么做到的。

在我进行的大部分的研究中,都没有使用Hex-Rays插件,因为插件具有不准确性。我显示代码大多数使用Hex-Rays,只是因为比较方便。

本文翻译自securityintelligence.com 原文链接。如若转载请注明出处。
分享到:微信
+10赞
收藏
胖胖秦
分享到:微信

发表评论

内容需知
  • 投稿须知
  • 转载须知
  • 官网QQ群8:819797106
  • 官网QQ群3:830462644(已满)
  • 官网QQ群2:814450983(已满)
  • 官网QQ群1:702511263(已满)
合作单位
  • 安全客
  • 安全客
Copyright © 北京奇虎科技有限公司 360网络攻防实验室 安全客 All Rights Reserved 京ICP备08010314号-66