翻译——在windbg的帮助下解剖.NET

[toc]

.NET是Microsoft生态系统中重要的组成部分,为不同语言和硬件平台之间的互操作性提供了共享框架。 许多Microsoft工具(如PowerShell)和其他管理功能依赖于.NET平台的功能。 这使得.NET成为恶意软件开发人员的诱人语言。 因此,恶意软件研究人员还必须熟悉该语言,并具有分析在平台上运行的恶意软件的必要技能。

诸如ILSpy之类的分析工具有助于研究人员从应用程序中反编译代码,但不能用于自动分析许多样本。 在本文中,我们将介绍如何使用WinDBG来使用Microsoft提供的SOS扩展来分析.NET应用程序。

本文主要介绍:
如何通过在.NET API中插入断点来分析PowerShell脚本。
如何轻松创建一个脚本,以便在分析加壳的逻辑后自动解压缩.NET样本。

此外,您可以在我们的github上下载一个Python脚本(基于WinDBG pykd扩展),以自动分析.NET。 这个脚本也将在文章中描述。

SOS 扩展

SOS Extension为WinDBG提供.NET支持。 扩展提供了丰富的命令集; 在本文中,我们将仅介绍一些对分析有用的内容。

首先,SOS扩展名不在同一个库中,这取决于你所使用的.NET的版本。 在我们能够使用SOS扩展之前,我们必须将库加载到WinDBG中。

对于.NET 4,扩展名位于CLR.dll中,可以加载以下命令:

1
.loadby sos clr

而.NET 2 和 3的话:

1
.loadby sos mscorwks

以下是本文中使用的命令:
!bpmd:这个命令用于在.NET代码中设置断点。 该命令有两个参数。 第一个参数是函数所在的.NET dll,第二个是函数名。
!CLRStack:此命令显示CLR堆栈内容。 识别.NET函数的参数很有用。
!DumpObj:此命令显示有关参数中指定的特定对象的信息。

在本文中,这3个命令将用于在特定的.NET API中创建断点,以获取传递给API的参数,并显示内容。

案例1:POWERSHELL分析

很少有人意识到PowerShell可以使用.NET框架。 通过检查.NET API使用情况,我们可以轻松自动化PowerShell分析。

示例1:启动过程API

在本例中,我们将分析以下PowerShell代码:

1
PS> start-process notepad.exe

执行此任务时,PowerShell将调用Process.Start 这个API。 所以,我们可以在这个API设置之断点:

1
2
3
4
5
6
7
8
9
0:011> .loadby sos clr

0:011> !bpmd system.dll System.Diagnostics.Process.Start
Found 6 methods in module 00007fff97581000...
breakpoint: bp 00007FFF977C96D9 [System.Diagnostics.Process.Start(System.Diagnostics.ProcessStartInfo)]
breakpoint: bp 00007FFF97E8057D [System.Diagnostics.Process.Start(System.String, System.String)]
breakpoint: bp 00007FFF97E80539 [System.Diagnostics.Process.Start(System.String)]
breakpoint: bp 00007FFF97E804B6 [System.Diagnostics.Process.Start(System.String, System.String, breakpoint: bp 00007FFF977C72DA [System.Diagnostics.Process.Start()]
Adding pending breakpoints...

断点设置完成后,我们可以输入命令“g”来执行PowerShell脚本。 执行Start-Process时,WinDBG将会停止:

1
2
3
Breakpoint 0 hit
System_ni+0x2496d9:
00007fff`977c96d9 488d0d08711e00 lea rcx,[System_ni+0x4307e8 (00007fff`979b07e8)]

CLRStack命令会显示Process.Start API的参数。 在我们的例子中,参数是一个System.Diagnostics.ProcessStartInfo对象。

1
2
3
4
5
6
0:008> !CLRStack -p
OS Thread Id: 0x2d34 (8)
Child SP IP Call Site
000000a7f9ace700 00007fff977c96d9 System.Diagnostics.Process.Start(System.Diagnostics.ProcessStartInfo)
PARAMETERS:
startInfo (<CLR reg>) = 0x0000028cbd5faa18

