DotDumper: DotNet ベースのマルウェアを自動的に解凍

特定のファイルを信頼性の高い方法で自動的に検出および分類することは、多くの場合、マルウェア分析の究極の目標と見なされています。そこに至るまでは数多くの試練と苦難が待ち受けています。そのため、それらを軽減するようなシステムが作られることは高く評価されています。バイナリをターゲットとする DotNet に関して言えば、新しいオープンソース ツール DotDumper は、(メモリ内の) アクティビティのログ記録、興味深いメモリ セグメントのダンプ、特定のサンプルからの特性の抽出など、重要な手順のいくつかを支援することを目的としています。このブログでは、今回発表したDotDumper の使用法等についてお伝えします。このツールとそのソース コードは、こちらから入手が可能です。


DotDumperの必要性

手短に言えば、手動での開梱は退屈なプロセスであり、アナリストは莫大な時間を費やすことになります。難読化されたバイナリは、アナリストが特定のファイルをアンパックするために費やさなければならない時間をさらに増やします。これを拡大する場合、組織は、おそらくスケーラブルなサンドボックスと組み合わせて、マルウェアを毎日分析する多数のアナリストを必要とします。失われた貴重な時間は、広く拡散しているありふれた一般的なマルウェアに対してではなく、注意を払う必要のあるキャンペーンやサンプルを掘り下げて新しい脅威を発見することに費やすべきです。結局、アナリストは干し草の山から数本の針を探すような状況に陥ってしまいます。

では、DotDumper はどのような違いをもたらすのでしょうか? DotDumper を介して DotNet ベースのマルウェア サンプルを実行すると、3 つの形式 (人間が判読できるプレーンテキスト、JSON、および XML) の重要な、コンテキスト化された、一般的な関数呼び出しのログ ファイルと、有用なメモリ内セグメントからのコピーが提供されます。そのため、アナリストは関数呼び出しログをざっと見ることができます。さらに、ダンプされたファイルをスキャンして分類することで、マルウェアのサンプルとそこに含まれるデータについてさらに洞察を得ることができます。これにより、トリアージおよびインシデント対応プロセスに不可欠な時間が短縮され、SOC アナリストおよび研究者の時間が解放され、より高度な分析ニーズに対応できます。


特徴

コンテキスト化関数呼び出しとその結果をログに記録してダンプするために、DotDumper はリフレクション フックとマネージ フックを組み合わせて使用​​します。これらはすべて純粋な C# で記述されています。以下では、圧縮された AgentTesla スティーラー サンプルの DotDumper の結果の抜粋と組み合わせて、主要な機能を強調し、詳しく説明します。そのハッシュは以下のとおりです。

SHA-256 b7512e6b8e9517024afdecc9e97121319e7dad2539eb21a79428257401e5558d
SHA-1 c10e48ee1f802f730f41f3d11ae9d7bcc649080c
MD-5 23541daadb154f1f59119952e7232d6b

 


コマンド行インターフェースの使用

DotDumper には、さまざまな引数を使用して、コマンドライン インターフェイスからアクセスできます。下の画像は、ヘルプ メニューを示しています。すべての引数について説明するわけではなく、最もよく使用される引数について説明することに注意してください。

コマンドライン インターフェイス メニュー図1:コマンドライン インターフェイス メニュー

特定のサンプルを実行するための最小要件は、ファイル名またはファイル パスと共に「-file」引数を指定することです。フル パスが指定されている場合は、それが使用されます。ファイル名が指定されている場合、現在の作業ディレクトリと、DotDumper の実行可能場所のフォルダーがチェックされます。

ディレクトリ名が指定されていない場合、「-log」フォルダ名は、拡張子 (ある場合) を除いたサンプルのファイル名と同じに設定されます。このフォルダーは、DotDumper が存在するのと同じフォルダーにあり、ログとダンプされたファイルが保存されます。

ライブラリ、またはバイナリへの代替エントリ ポイントの場合、「-overrideEntry true」を使用してエントリ ポイントをオーバーライドする必要があります。さらに、「-fqcn My.NameSpace.MyClass」を使用して名前空間を含む完全修飾クラスを提供する必要があります。これにより、選択するクラスが DotDumper に通知されます。これは、指定された関数名 (「-functionName MyFunction」を使用) が取得される場所です。

選択した関数に引数が必要な場合は、「-argc」を使用して引数の数と必要な引数の数を指定する必要があります。引数の型と値は、「string|myValue int|9」として提供されます。値にスペースが使用されている場合、コマンドライン インターフェイスの引数は引用符で囲み、単一の引数として渡されるようにする必要があることに注意してください。

