恶意软件可以由许多组件组成。通常我们会遇到充当恶意下载程序的宏和脚本。一些功能也可以通过与位置无关的代码实现 - 所谓的 shellcode。但是当谈到更复杂的元素或核心模块时,我们几乎理所当然地认为它将是一个本机 Windows 可执行格式的 PE 文件。
原因很简单:在 PE 文件中提供复杂功能比在 shellcode 中提供复杂功能要容易得多。PE 格式具有明确定义的结构,允许更大的灵活性。有一些标题可以定义应该加载哪些导入,以及应该如何应用重定位的位置。这是为 Windows 编译应用程序时生成的默认格式,然后 Windows Loader 使用其结构来加载和执行这些应用程序。即使恶意软件作者编写自定义加载器,它们也主要用于 PE 格式。
但是,有时就会发现异常。上次,当分析到与 Hidden Bee 相关的有效负载(由 Underminer 漏洞利用工具包丢弃)时,就发现了一些不寻常的东西。丢弃了两个没有遵循 PE 格式的有效负载。然而,他们的结构看起来组织得很好,比经常通常遇到的 shellcode 片段更复杂。所以决定仔细研究一下,发现这个恶意软件的作者实际上是按照一致的结构创建了自己的可执行格式。
概观
第一个有效载荷:b3eb576e02849218867caefaa0412ccd(扩展名为.wasm,模仿 Web Assembly)是一个加载器,下载并解压缩一个 Cabinet 文件:
第二个有效载荷:11310b509f8bf86daa5577758e9d1eb5,从 Cabinet 拆包:
现在我们首先可以看到,与大多数 shellcode 相反,它不是从代码开始,而是从一些标头开始。比较两个模块,我们可以看到在两种情况下头部具有相同的结构。
头
先仔细研究了解读标题中特定字段的含义。
第一个 DWORD:0x10000301 在两者中是相同的。然而没有找到与该模块中任何部分对应的数字。因此,就假设它是一个神奇的数字,它构成了这种格式的标识符。
接下来,两个 WORD 是与加载导入相关的元素的偏移量。第一个(0x18)指向 DLL 列表。第二个块(0x60)起初看起来更神秘。当加载在 IDA 中模块的时候,可以理解它的含义。就可以看到对这些字段的交叉引用:
我们看到它们被用作 IAT - 它们应该填充导入函数的地址:
下一个值是 DWORD(0x2A62)。如果继续在 IDA 中遵循它,我们会看到它导致新函数的开始:
任何其他函数都不会引用此函数,因此我们可以怀疑它是程序的入口点。
下一个值(0x509C)的含义很容易猜到,因为它与完整模块的大小相同。
然后,我们有标题的最后两个 DWORD。第二个 DWORD(0x4D78)导致结构与 PE 的重定位非常相似。我们可以猜测它必须是模块的重定位表,而前一个 DWORD 指定它的大小。
这就是如何重建完整的标题:
入口
正如从“头”部分了解到的那样,DLL 的列表从偏移量 0x18 开始。可以看到每个 DLL 的名称前面都有一个数字:
这些数字与 DLL 名称不对应:在两个不同的模块中,相同的 DLL 分配了不同的编号。但如果总结所有数字,发现它们的总和与 IAT 中的 DWORD 数相同。因此,就可以做出有根据的猜测,这些数字是指定从特定 DLL 导入的函数数量。
还可以将其描述为以下结构(未指定名称的长度):
然后,IAT 作为 DWORD 列表:
恶意软件中常见的是,当函数的名称不是作为显式字符串给出时,它们是通过校验和导入的。在这种情况下也是如此。猜测用于计算校验和的适当函数可能更困难。幸运的是,在 loader 组件中找到了它:
知道使用函数的名称配对了适当的校验和:
一旦检索到函数的地址,就将其存储在 IAT 中以代替校验和。
重新定位
创建重定位表很简单。它由 DWORD 列表组成,这些 DWORD 标识代码中的位置的偏移量,应该在其中添加模块已加载的基础。如果不应用重定位,模块将崩溃(因此,它不像典型的 shellcode 那样与位置无关)。
与 PE 格式的比较
虽然 PE 格式很复杂,但有各种标题,这个标题只包含必需品。这里通常完全省略了通常存储在 PE 头中的大多数信息。
你可以在这里看到 Ange Albertini 可视化的 PE 格式。
将其与当前分析格式的可视化进行比较:
静态分析
还可以将此代码作为一大堆原始代码加载到 IDA 中。但是,还缺少重要信息。由于文件不遵循 PE 结构,并且其导入表是非标准的,因此是很难理解在哪个偏移量处进行哪些 API 调用。为了解决这个问题,创建了一个工具,将哈希解析为函数名,并生成一个 TAG 文件来标记每个函数地址将被填充的偏移量。
可以使用 IFL 插件将这些标记加载到 IDA 中 :
标记所有 API 函数后,更容易理解模块执行的操作。例如,在这里,我们可以看到它将与 C2 服务器建立连接:
动态分析
此格式是自定义的,因此典型的分析工具不支持此格式。但是,在了解它之后,你就可以编写自己的工具,例如头文件和加载器的解析器,它将有助于运行此格式并动态分析它。
与 PE 相比,该模块没有任何部分。因此,需要使用 RWX(读写-执行)访问将其加载到连续的内存区域中。遍历重定位列表,还将添加模块的基础值添加到列出的地址。然后,还必须通过哈希解析导入的函数并填充 thunk 中的地址。准备好后,只需要跳到模块的入口点即可。
总结
这里描述的元素非常简单:它们作为完整恶意软件包的第一阶段,下载其他部分并将其注入进程。然而,他们的作者展示了一些创造力,决定发明一种比完全成熟的 PE 更简单的自定义格式,但比典型的 shellcode 更进一步。
与独立的 shellcode 相比,这样的模块不是自给自足的,不能以微不足道的方式加载,但必须先进行解析。鉴于格式是自定义的,现有工具不支持。对于恶意软件分析师来说,这就是编程技巧派上用场的地方。
幸运的是,完全自定义格式在恶意软件并不常见。通常,作者严重依赖现有格式,不时腐蚀或定制 PE 标题的选定部分。
by hasherezade
Commentaires