最后,DumpObj命令显示此对象的内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
0:008> !DumpObj /d 0000028cbd5faa18
Name: System.Diagnostics.ProcessStartInfo
MethodTable: 00007fff979ae380
EEClass: 00007fff975e29f0
Size: 144(0x90) bytes
File: C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System\v4.0_4.0.0.0__b77a5c561934e089\System.dll
Fields:
MT Field Offset Type VT Attr Value Name
00007fff9897de98 40027f3 8 System.String 0 instance 28cbd5fde18 fileName
00007fff9897de98 40027f4 10 System.String 0 instance 000 arguments
[...redacted...]
00007fff9897ad70 4002806 58 System.WeakReference 0 instance 000 weakParentProces
00007fff979af0a0 4002807 60 ....StringDictionary 0 instance 000 environmentVaria
00007fff982e5ec0 4002808 68 ...tring, mscorlib]] 0 instance 000 environment

ProcessStartInfo对象的第一个字段是filename,类型是System.String对象。 我们可以使用DumpObj检索对象的内容:

1
2
3
4
5
6
7
0:008> !DumpObj /d 0000028cbd5fde18
Name: System.String
MethodTable: 00007fff9897de98
EEClass: 00007fff982d35f0
Size: 88(0x58) bytes
File: C:\WINDOWS\Microsoft.Net\assembly\GAC_64\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
String: C:\WINDOWS\system32\notepad.exe

那我们就可以看到我们运行的程序的路径了

示例2:DownloadFile API

代码如下:

1
2
PS> $a = New-Object System.Net.WebClient
PS> $a.DownloadFile("http://blog.talosintelligence.com/","c:\users\lucifer\demo.txt")

此代码的目的是将文件下载并存储在硬盘驱动器上。 恶意软件经常使用这种技术来下载payload。

如果是这种情况,我们必须在DownloadFile API上放上断点,然后按’g’来执行PowerShelI:

1
2
3
4
5
6
7
8
9
0:008> .loadby sos clr
0:008> !bpmd system.dll System.Net.WebClient.DownloadFile
Found 2 methods in module 00007fff97581000...
MethodDesc = 00007fff976c1fe8
MethodDesc = 00007fff976c1ff8
Setting breakpoint: bp 00007FFF97DCAE0C [System.Net.WebClient.DownloadFile(System.Uri, System.String)]
Setting breakpoint: bp 00007FFF97DCADBC [System.Net.WebClient.DownloadFile(System.String, System.String)]
Adding pending breakpoints…
0:008> g

之后便会断下来

