paint-brush
分析 GuLoader:如何实现复杂样本的反混淆经过@anyrun
595 讀數
595 讀數

分析 GuLoader:如何实现复杂样本的反混淆

经过 ANY.RUN6m2023/06/22
Read on Terminal Reader

太長; 讀書

本文重点介绍静态分析,但如果您想动态分析 Gu 加载程序样本,可以使用 **ANYRUN** 云恶意软件沙箱。请访问我们的博客,找到我们将分析的示例,以及解包说明和 Ghidra 脚本,该脚本部分自动化了我们将要介绍的大部分内容。
featured image - 分析 GuLoader:如何实现复杂样本的反混淆
ANY.RUN HackerNoon profile picture
0-item


作为恶意软件研究人员,您经常会遇到复杂、严重混淆的样本。我们今天处理的恶意软件 GuLoader 就是一个典型的例子。


看看这段伪代码,它是通过反编译其汇编代码生成的——它很难看且不可读。




面对这样的难题,你可能会不知所措。你应该从哪里开始呢?您如何处理该样本的分析?让我们分解一下。


那么,在本文中,我们将使用 GuLoader 作为参考,探讨反混淆此类代码的策略。您将了解:


  • 对手常用的混淆策略
  • 如何击败他们
  • 如何对 GuLoader 样本进行反混淆


本文基于 ANY.RUN 之前发布的GuLoader 恶意软件分析。请访问我们的博客,找到我们将分析的示例,以及解压说明和 Ghidra 脚本,该脚本部分自动化了我们从现在开始将要介绍的大部分内容。


本文重点关注静态分析。但如果您想动态分析 GuLoader 样本,则可以使用ANY.RUN云恶意软件沙箱。


使用您的企业电子邮件申请企业计划的14 天免费试用。充分利用 VM 实例的延长生命周期、无限任务以及 Windows 版本 7 至 11。



动态分析可让您通过将恶意软件的行为与其技术设置联系起来,了解恶意软件在现实世界中的工作原理。这是一种检查其在不同系统设置中的操作并收集 IOC 的方法。


事不宜迟,让我们开始吧。


识别混淆方法

解压样本后,我们开始手动分析 shellcode,并很快意识到它已被混淆。研究代码使我们能够将 GuLoader 使用的混淆技术分为几类:


  • XMM指令
  • 无条件 JMP 指令
  • 垃圾指令
  • 假比较说明
  • 假 PUSHAD 指令
  • 虚假 PUSH 指令
  • 不透明谓词
  • 和算术表达式


现在是停下来分析正在使用的混淆方法并制定反混淆策略的好时机。


例如,在我们的例子中,大多数技术引入的代码不会改变最终的执行结果。因此,我们通常可以安全地“NOP”它们以提高可读性。但请谨慎行事——正如我们很快就会发现的那样,并非所有混淆的代码都与程序的操作无关。


现在,让我们单独检查这些混淆技术,看看如何击败它们。


1.XMM指令

代码中散布着许多 XMM 指令。这些看起来是无序的,增加了分析过程的复杂性。我们从解压的 shellcode 的第一个字节就遇到了它们。


请注意,由于缺乏默认支持,许多仿真引擎都会遇到困难。我们对 Angr、Triton 和 Ghidra 的嵌入式引擎进行了测试,结果都没有达到要求。

我们是如何处理的

在 GuLoader 的例子中,XMM 指令实际上并不影响代码的预期行为。您会在许多恶意软件中遇到类似的混淆方法。因此,我们可以安全地将所有 XMM 指令替换为“NOP”,如下表所示:


以下是 Ghidra 反汇编程序中的结果:



2. 无条件JMP指令

无条件 JMP 指令将代码分割成更小的块。此方法经常用于避免防病毒软件和其他安全工具的检测。此外,它还可能使分析师的工作变得更加耗时和令人沮丧,因为他们必须在这些块之间跳转,尤其是在处理大量代码时。 GuLoader 和其他恶意软件通常采用这种技术。



我们是如何处理的

这种混淆方法很容易被破解。反编译代码中的反汇编器通常会成功连接这些块,从而提高代码可读性,即使存在这些无条件跳转也是如此。因此,我们可以保留小块原样,而不需要合并它们。


3.垃圾指令

垃圾汇编指令通常作为额外的混淆层发挥作用。这些指令不执行任何有形功能,通常保持寄存器值、执行流程或内存不变。


