使用Speakeasy仿真框架模拟内核模式Rootkit

2020年8月,我们发布了一篇博客文章,主要内容是如何使用Speakeasy仿真框架来仿真用户模式的恶意软件,例如shellcode,下面就是那篇文章。

https://github.com/fireeye/speakeasy
https://www.fireeye.com/blog/threat-research/2020/08/emulation-of-malicious-shellcode-with-speakeasy.html

除了用户模式仿真之外,Speakeasy还支持内核模式Windows二进制文件的仿真。当恶意软件作者使用内核模式恶意软件时,通常会采用设备驱动程序的形式,其最终目标是完全感染受感染的系统。该恶意软件通常不与硬件交互,而是利用内核模式完全破坏系统并保持隐藏状态。

0x01 动态分析内核Rookit的挑战

理想情况下,可以使用反汇编工具之类的工具静态地逆向内核模式样本。但是,二进制程序和用户模式样本一样会混淆内核恶意软件。另外,静态分析通常是昂贵且费时的。如果我们的目标是自动分析同一恶意软件家族的许多变体,则可以动态分析恶意驱动程序样本。

与用户模式样本相比,对内核模式恶意软件的动态分析可能会涉及更多难点技术。为了调试内核恶意软件,需要创建适当的环境。这通常涉及将两个独立的虚拟机设置为调试器和被调试器。然后可以将该恶意软件作为内核服务加载,其中可以使用WinDbg之类的工具远程调试驱动程序。

存在一些使用Hook或其他监控技术的沙箱应用程序,但通常以用户模式应用程序为目标。对于内核模式代码具有类似的沙箱监控工作,将需要深入的系统级Hook,这可能会对系统产生很大的影响。

0x02 驱动程序仿真

对于恶意驱动程序,仿真已被证明是一种有效的分析技术。不需要自定义设置,并且可以大规模模拟驱动程序。此外,与沙盒环境相比,最大的代码覆盖范围更容易实现。通常,rootkit可能会通过I / O请求数据包(IRP)处理程序(或其他回调函数)执行恶意功能。在普通的Windows系统上,当其他应用程序或设备向驱动程序发送输入/输出请求时,将执行这些例程。这包括一些常见任务,例如读取,写入或将设备I / O控制(IOCTL)发送给驱动程序以执行某种类型的功能。

使用仿真技术,可以使用IRP数据包直接调用这些入口点,以便在rootkit中发现尽可能多的功能。正如我们在第一篇Speakeasy博客文章中所讨论的,在发现其他入口点时会对其进行仿真。驱动程序的DriverMain入口点负责初始化函数分配表,该表被调用以处理I / O请求。在主入口点完成后,Speakeasy将尝试通过提供虚拟IRP来仿真这些功能。此外,顺序模拟所有创建的系统线程或工作项,以获取尽可能多的代码覆盖率。

0x03 模拟内核模式Rootkit

在此博客文章中,我们将分析Speakeasy在模拟一个公开名为Winnti的真实内核模式Rootkit中的情况。尽管该样本被发现已经很久了,但还是选择了它,因为它实现了一些经典的rootkit功能。这篇文章的目的不是讨论恶意软件本身的分析,因为它已经过时了。相反,我们将专注于对Rootkit的仿真。

我们将分析的Winnti示例信息如下:

SHA256 hash:c465238c9da9c5ea5994fe9faf1b5835767210132db0ce9a79cb1195851a36fb

原始文件名Ltcprelay.sys。

对于本文的大部分内容,我们将检查Speakeasy生成的仿真报告。注意:由于内核补丁程序保护(PatchGuard)可以防止对关键内核数据结构进行修改,因此该32位rootkit所采用的许多技术在现代64位版本的Windows上将不起作用。

首先,我们将使Speakeasy使用图1所示的命令行来模拟内核驱动程序。我们使用Speakeasy创建一个完整的内存转储(使用“ -d”参数),以便以后可以获取内存。我们提供了内存跟踪参数(“ -m”),它将记录恶意软件执行的所有内存读取和写入操作。这对于检测钩子和直接内核对象操纵(DKOM)很有用。

使用Speakeasy仿真框架模拟内核模式Rootkit

图1:用于模拟恶意驱动程序的命令