「-raceTime」や「-deprecated」など、あまり使用されないその他のオプションは、デフォルト設定で安全ですが、DotNet フレームワークの変更により、将来的に調整が必要になる可能性があります。それらは現在、コマンドライン インターフェイスで公開されており、必要に応じて変更を簡単に許可できます。古いバージョンの DotDumper を使用している場合でも変更できます。


ロギングとダンプ

ロギングとダンプは、DotDumper の 2 つのコア機能です。分析にかかる時間を最小限に抑えるには、ログによってアナリストにコンテキストを提供する必要があります。これは、ログに記録された関数呼び出しごとに次の情報をアナリストに提供することによって行われます。

  • 関数の呼び出し元に基づくスタック トレース
  • 名前、バージョン、暗号化ハッシュなど、呼び出し元のアセンブリ オブジェクトに関する情報
  • 元のサンプルではない場合に呼び出し元となる親アセンブリ
  • 関数の引数の型、名前、および値
  • 関数の戻り値の型、名前、および値 (存在する場合)
  • 指定された関数呼び出しに対応する、ディスクにダンプされるファイルのリスト

ダンプされた各ファイルのファイル名は、ファイルの SHA-256 ハッシュと同じであることに注意してください。

上記を明確にするために、ログの抜粋を以下に示します。抜粋は、前述の AgentTesla サンプルの詳細を示しており、DotNet の Assembly.Load 関数を使用して第 2 ステージをロードしています。

インターセプトされた Assembly.Load(byte[] rawAssembly) 関数呼び出しのログ図2: 傍受された Assembly.Load(byte[] rawAssembly) 関数呼び出しのログ

最初に、元の関数の戻り値の型、名前、および引数と共に、ローカル システム時刻が指定されます。次に、サンプルのメイン関数がコンストラクターにつながり、コンポーネントを初期化し、2 つのカスタム関数を呼び出すスタック トレースが示されます。Assembly.Load 関数は、「NavigationLib.TaskEightBestOil.GGGGGGGGGGGGGGGGGGGG(String str)」内から呼び出されました。これにより、アナリストが関心のある場合にこの呼び出しの周りのコードを見つけるためのコンテキストが提供されます。

次に、アセンブリ呼び出し順序に関する情報が提供されます。ロードされるステージが多いほど、コールがどのステージを介して行われたかを確認することがより複雑になります。通常、あるステージが次のステージをロードすると予想されますが、場合によっては、後のステージが非線形の順序で前のステージを利用します。さらに、元のアセンブリに関する情報が提供され、分析者のデータがさらに充実します。

次に、親ハッシュが与えられます。ステージの親は、この例ではまだ存在しない前のステージです。新しくロードされたステージは、このステージを親として持ちます。これにより、アナリストはイベントをより簡単に関連付けることができます。

最後に、関数の戻り値の型と値が、フックされた関数に渡される各引数の型、名前、および値と共に格納されます。変数のサイズが 100 バイトを超える場合は、代わりにディスクに格納されます。次に、値を表示するのではなく、ファイルを参照するためにログに参照が挿入されます。一部の配列はサイズが数千のインデックスであるため、ログの印刷で問題が発生しないようにしきい値が設定されています。


リフレクション

Microsoft のドキュメントによると、リフレクションは「[…] アセンブリ、モジュール、および型をカプセル化するオブジェクトを提供する」と最もよく要約されています。つまり、これにより、マルウェア サンプルから DotNet クラスと関数を動的に作成して呼び出すことができます。DotDumper には、DotNet Framework ベースである限り、アナリストが実行可能ファイルとライブラリの両方を読み込んで分析できるリフレクティブ ローダーが含まれています。

ローダーを利用するには、コマンドライン インターフェイスでエントリ ポイントを上書きし、特定のファイル内でクラス (存在する名前空間を含む) と関数名を指定する必要があります。オプションで、すべてのネイティブ型とその配列に対して、指定された関数に引数を提供できます。ネイティブ型の例としては、int、string、char、および int[]、string[]、char[] などの配列があります。すべての引数は、タイプと値の両方を指定するコマンドライン インターフェイスを介して提供されます。

エントリ ポイントをオーバーライドしないと、デフォルトのエントリ ポイントが使用されます。デフォルトでは、サンプルが引数なしで実行されたかのように、空の文字列配列がサンプルのメイン関数に渡されます。

さらに、ローダーはリフレクションを使用して、次の段階で特定のクラスの特定の関数を呼び出すことがよくあります。場合によっては、後でリソースを復号化するために使用される引数も渡されます。前述の AgentTesla サンプルでは、​​まさにこのシナリオが実行されます。以下に示すように、DotDumper の呼び出し関連のフックは、これらの発生をログに記録します。

第 2 ステージを呼び出すためのインターセプトされたフック図3: 2 番目のステージを呼び出すためにインターセプトされたフック

