01前言
恶意软件反分析策略包括使逆向工程复杂化的混淆技巧、逃避沙箱的反沙箱技巧、绕过静态检测的打包技巧等。本文将以 Roshtyak 恶意软件为例介绍这些策略。Roshtyak 是 Raspberry Robin 使用的 DLL 后门,于2021年9月首次被发现,主要通过受感染的USB设备传播。选择该软件为样本是因为Roshtyak设计了很多反分析策略,有利于帮助其他研究人员识别和应对类似的技巧、强化分析环境。
02Roshtyak介绍
Roshtyak 包含14 层保护层,每层都经过高度混淆并服务于特定目的。一些工件表明这些最初是 PE 文件,但被转换为只有以前的层知道如何解密和加载的自定义加密结构。许多反调试器、反沙盒、反虚拟机和反仿真器检查遍布各个层。如果其中一项检查检测到分析环境将执行以下操作之一。
恶意软件调用自身的TerminateProcess函数,避免显示任何进一步的恶意行为并保持后续层的加密。
Roshtyak终止自身进程。但由于Roshtyak的混淆特性,可能无法立即清楚崩溃的原因是有意为之还是因为存在漏洞。
恶意软件进入无限循环。由于循环本身位于混淆代码中并且跨越数千条指令,因此很难确定循环是否在为攻击做准备。
03反分析行为
3.1段寄存器
在执行早期,Roshtyak 更倾向于使用不调用导入函数的检查。如果其中一项检查成功,则示例可以安静地退出,这样不会生成可疑的 API 调用痕迹。下面是 Roshtyak 检查 gs 段寄存器行为的示例。该检查是隐形的,周围的垃圾指使其非常容易被忽视。
展开全文
(只有带下划线的指令是有效的)
此检查背后的第一个想法是检测单个执行。在上面的代码片段之前,cx的值被初始化为2。在弹出ecx指令之后,Roshtyak检查cx是否仍然等于2。此为预期内的行为,因为该值应该在正常情况下通过堆栈和gs寄存器传播。但单个事件会重置gs选择器的值,这会导致最后弹出一个不同的值到ecx中。
这项检查还有更多内容。gs的值会暂时更改为2。在检查之后Roshtyak会进入一个循环计算迭代次数,直到gs的值不再是 2。gs选择器在线程上下文切换后也会被重置,可见循环本质上是计算迭代次数,直到发生上下文切换。Roshtyak将多次重复此过程,最终求出结果的平均值并检查是否属于裸机执行环境的合理范围。如果示例在虚拟机管理程序或模拟器中运行,则平均迭代次数可能会超出此范围,因此Roshtyak能够检测到不需要的执行环境。
Roshtyak还会检查cs段寄存器的值是0x1b还是0x23。此时0x1b是在原生x86 Windows上运行时的预期值,而0x23是在WoW64中运行时的预期值。
3.2通过随机 ntdll gadget注入APC
当Roshtyak 与C&C服务器通信时,会生成一个新的看似无害的进程,例如regsvr32.exe。通过使用共享段,该进程将通信模块注入到新进程的地址空间中。被注入的模块通过APC注入执行,调用的是NtQueueApcThreadEx函数。
分析发现ApcRoutine 参数(标记要安排执行的目标例程)并不指向注入模块的入口点。而是指向 ntdll 中看似随机的地址。而这个地址实际不是随机选择的,而是 Roshtyak 扫描了 ntdll 的代码段来查找pop r32;retgadget(不包括pop,因为旋转堆栈不可取),并随机选择一个作为ApcRoutine。
( ret gadget 用作 APC 注入的入口点)
查看ApcRoutine的调用约定, pop指令使堆栈指针指向NtQueueApcThreadEx 的SystemArgument1参数,因此 ret 指令有效地跳转到SystemArgument1指向的任何位置。这意味着通过滥用这个gadget,Roshtyak可以将 SystemArgument1作为APC注入的入口点。这个操作混淆了控制流并使NtQueueApcThreadEx调用看起来更合法。如果有人挂钩此函数并检查ApcRoutine参数,该指向ntdll代码段的事实可能会使分析人员认为这是个非恶意调用。
3.3检查组合写入内存的读/写性能
Roshtyak会分配一个带有PAGE_WRITECOMBINE标志的大内存缓冲区。该标志修改缓存行为以优化顺序写入性能,不过这是以读取性能和可能的内存排序为代价的。Roshtyak使用它来检测它是否在物理设备上运行。首先写入分配的缓冲区,然后从分配的缓冲区中读取,同时使用一个单独的线程作为计数器来测量读写性能。该实验重复32次,只有在大多数情况下写性能至少是读性能的6倍时才会通过检查。如果检查失败,Roshtyak就会故意选择漏洞的RC4密钥,导致无法正确地解密下一层。
3.4隐藏shellcode
Roshtyak 注入的shellcode也会被隐藏。当准备代码注入时,程序首先创建一个大段并将其作为PAGE_READWRITE映射到当前进程中。然后用随机数据填充该段,并将shellcode 放置在随机数据内的随机偏移处。由于shellcode只是一个相对较小的加载器,后面跟着看起来是随机的打包数据,所以整个段看起来像随机数据。
共享区段内字节的直方图如上。看起来几乎随机,最可疑的符号通过空字节的轻微过度来表示。
接下来该段从当前进程中取消并映射到目标进程中,在目标进程中使用上述 APC 注入技巧执行该段。添加随机数据是为了隐藏 shellcode 。仅从目标进程的内存转储来看,该段可能充满了随机数据并且不包含任何有效的可执行代码。即使分析人员怀疑该段中间某处的实际有效代码也不容易找到它的确切位置。
如上,共享段中 shellcode可能很难确定确切的起始地址,因为通常是从奇数bt指令开始的。
3.5 Ret2Kernel32
Roshtyak特别注意清理自己的攻击痕迹。当不再需要某个字符串或某段内存时,Roshtyak就会清除或释放它,试图借此清除更多痕迹。Roshtyak的图层也是如此。每当一层完成其工作时,就会在将执行传递到下一层之前进行自我释放。但该层不能简单直接地自我释放。如果在当前正在执行的内存区域上调用VirtualFree,整个进程将会崩溃。
因此,Roshtyak通过在层转换期间执行的ROP链来释放层以避免此问题。当一个层即将退出时,程序会在堆栈上构造一个 ROP链并返回其中。下面可以看到这样一个ROP链的示例。该链首先返回VirtualFree和UnmapViewOfFile以释放上一层的内存。然后返回到下一层。下一层的返回地址设置为 RtlExitUserThread,保障执行安全。
一个简单的ROP链由VirtualFree -> UnmapViewOfFile -> next layer -> RtlExitUserThread组成。
3.6篡改堆栈中的返回地址
如上一节所示,Roshtyak喜欢使用ret指令调用函数。下一个技巧与此类似,通过操作堆栈使ret指令跳转到所需的地址。
Roshtyak会扫描当前线程的堆栈,寻找指向前一层代码段的指针,与其他层不同,这一层没有使用ROP链技巧释放。用想要调用的地址替换所有这些指针。然后使代码多次返回,直到 ret指令遇到一个被劫持的指针,将执行重定向到所需的地址。
3.7间接注册表写入
Roshtyak可以执行许多可疑的注册表操作,例如设置 RunOnce项以实现持久性。由于对此类密钥的修改可能会被监控,Roshtyak会试图绕过监控。首先生成一个随机注册表项名称,并使用ZwRenameKey将RunOnce项临时重命名为随机名称。重命名后Roshtyak会在临时密钥中添加一个新的持久性条目,然后最终将其重命名为RunOnce。这种写入注册表的方法很容易被检测到,但可能会绕过一些简单的基于挂钩的监控方法。
同样地,Roshtyak使用多种方法来释放文件。除了对 NtDeleteFile的明显调用之外,Roshtyak还可以通过对 ZwSetInformationFile的调用中设置 FileDispositionInformation或FileRenameInformation 来有效地释放文件。然而与注册表修改方法不同的是,该操作不是为了逃避检测而实现的。相反如果对NtDelete文件的初始调用失败,Roshtyak将会尝试这些替代方法。
3.8命令行清除
Roshtyak 的核心是用非常可疑的命令行执行的,例如RUNDLL32.EXE SHELL32.DLL,ShellExec_RunDLL REGSVR32.EXE -U /s "C:\Users\\AppData\Local\Temp\dpcw.etl."。Roshtyak会通过清除从各种来源收集的命令行信息来在执行期间隐藏这些看起来不合法的命令。
首先调用GetCommandLineA和GetCommandLineW并清除两个返回的字符串。然后尝试清除PEB->ProcessParameters->CommandLine指向的字符串(即使已经指向一个已经被清除的字符串)。由于Roshtyak经常在 WoW64 下运行,程序还调用 NtWow64QueryInformationProcess64来获取指向PEB64 的指针,目的是清除通过遍历这个“第二个”PEB获得的 ProcessParameters->CommandLine。虽然清除命令行可能是为了让Roshtyak看起来更合法,但完全清除任何命令行也很容易被察觉。该恶意软件作者提出了一种基于这些可疑的空命令行的检测方法。
Roshtyak的核心流程如Process Explorer所示,需要注意可疑的空命令行。
3.9大型可执行映射
Roshtyak会创建一个大小为0x386F000的大型PAGE_EXECUTE_READWRITE映射。然后将这个映射映射到自己的地址空间9次。在这之后将映射设置为0x42 (inc edx的操作码),除了最后六个字节之外,填充inc ecx指令和jmp dword ptr [ecx]。接下来将映射视图的9个基址放入一个数组中,后面是单个ret指令的地址。最后将ecx指向这个数组并调用第一个映射视图,该操作会依次调用所有映射视图,直到最后的ret指令停止。返回后Roshtyak 验证 edx 恰好增加了 0x1FBE6FCA 倍 (9 * (0x386F000 - 6))。
根据分析,这可能是另一个反模拟器检查。例如在某些模拟器中,映射段可能没有完全实现,因此写入映射视图的一个实例的指令有可能不会传播到其他八个实例。另一种猜测是这种检查可以用于请求模拟器可能无法提供的大量内存。毕竟所有视图的总和几乎是标准32位用户模式地址空间的一半。
04总结
在这篇文章中我们对与Raspberry Robin相关的后门有效载荷 Roshtyak进行了深入研究。重点是通过分析Roshtyak的保护机制来展示一些前所未见的反调试器/反沙盒/反虚拟机技巧,讨论了Roshtyak的高度混淆。综上所述,恶意软件中的大量的混淆和先进的反分析技巧会使逆向分析工作难度大大增加。
特别声明
本文仅代表作者观点,不代表本站立场,本站仅提供信息存储服务。