一、前言
过去两年可以称为勒索软件之年。毫无疑问,勒索软件是最为流行的一种恶意软件。但去年年底时,我们开始注意到一种现象,那就是勒索软件日渐式微,挖矿程序开始粉墨登场。这种趋势可能会延续到2018年,并随着时间的推进不断增长。
从受害者的角度来看,某种程度上这也算是一种解脱,因为挖矿程序不会像勒索软件带来巨大的安全风险。虽然挖矿程序的确会降低系统性能,但当你清除掉这些程序,就可以像之前那样继续使用自己的主机。你不会损失任何数据,这一点与勒索软件大为不同。
从恶意软件研究人员的角度来看,挖矿程序并没有太多吸引人的地方。这些程序不值得深入研究,主要是因为它们都是基于众所周知的开源组件开发而成,很少或者没有经过混淆处理。
然而,随着时间的推移,我们发现挖矿程序开始引入一些非常有趣的技巧。在最近发现的一个样本中,我们观察到了一种名为“天堂之门(Heaven’s Gate)”的技术,利用这种技术,恶意软件可以通过32位的加载器注入到64位进程中。这种技巧并不新颖,最早可回溯到2009年,但在捕获到的新样本中发现这种技术仍然是非常新奇的一件事情。
如果你是恶意软件分析方面的初学者,欢迎你继续阅读本文,了解什么是天堂之门,以及如何分析这种技术。
二、恶意样本
释放器 #1:7b3491e0028d443f11989efaeb0fbec2
我们在分析Ngay的后续攻击活动中找到了这个样本(参考此处了解更多细节)。在调查类似样本的过程中,我找到了@_qaz_qaz发表的一篇文章,这篇文章中介绍了某次攻击活动中出现的一个类似样本,但他在并没有分析天堂之门技术。
释放器 #2:ed575ba72ea8b41ac2c31c8c39ce303b
32位的加载器(在第一阶段中释放出来):ca54fa2cf8a7e3e2cd457811f336de44
三、恶意行为分析
为了观察注入行为,我们必须在64位系统上运行这个样本。我们可以发现样本会运行notepad程序,程序使用的参数为典型的挖矿参数:
在ProcessExplorer中查看内存字符串时,我们可以清晰地看到正在运行的并不是真正的notepad程序,而是xmrig这个门罗币挖矿程序:
因此,目前我们可以确认一点,那就是内存中notepad的镜像已经被替换,攻击者很有可能使用的是RunPE(Process Hollowing)技术。
攻击者使用的是32位释放器,但它会将载荷注入到64位notepad中:
有趣的是,官方的Windows API并不支持这种注入方式。我们可以通过64位应用程序(使用Wow64 API)读写32位进程的内存,但不能反过来。
但还存在一些非官方的解决方案可以实现这个目标,比如名为“天堂之门(Heaven’s Gate)”的技术。
四、天堂之门概述
2009年,黑客Roy G. Biv首次提出了天堂之门技术。随后出现了各种适配程序,比如Wow64ext库以及W64oWoW64库。2015年,Alex Ionescu在一篇文章中介绍了针对此技术的缓解措施。
现在我们来看一下这种技术的工作原理。
4.1 在64位Windows上运行32位进程
在64位Windows上运行的每个32位进程都会运行在名为WoW64的一个特殊子系统中,该子系统可以模拟32位环境。我们可以把它看成在64位进程中创建的一个32位沙盒。因此,系统首先会为进程创建64位环境,然后在里面再创建32位环境。应用程序会在32位环境中运行,不能访问64位环境。
如果我们利用64位扫描器从外部扫描32位进程,我们可以看到该进程内部中同时包含32位和64位DLL。最关键的一点是,它包含2个版本的NTDLL:32位版(加载自SysWow64目录)以及64位版(加载自System32目录):
然而,32位进程无法看到64位环境,只能使用32位DLL。为了注入到64位进程中,我们需要使用64位版本的相关函数。
4.2 代码段
为了访问被禁用的64位环境,我们需要理解隔离机制的具体原理,事实证明这并不复杂。我们可以通过代码段中的不同地址访问32位及64位代码:32位的地址为0x23,64位的地址为0x33。
如果我们按照正常方式调用某个地址,那么系统会使用默认设置的模式来解析这个地址。然而,我们可以使用汇编指令显示指定具体使用哪种模式。
五、挖矿程序中的天堂之门
之前已经有人分析过这个挖矿程序,这里我们就略过完整分析,直接跳到比较有趣的那部分内容。恶意软件会检查运行环境,如果发现自己运行在64位环境中,则会执行另一个分支,注入64位进程:
经过一些反分析检查步骤后,恶意软件会创建一个新的、处于挂起状态的64位进程(本案例中创建的是notepad进程):
恶意载荷后面会注入到这个进程中。
前面我们提到过,为了将载荷注入64位进程中,我们需要使用64位的函数。
首先,加载器获得了64位NTDLL的一个句柄:
关于get_ntdll
函数内部的处理过程需要进一步分析一下。作为参考,我们可以了解一下ReWolf库中的类似代码。
为了访问进程所对应的64位环境,我们需要修改段选择器(segment selector)。来看一下这款恶意软件如何进入64位模式:
以上代码貌似直接来自于这个开源库:
https://github.com/rwfpl/rewolf-wow64ext/blob/master/src/internal.h#L26
段选择器0x33被压入栈中。随后,恶意软件会调用下一行:(通过这种方式,下一行的地址同样会被压入栈中)
压入栈中的地址加了5字节偏移量:
最后,程序调用了RETF指令。RETF代表“far return”,是一条远转移指令。与RET指令不同的是,该指令可以同时指定执行流程应返回的具体地址及段(segment)。该指令会从栈上获取两个双字(DWORD)作为参数。因此,当执行RETF指令时,实际的返回地址为0x33:0x402A50
:
成功完成段修改后,系统会把从该地址开始的代码解释为64位代码。因此,在调试器中看到的32位代码实际上是64位代码:
为了快速切换相应的代码视图,我选择使用PE-bear的一个实用功能:
这段代码解释成64位代码后如下所示:
可以看到,这段代码的功能是将R12寄存器中的内容移动到栈上的某个变量中,然后再切换为32位模式。这么处理的目的是为了获取64位的Thread Environment Block (TEB,线程环境块),从中可以获取到64位的Process Environment Block (PEB,进程环境块),你可以参考这段代码了解更多信息。
恶意软件使用64位的PEB作为起点来搜索64位的NTDLL。搜索方式比较直白,用到了指向已加载库的一个指针,该指针为PEB结构中一个字段(大家可以参考另一份代码,这份代码封装得比较好)。在PEB结构中我们可以找到名为Ldr
的一个字段:
Ldr
是类型为_PEB_LDR_DATA
的一个结构体,该结构中包含名为InMemoryOrderModuleList
的一个entry:
该列表中包含当前进程在内存中已加载的所有DLL。我们需要遍历这个列表,找到我们感兴趣的DLL(即NTDLL)。这也就是前面提到过的get_ntdll
函数的具体功能。为了找到正确的名字,该函数会调用某个函数(我们将其标记为is_ntdll_lib
),逐字符检查程序库的名字是否与ntdll.dll
相匹配,如下所示:
如果名字相匹配,则通过两个寄存器返回这个程序库的地址:
找到NTDLL后,我们只需要找到正确的函数即可。我们可以浏览DLL的导出函数表来完成这个任务:
需要获取如下几个函数:
NttUnmapViewOfSection
NtGetContextThread
NtAllocateVirtualMemory
NtReadVirtualMemory
NtWriteVirtualMemory
NtSetContextThread
这些函数是RunPE技术中常用的函数。首先,NtUnmapViewOfSection
函数用来取消原始PE文件的映射。随后,恶意软件分配远程进程所需的内存,将新的PE写入这段内存中。最后,恶意软件会修改该进程的内容,开始执行注入的模块。
恶意软件会保存这些函数的地址,稍后再调用这些函数来操控远程进程(参考类似代码)。
六、总结
目前为止,挖矿程序作者并没有给我们带来太多惊喜,他们非常依赖开源组件来实现预期目标。本文分析的这个样本也是如此,作者还是用到了之前已经实现的某种技术。
天堂之门这个技术几年前早已问世。出于隐蔽性考虑,某些恶意软件会用到这种技术。但就本文分析的这个挖矿样本而言,作者的真正目标可能是想在目标架构中使用适当的载荷,以最大化利用目标环境性能。