Cisco Japan Blog

WinDBG の力で .NET を解明

7 min read



投稿者:Paul Rascagnerespopup_iconWarren Mercerpopup_icon
はじめに

異なる言語およびハードウェア プラットフォーム間に相互運用のための共有フレームワークを提供する Microsoft エコシステムのコンポーネントとして、.NET はその重要性をますます高めています。PowerShell やその他の管理機能など、多くの Microsoft のツールは、その機能を .NET プラットフォームに依存しています。こういった状況の中、.NET がマルウェア開発者にとっても魅力的な言語となっているのは言うまでもありません。したがって、マルウェアの研究者は、言語にも精通するとともに、このプラットフォームで動作する悪意あるソフトウェアの分析に必要なスキルを備えている必要があります。

ILSpypopup_icon などの分析ツールは、アプリケーションからコードを逆コンパイルすることでは研究者の役に立ちますが、これらを使って多くのサンプルの分析を自動化することはできません。この記事では、Microsoft が提供する SOSpopup_icon 拡張を使用して .NET アプリケーションを WinDBG で分析する方法について、検証します。

この記事では次のことを説明します。

  • .NET API にブレークポイントを挿入して PowerShell スクリプトを分析する方法。
  • パッカーのロジックの分析に従って .NET サンプルを自動的に展開するスクリプトを簡単に作成する方法。

さらに、Cisco Talos の githubpopup_icon で Python スクリプト(WinDBG pykd 拡張popup_iconに基づく)をダウンロードして .NET の分析を自動化することができます。 このスクリプトは本記事にも記載します。
SOS 拡張

SOSpopup_icon 拡張により、WinDBG で .NET がサポートされるようになります。この拡張では豊富なコマンド セットが提供されます。この記事では分析に利用できるコマンドを取り上げますが、それらはそのごく一部です。

まず、SOS 拡張を配置する場所は、使用する .NET のバージョンによって異なっています。SOS 拡張を使用するには、WinDBG にライブラリをロードする必要があります。

.NET 4 の場合、拡張は CLR.dll に配置することで、次のコマンドでロードできます。

.loadby sos clr

.NET 2 および 3 では、SOS 拡張は mscorwks ライブラリに配置します。

.loadby sos mscorwks

この記事では次のようなコマンドを使用します。

  • !bpmd:このコマンドは、マネージ コード(.NET)にブレークポイントを挿入するために使用します。このコマンドは 2 つの引数を使用します。最初の引数は .NET dll で、関数の場所を示します。2 番目の引数は関数名です。
  • !CLRStack:このコマンドは、CLR スタックの内容を表示します。.NET 関数の引数を特定するのに役立ちます。
  • !DumpObj:このコマンドは、引数で指定された特定のオブジェクトに関する情報を表示します。

この記事ではこれら 3 つのコマンドを使用して、特定の .NET API 内にブレークポイントを作成し、API に渡された引数を取得し、内容を表示します。

使用例 #1:PowerShell 分析

あまり知られていませんが、PowerShell は .NET Framework を使用できます。.NET API の使用状況を調べることにより、PowerShell 分析を簡単に自動化できます。

例1:Start-Process API

この例では、次の PowerShell コードを分析します。

PS> start-process notepad.exe

このタスクを実行する際、PowerShell は Process.Start() API. を使用します。これにより、(SOS 拡張をロードした後)この API にブレークポイントを挿入できます。これは、コード実行を意図的に停止する箇所です。

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...

ブレークポイントを設定したら、PowerShell スクリプトを実行するコマンド「g」を入力できます。Start-Process プロセスが実行されると WinDBG は停止します。

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

CLRStack コマンドは、Process.Start API に提供された引数を表示します。この例での引数は、System.Diagnostics.ProcessStartInfo オブジェクトです。

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 コマンドで、オブジェクトの内容を表示します。

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 を使用してオブジェクトの内容を取得できます。

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

filename 文字列がメモ帳のバイナリへのパスになっていることがわかります。

例2:DownloadFile API

この 2 番目の例では、次のコードを分析します。

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

このコードの目的は、ファイルをダウンロードしてハード ドライブに保存することです。これは、マルウェアがペイロードをダウンロードするためによく使用している手法です。