您也会在 GuLoader 中遇到这些。





留意不执行任何操作的指令(“NOP”、“FNOP”)以及移位或循环零位的指令(“SHL reg, 0”;“ROL reg, 0”)。还存在其他无影响的指令,例如“OR reg, 0”、“XOR reg, 0”、“CLD”、“WAIT”。


我们是如何处理的

处理虚假比较指令比仅仅用“NOP”替换垃圾指令更具挑战性。我们无法删除所有比较指令,因为有些比较指令对于正确的代码功能是必需的。解决这个问题的一种方法是“标记”我们遇到的所有比较指令。如果没有找到使用比较结果的指令,则可以安全地执行 NOP。如果我们发现条件跳转或类似的情况,我们会取消比较标记以避免删除。


下表显示了一个示例,其中除“CMP EDX,0x0”之外的所有比较指令都选择性地替换为 NOP:



5. 假PUSHAD指令

GuLoader 还采用了使用假“PUSHAD”指令以及匹配的“POPAD”指令的混淆策略。它们可以临时修改寄存器值,但会被“POPAD”恢复原始寄存器值而无效。



我们是如何处理的

我们的研究表明 GuLoader 中的所有“PUSHAD”指令都是无关的。因此,我们通过用 NOP 替换“PUSHAD”、“POPAD”和中间指令来解决这个问题:


然而,并非GuLoader中的所有“POPAD”指令都是垃圾指令。我们对那些没有相应“PUSHAD”的部分保持不变。


6. 虚假PUSH指令

另一种与前一种类似的混淆技术是使用假的“PUSH”指令。这些指令将一个值压入堆栈,然后立即将其弹出。


一个示例是包含“PUSH SS”指令,其后可能是修改寄存器或存储器位置的指令。然而,随后的“POP SS”将堆栈指针恢复到其初始值。



我们是如何处理的

击败假 PUSH 指令类似于假 PUSHAD 的过程,但保持非推送寄存器不变至关重要。



7. 不透明谓词

不透明谓词是总是返回 true 或 false 的条件语句,但它们很难分析或预测。这些可以在 GuLoader 的代码中找到,并且使逻辑理解变得复杂。


例如,诸如“MOV BL,0xB6”和“CMP BL,0xB6”之类的一对指令后面可以跟有诸如“JNZ ADDR”之类的条件跳转。比较总是返回 false,因为比较的值等于移动的值,使得跳转不必要且令人困惑。



我们是如何处理的

由于需要“预测”跳转条件,克服不透明谓词似乎具有挑战性。然而,我们的情况更加简单,因为所有不透明谓词都落在“PUSHAD”和“POPAD”块内。因此,我们只需将这些指令之间的所有谓词替换为 NOP。


8. 算术表达式

混淆算术表达式是 GuLoader 使用的更有趣的技术之一。它们使得理解实际执行的操作变得更加困难。这些表达式包含算术运算,例如加法或减法。有时,它们与其他混淆混合在一起,例如虚假比较、不透明谓词和垃圾指令。


一个示例是将常量值移入寄存器并执行算术运算:



另一个例子是将常量值压入堆栈并在内存上执行计算:




我们是如何处理的

为了反混淆 GuLoader 的算术表达式,我们采用了类似于处理假比较的方法。我们标记第二个参数是标量值的所有“MOV”指令,以及参数是标量的所有“PUSH”指令。当遇到算术运算时,我们更新第一条指令中的常量值并用 NOP 替换当前值。这样,第一条指令就有了结果,后续的算术指令被 NOP 代替。


以下是优化“MOV”和“PUSH”操作的示例:




请谨慎对待操作数的大小。在操作过程中保持正确的尺寸至关重要。


总结我们的分析

ANY.RUN专家的这篇文章中,我们使用 GuLoader 作为现实示例来强调典型的反混淆策略。我们首先分析代码,然后对类似的混淆策略进行分组,最后想出独特的方法来解决每个问题。


但重要的是要了解这些技术并不是一刀切的解决方案。每个恶意软件样本可能会呈现其独特的混淆策略,需要独特的对策。


我们的讨论强调了将反混淆等复杂任务分解为清晰、可管理步骤的重要性。请记住,成功反混淆任何恶意软件都需要详细分析、发现混淆模式并创建优化策略。