1
2
3
Breakpoint 7 hit
System_ni+0x84adbc:
00007fff`97dcadbc 4885d2 test rdx,rdx

在这种情况下,我们可以像以前一样使用CLRStack和DumpObj命令。 还可以手动,我们直接从寄存器获取值(第一个字符串位于RDX + 0xC中,第二个字符串位于R8 + 0xC中):

1
2
3
4
5
6
0:008> du rdx+c
0000028c`bd53f13c "http://blog.talosintelligence.co"
0000028c`bd53f17c "m/"
0:008> du r8+c
0000028c`bd53f3b4 "c:\users\lucifer\desktop\demo.tx"
0000028c`bd53f3f4 "t"

截图如下:

译者注: 32位的话如下(05083c88是第三个参数,对应上面的r8储存第3个参数)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
0:023> du edx+8
05083a24 "http://blog.talosintelligence.co"
05083a64 "m/"
0:023> dd esp
09e8ebac 05095ef8 09e8ebe4 07f35dbb 05083c88
09e8ebbc 04ab53b8 050724b0 00000000 00000000
09e8ebcc 00000000 00000000 0509328c 05095ef8
09e8ebdc 05091d40 09e8ec30 09e8ec40 66a8e96b
09e8ebec 05083c88 05083a1c 05073df4 00000000
09e8ebfc 05095fbc 05095f40 050938b4 050b6bb4
09e8ec0c 05095f50 0509328c 05073df4 05095ef8
09e8ec1c 00000000 00000000 00000000 00000000
0:023> du 05083c88+8
05083c90 "c:\users\lucifer\demo.txt"

案例2: .NET UNPACK

Talos每天处理包装恶意软件样本。 我们最近确定了一个包装的.NET可执行文件,它正在叙利亚政府网站上托管:http://www.syriantax.gov.sy/css/igfxCUIService.exe 最初我们想知道这是否是有针对性的攻击的一部分。 经过进一步的研究,我们现在认为,这个网站已被入侵,并被用来提供这种恶意软件。 恶意软件原来是njRAT,这是一个知名的远程管理工具,已经存在了很多年。 找到njRAT并不是特别有趣,我们认为写一个博客文章,看看njRAT的脱壳过程就相对有趣了。

因此,此用例将解释如何使用静态分析来处理未知的.NET包装程序。 我们还将介绍使用WinDBG的动态分析,以及如何创建一个WinDBG脚本,以自动化这种类型的打包程序的拆包过程。

静态分析

我们通过使用de4dot来开始我们对恶意软件样本的分析,因为它可以快速识别已知的壳。 这是一个开源分析平台(吾爱破解工具包2.0就有)

1
2
3
4
5
6
7
8
9
10
11
12
C:> de4dot-x64.exe -d -r c:\to_test

de4dot v3.1.41592.3405 Copyright (C) 2011-2015 [email protected]

Latest version and source code: https://github.com/0xd4d/de4dot
Detected Unknown Obfuscator (c:\to_test\21acd3457c1a58[...]1bfeeaf3c0cd79bfe)
Detected Unknown Obfuscator (c:\to_test\344ce133363f09[...]bbd2257a298484051)
Detected Unknown Obfuscator (c:\to_test\45c695e610d786[...]af65408fb6080300f)
Detected Unknown Obfuscator (c:\to_test\61653b2811fb7c[...]04f9807a775f25773)
Detected Unknown Obfuscator (c:\to_test\ac7bd77245bdf2[...]aee4d06563f057ca6)
Detected Unknown Obfuscator (c:\to_test\b607e87acdcb2e[...]d30eddddffbeec320)
Detected Unknown Obfuscator (c:\to_test\e93c0aed6bbb4a[...]6c2efe65942f83504)

在本节中,我们还将使用ILSpy———开源.NET反编译器。

XOR 变种

样本SHA-256:45c695e610d78178ec5ca6f4e1993afacf4e435b566cd2caf65408fb6080300f

入口点是ob6eaGgG7Bht6B35c0.G9puOotvCiNCkEEPD9.XHh0nc9pu,我们可以使用ILSpy获得此信息:

首先,程序解码一个Base64编码的字符串(变量G9puOotvCiNCkEEPD9.EHQI8XHAH)。 该解码的字符串将传递给函数G9puOotvCiNCkEEPD9.vovYCiNCk的第二个参数,最终作为XOR(异或) 的key:

通过向下看,我们可以通过查看ILSpy的反编译结果看到异或操作,就是这个符号”^”,我们可以确定这是XOR(异或)操作。

最后,函数的输出作为参数传递给函数Assembly.Load。 此函数用于加载.NET二进制文件。

AES 变种

样本SHA-256:21acd3457c1a589e117988fe0456e50ed627f051a97ccd11bfeeaf3c0cd79bfe

这个变种中包含的逻辑是相同的,但不是使用XOR混淆,而是使用AES加密(也称为Rijndael):
(译者注:可以看到下面用到了RijndaelManaged类)

最后,使用Assembly.Load函数将解密的数据加载到内存中。

共同点

虽然每个分析的样本使用的算法是不同的,但编码与加密的逻辑是完全相同的。 如果我们可以导出在Assembly.Load函数的参数中找到的字节数组变量,那么我们就可以获得脱壳了的恶意软件(译者注:因为调用Assembly.Load的时候,传递给该函数的参数就是解密后了程序数据)。

用windbg动态调试

先看 .NET 4

为了动态分析 .NET 4样本,我们需要获得WinDBG SOS扩展。 此扩展允许使用Microsoft Debugger进行 .NET 4调试。(译者注:这个SOS.dll就在 .NET目录啊)

让我们执行恶意软件吧…

第一步是在加载CLRJIT库时停下来:

1
2
3
4
5
6
7
8
9
10
0:000> sxe ld clrjit
0:000> g
(dc0.1594): Unknown exception - code 04242420 (first chance)
ModLoad: 70fc0000 71040000 C:\Windows\Microsoft.NET\Framework\v4.0.30319\clrjit.dll
eax=00000000 ebx=00800000 ecx=00000000 edx=00000000 esi=00000000 edi=0044e000
eip=7736e85c esp=006fe4fc ebp=006fe558 iopl=0 nv up ei pl nz na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000206

ntdll!NtMapViewOfSection+0xc:
7736e85c c22800 ret 28h

然后,我们加载WinDBG SOS扩展以对.NET应用程序进行分析:
(译者注:不应该是这个么:.loadby sos clr

1
0:000> .load "C:\\Psscor4\\x86\\x86\\psscor4.dll"

我们现在有了与.NET调试相关的新的WinDBG命令。 我们可以基于.NET API使用设置断点。 在这种情况下,我们对Assembly.Load API感兴趣:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
0:000> !bpmd mscorlib.dll System.Reflection.Assembly.Load
Found 8 methods in module 71041000...
MethodDesc = 71100b50
MethodDesc = 71100b7c
MethodDesc = 71100b88
MethodDesc = 71100b94
MethodDesc = 71100bb8
MethodDesc = 71100bd0
MethodDesc = 71100bdc
MethodDesc = 71100be8
Setting breakpoint: bp 71B29095 [System.Reflection.Assembly.Load(Byte[], Byte[], System.Security.Policy.Evidence)]
Setting breakpoint: bp 71B29037 [System.Reflection.Assembly.Load(Byte[], Byte[], System.Security.SecurityContextSource)]
Setting breakpoint: bp 71B28FFF [System.Reflection.Assembly.Load(Byte[], Byte[])]
Setting breakpoint: bp 71B28F9C [System.Reflection.Assembly.Load(Byte[])]
Setting breakpoint: bp 71395949 [System.Reflection.Assembly.Load(System.Reflection.AssemblyName, System.Security.Policy.Evidence)]
Setting breakpoint: bp 713F3479 [System.Reflection.Assembly.Load(System.Reflection.AssemblyName)]
Setting breakpoint: bp 71B28F3D [System.Reflection.Assembly.Load(System.String, System.Security.Policy.Evidence)]
Setting breakpoint: bp 713C880D [System.Reflection.Assembly.Load(System.String)]
Adding pending breakpoints...

(目前在扩展中有一个bug,需要执行两次命令)

当执行Assembly.Load函数时,调试器就暂停下来了:

1
2
3
4
5
6
7
8
9
0:000> g

Breakpoint 3 hit
eax=00000000 ebx=006ff2dc ecx=026b30b8 edx=0000000a esi=026b30b8 edi=006ff250
eip=71b28f9c esp=006ff210 ebp=006ff218 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246

mscorlib_ni+0xae8f9c:
71b28f9c e80368fdff call mscorlib_ni+0xabf7a4 (71aff7a4)

显然,我们可以使用CLRStack和DumpObj命令来完全获得前面用例中提到的参数。 在这个例子中,我们只会使用寄存器的内容——传递给Assembly.Load的参数可以通过esp寄存器找到:

1
2
3
4
5
6
7
8
9
10
0:000> dp esp

006ff210 00000000 026b30b8 006ff238 009504ae
006ff220 00000000 00000000 00000000 00000000
006ff230 00000000 00000000 006ff244 7240ea56
006ff240 00a149a8 006ff298 724293ef 006ff2dc
006ff250 006ff288 725b24b0 006ff3b0 724293a8
006ff260 ecebc740 006ff404 006ff370 006ff324
006ff270 7246e611 006ff2dc 00000000 ecebc740
006ff280 006ff250 006ff370 006ff424 725b0890

栈中的第二个值是指向字节数组的指针:0x026b30b8。

1
2
3
4
5
6
7
0:000> dp 026b30b8 
026b30b8 71504448 00005e00 00905a4d 00000003
026b30c8 00000004 0000ffff 000000b8 00000000

0:000> db 026b30b8+8 L16
026b30c0 4d 5a 90 00 03 00 00 00-04 00 00 00 ff ff 00 00 MZ..............
026b30d0 b8 00 00 00 00 00 ......

第二个参数0x5e00是字节数组的大小,之后我们可以看到以MZ:0x4d 0x5a开头的PE文件的文件头。 我们现在可以直接从WinDBG中转储被加密代码解密完成的样本:

1
.writemem C:\\unpacked_sample.exe 026b30b8+8 L00005e00

再看 .NET 2 、3

使用.NET版本2和3编译的恶意软件的动态分析过程是一样的。 不同的是参数如何传递给Assembly.Load API。 在这种情况下,参数不使用堆栈,而是存储在ECX寄存器中:

1
2
3
4
5
6
7
0:000> dp ecx
024ba0b8 71504448 00005e00 00905a4d 00000003
024ba0c8 00000004 0000ffff 000000b8 00000000

0:000> db ecx+8 L16
024ba0c0 4d 5a 90 00 03 00 00 00-04 00 00 00 ff ff 00 00 MZ..............
024ba0d0 b8 00 00 00 00 00

自动脱壳

得益于上述分析,我们可以创建一个通用的脱壳器。 您可以在附录2中找到.NET版本2,3和4的WinDBG脚本。

可以使用以下语法调用此脚本:

1
"c:\Program Files (x86)\Windows Kits\10\Debuggers\x86\cdb.exe" -c "$$>a< C:\unpack.script C:\unpacked_sample.exe" "c:\sample.exe"

以下是脚本执行的截图:

python 脚本

您可以从我们的github中下载一个python脚本来自动化的.NET分析。 该脚本需要pykd扩展才能在WinDBG中执行python。 该脚本使用本文前面提到的SOS命令,目的是获得更好的输出。 配置位于脚本的开头:

bp_list变量包含断点列表。 在该示例中,该脚本将在3个 .NET API(System.Diagnotics.Process.Start,System.Net.WebClient.Download.File和Sysyem.Reflection.Assembly.Load)上下断点。 3个函数的参数将显示在WinDBG中。

如果dump_byte_array变量设置为1,脚本将自动转储分析函数(其中有断点的位置)的参数中提供的字节数组。 转储将位于dump_byte_array_path目录中。

该脚本允许文本或json输出。 本文中的示例输出文本,但我们可以通过将JsonDebug变量设置为“True”来切换json。

示例1

下面是当调用Assembly.Load函数时脚本的输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
0:000> .loadby sos clr
0:000> .load pykd.dll
0:000> !py C:\Users\lucifer\NET_plugin.py
[.NET plugin] Beginning, setting breakpoints...
[.NET plugin] breakpoint: mscorlib.dll System.Reflection.Assembly.Load mscorlib_ni+0xb4fa65
[.NET plugin] breakpoint: mscorlib.dll System.Reflection.Assembly.Load mscorlib_ni+0xb4fa07
[.NET plugin] breakpoint: mscorlib.dll System.Reflection.Assembly.Load mscorlib_ni+0xb4f9cf
[.NET plugin] breakpoint: mscorlib.dll System.Reflection.Assembly.Load mscorlib_ni+0xb4f96c
[.NET plugin] breakpoint: mscorlib.dll System.Reflection.Assembly.Load mscorlib_ni+0x38a5a1
[.NET plugin] breakpoint: mscorlib.dll System.Reflection.Assembly.Load mscorlib_ni+0x3bda7d
[.NET plugin] breakpoint: mscorlib.dll System.Reflection.Assembly.Load mscorlib_ni+0xb4f90d
[.NET plugin] breakpoint: mscorlib.dll System.Reflection.Assembly.Load mscorlib_ni+0x3968dd
[.NET plugin] Let's go...

[.NET plugin] Breakpoint: System.Reflection.Assembly.Load(Byte[])
[.NET plugin] Argument 0: rawAssembly
[.NET plugin] !DumpObj /d 0x02f67e04
Name: System.Byte[]
MethodTable: 6b5f60f8
EEClass: 6b190878
Size: 5644(0x160c) bytes
Array: Rank 1, Number of elements 5632, Type Byte (Print Array)
Content: [email protected]........!..L.!This program cannot
Fields:
None

[.NET plugin] let's dump 0x02f67e04+8 Size:5644
.writemem c:\users\lucifer\Desktop\dump_1496942775_0x02f67e04_5644.dmp 0x02f67e04+8 L5644

Assembly.Load参数中的字节数组的内容将自动存储在c:\users\lucifer\Desktop\dump_1496942775_0x02f67e04_5644.dmp

示例2

以下是执行start-process的PowerShell脚本上的脚本输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
[.NET plugin] Breakpoint: System.Diagnostics.Process.Start(System.Diagnostics.ProcessStartInfo)
[.NET plugin] Argument 0: startInfo
[.NET plugin] !DumpObj /d 0x000001ad173cdb68
Name: System.Diagnostics.ProcessStartInfo
MethodTable: 00007ffd7e3ee798
EEClass: 00007ffd7e0229f0
Size: 144(0x90) bytes
File: C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System\v4.0_4.0.0.0__b77a5c561934e089\System.dll
Fields:
MT Field Offset Type VT Attr Value Name
07ffd69e969d0 40027fa 8 System.String 0 instance 01ad173d0f20 fileName
07ffd69e969d0 40027fb 10 System.String 0 instance 00000000000 arguments
07ffd69e969d0 40027fc 18 System.String 0 instance 1ad173d4bf8 directory
07ffd69e969d0 40027fd 20 System.String 0 instance 000000000000 verb
07ffd7e3c2a50 40027fe 78 System.Int32 1 instance 0 windowStyle
07ffd69ea1fb0 40027ff 7c System.Boolean 1 instance 0 errorDialog
07ffd69eafc48 4002800 70 System.IntPtr 1 instance 0 errorDialogPare
07ffd69ea1fb0 4002801 7d System.Boolean 1 instance 1 useShellExecut
07ffd69e969d0 4002802 28 System.String 0 instance 000000000000 userName
07ffd69e969d0 4002803 30 System.String 0 instance 000000000000 domain
07ffd69ea4068 4002804 38 ...rity.SecureString 0 instance 00000000 password
07ffd69e969d0 4002805 40 System.String 0 instance 0 passwordInClearText
07ffd69ea1fb0 4002806 7e System.Boolean 1 instance, 1 loadUserProfile
07ffd69ea1fb0 4002807 7f System.Boolean 1 instance 0 redirectStandar
07ffd69ea1fb0 4002808 80 System.Boolean 1 instance 0 redirectStandard
07ffd69ea1fb0 4002809 81 System.Boolean 1 instance 0 redirectStandard
07ffd69e9b048 400280a 48 System.Text.Encoding 0 instance 0 standardOutp
07ffd69e9b048 400280b 50 System.Text.Encoding 0 instance 0 standardErro
07ffd69ea1fb0 400280c 82 System.Boolean 1 instance 0 createNoWindow
07ffd69eadec8 400280d 58 System.WeakReference 0 instance 0000 weakParentPr
07ffd7e3ef4b8 400280e 60 ....StringDictionary 0 instance 0000 envVariables
07ffd697a69f0 400280f 68 ...tring, mscorlib]] 0 instance 0000 environment

[.NET plugin] !DumpObj /d 000001ad173d0f20
Name: System.String
MethodTable: 00007ffd69e969d0
EEClass: 00007ffd697950e0
Size: 82(0x52) bytes
File: C:\WINDOWS\Microsoft.Net\assembly\GAC_64\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
String: C:\WINDOWS\system32\calc.exe

该脚本显示有趣字段的参数和内容(在该示例中为fileName字符串)。(译者注:可以看到C:\WINDOWS\system32\calc.exe)

示例3

以下是在Powershell中使用DownloadFile API时脚本的输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
[.NET plugin] Breakpoint: System.Net.WebClient.DownloadFile(System.Uri, System.String)
[.NET plugin] Argument 1: address
[.NET plugin] !DumpObj /d 0x000001ad17315e78
Name: System.Uri
MethodTable: 00007ffd7e3f4cf0
EEClass: 00007ffd7dfc5fd0
Size: 72(0x48) bytes
File: C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System\v4.0_4.0.0.0__b77a5c561934e089\System.dll
Fields:
MT Field Offset Type VT Attr Value Name
07ffd69e969d0 400040b 8 System.String 0 instance 000001ad172c5ea8 m_String
07ffd69e969d0 400040c 10 System.String 0 instance 000000000 m_originalUnico
07ffd7e3f51d8 400040d 18 System.UriParser 0 instance 001ad17032b40 m_Syntax
07ffd69e969d0 400040e 20 System.String 0 instance 00000000000 m_DnsSafeHost
07ffd7e3c2788 400040f 30 System.UInt64 1 instance 37615763456 m_Flags
07ffd7e3f5590 4000410 28 System.Uri+UriInfo 0 instance 01ad17315f00 m_Info
07ffd69ea1fb0 4000411 38 System.Boolean 1 instance 0 m_iriParsing
07ffd69e969d0 40003fb 220 System.String 0 shared static UriSchemeFile
07ffd69e969d0 40003fc 228 System.String 0 shared static UriSchemeFtp
07ffd69e969d0 40003fd 230 System.String 0 shared static UriSchemeGoph
07ffd69e969d0 40003fe 238 System.String 0 shared static UriSchemeHttp
07ffd69e969d0 40003ff 240 System.String 0 shared static UriSchemeHttps
07ffd69e969d0 4000400 248 System.String 0 shared static UriSchemeWs
07ffd69e969d0 4000401 250 System.String 0 shared static UriSchemeWss
07ffd69e969d0 4000402 258 System.String 0 shared static UriSchemeMail
07ffd69e969d0 4000403 260 System.String 0 shared static UriSchemeNews
07ffd69e969d0 4000404 268 System.String 0 shared static UriSchemeNntp
07ffd69e969d0 4000405 270 System.String 0 shared static UriSchemeNet
07ffd69e969d0 4000406 278 System.String 0 shared static UriSchemeNetP
07ffd69e969d0 4000407 280 System.String 0 shared static SchemeDelimit
07ffd7e3b4bd0 4000412 288 ...etSecurityManager 0 static s_ManagerRef
07ffd69e96fb0 4000413 290 System.Object 0 shared static s_IntranetLock
07ffd69ea1fb0 4000414 9c4 System.Boolean 1 shared static s_ConfigInitia
07ffd69ea1fb0 4000415 9c5 System.Boolean 1 shared static s_ConfigInitia
07ffd7e3afef8 4000416 9c0 System.Int32 1 shared static s_IdnScope
07ffd69ea1fb0 4000417 9c6 System.Boolean 1 shared static s_IriParsing
07ffd69e96fb0 4000418 298 System.Object 0 shared static s_initLock
07ffd69e97b20 400041c 2a0 System.Char[] 0 shared static HexLowerChars
07ffd69e97b20 400041d 2a8 System.Char[] 0 shared static _WSchars

[.NET plugin] !DumpObj /d 000001ad172c5ea8
Name: System.String
MethodTable: 00007ffd69e969d0
EEClass: 00007ffd697950e0
Size: 94(0x5e) bytes
File: C:\WINDOWS\Microsoft.Net\assembly\GAC_64\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
String: http://blog.talosintelligence.com/
Fields:
MT Field Offset Type VT Attr Value Name
07ffd69e99310 400026f 8 System.Int32 1 instance 34 m_stringLength
07ffd69e97b88 400027 c System.Char 1 instance 68 m_firstChar
07ffd69e969d0 4000274 90 System.String 0 shared static Empty

[.NET plugin] Argument 2: fileName
[.NET plugin] !DumpObj /d 0x000001ad172c61c8
Name: System.String
MethodTable: 00007ffd69e969d0
EEClass: 00007ffd697950e0
Size: 92(0x5c) bytes
File: C:\WINDOWS\Microsoft.Net\assembly\GAC_64\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
String: c:\users\lucifer\desktop\demo.txt
Fields:
MT Field Offset Type VT Attr Value Name
07ffd69e99310 400026f 8 System.Int32 1 instance 33 m_stringLength
07ffd69e97b88 4000270 c System.Char 1 instance 63 m_firstChar
07ffd69e969d0 4000274 90 System.String 0 shared static Empty

第一个参数是System.URI对象。 对象自动解析,相关内容显示在WinDBG中。 在这种情况下,显示第一个字段(字符串m_string)。 此字符串包含URL。 第二个参数是也显示的字符串(译者注:下载到的位置)。

示例4

这是JSON启用的输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
0:020> .loadby sos clr
0:020> .load pykd
0:020> !py c:\Users\lucifer\DotNETPlugin.py
{
"date": 1500306926,
"bp": "System.Diagnostics.Process.Start(System.Diagnostics.ProcessStartInfo)",
"arguments": {
"0": {
"fields": {
"0": {
"Type": "System.String",
"Name": "fileName",
"string": "C:\\WINDOWS\\system32\\calc.exe"
},
"1": {
"Type": "System.String",
"Name": "arguments",
"string": ""
},
"2": {
"Type": "System.String",
"Name": "directory",
"string": "C:\\Users\\lucifer"
},
"3": {
"Type": "System.String",
"Name": "verb",
"string": ""
},
[...redacted...]
"20": {
"Type": "....StringDictionary",
"Name": "environmentVariables",
"value": "0000000000000000"
},
"21": {
"Type": "...tring,",
"Name": "environment",
"value": "instance"
}
},
"name": "startInfo",
"offset": "0x0000025c1c572170"
}
}
}

总结

WinDBG是由Microsoft提供的一个非常强大的工具。 如果不熟悉windbg的语法和界面意味你将忽略这么一个这么好的恶意软件分析工具。 有了正确的扩展,它可以很容易地用于分析.NET等类似代码。

我们希望这篇文章引导您的好奇心,并且您将在下一次分析.NET等类是代码时考虑WinDBG。

附录

加壳样本 SHA256

  • 21acd3457c1a589e117988fe0456e50ed627f051a97ccd11bfeeaf3c0cd79bfe
  • 344ce133363f005346210611d5abd2513934a32739bc6e1bbd2257a298484051
  • 45c695e610d78178ec5ca6f4e1993afacf4e435b566cd2caf65408fb6080300f
  • 61653b2811fb7c672584d00417cbc1a56c8372331f1913104f9807a775f25773
  • ac7bd77245bdf284d36ce1f9e2cb6a21d2dbd38aa1964dbaee4d06563f057ca6
  • b607e87acdcb2ef0f102298decc57ca3ea20fabbf02375fd30eddddffbeec320
  • e93c0aed6bbb4af734403e02d399c124f2d07f8e701fb716c2efe65942f83504

脱壳样本 SHA256

  • 35dee9106e4521e5adf295cc945355d72eb359d610230142e5dd4adda9678dee
  • b5ce02ee3dfccf28e86f737a6dde85e9d30ff0549ec611d115a1d575b5291c2e
  • d9a732dcf87764a87f17c95466f557fac33f041ac6f244dba006ba155d8e9aea
  • fe068ce56b258762c10cc66525c309e79026c0e44103ca9b223c51382722cb09

WinDBG 脚本

.NET4之前

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
sxe ld mscorjit
g
.loadby sos mscorwks
!bpmd mscorlib.dll System.Reflection.Assembly.Load
.echo "Weird bug... bp twice..."
!bpmd mscorlib.dll System.Reflection.Assembly.Load
g
r $t1 = ecx
.printf "Byte array: ";r $t1
r $t2 = poi($t1+4)
.printf "Size: ";r $t2
db $t1+8 L$t2
.echo "dump in the file: ${$arg1}"
.writemem ${$arg1} $t1+8 L$t2
.kill
q

.NET4

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
sxe ld clrjit
g
.load "C:\\Psscor4\\x86\\x86\\psscor4.dll"
!bpmd mscorlib.dll System.Reflection.Assembly.Load
.echo "Weird bug... bp twice..."
!bpmd mscorlib.dll System.Reflection.Assembly.Load
g
r $t1 = poi(esp+4)
.printf "Byte array: ";r $t1
r $t2 = poi($t1+4)
.printf "Size: ";r $t2
db $t1+8 L$t2
.echo "dump in the file: ${$arg1}"
.writemem ${$arg1} $t1+8 L$t2
.kill
q
自愿打赏专区