この例では、DownloadFile AP にブレークポイントを配置し、「g」を押して PowerShelI を実行する必要があります。

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

API が実行されると、WinDBG は PowerShell スクリプトの実行を自動的に停止します。

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

ここで、前のケースとまったく同じように CLRStack および DumpObj コマンドを使用することも可能です。今回はそうせずに、レジスタから直接、値を取得します(Microsoft のメモリ ロケーションの標準により、最初の文字列は RDX+0xC にあり、2 番目の文字列は R8+0xC にあります)。

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"

実行画面の一部を以下に示します。

使用例 #2:.NET の展開

Talos では日常的に、圧縮されたマルウェア サンプルを扱っています。先頃、シリア政府の Web サイトでホストされていた、圧縮 .NET 実行可能ファイル(http://www[.]syriantax[.]gov[.]sy/css/igfxCUIService.exe)を特定しました。最初に気になったのは、これが標的型攻撃の一部なのかどうかという点でした。その後の調査の結果、Web サイトが感染してこのマルウェアの配信に利用されたことが確信できました。このマルウェアは njRAT であると判明しました。njRAT は、長年広範囲に配信されてよく知られている、パブリックのリモート監視ツールです。njRAT の発見自体はそれほど興味深いことではないのですが、njRAT の展開方法についてブログ記事を書くのは有益であろうと考えました。

そういうわけで、この使用例では、静的分析を使用して未知の .NET パッカーに対処する方法を説明します。また、WinDBG を使用した動的分析とともに、このタイプのパッカーの展開プロセスを自動化する WinDBG スクリプトの作成方法も取り上げます。

静的分析

このマルウェア サンプルの分析では、最初に de4dot を使用しました。これを使うと既知のパッカーを迅速に特定できるからです。これはオープン ソースの分析プラットフォームで、ここpopup_icon から入手できます。

C:> de4dot-x64.exe -d -r c:\to_test
de4dot v3.1.41592.3405 Copyright (C) 2011-2015 de4dot@gmail.com
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)

このセクションでは、オープン ソースの .NET デコンパイラ、ILSpy も使用します。これはこちらpopup_icon から入手できます。

XOR 化された亜種

サンプル:45c695e610d78178ec5ca6f4e1993afacf4e435b566cd2caf65408fb6080300f

パッカーのエントリ ポイントは ob6eaGgG7Bht6B35c0.G9puOotvCiNCkEEPD9.XHh0nc9pu です。この情報は、ILSpy を利用して展開することで特定できます。

 

パッカーは最初に、Base64 でエンコードされた文字列をデコードします(変数 G9puOotvCiNCkEEPD9.EHQI8XHAH)。デコードしたこの文字列を、XOR キーとして機能する第 2 の引数とともに、関数 G9puOotvCiNCkEEPD9.vovYCiNCk() に渡します。

 

出力をスクロールし、逆コンパイルされた .NET 実行可能ファイルを見ていくと、ILSpy による XOR の動作を特定できます。使用されている「^」関数を見ることで、これが XOR 動作であることがわかります。

 

最後に、関数の出力が、引数として関数 Assembly.Load()popup_icon に渡されます。 この関数は、.NET バイナリをロードするのに使用されます。

 

Assembly.Load() に渡される引数はバイト配列で、Windows バイナリ(PE32)を含みます。この例では、展開されたマルウェアはバイト配列となっています。

AES 亜種

サンプル:21acd3457c1a589e117988fe0456e50ed627f051a97ccd11bfeeaf3c0cd79bfe

パッカーのこの亜種に含まれるロジックは同じですが、XOR 難読化を使用する代わりに AES 暗号化(別名 Rijndael)が使用されています。

 

最後に、Assembly.Load() 関数を使用して、復号されたデータがメモリにロードされます。

共通点

分析した各サンプルでは、使用されるアルゴリズムは異なっていますが(エンコーディングと暗号化)、ロジックはまったく同じです。Assembly.Load() 関数の引数で見つかったバイト配列型変数をダンプできれば、展開済みのマルウェアが得られます。

WinDBG による動的分析

.NET バージョン 4

