2020年8月,我们发布了一篇关于如何使用Speakeasy仿真框架来模拟用户模式恶意软件(例如shellcode)的文章。我们建议没有阅读本文的读者先阅读本文。除了用户模式模拟,Speakeasy还支持内核模式Windows二进制文件的模拟。当恶意软件作者使用内核模式恶意软件时,它通常采用设备驱动程序的形式,其最终目标是完全感染目标系统。恶意软件通常不与硬件交互,而是使用内核模式完全破坏系统并保持隐蔽。动态分析内核恶意软件的挑战理想情况下,可以使用反汇编程序等工具对内核模式样本进行静态分析。然而,二进制加壳程序与用户模式样本一样容易混淆内核恶意软件。此外,静态分析通常是昂贵且耗时的。如果我们的任务是自动分析同一恶意软件家族的多个变体,则可以动态分析恶意驱动程序样本。内核模式恶意软件的动态分析可能比用户模式样本更复杂。为了调试内核恶意软件,需要创建适当的环境。这个过程通常涉及设置两个独立的虚拟机作为调试器和被调试器。然后可以将此恶意软件作为按需内核服务加载,其中可以使用WinDbg等工具远程调试驱动程序。此外,存在一些使用挂钩或其他监视技术的沙盒应用程序,但通常针对用户模式应用程序。类似的沙箱监控适用于内核模式代码,这需要更深层次的系统级挂钩,这可能会产生噪音。DriverEmulation仿真是一种有效的恶意程序分析技术。这样,我们不需要自定义设置,就可以大规模模拟驱动程序。此外,通过这种方式比在沙盒环境中更容易实现更大的代码覆盖率。通常,rootkit可能会通过I/O请求数据包(IRP)处理程序(或其他回调)暴露恶意功能。在普通的Windows系统上,这些例程在其他应用程序或设备向驱动程序发送输入/输出请求时执行。这里包括常见的任务,例如读取、写入或将设备I/O控制(IOCTL)发送到驱动程序以执行某种类型的功能。使用仿真,可以使用IRP数据包直接调用这些入口点,以识别rootkit中尽可能多的功能。正如我们在第一篇Speakeasy文章中所讨论的,其他入口点在被发现时会被模拟。驱动程序的DriverMain入口点负责初始化将调用以处理I/O请求的函数分配表。主入口点完成后,Speakeasy将尝试通过提供虚拟IRP来模拟这些功能。此外,所有创建的系统线程或工作项都按顺序进行模拟,以覆盖尽可能多的代码。模拟内核模式植入器在本文中,我们将展示Speakeasy模拟真实内核模式植入器(Winnti)的案例。尽管我们之前使用过这个示例,但本文选择它是因为它以更透明的方式实现了一些经典的Rootkit功能。这篇文章不是对恶意软件本身的分析,因为它已经过时了,相反,我们将重点关注在模拟过程中捕获的事件。我们分析的Winnti样本的SHA-256哈希值为c465238c9da9c5ea5994fe9faf1b5835767210132db0ce9a79cb1195851a36fb,其原始文件名为tcprelay.sys。在本文的大多数情况下,我们将检查Speakeasy生成的模拟报告。请注意,由于内核补丁保护(PatchGuard)可防止修改关键内核数据结构,此32位rootkit使用的许多技术将无法在现代64位版本的Windows上运行。首先,我们将使用Speakeasy来模拟内核驱动程序,如下面的命令行所示。我们指示Speakeasy创建一个完整的核心转储(使用“-d”标志)以便稍后可以获取内存。这里还添加了内存跟踪标志(“-m”),它将记录恶意软件执行的所有内存读取和写入。这对于检测诸如挂钩和直接内核对象操作(DKOM)之类的事情非常有帮助。用于模拟恶意驱动程序的命令行:Speakeasy然后将开始模拟恶意软件的DriverEntry函数。驱动程序的入口点负责设置被动回调例程,该例程将为用户模式??I/O请求以及设备添加、删除和卸载的回调提供服务。我们可以查看恶意软件的DriverEntry函数的模拟报告(JSON报告中“epry_point”的“ep_type”标识),表明恶意软件找到了Windows内核的基地址。恶意软件通过使用ZwQuerySystemInformationAPI查找所有内核模块的基地址,然后查找名为“ntoskrnl.exe”的工具来执行此操作。然后恶意软件手动查找PsCreateSystemThreadAPI的地址。然后它用于启动系统线程以执行它们的实际功能。下图显示了从恶意软件入口点调用的API。tcprelay.sys入口点的关键功能:隐藏驱动程序对象恶意软件会在执行其主系统线程之前尝试隐藏自身。恶意软件首先在其自身的DRIVER_OBJECT结构中查找“DriverSection”字段。该字段包含一个包含所有已加载内核模块的链表,恶意软件会尝试断开自身链接,从而隐藏在列出已加载驱动程序的API中。在下方Speakeasy报告中的“mem_access”字段中,我们可以看到在其前后两次对DriverSection条目进行内存写入,从而将自身从链表中删除。指示tcprelay.sys恶意软件试图取消链接以隐藏的内存写入事件:如之前的Speakeasy文章所述,当在运行时创建线程或其他动态入口点时,框架会跟随它们进行模拟。在这种情况下,恶意软件会创建一个系统线程,Speakeasy会自动模拟它。我们转到新创建的线程(由“system_thread”的“ep_type”标识),我们可以看到恶意软件的真正功能开始了。恶意软件首先遍历主机上所有正在运行的进程,并寻找名为services.exe的服务控制器进程。最重要的是,通过配置JSON配置文件,可以记录模拟样本的进程列表。有关这些配置选项的更多信息,请参阅我们GitHub存储库上的Speakeasy自述文件。下图显示了此可配置进程列表的示例。提供给Speakeasy的进程列表配置字段:转到用户模式一旦恶意软件找到services.exe进程,它就会附加到进程上下文并开始检查用户模式内存,以找到导出的用户模式函数的地址。恶意软件这样做是为了将经过编码的内存驻留DLL注入到services.exe进程中。下图显示了rootkit用来解析其用户模式导出的API。tcprelay.sysrootkit用来解析其用户模式植入程序导出的日志记录API:解析导出函数后,rootkit准备注入用户模式??DLL组件。接下来,恶意软件手动将内存中的DLL复制到services.exe进程地址空间中。这些内存写入事件被捕获,如下图所示。将用户模式植入程序复制到services.exe时捕获的内存写入事件:rootkit用来执行用户模式代码的常用技术依赖于称为异步过程调用(APC)的Windows功能。APC是在所提供线程的上下文中异步执行的函数。使用APC,内核模式应用程序可以将代码排队以在线程的用户模式上下文中运行。恶意软件往往会想要注入到用户态,因为这样,它可以更容易地访问Windows内部的许多常用功能(例如网络通信)。另外,如果运行在用户态,在代码bug排查时被发现的概率很小。为了让APC按队列顺序在用户模式下启动,恶意软件必须将线程置于可警报状态。当一个线程将执行移交给内核线程调度程序并通知内核可以分配一个APC时,通常称为“warnable”(线程等待状态)。恶意软件在services.exe进程中搜索线程,一旦检测到可报警线程,就会为DLL分配内存注入内存,然后按队列顺序执行APC。Speakeasy模拟了这个过程中涉及的所有内核结构,特别是Windows系统上为每个线程分配的执行线程对象(ETHREAD)结构。恶意软件可能会尝试遍历此不透明结构以识别线程的警告标志(APC的有效候选者)何时设置。下图显示了Winnti恶意软件在手动解析services.exe进程中的ETHREAD结构以确认其处于alertable状态时记录的内存读取事件。在撰写本文时,默认情况下,模拟器中的所有线程都将自己表示为可警报的。当tcprelay.sys恶意软件确认线程是否处于可警报状态时记录的事件:接下来,恶意软件可以使用此线程对象来执行它想要的任何用户模式代码。未记录的函数KeInitializeApc和KeInsertQueueApc将分别初始化和执行用户模式??APC。下图显示了恶意软件用来将用户模式模块注入services.exe进程的API集。恶意软件执行一个shellcode存根作为APC目标,然后它将为注入的DLL执行加载程序。所有这些都可以从内存转储中恢复并在以后进行分析。tcprelay.sysrootkit用于通过APC注入用户模式日志记录API:网络挂钩注入用户模式后,内核组件将尝试安装网络混淆挂钩(大概是为了隐藏用户模式植入工具).Speakeasy跟踪并标记模拟空间中的所有内存。在内核模式仿真的上下文中,这包括所有内核对象(例如驱动程序、设备对象和内核模块本身)。我们观察到,恶意软件在植入用户模式后立即开始尝试挂钩内核组件。根据静态分析的结果,我们确认它用于网络隐藏。模拟报告的内存访问部分显示恶意软件修改了netio.sys驱动程序,特别是名为NsiEnumerateObjectsAllParametersEx的导出函数中的代码。当系统上的用户运行“netstat”命令时最终会调用此函数,并且可能会被恶意软件挂钩以隐藏受感染系统上连接的网络端口。此内联挂钩使用下图中显示的事件ID。恶意软件设置的内在函数挂钩以隐藏网络连接:此外,恶意软件还挂钩TCPIP驱动程序对象以实现额外的网络隐藏功能。具体来说,恶意软件会挂钩TCPIP驱动程序的IRP_MJ_DEVICE_CONTROL处理程序。查询活动连接时,用户模式代码可能会向此函数发送IOCTL代码。通过查找对关键内核对象的内存写入,可以使用Speakeasy轻松识别此类挂钩,如下图所示。用于挂钩TCPIP网络驱动程序的内存写入事件Speakeasy分发伪造的SSDT,以便恶意软件可以与之交互。SSDT是一个函数表,它将内核功能暴露给用户模式代码。下图中的事件表明SSDT结构在运行时被修改了。Speakeasy检测到的SSDT钩子:如果我们查看IDAPro中的恶意软件,我们可以确认该恶意软件已经修改了ZwQueryDirectoryFile和ZwEnumerateKeyAPI的SSDT条目,以隐藏自己免受文件系统分析和注册表分析。修改了SSDT文件功能IDAPro中显示的隐藏:设置这些挂钩后,系统线程将启动。驱动程序中的其他入口点(例如IRP处理程序和DriverUnload例程)不值得分析,因为它们主要包含示例驱动程序代码。获取注入的用户模式植入物现在我们知道驱动程序如何在系统中隐藏自己,我们可以使用Speakeasy创建的内存转储来获取先前注入的DLL。打开我们在模拟时创建的ZIP文件,我们可以找到上面引用的内存标签。我们很快确认内存块具有有效的PE标头,并且能够成功将其加载到IDAPro中,如下图所示。从Speakeasy内存转储中恢复的注入用户模式??DLL:总结在这篇博文中,我们探索了如何使用Speakeasy从内核模式二进制文件中高效自动地识别Rootkit活动。Speakeasy可用于快速对内核二进制文件进行分类,解决了通常难以执行的动态分析问题。如果您想了解更多信息或查看源代码,欢迎访问我们的GitHub存储库(https://github.com/fireeye/speakeasy)。本文翻译自:https://www.fireeye.com/blog/threat-research/2021/01/emulation-of-kernel-mode-rootkits-with-speakeasy.html
