Cisco Japan Blog

JavaScript のブリッジにより、WinDbg でのマルウェア分析が容易に

2 min read



はじめに

私たちはマルウェア研究者として、1 週間のうち数日をマルウェアのデバッグに費やして、理解を深めようと努めています。デバッグ用のツールには、OllyDbgpopup_iconx64dbgpopup_iconIDA Propopup_iconImmunity Debuggerpopup_icon など、人気のある強力なユーザ モード ツールがいくつかあります。

これらのデバッガはいずれも、OllyScript のような独自の言語や Python など、何らかのスクリプト言語を利用してタスクを自動化しています。ただし、カーネル モードでの分析となると、選択肢は実際には 1 つしかありません。Windows デバッグ エンジンです。そのインターフェイスには、CDB、NTSD、KD、WinDbg があります。

その中で最も使いやすいのは WinDbg ですが、残念ながらそれでさえも、世界で最も使いにくいデバッガの 1 つとして広く認識されています。

矛盾することも多いわかりづらいコマンド シンタックスと時代遅れのユーザ インターフェイスを組み合わせた WinDbg コマンドは、慣れるまでに時間がかかります。ユーザにとっては、この式に従来の WinDbg スクリプト言語を追加しても事態が改善するわけではありません。独特な特殊性が加わって、さらに複雑になるだけです。

幸い、Windows 10 向けの新しい WinDbg Preview が、最新のプログラミング環境に対応しています。この Preview には、新しい JavaScript エンジンと、JavaScript のオブジェクト/関数セットを通じて確認できるデバッグ データ モデルが含まれています。

これらの新機能のおかげで、すでによく知られているユーザ インターフェイス要素を使用して、WinDbg を、Visual Studio などの最新のプログラミング環境に適合させることができます。今回の記事では、この新しいバージョンの WinDbg デバッガ データ モデルと、JavaScript および dx コマンドによる新しいインターフェイスについて見ていきます。

デバッガ データ モデル

デバッガ データ モデルpopup_iconとは、デバッガ拡張機能(WinDbg も含む)のユーザ インターフェイスが、一貫したインターフェイスを介して、さまざまな内部デバッガ オブジェクトにアクセスできるようにするための、拡張可能なオブジェクト モデルです。

データ モデルを利用して確認できるマルウェア分析関連オブジェクトには、次のものがあります。

  • デバッグ セッション
  • プロセス
  • プロセス環境(例:Peb や Teb)
  • スレッド
  • モジュール
  • スタック フレーム
  • ハンドル
  • デバイス
  • コード(逆アセンブラ)
  • ファイル システム
  • デバッガ制御
  • デバッガ変数
  • 疑似レジスタ

dx 表示式

上記のオブジェクト タイプはいずれも、新しい dx(デバッガ オブジェクト モデル表示式)popup_iconコマンドを使用して確認できます。dx コマンドを使用することで、C++ に似たシンタックスを使用してオブジェクトにアクセスし、式を評価することができます。この方法は、MASM と C++popup_icon 式エバリュエータを組み合わせた複雑な方法よりも、シンプルで一貫しています。 NatVis functionalitypopup_icon が WinDbg に追加されたおかげで、dx コマンドの結果が、DMLpopup_icon をデフォルト出力とする、よりユーザに親しみやすい形式で表示されるようになっています。

dx コマンドの詳細を確認するには、まず WinDbg コマンド ウィンドウに dx Debugger と入力します。すると、確認したデータ モデルのトップ レベルのネームスペースが表示されます。ネームスペースには、Sessions、Settings、State、Utility の 4 つがあります。DML では、生成された出力結果にハイパーリンクが付いています。ユーザは、ハイパーリンクをクリックするだけで、個々のネームスペースを詳細に分析していくことができます。たとえば、Sessions のハイパーリンクをクリックすると、dx -r1 Debugger.Session コマンドが実行され、その結果が表示されます。

トップ レベルのネームスペースからプロセスにドリル ダウン

