概述
Elastic 安全实验室持续监控活跃威胁,例如 GULOADER,也称为 CloudEyE – 一个逃避性 shellcode 下载器,多年来一直高度活跃,并且不断开发。最近的变化之一是在新的活动中向其向量化异常处理程序 (VEH) 添加异常,这为其已经很长的反分析技巧列表增加了更多的复杂性。
虽然 GULOADER 的核心功能在过去几年中没有发生巨大变化,但这些混淆技术的不断更新使得分析 GULOADER 成为一个耗时且资源密集的过程。在本文中,我们将讨论在分类 GULOADER 时要讨论的以下主题:
- 回顾初始 shellcode 和解包过程
- 查找解密后的 shellcode 的入口点
- 讨论 GULOADER 的 VEH 的更新,该更新混淆了控制流
- 提供修补 VEH 的方法
初始 Shellcode
在我们的 样本中,GULOADER 预先打包在 NSIS (Nullsoft 可脚本安装系统) 安装程序中。提取安装程序后,主要组件为:
- NSIS 脚本 - 此脚本文件概述了所有各种配置和安装方面。
- System.dll - 位于
$PLUGINSDir
下。此文件将放置在临时文件夹中,以分配/执行 GULOADER shellcode。
- Shellcode - 加密的 shellcode 隐藏在嵌套文件夹中。
一种快速确定承载 shellcode 的文件的方法是,在执行 GULOADER 后,从 SysInternal 的进程监视器中监视 ReadFile
事件。在本例中,我们可以看到 shellcode 是从文件 (Fibroms.Hag
) 中读取的。
GULOADER 通过使用不同的 Windows API 函数的回调来执行 shellcode。这背后的主要原因是避免围绕用于进程注入的传统 Windows API(例如 CreateRemoteThread
或 WriteProcessMemory
)的检测。我们已经观察到 GULOADER 使用 EnumResourceTypesA
和 CallWindowProcW
。
通过查看 MSDN 中关于 EnumResourceTypesA
的文档,我们可以看到第二个参数需要指向回调函数的指针。从上面的屏幕截图中,我们可以看到新分配的 shellcode 被放入此参数中。
查找主 Shellcode 入口点
在最近的示例中,GULOADER 通过包含许多不同的垃圾指令和跳转,增加了初始 shellcode 开头的复杂性。下载器的逆向工程可能需要处理漫长的代码解混淆过程,该过程旨在破坏某些工具中的反汇编和控制流,因此很难找到核心 GULOADER shellcode 的实际启动位置。
一种查找初始调用的方法是利用 x64dbg 内的图形视图,并使用自下而上的方法来查找 call eax
指令。
跟踪初始控制流的另一种技术是利用逆向工程框架 Miasm。下面是一个快速示例,我们可以传入 shellcode 并反汇编指令以跟踪流:
from miasm.core.locationdb import LocationDB
from miasm.analysis.binary import Container
from miasm.analysis.machine import Machine
with open("proctoring_06BF0000.bin", "rb") as f:
code = f.read()
loc_db = LocationDB()
c = Container.from_string(code, loc_db)
machine = Machine('x86_32')
mdis = machine.dis_engine(c.bin_stream, loc_db=loc_db)
mdis.follow_call = True
mdis.dontdis_retcall = True
asm_cfg = mdis.dis_multiblock(offset=0x1400)
Miasm 删除了 142 个 jmp
指令,并浏览了垃圾指令,我们在其中配置它以在调用 EAX 的指令 (地址: 0x3bde
) 上停止。
JMP loc_3afd
-> c_to:loc_3afd
loc_3afd
MOV EBX, EAX
FADDP ST(3), ST
PANDN XMM7, XMM2
JMP loc_3b3e
-> c_to:loc_3b3e
loc_3b3e
SHL CL, 0x0
PSRAW MM1, MM0
PSRLD XMM1, 0xF1
JMP loc_3b97
-> c_to:loc_3b97
loc_3b97
CMP DL, 0x3A
PADDW XMM3, XMM5
PXOR MM3, MM3
JMP loc_3bde
-> c_to:loc_3bde
loc_3bde
CALL EAX
Miasm 的末尾
GULOADER 的 VEH 更新
GULOADER 的一个标志性技术是以其 向量化异常处理 (VEH) 功能为中心的。此功能使 Windows 应用程序能够在异常通过标准异常过程路由之前拦截和处理异常。恶意软件系列和软件保护应用程序使用此技术来使分析人员和工具难以跟踪恶意代码。
GULOADER 通过使用 RtlAddVectoredExceptionHandler
来添加 VEH,从而开始此过程。在 GULOADER shellcode 的执行过程中,会故意放置代码来触发这些不同的异常。当触发这些异常时,VEH 将检查硬件断点。如果未找到,GULOADER 将使用单字节 XOR 密钥(每个样本更改)和从异常发生位置开始的单字节偏移量,通过 CONTEXT 结构直接修改 EIP。我们将在后续部分中查看此技术的具体示例。下面是我们的示例的 VEH 的反编译:
虽然此技术不是新的,但 GULOADER 随着时间的推移不断添加新的异常;我们最近在过去几个月观察到添加了这两个异常:
EXCEPTION_PRIV_INSTRUCTION
EXCEPTION_ILLEGAL_INSTRUCTION
随着新的异常被添加到 GULOADER 中,它可能会最终破坏研究人员用于加快分析过程的工具。
EXCEPTION_PRIV_INSTRUCTION
让我们仔细研究最近添加的两个异常,以跟踪 VEH 工作流程。第一个异常 (EXCEPTION_PRIV_INSTRUCTION
) 是当尝试在处理器指令集中以不允许的特权级别执行特权指令时发生的。某些指令,例如下面 WRSMR 中的示例,需要来自内核级别的特权,因此当程序在用户模式下运行时,由于不正确的权限,它将触发异常。
EXCEPTION_ILLEGAL_INSTRUCTION
当程序尝试执行无效或未定义的 CPU 指令时,会触发此异常。在我们的示例中,当我们遇到诸如 vmclear
或 vmxon
之类的 Intel 虚拟化指令时,这将触发异常。
一旦发生异常,GULOADER VEH 代码将首先确定哪个异常代码导致了该异常。在我们的示例中,如果异常匹配以下五个中的任何一个,则代码将采用相同的路径,而不论具体是哪一个。
EXCEPTION_ACCESS_VIOLATION(访问冲突异常)
EXCEPTION_ILLEGAL_INSTRUCTION
EXCEPTION_PRIV_INSTRUCTION
EXCEPTION_SINGLE_STEP(单步异常)
EXCEPTION_BREAKPOINT(断点异常)
然后,GULOADER 将通过遍历 EXCEPTION_POINTERS 结构中找到的 CONTEXT 记录来检查是否存在任何硬件断点。如果在不同的调试寄存器中找到硬件断点,GULOADER 将在 CONTEXT 记录中返回 0
,这将最终导致 shellcode 崩溃。
如果没有硬件断点,GULOADER 将检索距离导致异常的地址 7 个字节的单个字节。当使用最后一个示例中的 vmclear
时,它将检索字节(0x8A
)。
然后,使用该字节,它将与另一个硬编码字节执行 XOR 操作。在我们的例子中(0xB8
),这在每个样本中都是唯一的。现在,使用派生的偏移量 0x32
(0xB8 ^ 0x8A
),GULOADER 将直接从 CONTEXT 记录中修改 EIP 地址,方法是将 0x32
添加到导致异常的前一个地址(0x7697630
),从而导致下一个代码从地址 (0x7697662
) 执行。
由于中间存在不同的垃圾指令,并且重复触发异常(在我们的示例中,我们统计了 229 个唯一的异常),不难理解为什么这会破坏不同的工具并增加分析人员的时间。
控制流清理
为了使控制流更易于跟踪,分析人员可以通过跟踪执行、记录异常并使用先前讨论的 EIP 修改算法修补 shellcode 来绕过 VEH。对于此过程,我们利用了 TinyTracer,这是一个由 @hasherezade 编写的工具,它利用了 Pin,一个动态二进制检测框架。这将允许我们捕获触发异常的不同地址,因此使用上面的 vmclear
示例,我们可以看到地址为 0x7697630
,生成了一个调用 KiUserExceptionDispatcher
的异常,该函数负责处理用户模式异常。
一旦收集和过滤了所有异常,就可以将其传递到 IDAPython 脚本中,在其中我们遍历每个地址,使用第 7 个字节和 XOR 密钥(0xB8
)计算偏移量,然后使用短跳转修补生成异常的所有指令。
下图是一个修补在地址 0x07697630
和 0x0769766C
处触发异常的指令的示例。
下图表示应用全局修补之前的控制流图。带有 vmclear
指令的基本块以橙色突出显示。通过实现 VEH,GULOADER 会扁平化控制流图,使其更难以跟踪程序逻辑。
在使用 jmp
指令修补 VEH 后,这将通过将它们连接在一起来转换基本块,从而降低 shellcode 流背后的复杂性。
使用这种技术可以加速清理过程,但重要的是要注意它不是一种万无一失的方法。在此实例中,仍然会有相当多的代码/功能需要分析,但这绝对在通过删除 VEH 来简化代码方面大有帮助。完整的 POC 脚本位于 此处。
结论
GULOADER 具有许多不同的功能,可以破坏反汇编、阻碍控制流并使研究人员难以分析。尽管如此,并且该过程并不完美,但我们可以通过不同的静态或动态过程来应对这些特性,以帮助减少分析时间。例如,我们观察到,对于 VEH 中的新异常,我们仍然可以跟踪它们并修补 shellcode。此过程将引导分析人员走上正确的道路,更接近访问 GULOADER 的核心功能。
通过分享我们的一些工作流程,我们希望在您在野外遇到 GULOADER 时提供多个收获。根据 GULOADER 的变化,未来的行为很可能需要新的和不同的策略。对于检测 GULOADER,以下部分包括 YARA 规则,并且可以找到本文中的 IDAPython 脚本 此处。有关最新威胁研究的新更新,请查看我们的 Elastic Security Labs 团队的 恶意软件分析部分。
YARA
Elastic Security 创建了不同的 YARA 规则来识别此活动。以下是用于识别 GULOADER 的一个 YARA 规则的示例。
rule Windows_Trojan_Guloader {
meta:
author = "Elastic Security"
creation_date = "2023-10-30"
last_modified = "2023-11-02"
reference_sample = "6ae7089aa6beaa09b1c3aa3ecf28a884d8ca84f780aab39902223721493b1f99"
severity = 100
arch = "x86"
threat_name = "Windows.Trojan.Guloader"
license = "Elastic License v2"
os = "windows"
strings:
$djb2_str_compare = { 83 C0 08 83 3C 04 00 0F 84 [4] 39 14 04 75 }
$check_exception = { 8B 45 ?? 8B 00 38 EC 8B 58 ?? 84 FD 81 38 05 00 00 C0 }
$parse_mem = { 18 00 10 00 00 83 C0 18 50 83 E8 04 81 00 00 10 00 00 50 }
$hw_bp = { 39 48 0C 0F 85 [4] 39 48 10 0F 85 [4] 39 48 14 0F 85 [7] 39 48 18 }
$scan_protection = { 39 ?? 14 8B [5] 0F 84 }
condition:
2 of them
}
观察
所有可观察对象也可在 下载 中以 ECS 和 STIX 格式提供。
本研究中讨论了以下可观察对象。
可观察对象 | 类型 | 名称 | 参考 |
---|---|---|---|
6ae7089aa6beaa09b1c3aa3ecf28a884d8ca84f780aab39902223721493b1f99 | SHA-256 | Windows.Trojan.Guloader | GULOADER 下载器 |
101.99.75[.]183/MfoGYZkxZIl205.bin | url | NA | GULOADER C2 URL |
101.99.75[.]183 | ipv4-addr | NA | GULOADER C2 IP |