最初の行の関数名は、DotNet Framework の内部関数ではなく、第 2 段階の特定の関数への呼び出しです。3 つの引数の型と名前は、関数のシグネチャに記載されています。それらの値は、関数の引数情報セクションにあります。これにより、アナリストは、指定された引数の値を使用してカスタム ローダーで 2 番目のステージをロードできます。または、以前にダンプされたステージをロードして引数を提供することにより、DotDumper を使用してこれを実行することもできます。


マネージド フック

管理されたフックに入る前に、フックがどのように機能するかを理解する必要があります。ここで考慮すべき主な変数が 2 つあります。ターゲット関数と、フックと呼ばれる被制御関数です。簡単に言えば、ターゲット関数 (つまり、Assembly.Load) のメモリは、代わりにフックにジャンプするように変更されます。そのため、プログラムの実行フローは迂回されます。その後、フックは任意のアクションを実行し、必要に応じて元の関数を呼び出します。その後、必要に応じて戻り値と共に実行を呼び出し元に返します。以下の図は、このプロセスを示しています。

フックのインストールと使用のフローチャート図4: フックのインストールと使用のフローチャート

管理フックとは何かを理解するには、フックとは何かを知ることが不可欠です。マネージ コードは、DotNet ランタイムや Java の仮想マシンなど、管理された仮想環境で実行されます。マネージ関数が存在するメモリ アドレスの取得は、Cなどのアンマネージ言語とは異なります。両方の関数の正しいメモリ アドレスを取得したら、安全でない C# を使用してメモリに直接アクセスし、DotNet の相互運用性サービスを使用してネイティブ Windows API 機能を呼び出すことで、フックを設定できます。


簡単に拡張可能

DotDumper は外部依存関係のない純粋な C# で記述されているため、Visual Studio を使用してフレームワークを簡単に拡張できます。コードは、このブログ、GitHub、クラス、関数、およびソース コードのインラインで文書化されています。これを明確な命名スキームと組み合わせることで、誰でもツールを必要に応じて変更できるため、ツールを理解するために費やす必要のある時間と労力を最小限に抑えることができます。代わりに、開発者とアナリストはツールの改善に集中することができます。


既知のツールとの違い

DotDumper の目的と機能が明確であるため、 ILSpydnSpyExde4dot、またはpe-sieveなどの既知の公開ツールと重複しているように見えるかもしれません。あるツールが別のツールよりも優れていると宣言する意図はありませんが、ツールの違いについては注意してください。

DotDumper の目標は、DotNet ターゲット サンプルからの重要でコンテキスト化された一般的な関数呼び出しをログに記録してダンプすることです。ILSpy は DotNet の逆アセンブラーおよび逆コンパイラーですが、ファイルの実行は許可しません。dnSpyEx (およびその前身である dnSpy) は、逆アセンブラーおよび逆コンパイラー コンポーネントとして ILSpy を利用し、デバッガーを追加します。これにより、メモリを手動で検査および操作できます。de4dot は、DotNet バイナリの難読化を解除するためにのみ使用され、人間の目でのコードの可読性を向上させます。この比較の最後のツールである pe-sieve は、使用されているプログラミング言語を無視して、実行中のプロセスからマルウェアを検出してダンプすることを目的としています。以下の表は、上記のツールの概要を整理したものです。

ILSpy dnSpyEx de4dot

pe-sieve

DotDumper
逆アセンブラー
デバッガ
難読化解除ツール
メモリー内セグメントのダンプ
アンマネージ コードのサポート
DotNetのサポート

今後について

DotDumper は常にレビューと開発が行われており、そのすべてが、バグ修正と新機能の追加という 2 つの主な関心領域に焦点を当てています。開発中、コードはテストされましたが、DotNet フレームワークの機能に変更される可能性があるフックが挿入されているため、コードにバグがある可能性が非常に高いと思われます。バグに遭遇した人は誰でも、GitHub リポジトリでイシューをOpenするように促されます。新機能の提案は、GitHub リポジトリからも可能です。GitHub アカウントをお持ちの方、または公の場でのやりとりを避けたい方は、お気軽にTwitter プライベート メッセージをお送りください。

言うまでもなく、分析中に DotDumper を使用したり、または創造的な方法で使用された際には、公開でも非公開でも構いませんので、気軽にご連絡ください。このホームメイドツールについて、ぜひ意見交換させてください。

DotDumper にはさらに多くの機能が用意されており、利用可能になり次第、更新がコミュニティに送信されます。

※本ページの内容は2022年8月11日(US時間)更新の以下のTrellix Storiesの内容です。
原文:DotDumper: Automatically Unpacking DotNet Based Malware
著者:Max Kersten