さらにいくつか下位階層を展開すると(-r dx コマンド オプションで制御することも可能)、すべてのプロセスとそのプロパティのリストにたどり着きます。その中には、Process デバッガ オブジェクトの KernelObject メンバーとして確認された、_EPROCESS カーネル オブジェクト フィールドが含まれます。以前のバージョンの WinDbg を使用していた人であれば、dx コマンドによって調査がやりやすくなったことに、きっと気が付くでしょう。

dx コマンドはタブ補完もサポートしています。これにより、データ モデルのナビゲーションがさらに容易になるほか、デバッガ変数や擬似レジスタなどの WinDbg 内部や、オペレーティング システムについても、ユーザが理解できるようになります。 たとえば、内部デバッガ変数リストを順番に表示するには、dx @$ と入力してから、キーボードの Tab キーを繰り返し押します。すると、$argreg から始まって、定義されているすべての擬似レジスタpopup_iconが繰り返し順番に表示されます。

疑似レジスタと内部変数を使うと、dx コマンドの後にオブジェクト パスを全部入力しなくてもよいので便利です。Debugger.Sessions[0] の代わりに擬似レジスタ @$cursession を使うこともできます。@$cursession は、現在のセッション データ モデル オブジェクトを示しています。現在のプロセスを扱う必要がある場合には、dx Debugger.Sessions[0].Process[procid] という長い文字列の代わりに、dx @$curprocess と入力するだけですみます。

Linq クエリ

Linq(統合言語クエリ)popup_iconは、.NET ソフトウェア エンジニアにとってはすでになじみのある概念です。Linq では、dx コマンドによって確認されたオブジェクト コレクションに対して、SQL のようなクエリを作成することができます。

通常の .NET 開発では、Linq 式を作成するために使用可能なシンタックスは 2 つありますが、dx コマンドを利用した WinDbg では、ラムダ式のシンタックスを使用してpopup_iconクエリを作成することのみがサポートされています。Linq クエリにより、コレクション オブジェクトをさまざまな角度から分析し、目的の情報を抽出することができます。

Linq 関数「Where」を使用すれば、ラムダ式の引数で指定された条件を満たすオブジェクトのみを選択することができます。ラムダ式の引数は、関数の引数として提供されます。たとえば、名前に「Google」という文字列を含むプロセスのみを表示するには、次のように入力します。

dx @$cursession.Processes.Where(p => p.Name.Contains("Google"))

「Select」関数では、SQL と同様に、コレクション内のオブジェクト メンバーの中から、表示したいものを選択できます。たとえば、「Where」関数を使用してすでにフィルタ処理したプロセスに「Select」を使用することで、プロセス名とその ID のみを取得できます。

dx -r2 @$cursession.Processes.Where(p => p.Name.Contains("Google")).Select(p => New { Name=p.Name, Id=p.Id })

さらに 1 レベル下がって、確認された _EPROCESS カーネル オブジェクトでは、確認中のプロセスが所有するハンドルのサブセットを表示することができます。たとえば、ユーザ モードのルートキットにより隠蔽されているプロセスを見つけるための 1 つの方法として、Windows クライアント/サーバ サブシステム プロセス(csrss.exe)のプロセス ハンドルを抽出し、そのリストを、標準のプロセス抽出コマンドを使用して生成したリストと比較します。

csrss.exe によって作成されたプロセスをリストアップする前に、以下のように、csrss.exe プロセス オブジェクトを探し、見つかったらコンテキストにスイッチする必要があります。

dx @$cursession.Processes.Where(p => p.Name.Contains("csrss.exe"))[pid].SwitchTo()

この時点で Linq クエリを実行して、csrss.exe ハンドル テーブル内に存在するプロセスのメイン モジュールへのパスを表示することができます。

dx @$curprocess.Io.Handles.Where(h => \h.Type.Contains("Process")).Select(h => 
h.Object.UnderlyingObject.SeAuditProcessCreationInfo.ImageFileName->Name)

ImageFileName は _OBJECT_NAME_INFORMATION タイプの構造体へのポインタであるため、矢印を使用してデリファレンスし、モジュールのパスを含む「Name」フィールドにアクセスする必要があります。

