BugChecker
はじめに
BugChecker は、Windows 11(および Windows XP もサポート: Windows XP から 11 までの x86 および x64 バージョンに対応)向けの、SoftICE に似たカーネルおよびユーザーデバッガです。BugChecker は、WinDbg や KD のように、デバッグ対象のシステムに接続するための2台目のマシンを必要としません。このバージョンの BugChecker(20年前に開発されたオリジナル版とは異なり)は、NTOSKRNL 内部の非公開 KD API を活用しています。KD API により、WinDbg/KD は仮想メモリの読み書き、レジスタの読み書き、アドレスへのブレークポイント設定などの呼び出しを行うことができます。
対照的に、オリジナルの BugChecker は、SoftICE と同様に、複数のカーネル API(公開および非公開の両方)をフックし、APIC を制御し、IPI を送信するなどして、システムを「乗っ取る」方法を採用していました。このアプローチは、サポートされるすべての Windows のバージョンおよびサブバージョン(関数シグネチャレベル)だけでなく、可能なすべてのハードウェア構成との互換性を確保する必要があるため、複雑さが指数関数的に増加し(システムの安定性が低下します)。さらに、20年後の現在、PatchGuard がこの解決策を不可能にしています。
これに対して、このバージョンの BugChecker は、カーネル内の KdSendPacket および KdReceivePacket への呼び出しをインターセプトすることで、外部のカーネルデバッガを実行している2番目のシステムとしてデバッグ対象のマシンに自身を提示しますが、実際にはすべてが同一マシン上で行われます。通常、これは KDCOM.DLL を置き換え(Windows で KD API のシリアルケーブル通信を実装するモジュールです)、システムをカーネルデバッグモードで起動することで実現されます。このアプローチ(VirtualKD に触発されています)は、複雑さを軽減し、安定性と互換性(および移植性、例えば ARM への対応や、モジュール性。低レベルのデバッガ機能は KdXxxPacket の背後に実装され、カスタム実装に置き換えることができます)を向上させます。さらに、起動時に(「偽の」ではありますが)カーネルデバッガが存在することで、Windows は PatchGuard を無効にします。
現時点では、BugChecker は入力に PS/2 キーボード、出力にリニアフレームバッファを必要とします。最近のノートパソコンの多くに内蔵されているキーボードは依然として PS/2 であることに注意してください。
機能
- Windows XP から Windows 11 までの x86 および x64、SMP カーネルをサポート。x64 上の WOW64 プロセスもサポート。
- QuickJSPP を統合。これは QuickJS を MSVC++ に移植したものです。BugChecker は QuickJS を呼び出す前に、FPU 状態を保存し(x86 の場合)、128KB に拡張されたスタックに切り替えます。
- コマンドは JS 式を受け付けます。例えば、「U rip+rax*4」や「U MyJsFn(rax+2)」は有効なコマンドです。カスタム関数はスクリプトウィンドウで定義できます。CPU レジスタは BugChecker によって自動的にグローバルスコープ変数として宣言されます。
- PDB シンボルファイルをサポート。PDB ファイルは手動で指定するか、Symbol Loader がシンボルサーバーからダウンロードできます。
- JavaScript コードは、以下の非同期関数を呼び出すことができます: WriteReg、ReadMem、WriteMem。
- ブレークポイントには JS 条件を設定できます。条件が 0 と評価された場合、「breakin」は発生しません。これにより、実行フローを変更できる「ログポイント」やブレークポイントを設定できます。
- ログウィンドウには、カーネルデバッガに送信されたメッセージ(例: DbgPrint メッセージ)が表示されます。
- シンタックスハイライト付きの JavaScript ウィンドウ。
- タブキーを使用すると、数桁入力することで画面上のすべての16進数を循環したり、数文字入力することでそれらの文字を含むすべてのシンボルを循環したりできます。
- EASTL と C++20 コルーチンにより、新しいコマンドの作成が簡単になります。プルリクエストをお待ちしています!
動画 (YouTube)
Windows 11 22H2、VirtualBox 7.0.4 上での BugChecker のデモ。ユーザーモードスレッドの実行フローを変更する JavaScript ブレークポイント条件を作成しています。

