如何破解MassLogger使用的反分析策略

2020-10-20 11:15:00828人阅读

FLARE团队最近刚刚完成了对MassLogger的分析,MassLogger是一个相当新的凭证窃取软件。尽管MassLogger缺少新颖的功能,但还是采用了一种复杂的技术,该技术在运行时取代了Microsoft中间语言(MSIL),从而阻碍了静态分析。截至发稿时,只有一篇文章详细介绍过MassLogger的混淆技术。本文,看看FLARE团队是如何深入研究MassLogger凭证窃取程序和.NET运行时的。

MassLogger的基本情况

MassLogger是一个.NET凭证窃取软件,它从启动程序(6b975fd7e3eb0d30b6dbe71b8004b06de6bba4d0870e165de4bde7ab82154871)开始发起攻击,该启动程序使用简单的反调试技术,在识别时可以轻松绕开安全监控。此第一阶段加载程序最终会对第二阶段程序集进行XOR处理,然后解密、加载并执行名为Bin-123.exe的最终MassLogger有效载荷(bc07c3090befb5e94624ca4a49ee88b3265a3d1d288f79588be7bb356a0f9fae)。最终的有效载荷可以轻松提取并独立执行。因此,我们将专门关注使用主要反分析技术的最后有效载荷。

基本的静态分析是不会发现什么有价值的东西的,不过我们注意到了一些有趣的字符串,但它们不足以为我们提供有关该恶意软件功能的任何提示。在受控环境中执行有效载荷表明,该示例删除了一个日志文件,该文件标识了恶意软件家族,其版本以及最重要的一些配置选项。图1中描述了一个示例日志文件,随着示例的运行,我们还可以从内存中提取一些有趣的字符串。但是,基本的动态分析不足以提取所有基于主机的指标(HBI),基于网络的指标(NBI)和完整的恶意软件功能。我们必须进行更深入的分析,以更好地理解样本及其功能。



1597546864123070.png

1597546872484847.png

1597546879472146.png

MassLogger日志样本

反编译处理

与许多其他.NET恶意软件一样,MassLogger混淆了其所有方法名称甚至方法控制流。我们可以使用de4dot自动对MassLogger有效载荷进行混淆处理。但是,查看经过混淆处理的有效载荷后,我们很快发现了一个主要问题:大多数方法几乎不包含逻辑,如图2所示。

1597546889946633.png

显示空方法的dnSpy

在dnSpy的中间语言(IL)视图中查看原始的MassLogger有效载荷,可以确认大多数方法不包含任何逻辑,只是不返回任何内容。这显然不是真正的恶意软件,因为我们已经通过动态分析观察到该样本确实在执行恶意活动并记录到日志文件中。我们只剩下几个方法,最明显的是主模块构造函数中首先调用的带有标记0x0600049D的方法。

1597546950114228.png

显示该方法详细信息的dnSpy IL视图

方法0x0600049D的控制流已被混淆为一系列switch语句,在dnSpy作为调试器的帮助下,我们仍然可以在某种程度上遵循该方法的高级逻辑。但是,全面分析该方法将非常耗时。相反,当第一次分析此有效载荷时,我会选择快速扫描整个模块以寻找提示。幸运的是,我发现了一些在基本静态分析期间被忽略的有趣字符串:clrjit.dll,VirtualAlloc,VirtualProtect和WriteProcessMemory,如图4所示。

1597546959151865.png

分散在整个模块中的有趣字符串

上网快速搜索“clrjit.dll”和“VirtualProtect”,可以迅速了解一种通常称为“JIT挂钩( Just-In-Time Hooking)”的技术。本质上,JIT挂钩涉及在compileMethod()函数上安装挂钩,JIT编译器将在该函数上将MSIL编译为程序集(x86,x64等)。有了挂钩,恶意软件就可以轻松地用包含原始恶意软件逻辑的真实MSIL替换每个方法体。为了全面了解此过程,让我们先探索一下.NET可执行文件,.NET方法以及MSIL如何变成x86或x64程序集等问题。

.NET可执行方法

.NET可执行文件只是具有可执行(PE)格式的另一个二进制文件,现在网上有大量资源详细描述PE文件格式,.NET元数据和.NET令牌表。不过这些并不是本文讲解的重点,本书我们会将重点放在.NET方法上。

.NET程序集中的每个.NET方法都由一个令牌标识,实际上,.NET程序集中的所有内容,无论是模块,类,方法原型还是字符串,都由令牌标识。让我们看一下由令牌0x0600049D标识的方法,如图5所示。最有效字节(0x06)告诉我们,这个标记是一个方法标记(类型0x06),而不是模块标记(类型0x00)、TypeDef标记(类型0x02)或LocalVarSig标记(类型0x11)。三个最低有效字节告诉我们该方法的ID,在本案例中为0x49D(decimal为1181)。此ID也称为方法ID(MID)或方法的Row ID。rowid是一个用来唯一标记表中行的伪列。它是物理表中行数据的内部地址,包含两个地址,其一为指向数据表中包含该行的块所存放数据文件的地址,另一个是可以直接定位到数据行自身的这一行在数据块中的地址。