然后,Speakeasy将开始模拟恶意软件的DriverEntry功能。驱动程序的入口点负责设置被动回调例程,这些例程将为用户模式I / O请求以及用于设备添加,删除和卸载的回调提供服务。查看恶意软件的DriverEntry函数的仿真报告(在JSON报告中以“ entry_point”的“ ep_type”标识),表明该恶意软件找到了Windows内核的基址。该恶意软件通过使用ZwQuerySystemInformation API来查找所有内核模块的基址,然后查找“ ntoskrnl.exe”来实现此目的。然后,该恶意软件会手动找到PsCreateSystemThread API的地址。然后,它用于启动系统线程以执行其实际功能。图2显示了从恶意软件中调用的API

使用Speakeasy仿真框架模拟内核模式Rootkit图2:tcprelay.sys入口点中的关键函数

0x04 隐藏驱动程序对象

该恶意软件会在执行其主系统线程之前尝试隐藏自身。恶意软件首先在其自己的DRIVER_OBJECT结构中查找“ DriverSection”字段。该字段包含一个包含所有已加载内核模块的链接列表,恶意软件尝试将自身取消链接以从列出已加载驱动程序的API中隐藏。在图3所示的Speakeasy报告中的“ mem_access”字段中,我们可以看到在驱动程序段本身之前和之后两次写入内存,这会将自身从链接列表中删除。

使用Speakeasy仿真框架模拟内核模式Rootkit图3:表示tcprelay.sys恶意软件的内存写入事件,尝试将自身断开链接以隐藏

当在运行时创建线程或其他动态入口点时,框架将跟随它们进行仿真。在这种情况下,恶意软件会创建一个系统线程,而Speakeasy会自动对其进行仿真。

转到新创建的线程(由“ system_thread”的“ ep_type”标识),我们可以看到恶意软件开始了其真正的功能。该恶意软件首先枚举主机上所有正在运行的进程,然后查找services.exe任务管理器进程。重要的是要注意,返回到仿真样本的流程清单可以通过运行时提供的JSON配置文件进行配置。有关这些配置选项的更多信息,请参见我们的GitHub存储库上的Speakeasy README 。

https://github.com/fireeye/speakeasy

此可配置过程清单的示例如图4所示。

使用Speakeasy仿真框架模拟内核模式Rootkit图4:提供给Speakeasy的流程列表配置字段

0x05 Rootkit通过APC转入用户模式

一旦恶意软件找到了services.exe进程,它将附加到其进程上下文并开始检查用户模式内存,以便找到导出的用户模式函数的地址。恶意软件会这样做,以便以后可以将编码的内存驻留DLL注入services.exe进程。图5显示了rootkit用于调用其用户模式导出的API。

使用Speakeasy仿真框架模拟内核模式Rootkit图5:tcprelay.sys rootkit用于调用其用户模式植入的导出日志API

解决了导出的功能后,rootkit准备注入用户模式DLL组件。接下来,恶意软件手动将内存中的DLL复制到services.exe进程地址空间中。这些内存写事件被捕获并显示在图6中。

使用Speakeasy仿真框架模拟内核模式Rootkit 图6:将用户模式植入复制到services.exe时捕获的内存写入事件

Rootkit用于执行用户模式代码的常见技术涉及一种称为“异步过程调用(APC)”的Windows功能。APC是在提供的线程的上下文中异步执行的功能。使用APC可使内核模式应用程序将代码排队以在线程的用户模式上下文中运行。恶意软件通常希望注入用户模式,因为Windows内的许多常用功能(例如网络通信)可以更容易地访问。

为了使APC排队以用户模式启动,恶意软件必须将线程定位为“可更改”状态。当线程将其执行量交予内核线程调度程序并通知内核它们能够分派APC时,它们被称为可警报的。恶意软件在services.exe进程中搜索线程,一旦检测到可警告的线程,它将为DLL分配内存以注入,然后将APC排队执行。

Speakeasy模拟此过程中涉及的所有内核结构,特别是为Windows系统上的每个线程分配的执行线程对象(ETHREAD)结构。恶意软件可能会尝试遍历这个不透明的结构,以识别何时设置了线程的警报标志(并因此确定了APC的有效候选对象)。图7显示了Winnti恶意软件在services.exe进程中手动解析ETHREAD结构以确认它可发出警报时记录的内存读取事件。在撰写本文时,默认情况下,仿真器中的所有线程都将自己表示为可警报。

