2024-11
WiryJMPer 的混淆纠结
我们征服WiryJMPer的混淆故事始于一个看似ABBC Coin钱包的简单二进制文件。我们认为该文件有些可疑,因为文件大小是正常文件的三倍,文件中的字符串对应于来自SoftwareOK的其他软件WinBin2Iso版本316。ABBC Coin最初名为Alibaba Coin,且与阿里巴巴集团无关是一种山寨币,是很多基于区块链的加密货币之一。而WinBin2Iso则是一款将各种CD/DVD/Bluray媒体的二进制映像转换为ISO格式的软件。行为分析显示,伪装成ABBC Coin钱包的二进制文件实际上是一个下载器,接下来我们将其称为WiryJMPer。WiryJMPer在两个无害的二进制文件之间隐藏了Netwire有效负载。
有效负载的第一阶段看似是一个普通的WinBin2Iso二进制文件,但具有异常大的rsrc部分。通常处理窗口消息的JMP指令跳转到rsrc部分,控制流随之开始。这导致WinBin2Iso窗口短暂无响应后被ABBC Coin钱包窗口替换。这个窗口在启动时始终显示,因此是感染的一个良好指示。
尽管这种功能本身并不新颖,也没有采用沙箱规避技术,但这种混淆方式足够独特,引起了我们的注意。控制流的混淆和低级代码抽象的结合使得对该恶意软件工作流程的分析相当繁琐。此外,病毒检测网站VirusTotal的低检测率截至2019年7月8日,仅6个样本被检测出也给我们提供了一个很好的理由,去翻找工具箱进行分析。此外,在分析过程中,我们发现混淆的加载器在RC4密钥调度期间也可能利用了一个自定义的基于栈的虚拟机,这更引起了我们的兴趣。
基于上述原因以及总体低出现率,此次分析将重点放在混淆本身上。由此产生的副作用和恶意软件的功能将主要作为附录提及。
高级概述
恶意软件从修补跳转的 WinBin2Iso 二进制文件开始。这个跳转指向rsrc部分,在那里一个加载器被解密并加载到内存中,同时进行重定位。
原始的WinBin2Iso二进制文件,注意跳转返回到GetMessageA
修补后的WinBin2Iso二进制文件,注意跳转指向完全不同的地址范围
该加载器负责所有操作,直到控制流被重定向到Netwire。它加载ntdlldll到内存中,解密一些辅助数据,如LNK文件名或RC4解密密码。随后,它解密Netwire,同样加载到内存中,以及作为“诱饵”的二进制文件在本例中为ABBC Coin钱包,该文件被保存到磁盘上。
WiryJMPer工作流程的高级概述
vpn梯子随后,控制流被重定向到Netwire。Netwire基本上是一个标准的远程访问工具,没有进行重大修改。Netwire的指挥控制CampC地址为46166160[]158,遗憾的是,在我们进行分析时该地址没有响应。
加载器还试图通过将原始二进制文件复制到APPDATAabbcdriverexe并在启动文件夹中创建指向该二进制文件的LNK文件来实现持久化。
访问加载器
紧接着修补跳转的代码由许多小代码块组成,这些小代码块通过跳转构成的网络连接。这使得在没有任何预处理的情况下,静态分析该二进制文件变得相当困难。在模拟器的帮助下,我们能够重建调用图并连接其中的一些代码块。尽管这种方法仍然导致结果相对不理想,但它使我们能够删除一些无用的指令并简化控制流。注意,这种混淆还利用了所谓的不透明谓词,即带有一个永远不会被达到的分支的条件跳转。若要保持静态分析,我们必须采用符号执行或其他启发式方法来解析这些跳转。然而,出于分析的目的,一个简单的启发式方法即条件分支后面的代码应指向“合理”的指令已足够。
几个连接的连续代码块,注意条件跳转的预设结果。
紧急检查沙箱行为日志显示有两次调用RtlDecompressBuffer,第二次调用解压了我们的Netwire有效负载。不幸的是,压缩缓冲区的地址指向在执行过程中分配的地址,这意味着我们无法在静态分析中直接访问它们,因此我们必须深入挖掘以检索有效负载。
找到ESI的内容很容易,我们只需在ESP下挖掘即可。注意多余的跳转已被删除。
第一次RtlDecompressBuffer调用通过rep movsb从0x01e6d44f加载数据。此指令将数据从二进制文件自身加载到已分配的内存VirtualAlloc。由于用户空间调试器可能被样本检测到,我们决定使用内核调试器,这将我们引导至位于0x004f9000的偏移量,该地址指向rsrc部分中的数据。对数据是否与RtlDecompressBuffer的输入匹配进行简要检查后,我们发现这些数据是加密的。幸运的是,解密循环位于rep movsb后面的代码块中。
解密例程。
在0x1e75ad6处的异或操作中,我们发现密钥隐藏在EBX中,巧合的是在我们rep movsb之前的0x01e727a5加载。此外,我们看到该密钥是静态的,因此整个缓冲区以0xca81c398进行异或操作。然后该缓冲区通过前面提到的RtlDecompressedBuffer进行解压。
这解答了我们第一个问题:压缩加载器位于哪里?现在剩下两个未解答的问题:我们在哪可以找到Netwire有效负载,及在最后启动的“诱饵”二进制文件ABBC Coin钱包位于何处?
加载器重定位
在执行流程从rsrc部分进入加载器之前,由于VirtualAlloc的非确定性质以及加载器中绝对跳转的存在,需要进行重定位。重定位的实现方式与PE文件中的标准重定位相同。重定位表的结构如下:
要修补的指令的偏移是这样计算的:relochigh reloclow[i] amp 0xfff allocatedmemorybase
不意外的是,原始基址 (0x10000000) 与VirtualAlloc分配的内存基址之间的差已被加入到宿主32位值中。reloclow的列表被遍历,直到指针指向重定位表的下一行。
访问有效负载
由于Netwire有效负载是通过RtlDecompressBuffer解压的,因此使用我们之前使用的技巧应该能轻松跟踪。不过,由于还有其他内容被释放或提取,我们改为对VirtualAlloc设置了断点。
第一个VirtualAlloc是在解压后的加载器之后,它为通过NtReadFile加载的ntdlldll准备空间。这成为一种相当常见的反调试技巧,因为调试器通常不能识别对此手动复制DLL的调用。
接下来,这里变得更有趣。第二个VirtualAlloc为一些内部配置LNK文件名,稍后将使用的RC4密钥分配空间,这些空间位于rsrc部分0x01e632ac。同样,这段二进制块是用简单的异或密码加密的,密钥为0x98c381ca。从现在开始,我们将假设所有内容都使用硬编码密钥的异或密码加密,这可能简化分析,因为从明文密文对中恢复密钥是很直接的。
然而,我们的假设在下一个有效负载0x004899B6上立即失败,其解密循环包含以下指令请注意,我们已删除多余的跳转:
这是RC4密码的密钥流生成循环。这给我们带来了另一个问题我们希望找到用于实例化0x09458248的表的密钥。不幸的是,RC4密码的密钥调度被基于栈的虚拟机混淆,因此我们又开始调试。为了说明后面的过程,我们将重新提及RC4密钥调度算法见下面的Python实现。
Python中的RC4密钥调度
这次,我们在虚拟机中用于写入表的指令上设置了断点,这使我们能够恢复寄存器的状态,从而访问S[i]和S[j]的地址。通过减去RC4表的基址,我们获得了相应的索引i和j。现在,通过RC4密钥调度轻松迭代并从索引j恢复密钥字节。更具体地说,我们得到了一串序列:
105 120 89 105 77 70 82 88 56 83 78 70 68 74 112 72104 85 82 121105 120 89 105 77 70 82 88 56 83 7870 68 74 112 72 104 85 82 121105 120 89 105 77 7082 88 56 83 78 70 68 74 112 72 104
这个序列显然有重复加粗部分,而这些值似乎落在可打印的ASCII范围内。基于这些观察,我们恢复了RC4密钥:ixYiMFRX8SNFDJpHhURy。这个密钥是从第二个VirtualAlloc中的缓冲区包含内部配置加载到加载器中的。该数据块的解密产生了ABBC Coin钱包的二进制文件。
有趣的是,提取的二进制文件与真实的ABBC Coin钱包相匹配版本391。随后,该二进制文件将被提取到临时目录,并以名匹配以下正则表达式:[AZaz]{5}exe命名,并立即执行。我们推测这旨在掩盖原始二进制文件的真实目的。如果原始二进制文件已经在APPDATA目录中,则不会执行此有效负载。碰巧原始二进制文件被复制到此位置,因此如果在启动时运行通过前面提到的LNK文件,则不会启动ABBC Coin钱包。
我们将在后面的虚拟机部分讨论虚拟机本身,但我们会简单查看其其中一个部分,称为调度器实际上WiryJMPer还有三个类似的调度器。你可能会注意到一个细节:
在RC4密钥调度期间使用的四个虚拟机调度器之一。突出部分之间的对应关系并非偶然。
回想一下,加载器所在的内存是在调用RtlDecompressBuffer之前由VirtualAlloc分配的,而虚拟机在加载器中。因此,地址0x0034AF8C的字节不可能一开始就使用此偏移量设置,而必须在运行时进行修补,因为加载器的地址范围在此之前并不知道。这也表明了重定位的必要性。
现在,我们只需在样本中找到Netwire。我们使用相同的方法找到为Netwire分配内存的VirtualAlloc,我们在写入时设置了断点,然后找到了Netwire被复制的位置rsrc部分内的偏移0x01e585b6。幸好它是用相同密钥的RC4加密的。解密产生了一个UPX打包的Netwire,从而结束了有效负载的提取,并确认了沙箱行为日志中提示的其存在。
虚拟机
在分析过程中,我们发现RC4密钥调度是实现为一个自定义的基于栈的虚拟机。
常见基于栈的虚拟机示意图
每个虚拟机的第一部分是调度器。WiryJMPer的虚拟机使用四个不同的switch dispatchers。一般来说,switch dispatchers通过switch语句或类似构造来跳转到对应所需指令的代码。指令通过跳转表被转换为特定地址或偏移,并推入栈中,然后调用ret指令,尽管也可以有其他构造。典型的设置如下面的图所示,但请注意,WiryJMPer的虚拟机更复杂,因为它有多个调度器,栈溢出检查并不跟随所有指令。
虚拟机的另一个调度器。
由于每个虚拟指令必须确定性地到达调度器或退出虚拟机,我们通过跟踪指向这些调度器的引用来尝试触及这些指令。由于该虚拟机是基于栈的,因此通过栈而不是寄存器传递参数。通用寄存器主要用于局部操作,尽管某些寄存器如ESI指令指针、ESP栈帧基址或EBP栈顶对虚拟机具有全局影响。
这些指令与例如WProtect虚拟机相似算数运算、跳转、内存/栈读取和写入等。我们假设一种典型的设置,其中在初始化期间寄存器被放置到栈中的特定位置,然后在退出时从这些位置加载。由于指令数量较多且存在一些重复,我们只提供几个例子。
从栈中读取DWORD
跳转
将DWORD写入内存
相似文件
我们发现了一些文件采用相同的方案修补过的WinBin2Iso二进制文件,用于解压Netwire和另一个二进制文件。例如,诱饵有效负载指向了一个不同但合法的Bitcoin Core安装程序版本0180。其他则指向Yoroi钱包、Neon钱包、ZecWallet、DigiByte Core、OWallet、Verge核心钱包等。共同的特征似乎是加密货币钱包。
结论
尽管该恶意软件的功能并不创新,但它成功地在某段时间内避开了检测,这很可能归因于混淆和相对较低的出现率。所采用的混淆可以通过行为分析轻松克服,然而,在掩盖恶意软件操作的细节方面,它做得相当不错。诱饵显示多个无关窗口的慢速设置可能对技术用户显得可疑,而提供“诱饵”二进制文件可能足以安抚普通用户。
事件指示IoC
仓库 https//githubcom/avast/ioc/tree/master/WiryJMPerSHA256列表 https//githubcom/avast/ioc/blob/master/WiryJMPer/samplessha256分析样本
文件 SHA256WiryJMPer f1963b44a9c887f02f6e9574aea863974be57a033600047b8e0911f9dbcb9914ABBC Coin钱包 7477159797a7f06e3c153662bfef624d056e64b552f455fe53e80f0afb0a1860Netwire有效负载 6daa1ff03fdbbb58b1f41d2f7dc550ee97fc5b957252b7f1703c81c50b3d406f
Netwire有效负载CampC 46166160[]158
相似样本
SHA256
6e1cfde5278d03c6df204d845d165673df89cfd047f4eda97816ee351115a6524b7bd8581b85bb33d4748aaeda6a3e5ec8f930751688ffb6854522411f3ad27581740ad6a3f0e5c1698132524e0d4b23b4f4773761bca68fdaef33748ef299e3880de7e64c0678a38ef6964b6ff2f48e426449426b58a516556285421c223374125cf6b01deb86df16e0961021a57b28177b8efedc6bf4f617bef940cf4b9d7404a92a7e171b583c40cee9d2760b20fa8324e45f3938f7d41f48065829103ebd4a3d3e85d09074ed1e1de5e48c97c4e42fbcb3cfb44b213c0224ffb191dcd1c20631ace562e077814c7788b9fe10c865579a29cf180654658f30ab38387a13e3d1457c238b99ca8904693551f92310acae561c68c20a8caafe3391d927d7618eea855c2b53419dcd81e677520d4e55d41cb5ce2933f550edd6520cce15da93fc
标记为 分析, 混淆, 逆向分析
分享 XFacebook