.NET 4 のサンプルの動的分析を行うには、こちらpopup_iconから入手できる WinDBG SOS 拡張を取得する必要があります。この拡張により、Microsoft Debugger を使用した.NET 4 のデバッグが可能になります。

では、圧縮されたマルウェアを実行します。

最初の手順は、CLRJIT ライブラリがロードされたらデバッガの実行を停止することです。

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 アプリケーション(管理されるアプリケーション)の分析を行います。

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

これで、.NET デバッグに関連する新しい WinDBG コマンドを利用できるようになりました。ブレークポイントは、.NET API の使用状況に基づいて設定できます。この例では、Assembly.Load() API に着目します。

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...

(現時点では、拡張にバグがあり、コマンドを 2 回実行する必要があります)。

Assembly.Load() 関数が実行されると、デバッガはマルウェアの実行を停止します。

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() に渡される引数は、Stack (ESP) で入手できます。

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

スタックの 2 番目の値は、バイト配列へのポインタです(0x026b30b8)。

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                                ......

2 番目の引数、0x5e00 は、バイト配列のサイズです(赤字)。その後には、MZ で始まる PE ファイルのファイル ヘッダー、0x4d 0x5a が続きます(青字。ただし、データがリトル エンディアン形式で保存されているため、反転)。これで、WinDBG 内から直接、展開済みサンプルをダンプできます。

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

.NET バージョン 2 および 3

.NET バージョン 2 と 3 でコンパイルされたマルウェアの動的分析プロセスは同じです。ただし、Assembly.Load() API に引数を渡す方法が違います。ここでは、引数はスタックを使用せず、ECX レジスタに保存されます。

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 スクリプトを記載しています。

このスクリプトは、次の構文を使用して呼び出すことができます。

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

スクリプト実行のスクリーンショットを以下に示します。

Python スクリプト

.NET 分析を自動化する Python スクリプトを Cisco Talos の github リポジトリpopup_icon からダウンロードできます。 WinDBG で Python を実行できるようにするには、スクリプトに pykd 拡張popup_iconが必要です。スクリプトは、この記事で前述した SOS コマンドを使用します。その目的は、より良い出力を得ることです。スクリプトの先頭で、設定を記述しています。

dump_byte_array=1
dump_byte_array_path="c:\\path\\to\\directory\\"

bp_list = [ ["system.dll", "System.Diagnostics.Process.Start"],
            ["system.dll", "System.Net.WebClient.DownloadFile"],
            ["mscorlib.dll", "System.Reflection.Assembly.Load"]
          ]

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 関数が呼び出された際のスクリプトの出力を以下に示します。

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:     
MZ......................@...............................................!..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 スクリプトのスクリプト出力を以下に示します。

[.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 文字列)の内容を表示しています。

例 3:

Powershell で DownloadFile API が使用された際のスクリプトの出力を以下に示します。

[.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                s  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 が含まれています。2 番目の引数は文字列であり、これも表示されています。

例 4:

JSON 形式のスクリプト出力を以下に示します(start-process 実行)。

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 が提供する非常に強力なツールです。構文やインターフェイスになじみがないため、マルウェア分析ツールとしてあまり認識されていないかもしれません。適切な拡張機能があれば、マネージ コード(.NET)の分析に簡単に利用できるのです。

この記事が皆さんの好奇心を刺激して、今後、.NET などのマネージ コードの分析が必要となった場合には、WinDBG を検討していただけると幸いです。

付録

IOC

圧縮されたサンプル SHA256

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

展開されたサンプル SHA256

  • 35dee9106e4521e5adf295cc945355d72eb359d610230142e5dd4adda9678dee
  • b5ce02ee3dfccf28e86f737a6dde85e9d30ff0549ec611d115a1d575b5291c2e
  • d9a732dcf87764a87f17c95466f557fac33f041ac6f244dba006ba155d8e9aea
  • fe068ce56b258762c10cc66525c309e79026c0e44103ca9b223c51382722cb09

WinDBG スクリプト

.NET 4 よりも前

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

.NET 4

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

 

本稿は 2017年7月19日に Talos Grouppopup_icon のブログに投稿された「Unravelling .NET with the Help of WinDBGpopup_icon」の抄訳です。

 

コメントを書く