1597546978700128.png

方法0x0600049D的方法细节

要查找有关此方法的更多信息,请查看.NET元数据目录中.NET元数据流的“#~”流的表中查找,如图6所示。我们遍历该条目的编号1181或0x49D用于查找方法元数据的方法表,该方法元数据包括方法体的相对虚拟地址(RVA),各种标志,指向方法名称的指针,指向方法签名的指针,最后是这种方法指向用于以下参数的参数规范的指针。请注意,MID从1而不是0开始。

1597547567131200-1.png

来自PE文件头的方法细节

对于方法0x0600049D,方法体的RVA为0xB690。该RVA属于.text部分,其RVA为0x2000。因此,此方法体从.text部分中的0x9690(0xB690 – 0x2000)字节开始。 .text部分根据标题从文件的0x200字节开始。结果,我们可以在文件中偏移0x9890(0x9690 + 0x200)字节的位置找到方法体。我们可以在图7中看到方法体。

1597546988194354-1.png

十六进制编辑器中的方法0x0600049D体

.NET方法体

.NET方法体以方法体标头开头,后跟MSIL字节。 .NET方法有两种类型:小方法和大方法。查看方法体标头的第一个字节,这两个最低有效位会告诉我们该方法是小的(最后两位为10)还是大的(最后两位为11)。

.NET 小方法

让我们看一下方法0x06000495,按照前面所述的相同步骤,我们检查方法表的行号0x495(decimal为1173,对SQL Server而言,Decimal可用来保存具有小数点而且数值确定的数值,它不像floatreal是用来保存近似值。),发现方法体RVA为0x7A7C,它转换为0x5C7C作为文件的偏移量。在此偏移量下,方法体的第一个字节为0x0A(二进制格式为10 1010)。

如何破解MassLogger使用的反分析策略

方法0x06000495元数据和体

由于两个最低有效位是10,因此我们知道0x06000495是一个小的方法。对于一个小方法,方法正文标头只有一个字节长。两个最小有效位是10,表示这是一个小方法,六个最有效位告诉我们要跟踪的MSIL的大小(即MSIL的长度)。在本例中,六个最有效的位是000010,这告诉我们方法体有两个字节长。0x06000495的整个方法主体是0A 16 2A,后跟一个NULL字节,该字节已由dnSpy分解,如图9所示。

1597547146173506.png

dnSpy IL视图中的方法0x06000495

.NET大方法

返回到偏移量为0x9890到文件(RVA 0xB690)中的方法0x0600049D(条目号1181),方法体的第一个字节为0x1B(或二进制数0001 1011)。最低两位是11,表示0x0600049D是大方法。 大方法体标头的长度为12个字节,不过对其结构的介绍超出了本博客文章的范围。我们真正关心的字段是此大标头中偏移量为0x04字节的四字节字段。该字段指定此方法正文标头之后的MSIL的长度。对于方法0x0600049D,整个方法体标头为“1B 30 08 00 A8 61 00 00 75 00 00 11”,后面的MSIL长度为“A8 61 00 00”或0x61A8(decimal为25000)字节。

1597547158152639.png

十六进制编辑器中的方法0x0600049D体

JIT编译

无论一个方法是小的还是大的,它都不是按原样执行的。当.NET运行时需要执行一个方法时,它将完全按照前面所述的过程查找方法体,该体包括方法体标头和MSIL字节。如果这是第一次运行该方法,则.NET运行时将调用即时编译器,该编译器将MSIL字节提取并将其编译为x86或x64程序集,具体取决于当前进程是32位还是64位。经过一些准备,JIT编译器最终将调用compileMethod()函数。整个.NET运行时项目是开源的,可以在GitHub上获得。我们可以很容易地发现compileMethod()函数具有以下原型(图11):

1597547186129219.png

compileMethod()函数原型

下图显示了CORINFO_METHOD_INFO结构。

1597547219182496.png

CORINFO_METHOD_INFO结构

ILCode是指向要编译的方法的MSIL的指针,而ILCodeSize告诉我们MSIL的具体时间。 compileMethod()的返回值是一个错误代码,指示成功或失败。如果成功,nativeEntry指针将使用包含从MSIL编译的x86或x64指令的可执行内存区域的地址填充。

MassLogger JIT挂钩

让我们回到MassLogger,一旦主模块初始化运行,它将首先解密其他方法的MSIL。然后,它安装一个挂钩以执行自己的compileMethod()版本(方法0x06000499)。此方法将真实的恶意软件的MSIL字节替换为原始compileMethod()的info参数的ILCode和ILCodeSize字段。

除了替换MSIL字节外,MassLogger还在模块初始化时修补方法体标头。从图13中可以看到,磁盘上的方法0x060003DD的方法体标头(文件偏移为0x3CE0)与内存中的标头(RVA 0x5AE0)不同。唯一保持一致的两件事是最不重要的两个位,它表示方法是小的还是大的。要成功地击败这种反分析技术,我们必须恢复真实的MSIL字节以及正确的方法体标头。