このほかにも、役に立つ Linq クエリが数多くあります。たとえば、SQL の Order By 句のように、いくつかの条件に基づいて表示結果を並べ替えたり、「Count」関数を使用してクエリ結果をカウントしたりすることができます。Linq クエリは JavaScript 拡張機能でも使用できますが、やはりシンタックスは若干異なっています。?JavaScript 内での Linq 使用例については、このブログ記事の中で後述します。

WinDbg と JavaScript

デバッガ データ モデルの基礎と、その内容を確認するための dx コマンドについて説明したので、ここからは、WinDbg 用の JavaScript 拡張機能を取り上げます。Jsprovider.dll は、ネイティブの WinDbg 拡張機能です。これにより、Microsoft Chakra JavaScript エンジンのあるバージョンを使用して WinDbg にスクリプトを追加し、データ モデルにアクセスすることができます。Jsprovider は、デフォルトでは WinDbg プロセス スペースにロードされませんので、手動でロードする必要があります。これは、その他の JavaScript ベースの拡張機能と競合しないようにするためです。

Jsprovider は、拡張機能をロードするための標準的なコマンドでロードできます。

.load jsprovider.dll

この記事では、脅威研究者がマルウェア サンプルを分析する際に作成する従来のスクリプトについて説明していますが、既存のバイナリ拡張機能と同じように使用できる WinDbg 拡張機能を、JavaScript 拡張機能で作成することもできます。JavaScript ベースの拡張機能の作成方法については、公式の WinDbg JavaScript サンプルの GitHub リポジトリpopup_iconで提供されているいずれかの拡張機能を調査して確認してください。

WinDbg Preview には、JavaScript コードを作成するためのフル機能の統合開発環境(IDE)が含まれており、実行中のプログラムをデバッグしたりメモリ ダンプを調べたりしながら、コードをリファクタリングすることが可能です。

JavaScript ベースのスクリプトをロードして実行するには、次の WinDbg コマンドを使用します。ありがたいことに、JavaScript ベースのスクリプトを扱うコマンドは、WinDbg スクリプトを管理する煩雑な標準シンタックスよりも簡単です。

  • .scriptload コマンドは、WinDbg に JavaScript スクリプトまたは拡張機能をロードしますが、実行はしません。
  • .scriptrun は、ロードされたスクリプトを実行します。
  • .scriptunload は、WinDbg とデバッガ データ モデルのネームスペースからスクリプトをアンロードします。
  • .scriptlist は、現在ロードされているすべてのスクリプトをリスト アップします。

JavaScript のエントリ ポイント

JavaScript プロバイダーは、スクリプトのロードに使用するスクリプト コマンドに応じて、事前に定義されたユーザ スクリプトのエントリ ポイントのいずれかを呼び出すか、スクリプト ルート レベルでコードを実行します。

脅威研究者の観点から言うと、主なエントリ ポイントは 2 つあります。1 つ目は、initializeScript という、スクリプト コンストラクタ関数の一種です。これは、.scriptload コマンドが実行されたときにプロバイダーによって呼び出されます。この関数は通常、グローバル変数を初期化したり、定数、構造体、オブジェクトを定義したりするために呼び出されます。

InitializeScript 関数内で定義されたオブジェクトは、host.namespacePropertyParent 関数と host.namedModelParent 関数を使用して、デバッガ データ モデルのネームスペースにブリッジされます。ブリッジされたオブジェクトは、データ モデル内のその他のネイティブ オブジェクトと同様に、dx コマンドを使用して調査することができます。

2 つ目のエントリ ポイントは、より重要な invokeScript 関数です。これは、C 言語の main 関数に相当します。この関数は、ユーザが .scriptrun WinDbg コマンドを実行したときに呼び出されます。

JavaScript の詳細確認に役立つコツ

たとえば、日々の調査で定期的に使用する関数一式が保持されている「myutils.js」というスクリプトがあるとします。最初にすべきなのは、.scriptload 関数を使用してスクリプトをロードすることです。

ユーザの Desktop フォルダからスクリプト関数をロード

WinDbg JavaScript モジュールとネームスペース

デバッガの操作で主に使用する JavaScript オブジェクトは、host オブジェクトです。WinDbg Preview スクリプト エディタを使用している場合、使用可能な関数およびメンバーの名前を把握するには、Intellisensepopup_iconタブ補完および関数ドキュメンテーション機能が役立ちます。

