Rootkit和Bootkit:现代恶意软件逆向分析和下一代威胁
上QQ阅读APP看书,第一时间看更新

2.2 剖析Rootkit驱动程序

Festi Rootkit主要是通过类似于第1章讨论的TDL3 Rootkit的PPI方案分发的。Dropper(植入程序)有一个相当简单的功能—在系统中安装一个内核模式驱动程序,该驱动程序实现了恶意软件的主要逻辑。内核模式组件注册为“系统启动”内核模式驱动程序,并随机生成名称,这意味着在初始化期间,恶意驱动程序将在系统启动时加载和执行。

Dropper病毒

Dropper是一种特殊的感染类型,它携带有效负载到受害者系统内部。负载经常被压缩、加密或混淆。一旦执行,Dropper将从它的映像中提取有效负载,并将其安装到一个受害系统上(也就是说,将其植入到系统上—这就是这种类型的感染者的名称由来)。与Dropper不同,下载者(另一种类型的感染者)自身不携带有效负载,而是从远程服务器下载。

Festi僵尸网络只针对微软Windows x86平台,没有针对64位平台的内核模式驱动程序。这在它发行的时候很好,因为仍然有很多32位操作系统在使用,但现在这意味着Rootkit基本上已经过时了,因为64位系统的使用数量已经超过了32位系统。

内核模式驱动程序有两个主要职责:从命令和控制(C&C)服务器请求配置信息,以及以插件的形式下载和执行恶意模块(见图2-2)。每个插件专用于特定的任务,例如对指定的网络资源执行DDoS攻击,或向C&C服务器提供的电子邮件列表发送垃圾邮件。

0

图2-2 Festi Rootkit的操作

有趣的是,插件并不存储在系统硬盘驱动器上,而是存储在易失性内存中,这意味着当受感染的计算机被关闭或重新启动时,插件就会从系统内存中消失。这使得恶意软件的取证分析变得非常困难,因为存储在硬盘上的唯一文件是主内核模式驱动程序,它既不包含有效负载,也不包含攻击目标的任何信息。

2.2.1 C&C通信的Festi配置信息

为了使Festi能够与C&C服务器通信,Festi提供了三种预定义的配置信息:C&C服务器的域名、用于加密bot(僵尸主机)和C&C之间传输的数据的密钥,以及bot版本信息。

这个配置信息被硬编码到驱动程序的二进制文件中。图2-3显示了内核模式驱动程序的一个节表,其中有一个名为.cdata的可写节,用于存储配置数据以及用于执行恶意活动的字符串。

0

图2-3 Festi内核模式驱动程序节表

该恶意软件使用一个简单的算法来混淆内容,该算法使用一个4字节的密钥对数据进行异或运算。.cdata节在驱动程序初始化开始时被解密。

表2-1中列出的.cdata节中的字符串可以引起安全软件的注意,因此混淆它们有助于僵尸主机逃避检测。

表2-1 Festi配置数据部分的加密字符串

0

2.2.2 Festi的面向对象框架

与许多内核模式驱动程序不同,这些程序通常是使用过程编程范式用普通C语言编写的,Festi驱动程序具有面向对象的架构。恶意软件实现的架构的主要组件(类)包括:

  • 内存管理器:分配和释放内存缓冲区。
  • 网络套接字:通过网络发送和接收数据。
  • C&C协议解析器:解析C&C消息并执行接收到的命令。
  • 插件管理器:管理下载插件。

这些组件之间的关系如图2-4所示。

0

图2-4 Festi内核模式驱动程序的架构

由图2-4可见,内存管理器是其他组件的中心组件。

这种面向对象的方法使得恶意软件可以很容易地移植到其他平台上,比如Linux。要做到这一点,攻击者只需要更改由组件接口隔离的系统特定代码(如为内存管理和网络通信而调用系统服务的代码)。例如,下载的插件几乎完全依赖于主模块提供的接口,它们很少使用系统提供的例程来执行系统特定的操作。

2.2.3 插件管理

从C&C服务器下载的插件被恶意软件加载和执行。为了有效地管理下载的插件,Festi维护了一个指针数组,这个指针指向一个特别定义的PLUGIN_INTERFACE结构。每个结构对应于内存中的一个特定插件,并为bot提供特定的入口点—负责处理从C&C接收的数据的例程,如图2-5所示。通过这种方式,Festi可以跟踪所有加载到内存中的恶意插件。

0

图2-5 PLUGIN_INTERFACE结构的指针数组的布局

代码清单2-1显示了PLUGIN_INTERFACE结构的布局。

代码清单2-1 定义PLUGIN_INTERFACE结构

0

前两个例程InitializeRelease分别用于插件的初始化和终止。其后面的两个例程GetVersionInfo_1GetVersionInfo_2用于获取相关插件的版本信息。

例程WriteIntoTcpStreamReadFromTcpStream用于在插件和C&C服务器之间交换数据。当Festi向C&C服务器传输数据时,它遍历指向插件接口的指针数组,并执行每个注册插件的WriteIntoTcpStream例程,将指向TCP流对象的指针作为参数传递。TCP流对象实现网络通信接口的功能。