1597547241125795.png

驻留在磁盘上与加载到内存中时具有不同标头的相同方法体

击败JITM的JIT方法体替换

为了自动恢复MSIL和方法体标头,另一位FLARE团队成员建议的一种可能方法是在加载并允许MassLogger模块构造函数运行之前,在compileMethod()函数上安装我们自己的挂钩。使用托管挂钩(新的compileMethod()是用C#编写的托管方法)和本机挂钩(新的compileMethod()是用C或C ++编写的本机挂钩),有许多关于挂钩compileMethod()的教程和开源项目。但是,由于MassLogger挂钩compileMethod()的独特方式,我们无法使用许多上述项目实现的vtable挂钩技术。除挂钩外,JITM还包括.NET加载程序。此加载程序首先加载本机挂钩DLL(jitmhook.dll)并安装该挂钩。然后,加载程序加载MassLogger有效载荷并执行其入口点。这将导致MassLogger的模块初始化代码执行并安装自己的挂钩,但是钩住的是jitmhook.dll代码而不是原始的compileMethod()。执行MassLogger入口点的另一种方法是调用RuntimeHelpers.PrepareMethod()API强制JIT编译器在所有方法上运行。这种方法更好,因为它避免了运行恶意软件,并且有可能恢复样本自然代码路径中未调用的方法。但是,这需要额外的工作来强制所有方法正确编译。

要加载和恢复MassLogger方法,请运行以下命令(图14):

1597547278177837.png

运行jitm的命令

超时结束后,你应该看到在当前目录中创建的文件jitm.log和jitm.json。 jitm.json包含从Bin-123.exe恢复的所有方法的方法令牌,方法体标头和MSIL。剩下要做的唯一事情就是重建.NET元数据,以便我们可以执行静态分析。

1597547287112393.png

示例jitm.json

重新构建

由于解密的方法体标头和MSIL可能不适合原始.NET程序集,因此最简单的方法是向MassLogger添加一个新部分和对应的标头。JITM还包括以下Python 2.7帮助程序脚本以自动执行此过程:Script\pydnet.py。

通过将方法体标头和每种方法的MSIL添加到新的PE部分(如XXX所示),我们可以轻松地解析.NET元数据并修复每种方法的RVA以指向新部分中的正确方法体。不幸的是,我没有找到任何Python库来轻松解析.NET元数据和MethodDef表。因此,JITM还包括部分实现的.NET元数据解析器:Script\pydnet.py。该脚本使用pefile和vivisect模块,并将PE文件解析到Method表,以提取所有方法及其关联的RVA。

1597547309613318.png在添加名为FLARE的其他部分之前和之后的Bin-123.exe

最后,为了将所有内容捆绑在一起,JITM提供了Script \ fix_assembly.py来执行以下任务:

1.将在jitm.json中恢复的每个方法的方法体标头和MSIL写入一个名为“section.bin”的临时二进制文件中,同时记住相关的方法标记和到section.bin中的偏移量。

2.使用addsection.py将section.bin添加到Bin-123.exe中,并将数据保存到新文件中,例如Bin-123.fixed.exe。

3.使用pydnet.py解析Bin-123.fixed.exe并更新MethodDef表中每个方法项的RVA字段,以将指向正确的RVA指向新部分。

最终结果是部分重构的.NE程序集,尽管要使程序集正确运行还需要进行其他工作,但足以进行静态分析以了解恶意软件的高级功能。

让我们看一下重构的方法0x0600043E,该方法为恶意软件配置实现了解密逻辑。与原始MSIL相比,重建的MSIL可以显示出,该恶意软件在CBC模式下使用带有PKCS7填充的AES-256。通过动态分析和静态分析的组合,我们还可以轻松地将密钥标识为“ Vewgbprxvhvjktmyxofjvpzgazqszaoo”,并将IV用作作为参数传递的Base64编码缓冲区的一部分。

1597547330121708.png

在修复程序集之前和之后的方法0x0600043

有了这些知识,我们就可以编写一个简单的工具来解密恶意软件配置并恢复所有HBI和NBI(图18)。


1597547350168455.png

1597547357847305.png

解密的配置

总结

使用JIT编译器挂钩替换MSIL是一项强大的反分析技术,安全人员几乎无法对其进行静态分析。尽管这项技术不是新技术,但我还没有看到很多.NET恶意软件使用它。希望通过这篇文章,分析人员将拥有分析MassLogger或使用类似技术的任何恶意软件。


本文转载自:嘶吼

作者:gejigeji

原文地址:https://www.4hou.com/posts/o7lj

本文翻译自:https://www.fireeye.com/blog/threat-research/2020/08/bypassing-masslogger-anti-analysis-man-in-the-middle-approach.html

0
现金券
0
兑换券
立即领取
领取成功