IntelliSense を実行中

試したい場合は、スクリプトが実行されるたびに呼び出される invokeScript 関数に、自分のコードを追加してみることができます。コードに問題がなければ、コードをリファクタリングして独自の関数セットを定義することができます。

JavaScript インターフェイスで確認できる機能をさらに詳細に分析する前に、2 つの重要なヘルパー関数を作成することをお勧めします。画面にテキストを表示する関数と、標準の WinDbg コマンドを使用してデバッガを操作するための関数です。

この 2 つの関数は、ユーザと対話したり、デバッグに必要だが JavaScript のネイティブにはない機能の代替機能を作成したりする際に役に立ちます。

この例では、これらの関数に logme と exec という名前を付けました。これらの関数は、基本的に JavaScript 関数のラッパーに過ぎませんが、ネームスペース階層をすべて入力しなくても利用できるという利点があります。

JavaScript WinDbg API のパーツをラップしたヘルパー関数

function exec では、host.namespace.Debugger ネームスペースを参照することにより、WinDbg コマンド ラインから dx コマンドでアクセスするのと同じオブジェクト階層に、JavaScript からアクセスできることがわかります。

ExecuteCommand 関数は、既知の WinDbg コマンドのいずれかを実行し、結果をプレーン テキスト形式で返します。これを解析すると必要な結果が得られます。このアプローチは、Python ベースの一般的な WinDbg 拡張機能 pykd のアプローチと、あまり違いはありません。ただ、Jsprovider では、ほとんどの JavaScript 拡張機能関数が返す JavaScript オブジェクトを解析せずにスクリプトに使用できるので、その点で pykd よりもメリットがあります。

たとえば、ループ可能な host.currentProcess.Modules にアクセスすることで、プロセス モジュールのコレクションをループ処理できます。ループ可能配列の各メンバーは、Module クラスのオブジェクトであり、そのプロパティ(今回のケースでは名前)を表示することができます。

Intellisense は、JavaScript オブジェクトのすべてのメンバーを常に表示できるとは限らないため、for-in ループ ステートメントが非常に便利です。このループでは、出力できるすべてのオブジェクト メンバーの名前をループして、調査や開発に利用することができます。

Module オブジェクトのメンバーを表示

一方、for-of ループ ステートメントでは、ループ可能オブジェクトのすべてのメンバーがループ処理され、それぞれの値が返されます。これら 2 つのループでの形式の違いを覚えておくことが重要です。

現在のプロセス スペースにロードされているモジュールのリストを出力

また、ロードされたモジュールのプロセス環境ブロック(PEB)のリンク リストをループ処理することによって、ロード済みモジュールの一覧を取得することもできます。ただし、そのためには、JavaScript 関数 host.namespace.Debugger.Utility.Collections.FromListEntry を呼び出して、リンク リストをコレクションに変換しなければならないので、必要な準備作業は増えます。ロード済みモジュールのリンク リストを JavaScript モジュール配列に変換してそのプロパティを表示する関数の全体を、下記に示します。