非常に制限された環境(Raspberry Pi 4、4GB RAM)で、QEMU 上の Windows XP(512MB RAM)を介して BugChecker を実行。ユーザーモードからカーネルへのすべての SYSENTER 呼び出しをログに記録するためにブレークポイントを使用。サービスインデックスは JavaScript 配列に保存されます。

HP Pavilion Dv2000(PS/2 キーボード搭載の古い PC)のベアメタル上で直接 BugChecker を実行。OS は Windows 7 Home 32bit。

インストール手順
はじめに
BugChecker をインストールおよび使用する際は、セキュアブートが無効になっていることを確認してください。通常は後で再度有効にできます。VMware または VirtualBox を使用している場合、仮想マシンの設定でセキュアブートを無効にできます。
Windows 8、10、11 を使用している場合は、bcdedit /set "{current}" bootmenupolicy legacy コマンドを使用して、レガシーブートメニューを有効にすることも検討してください。これにより、起動中に BugChecker のブートオプションを選択し、同時にドライバ署名の強制を無効にできるため、よりスムーズな体験が可能になります。
手順
最初のステップは、Symbol Loader を起動することです:

必要であれば、「Disable Display Drvs」ボタンをクリックしてディスプレイドライバを無効にします。同じことは Windows のデバイスマネージャーでも実行できます。ディスプレイドライバを無効にすると、システムの再起動後も無効のままになります。BugChecker を使用しない場合、後でいつでも再び有効にできます。
ここでのポイントは、BugChecker がインターフェイスを描画するために、32ビット/ピクセル形式のリニアフレームバッファを必要とすることです。ディスプレイドライバを無効にすると、Windows は UI 描画のハードウェアアクセラレーションを破棄し、VGA 互換モードにフォールバックします。ベアメタルまたは VMware で実行する場合は、ディスプレイドライバを無効にする必要があります。VirtualBox で実行する場合は、後述するようにディスプレイドライバを無効にするか、BugChecker.dat の vm_screen 設定を設定する必要があります。QEMU で実行する場合は、ディスプレイドライバを無効にする必要はありませんが、「-vga std」ディスプレイデバイスを指定してください。
VGA 互換モードは最大画面解像度を制限する可能性があることに注意してください。VMware は最大解像度 1152x864 に制限されます。「-vga std」ディスプレイデバイスを使用した QEMU では、この制限はありません。
興味深いことに、BugChecker が複数のグラフィックスカードを搭載したシステムにインストールされている場合、1つのグラフィックスカードのディスプレイドライバのみを無効にすることが可能で、そのカードは BugChecker UI を表示する画面に接続されます。2つ目のカード(メインディスプレイとして設定)は、OpenGL や DirectX サポートを含むすべての 2D および 3D アクセラレーション機能を保持します(注: VMware、Windows 11、DisplayLink ディスプレイでテスト済み)。
次に、「Start Driver」をクリックし、続いて「Auto Detect」、最後に「Save」をクリックします。「Auto Detect」はフレームバッファの幅、高さ、物理アドレス、ストライドを自動的に判別できるはずです。ただし、これらの設定を手動で指定することもできます(終了時に必ず「Save」をクリックしてください)。「Stride」が 0 の場合、ドライバの起動時に「Width」* 4 として自動計算されます。「Address」(フレームバッファの物理アドレス)は、Windows デバイスマネージャーでディスプレイデバイスの「プロパティ」を開き、「リソース」タブで確認できます。
次に、「KDCOM Hook Method」セクションで「Callback」をクリックし、「Copy/Replace Kdcom」をクリックします。その後、システムを再起動できます。
このセットアッププロセスは一度だけ実行する必要があり、必要に応じてディスプレイドライバを再び有効にできます。ただし、BugChecker を使用する際は、設定によってはディスプレイドライバを再度無効にする必要があります。
VirtualBox 向け vm_screen 設定 (実験的)
BugChecker.dat の vm_screen 設定により、Symbol Loader で事前に画面解像度を指定したり、ディスプレイドライバを無効にしたりすることなく、VirtualBox 内で BugChecker デバッガ UI を開くことができます。
このアイデアは、仮想ディスプレイデバイスの I/O ポートとコマンドバッファに直接書き込むことで、現在の画面解像度を取得し、フレームバッファの更新をハイパーバイザーに通知するというものです。
このソリューションは X.org xf86-video-vmware ドライバに触発されました。
このソリューションは VirtualBox VM でのみ機能し、BugChecker.dat ファイルを手動で編集する必要があります:

- Symbol Loader で、フレームバッファの幅と高さを可能な最大解像度(つまり、コンピュータ画面のサイズ)に手動で設定します。ストライドは 0 に設定します。
- BugChecker.dat ファイルは Symbol Loader によって "C:\Windows\BugChecker" に作成されます。
- vm_screen 設定は "settings->framebuffer" の下に追加する必要があります。
- このファイルの設定の階層は、タブ文字(スペースではありません)によって決まります。
- 設定の形式は、Command_Buffer_Start_Address (カンマ) Command_Buffer_End_Address (カンマ) I/O_Port_Base です。
- 重要: VM 設定の「ディスプレイ」で、グラフィックスコントローラとして「VBoxSVGA」を選択し、「3D アクセラレーションを有効にする」のチェックを外します。
これは実験的な機能です。将来的には、この設定は Symbol Loader によって自動的に追加される予定です。
実装済みコマンド
コマンド名と構文は、オリジナルの SoftICE for NT のものにできるだけ近づけて選択されています:
- ? javascript-expression: JavaScript 式を評価します。
- ADDR eprocess: プロセスコンテキストに切り替えます(OS に制御を戻します)。
- BC list|*: 1つ以上のブレークポイントをクリアします。
- BD list|*: 1つ以上のブレークポイントを無効にします。
- BE list|*: 1つ以上のブレークポイントを有効にします。
- BL (パラメータなし): すべてのブレークポイントを一覧表示します。
- BPX address [-t|-p|-kt thread|-kp process] [WHEN js-expression]: 実行ブレークポイントを設定します。
- CLS (パラメータなし): ログウィンドウをクリアします。
- COLOR [normal bold reverse help line]|[reset]: 画面の色を表示、設定、またはリセットします。
- DB/DW/DD/DQ [address] [-l len-in-bytes]: メモリを 8/16/32/64 ビット値として表示します。
- EB/EW/ED/EQ address -v space-separated-values: メモリを 8/16/32/64 ビット値として編集します。
- KL EN|IT: キーボードレイアウトを設定します。
- LINES [rows-num]: 現在の表示行数を表示または設定します。
- MOD [-u|-s] [search-string]: モジュール情報を表示します。
- P [RET]: プログラムステップを1つ実行します。
- PAGEIN address: メモリのページを強制的にページインします(OS に制御を戻します)。
- PROC [search-string]: プロセス情報を表示します。
- R register-name -v value: レジスタの値を変更します。
- STACK [stack-ptr]: スタックをスキャンしてリターンアドレスを探します。
- T (パラメータなし): 1つの命令をトレースします。
- THREAD [-kt thread|-kp process]: スレッド情報を表示します。
- U address|DEST: 逆アセンブル命令を表示します。
- VER (パラメータなし): バージョン情報を表示します。
- WD [window-size]: 逆アセンブラウィンドウの表示を切り替えるか、サイズを設定します。
- WIDTH [columns-num]: 現在の表示列数を表示または設定します。
- WR (パラメータなし): レジスタウィンドウの表示を切り替えます。
- WS [window-size]: スクリプトウィンドウの表示を切り替えるか、サイズを設定します。
- X (パラメータなし): BugChecker 画面を終了します。
ビルド手順
前提条件
- Visual Studio 2019
- Windows Driver Kit 7.1.0
注: WDK はデフォルトの場所 (X:\WinDDK。X は BugChecker のソースが保存されているドライブ) にインストールする必要があります。
カーネルドライバをビルドするためのステップバイステップガイドはこちらにあります。
Visual Studio プロジェクトの説明
- BugChecker: これが BugChecker カーネルドライバであり、デバッガ全体が実装されています。"Release|x86" および "Release|x64" の出力ファイルが最終パッケージに含まれます。初期化中、ドライバは "\SystemRoot\BugChecker\BugChecker.dat" にある設定ファイルを読み込み(すべてのシンボルファイルもこのディレクトリに保存されます)、カーネル空間で "KDCOM.dll" を探します。見つかった場合、そのエクスポート関数 "KdSetBugCheckerCallbacks" を呼び出そうとし、KdSendPacket と KdReceivePacket をフックします。
- SymLoader: これが Symbol Loader です。"Release|x86" の出力ファイルのみが最終パッケージに含まれます。Symbol Loader は、BugChecker の設定を変更し(設定は "\SystemRoot\BugChecker\BugChecker.dat" に書き込まれます)、PDB ファイルをダウンロードし、カスタム KDCOM.dll モジュールをインストールするために使用されます。
- KDCOM: これがカスタム KDCOM.dll モジュールであり、システム起動時に NTOSKRNL によって読み込まれます。ドライバが KdSendPacket と KdReceivePacket をフックするために呼び出す "KdSetBugCheckerCallbacks" 関数をエクスポートします。
- pdb: これは Ghidra の "pdb" プロジェクトです。オリジナル版は PDB ファイルの内容を xml 形式で標準出力に出力します。コードは BCS ファイルを生成するように変更されました。
- NativeUtil: Symbol Loader は Windows x64 の WOW64 アプリケーションであるため、アーキテクチャネイティブイメージから呼び出す必要がある API(例えば、デバイスおよびドライバインストール API)への呼び出しはここに移動されました。
- HttpToHttpsProxy: これは ASP.NET Core アプリケーションであり、Windows XP で実行する際に Symbol Loader のインターネットプロキシとして機能します。XP は古い TLS サポートしか持たないため、Symbol Loader は任意のシンボルサーバーからファイルをダウンロードできません。同じネットワーク上の IIS にこのアプリケーションを展開した後、Symbol Loader でサーバー URL の前に "http://<YOUR_IIS_SERVER_IP>/HttpToHttpsProxy/" を付けることで、Windows XP でシンボルサーバーからファイルをダウンロードできるようになります。
クレジット
- VirtualKD: BugChecker の最初の POC は VirtualKD を改造して構築されました。
- BazisLib: Symbol Loader の「Copy/Replace Kdcom + Add Boot Entry」ボタンの背後にあるコードは VirtualKD からのもので、BazisLib を使用しています。
- EASTL: ここで MSVC++ STL を使用する方法はありません。EASTL は優れた代替手段です。
- Ghidra: BugChecker の "pdb" プロジェクトは Ghidra からのものです。BCS ファイルを生成するように変更されました。
- Zydis: BugChecker の逆アセンブラウィンドウ用。
- QuickJSPP(QuickJS の MSVC++ 移植版):カーネルドライバに統合された JavaScript エンジン用。
- ReactOS: Windows KD の内部型定義用。
- SerenityOS: BugChecker メモリアロケータで使用される低レベルビットマップ操作関数用。私は、Andreas のビデオを見た後(C/C++ およびあらゆる種類の低レベルプログラミングから10年間離れていた後)に BugChecker を始めたため、SerenityOS の小さな一片を BugChecker に含めたいと思いました。