使用Speakeasy仿真框架模拟内核模式Rootkit 图7:tcprelay.sys恶意软件确认线程可警报时记录的事件

接下来,恶意软件可以使用此线程对象执行所需的任何用户模式代码。未记录的函数KeInitializeApc和KeInsertQueueApc将分别初始化并执行用户模式APC。图8显示了恶意软件用来将用户模式模块注入services.exe进程的API集。该恶意软件执行一个shellcode作为APC的目标,然后将为注入的DLL执行一个加载程序。所有这些都可以从内存转储包中恢复并在以后进行分析。

使用Speakeasy仿真框架模拟内核模式Rootkit图8:tcprelay.sys rootkit用于通过APC进入用户模式的日志API

0x06 Hook内核组件用于网络隐藏

注入用户模式后,内核组件将尝试安装网络混淆钩子(大概是隐藏用户模式Rootkit)。Speakeasy跟踪并标记仿真空间中的所有内存,在内核模式仿真的上下文中,这包括所有内核对象(例如,驱动程序和设备对象,以及内核模块本身)。观察到恶意软件将其用户模式植入后,我们立即看到它开始尝试Hook内核组件,在静态分析过程中已确认将其用于网络隐藏。

仿真报告的内存访问部分显示,恶意软件修改了netio.sys驱动程序,特别是在名为NsiEnumerateObjectsAllParametersEx的导出函数中的代码。当系统上的用户运行“ netstat”命令时,最终会调用此功能,并且恶意软件可能会hook此功能,以隐藏受感染系统上的已连接网络端口。此内联Hook由图9中捕获的事件标识。

使用Speakeasy仿真框架模拟内核模式Rootkit图9:恶意软件设置的内联函数Hook隐藏网络连接

此外,恶意软件还会hook Tcpip驱动程序对象,以完成其他网络隐藏。具体而言,该恶意软件将Tcpip驱动程序的IRP_MJ_DEVICE_CONTROL处理程序Hook。查询活动连接时,用户模式代码可能会将IOCTL代码发送给此函数。通过查找对关键内核对象的内存写入,可以使用Speakeasy轻松识别这种钩子,如图10所示。

使用Speakeasy仿真框架模拟内核模式Rootkit图10:用于 hook Tcpip网络驱动程序的内存写入事件

0x07 Hook系统服务描述符表

最后,rootkit将尝试使用系统服务描述符表(SSDT)的近乎古老的技术来隐藏自身。Speakeasy分配了伪造的SSDT,以便恶意软件可以与其进行交互。SSDT是一个函数表,可将内核函数公开给用户模式代码。图11中的事件表明,在运行时修改了SSDT结构。

使用Speakeasy仿真框架模拟内核模式Rootkit图11:Speakeasy检测到的SSDTHook

如果我们查看IDA Pro中的恶意软件,则可以确认该恶意软件已修补了ZtQueryDirectoryFile和ZwEnumerateKey API API的SSDT条目,这些API用来将自己隐藏在文件系统和注册表分析之外。SSDT补丁函数如图12所示。

使用Speakeasy仿真框架模拟内核模式Rootkit图12:IDA Pro中显示的文件隐藏SSDT 补丁函数

设置完这些Hook之后,系统线程将退出。

0x08 从dump文件中获取用户模式Rootkit

现在我们已经知道了驱动程序在系统上隐藏自身的方式,可以使用Speakeasy创建的内存转储来获取前面讨论的注入的DLL。打开我们在仿真时创建的zip文件,我们可以找到图6中引用的内存标签。我们迅速确认该内存块具有有效的PE标头,并成功地将其加载到IDA Pro中,如图13所示。

使用Speakeasy仿真框架模拟内核模式Rootkit图13:从Speakeasy内存转储中恢复的注入用户模式DLL

0x09 分析总结

在此博客文章中,我们讨论了Speakeasy如何有效地从内核模式二进制文件自动识别rootkit活动。Speakeasy可用于快速分类内核二进制文件,否则这些二进制文件可能难以动态分析。有关更多信息和代码文件,请转到我们的GitHub存储库。

郑重声明:本网站发布的内容(图片、视频和文字)以及用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服