function ListProcessModulesPEB (){

//Iterate through a list of Loaded modules in PEB using FromListEntry utility function

for (var entry of 
host.namespace.Debugger.Utility.Collections.FromListEntry(host.currentProcess.KernelObject
.Peb.Ldr.InLoadOrderModuleList, "nt!_LIST_ENTRY", "Flink")) {

//create a new typed object using a _LIST_ENTRY address and make it into _LDR_TABLE_ENTRY

  var 
loaderdata=host.createTypedObject(entry.address,"nt","_LDR_DATA_TABLE_ENTRY");

//print the module name and its virtual address

logme("Module "+host.memory.readWideString(loaderdata.FullDllName.Buffer)+" at "+
 loaderdata.DllBase.address.toString(16) + " Size:
 "+loaderdata.SizeOfImage.toString(16));
  }
}

この関数には、プロセス メモリから値を読み取るためのコードが含まれています。このコードは、host.memory ネームスペースにアクセスして、readMemoryValues 関数、readString 関数、readWideString 関数のいずれか(読み取る必要のあるデータのタイプによって異なる)を呼び出すものです。

JavaScript の 53 ビットの整数幅制限

JavaScript を使用した WinDbg プログラミングは、標準の WinDbg スクリプトと比べると比較的シンプルですが、いくつか問題があることを認識しておく必要がります。1 つ目は、JavaScript での整数幅が 53 ビットに制限されていることです。これにより、ネイティブの 64 ビット値を処理する場合にいくつか問題が起こる可能性があります。そのため、JavaScript 拡張機能には、host.Int64 という特別なクラスがあります。64 ビットの数字を扱う場合には、このクラスのコンストラクタを呼び出す必要があります。幸いにも、53 ビット オーバーフローが発生する可能性があるときは、インタープリタから警告が発せられます。

host.Int64 オブジェクトでは、さまざまな関数によって算術演算やビット演算を実行できます。PspCreateProcessNotifyRoutine 関数を使用して登録されたコールバック配列をループ処理する関数を作成しようとした際に(この記事内で後述)、筆者は、64 ビット幅の And 演算を適用する方法を見つけられませんでした。ビット演算関数は 53 ビット幅に戻っているようでした。そのため、演算対象が 53 ビットより広いと、オーバーフローが生じます。

 

host.Int64 を 53 ビットの And 演算で処理すると正しい結果が得られ、それより広い場合は誤った結果となる

幸い、GetLowPart と GetHighPart という関数があります。これらはそれぞれ、64 ビット整数の下位 32 ビットまたは上位 32 ビットを返す関数です。この関数を使って、上位 32 ビットの値を左に 32 ビット分ずらし、そこに下位 32 ビットを追加することで、必要な And 演算を実施し、求める 64 ビットの値を復元することができます。

WinDbg JavaScript の実装が 53 ビットに制限されているのは厄介です。WinDbg チームがそれを克服して、特別な JavaScript クラスに頼ることなく 64 ビットの数値をサポートする方法を見つけることができれば、非常に喜ばしいことです。

JavaScript での Linq

Linq クエリを使用して、dx コマンドでデバッガ データ モデル オブジェクトとそのメンバーのサブセットにアクセスする方法についてはすでに説明しました。

しかし、JavaScript ではシンタックスが少し異なり、必要なデータ タイプを返す式を指定するか、必要なデータ タイプを返す Linq の verb 関数を呼び出す際の引数として、無名関数を指定する必要があります。たとえば、Linq の「Where」句の場合、返される値はブール型でなければなりません。「Select」句の場合は、選択するオブジェクトのメンバーか、クエリ対象オブジェクト メンバーのサブセットで構成される新しい無名オブジェクトを指定する必要があります。

以下に示しているのは、モジュールのリストをフィルタ処理する Linq 関数を使用することで、名前に「dll」という文字列が含まれるモジュールを抽出して、モジュール名とそのベース アドレスのみを表示するシンプルな例です。

function ListProcessModules(){

//An example on how to use LINQ queries in JavaScript
//Instead of a Lambda expression supply a function which returns a boolean for  Where clause or

let mods=host.currentProcess.Modules.Where(function (k) {return k.Name.includes("dll")})

//a new object with selected members of an object we are looking at (in this case a Module)

.Select(function (k) {return  { name: k.Name, adder:k.BaseAddress} });

    for (var lk of mods) {

      logme(lk.name+" at "+lk.adder.toString(16));

    }

}

オペレーティング システムの構造体を調査

カーネル関数と構造体のアドレスを取得するには、host.getModuleSymbolAddress 関数から始めるのが得策です。取得されたシンボルに格納されている実際の値を取得する必要がある場合は、host.memory.readMemoryValues 関数または単一の値に対するデリファレンス関数を使用して、アドレスをデリファレンスする必要があります。

下記に示しているのは、ドキュメント化された PspCreateProcessNotifyRoutinepopup_icon カーネル関数(プロセスが作成または終了されるたびに通知されるドライバ関数を登録する関数)を使用して登録されたコールバックを抽出する例です。この関数は、カーネル モード マルウェアがプロセスを隠蔽したり、ユーザ モード モジュールが停止されるのを回避したりするためにも使用されています。

この記事内の例は、Matthieu Suiche が開発した SwishDbgExtpopup_icon 拡張機能で実装されているコールバックを抽出するための C コードから着想を得ました。この WinDbg 拡張機能は、カーネル メモリ ダンプや、カーネル モード マルウェアに感染したシステムを分析するのに非常に役立ちます。

このコードから、JavaScript を使用することでさらに複雑な関数でも比較的簡単に実装できることがわかります。実際、WinDbg Preview IDE を使用すると、コードの作成、テスト、分析をすべて同時に実行できるので、JavaScript を使用した開発は、マルウェア研究者にとって理想的です。

function ListProcessCreateCallbacks() {

PspCreateNotifyRoutinePointer=host.getModuleSymbolAddress("ntkrnlmp",
"PspCreateProcessNotifyRoutine");
let PspCreateNotify=host.memory.readMemoryValues(PspCreateNotifyRoutinePointer, 1,8);
let
 PspCallbackCount=host.memory.readMemoryValues(host.getModuleSymbolAddress("ntkrnlmp",
"PspCreateProcessNotifyRoutineCount"),1,4);
logme ("There are "+PspCallbackCount.toString()+" PspCreateProcessNotify callbacks");

for (let i = 0; i<PspCallbackCount;i++){

    let
 CallbackRoutineBlock=host.memory.readMemoryValues(PspCreateNotifyRoutinePointer.
 add(i * 8),1,8);
    let CallbackRoutineBlock64=host.Int64(CallbackRoutineBlock[0]);
    
    //A workaround seems to be required here to bitwise mask the lowest 4 bits,
    //Here we have:
    //Get lower 32 bits of the address we need to mask and mask it to get
    //lower 32 bits of the pointer to the _EX_CALLBACK_ROUTINE_BLOCK 
      (undocumented structure known in ReactOS)
    
    let 
LowCallback=host.Int64(CallbackRoutineBlock64.getLowPart()).bitwiseAnd(0xfffffff0);
    
    //Get upper 32 bits of the address we need to mask and shift it left to 
create a 64 bit value
    let 
HighCallback=host.Int64(CallbackRoutineBlock64.getHighPart()).bitwiseShiftLeft(32);

    //Add the two values to get the address of the i-th _EX_CALLBACK_ROUTINE_BLOCK
    let ExBlock=HighCallback.add(LowCallback);
   
    //finally jump over the first member of the structure (quadword) to read  the 
    address of the callback
    let Callback=host.memory.readMemoryValues(ExBlock.add(8),1,8);

    //use the .printf trick to resolve the symbol and print the callback
    let rez=host.namespace.Debugger.Utility.Control.ExecuteCommand(".printf \"%y\n\", 
     " + Callback.toString());

    //print the function name using the first line of the response of.printf command
    logme("Callback "+i+" at "+Callback.toString()+" is "+rez[0]);
  }
}

ここには、前述した 64 ビット アドレスの操作が示されています。JavaScript の 53 ビット整数オーバーフローの発生を回避するために、64 ビットの値を上位 32 ビットと下位 32 ビットに分割し、別々にビット演算を適用しています。

もう 1 つ興味深いのは、標準的なデバッガ コマンド .printf を使用して、リバース シンボル解決が行われていることです。必要なシンボルのアドレスは、JavaScript 関数 host.getModuleSymbolAddress で取得できますが、このブログ記事執筆の時点では、アドレスからシンボル名を取得する関数はありません。そのため、指定されたシンボル名を含む文字列を返す %y フォーマット指定子を付けた .printf が、回避策として使用されています。

デバッグ スクリプトのデバッグ

どんな言語であれ、一般的な言語でスクリプトを開発している人は、開発を成功させるには、デバッグできるツール セットが必要であることを知っています。デバッガでは、ブレークポイントを設定して、変数とオブジェクトの値を確認できることが必要です。値の確認は、さまざまなオペレーティング システムの構造体にアクセスしたり、マルウェア サンプルを分析したりするスクリプトを書くときにも必要です。この場合も、WinDbg JavaScript 拡張機能が必要な機能を揃えています。これらの機能は、定常的に WinDbg を使用するユーザすべてになじみのあるコマンドを使った、デバッグ ツールとして提供されています。

.scriptdebug コマンドでデバッガを起動すると、特定のスクリプトをデバッグする JavaScript デバッガを利用できるようになります。デバッガにスクリプトを読み込んだら、デバッガを停止させるイベントを選択するオプションを設定し、スクリプト コードの特定の行にブレークポイントを指定します。

WinDbg の場合と同様、どのイベントの後にデバッガをブレークさせるかを定義するには、JavaScript デバッガで sxe コマンドを使用します。たとえば、スクリプトの最初に実行された行でブレークさせるには、単純に sxe en と入力します。コマンドが正常に実行されると、sx コマンドを使用して、取得可能なすべてのイベントのステータスを調べることができます。

sx コマンドで、さまざまな例外に対する JavaScript デバッガのブレーク ステータスを表示

この時点で、標準的な WinDbg シンタックスと同様に、bp コマンドを使用して、ブレークポイントを設定するスクリプトの行を指定することも可能です。ブレークポイントを設定するには、行番号と合わせて、行の中の位置も指定する必要があります(例:bp 77:0)。指定された行内の位置が 0 の場合、デバッガは自動的に、行内で設定可能な最初の位置にブレークポイントを設定します。これにより、目的のブレークポイントの位置を数えなくても済むようになります。

行位置 0 を指定することで、設定可能な最初の位置にブレークポイントを設定

必要なブレークポイントをすべて設定したら、デバッガを終了する必要があるのですが、これが少しわかりづらい手順です。デバッグ プロセスは、WinDbg 変数 @$scriptContents にアクセスしてデバッグ対象スクリプトの任意の関数を呼び出すか、通常どおり .scriptrun コマンドを使用してスクリプトを起動することによって、スクリプトを呼び出した後、続行します。もちろん、@$scriptContents 変数には、dx コマンドを使用してアクセスします。

@$scriptContents 変数を使用してデバッグ用スクリプトを起動可能

デバッガには、独自の JavaScript エバリュエータ コマンド ?? が含まれています。これは、JavaScript 式を評価して、スクリプトの変数とオブジェクトの値を調査できるコマンドです。

コマンド ? または ?? を使用して JavaScript 式の表示結果を調査

JavaScript デバッグは、適切な開発のために必要な、強力なツールです。その機能は、初期バージョンの JavaScript 拡張機能でもすでに十分ですが、WinDbg Preview のフル リリースが近づく中、やがてその機能がさらに充実して安定したものとなっていくことが望まれます。

まとめ

この記事が、公式の Microsoft JavaScript WinDbg 拡張機能を利用してマルウェアを分析する際に、役に立つ機能の参考になれば幸いです。JavaScript を通じて公開されている API は完全なものではありませんが、通常は、標準の WinDbg コマンドをラップしてその出力結果を解析することにより、制約を回避することが可能です。これは最善のソリューションではありませんので、JavaScript プロバイダーに新しい機能が直接追加されて、もっとわかりやすくスクリプトを作成できるようになることを願っています。

先頃、ファイル システム操作モジュールと Code ネームスペース モジュールが追加されたことからわかるように、Windows 用デバッグ ツール開発チームは、新しい JavaScript モジュールの開発に専念しているようです。これにより、コード分析にまったく新しい可能性が開かれます。これについては、次回以降のいずれかの記事で取り上げられるかもしれません。関心のある方は、Github の公式サンプル リポジトリで入手可能な、CodeFlowpopup_icon JavaScript 拡張機能を確認してください。

WinDbg と JavaScript を使用したマルウェア分析に関するヒントをさらに学びたい方は、5 月にコペンハーゲンで開催される CARO Workshoppopup_icon において、Cisco Talos がセッションを開催しますので、ご注目ください。

参考資料

 

本稿は 2019年2月18日に Talos Grouppopup_icon のブログに投稿された「JavaScript bridge makes malware analysis with WinDbg easierpopup_icon」の抄訳です。

コメントを書く