从C&C服务器接收数据后,僵尸主机执行插件的ReadFromTcpStream例程,以便注册的插件可以从网络流获得参数和特定于插件的配置信息。因此,每个加载的插件可以独立于所有其他插件与C&C服务器通信,这意味着插件可以相互独立开发,提高了它们的开发效率和架构的稳定性。

2.2.4 内置插件

在安装时,主要的恶意内核模式驱动程序实现两个内置插件:配置信息管理器和僵尸主机插件管理器。

1. 配置信息管理器

配置信息管理器插件负责请求配置信息并从C&C服务器下载插件。这个简单的插件定期连接到C&C服务器以下载数据。两个连续请求之间的延迟是由C&C服务器本身指定的,这可能是为了避免安全软件用来检测感染的静态模式。我们将在2.3节中描述僵尸主机和C&C服务器之间的网络通信协议。

2. 僵尸主机插件管理器

僵尸主机插件管理器负责维护下载的插件数组。它从C&C服务器接收远程命令,加载和卸载以压缩形式交付到系统的特定插件。每个插件都有一个默认的入口点Driver-Entry,并导出两个例程CreateModuleDeleteModule,如图2-6所示。

0

图2-6 Festi插件的导出地址表

CreateModule例程在插件初始化时执行,并返回一个指向PLUGIN_INTERFACE结构的指针,如代码清单2-1所示。它以一个指针作为参数,该指针指向主模块提供的几个接口,比如内存管理器和网络接口。

DeleteModule例程在卸载插件并释放之前分配的所有资源时执行。图2-7显示了加载插件的插件管理器算法。

0

图2-7 插件管理器算法

该恶意软件首先将插件解压到内存缓冲区中,然后将其映射到内核模式的地址空间中,作为一个PE映像。插件管理器初始化导入地址表(IAT)并将其重新定位到映射的映像。在该算法中,Festi还仿真了一个典型操作系统的运行时加载器和操作系统模块的动态链接器。

根据插件被加载还是被卸载,插件管理器执行CreateModuleDeleteModule例程。如果插件被加载,插件管理器将获得插件的ID和版本信息,然后将其注册到PLUGIN_INTERFACE结构。如果插件被卸载,恶意软件会释放之前分配给插件映像的所有内存。

2.2.5 Anti-Virtual机技术

Festi拥有检测它是否在VMware虚拟机中运行的技术,以避开沙箱和自动恶意软件分析环境。它尝试通过执行代码清单2-2所示的代码来获取现有VMWare软件的版本。

代码清单2-2 获取VMWare软件版本

0

Festi检查ebx寄存器,如果代码在VMware虚拟环境中执行,它将包含VMX值,如果没有执行,则返回0值。

有趣的是,如果Festi检测到虚拟环境存在,它不会立即终止执行,而是像在物理计算机上一样继续执行。当恶意软件从C&C服务器请求插件时,它会提交某些信息,以显示它是否正在虚拟环境中执行。如果是,C&C服务器可能不会返回任何插件。

这更像是一种躲避动态分析的技术:Festi不终止与C&C服务器的通信,以诱使自动分析系统认为Festi没有注意到它,而实际上C&C服务器已经知道被监控,所以不会提供任何命令或插件。当恶意软件检测到它在调试器或沙箱环境下运行时,通常会终止执行,以避免暴露配置信息和有效负载模块。

然而,恶意软件研究人员对这种行为很在行:如果恶意软件及时终止而没有执行任何恶意的活动,它可能会吸引分析师的注意,然后分析师可能会进行更深入的分析,找出为什么它不工作,最终发现数据和代码恶意软件正试图掩盖自己的痕迹。通过在检测到沙箱时不终止执行,Festi试图避免这些后果,但它确实指示其C&C不向沙箱提供恶意模块和配置数据。

Festi还检查系统上是否存在网络流量监控软件,这可能表明恶意软件已在恶意软件分析和监控环境中执行。Festi寻找内核模式驱动程序npf.sys(网络包过滤器)。该驱动属于Windows包捕获库(WinPcap),Wireshark等网络监控软件经常使用该驱动来访问数据链路网络层。存在npf.sys驱动,表示系统上安装了网络监控工具,这意味着对恶意软件来说是不安全的。

WinPcap

WinPcap允许应用程序捕获和传输网络包,绕过协议栈。它提供了内核级网络包过滤和监视的功能。这个库被许多开源和商业网络工具广泛用作过滤引擎,如协议分析器、网络监视器、网络入侵检测系统和嗅探器,包括广泛使用的工具,如Wireshark、Nmap、Snort和ntop。

2.2.6 反调试技术

Festi还通过检查从操作系统内核映像导出的KdDebuggerEnabled变量来检查系统中是否存在内核调试器。如果操作系统附加了系统调试器,则此变量为TRUE,否则,它为FALSE

Festi通过定期将调试寄存器dr0通过dr3调零来抵消系统调试器。这些寄存器用于存储断点的地址,删除硬件断点会妨碍调试过程。代码清单2-3中显示了清除调试寄存器的代码。

代码清单2-3 清除Festi代码中的调试寄存器

0

突出显示的写指令对调试寄存器执行写操作。如你所见,Festi在执行_ProtoHandler例程之前向这些寄存器写入0,这个例程负责处理恶意软件和C&C服务器之间的通信协议。

2.2.7 在磁盘上隐藏恶意驱动程序的方法

为了保护和隐藏存储在硬盘上的恶意内核模式驱动程序的映像,Festi挂载文件系统驱动程序,以便它可以拦截和修改发送到文件系统驱动程序的所有请求,以排除它存在的证据。

用于安装钩子的例程的简化版本如代码清单2-4所示。

代码清单2-4 连接文件系统设备驱动程序栈

0

该恶意软件首先尝试获取一个特定系统文件SystemRoot的句柄,该文件对应于Windows安装目录❶。然后,通过执行ObReferenceObjectByHandle系统例程❷,Festi获得一个指向FILE_OBJECT的指针,该指针对应于SystemRoot的句柄。FILE_OBJECT是操作系统用来管理设备对象访问的特殊数据结构,因此包含一个指向相关设备对象的指针。在我们的例子中,因为我们打开了SystemRoot的句柄,所以DEVICE_OBJECT与操作系统文件系统驱动程序相关。恶意软件通过执行IoGetRelatedDeviceObject系统例程获得指向DEVICE_OBJECT的指针❸,然后创建一个新的设备对象,并通过调用IoAttachDeviceToDeviceStack将其附加到获得的设备对象指针❹,如图2-8中文件系统设备栈的布局所示。Festi的恶意设备对象位于栈的顶部,这意味着对文件系统的I/O请求被重新路由到恶意软件。这允许Festi通过改变请求和向文件系统驱动程序返回数据来隐藏自己。

0

图2-8 Festi挂载的文件系统设备栈布局

在图2-8的最底部,你可以看到文件系统驱动程序对象和处理操作系统文件系统请求的相应设备对象。这里还可能附加一些文件系统过滤器。在图2-8的顶部,你可以看到Festi驱动程序连接到文件系统设备栈。

本设计使用并紧跟Windows的堆叠I/O驱动程序设计,重现了本机操作系统的设计模式。到目前为止,你可能已经看到了一种趋势:Rootkit的目标是干净、可靠地与操作系统融合,为自己的模块模仿成功的操作系统设计模式。实际上,通过分析Rootkit的各个方面,比如Festi对输入/输出请求的处理,你可以了解到很多关于操作系统内部的信息。

在Windows中,文件系统I/O请求表示为一个IRP,它从上到下遍历栈。栈中的每个驱动程序都可以观察和修改请求或返回的数据。这意味着Festi可以修改发送到文件系统驱动程序的IRP请求和相应的返回数据,如图2-8所示。

Festi使用IRP_MJ_DIRECTORY_CONTROL请求代码来监视IRP,该请求代码用于查询目录的内容,并监视与恶意软件的内核模式驱动程序所在位置相关的查询。如果它检测到这样一个请求,Festi将修改从文件系统驱动程序返回的数据,以排除与恶意驱动程序文件相对应的任何条目。

2.2.8 保护Festi注册表项的方法

Festi还使用类似的方法隐藏了一个与已注册的内核模式驱动程序相对应的注册表项。位于HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services中的注册表项包含Festi的驱动程序类型和文件系统上驱动程序映像的路径。这使得它很容易被安全软件检测到,所以Festi必须隐藏密钥。

为此,Festi首先挂载ZwEnumerateKey这个系统服务,通过修改系统服务描述符表(System Service Descriptor Table,SSDT)查询指定注册表项的信息并返回它的所有子键。SSDT是操作系统内核中的一种特殊数据结构,包含系统服务处理程序的地址。Festi将原始ZwEnumerateKey处理程序的地址替换为钩子的地址。

Windows内核补丁保护

值得一提的是,挂载方法—修改SSDT—只能在32位的Microsoft Windows操作系统上工作。正如在第1章中提到的,64位版本的Windows实现了Kernel Patch C内核补丁保护,也称为Patch Guard技术,以防止软件对某些系统结构打补丁,包括SSDT。如果PatchGuard检测到任何被监控数据结构的修改,系统就会崩溃。

ZwEnumerateKey钩子监视发送到HKLM\System\CurrentControlSet\Service服务注册表项的请求,该注册表项包含与系统上安装的内核模式驱动程序相关的子键,包括Festi驱动程序。Festi修改钩子中的子键列表以排除与其驱动程序相对应的条目。任何依赖ZwEnumerateKey获取已安装内核模式驱动程序列表的软件都不会注意到Festi恶意驱动程序的存在。

如果注册表被安全软件发现并在关机期间删除,Festi还能够替换注册表项。在这种情况下,Festi首先执行系统例程IoRegisterShutdownNotification,以便在系统关机时接收关闭通知。它检查关闭通知处理程序,查看系统中是否存在恶意驱动程序和相应的注册表项,如果不存在(也就是说,如果它们已经被移除),将恢复,以保证它在重启过程中一直存在。