WinDbgの使い方
WinDbgは、Microsoftが提供する強力なデバッガーで、Windowsアプリケーションやドライバーのデバッグに使用されます。WinDbgは、ユーザー空間とカーネル空間の両方で動作し、クラッシュダンプの解析やライブデバッグを行うことができます。また、リモートデバッグ機能も備えており、ネットワーク越しに他のマシンのデバッグを行うことも可能です。WinDbgは非常に多くの機能を提供していますが、以下に代表的な機能を挙げます。
- クラッシュダンプの解析: アプリケーションやOSそのものがクラッシュした際に生成されるダンプファイルを解析できます。これにより、クラッシュの原因を特定することができます。
- タイムトラベルデバッグ: 過去の実行状態を再現し、特定の時点でのアプリケーションの状態を確認できます。これにより、バグの再現や解析が容易になります。
- ライブデバッグ: 実行中のアプリケーションやドライバーをリアルタイムでデバッグできます。ブレークポイントを設定し、変数の値を確認したり、ステップ実行が可能です。
- リモートデバッグ: ネットワーク越しに他のマシンのデバッグを行うことができます。これにより、リモートサーバーや仮想マシン上で動作するアプリケーションのデバッグが可能です。
- デバッグシンボルの読み込み: シンボルファイル(PDBファイル)を使用して、ソースコードの変数名や関数名を表示できます。これにより、デバッグが容易になります。シンボルの詳細はこちらの記事を参照してください。
WinDbgの入手方法
WinDbgはクラシックバージョンとプレビューでリリースされていた最新バージョンの2系統があります。クラシックバージョンはWindows SDKに含まれており、オプションでインストールすることができます。最新バージョンはUWPアプリとして提供されており、Microsoft StoreやWinGetを通じて入手できる他、インストーラーパッケージも公開されています。
基本的に最新バージョンはクラシックバージョンの機能をすべてカバーしていますので、敢えてクラシックバージョンを使用する必要はありません。ただし、Process MonitorなどのSysinternalsツールでDbgHelp.dllのパスを指定する場合は、クラシックバージョンを使用するのがよいかと思います。最新バージョンはDbgHelp.dllのパスがC:\Program Files\WindowsApps\Microsoft.WinDbg_1.0.2304.0_neutral__8wekyb3d8bbwe\
のようにアプリケーションパッケージのバージョンを含むパスになっており、Sysinternalsツールで指定するには不便なためです。
WinDbgクラシックの入手方法
Windows SDKのISOファイルをダウンロードしてマウントし、Installersフォルダー内の"X64 Debuggers And Tools-x64 en-us.msi"または"X86 Debuggers And Tools-x86_en-us.msi"を実行してインストールすることができます。
基本的に最新バージョンのWindows SDKはサポート期間内のWindowsのバージョン上でも利用できますので、Windows 10やWindows Server 2016などの古いバージョンで利用する場合も最新のWindows SDKを利用することができます。x64版とx86版のどちらをインストールするかは、デバッグ対象のアプリケーションが32ビットか64ビットかに依ります。32ビットアプリケーションをデバッグする場合はx86版、64ビットアプリケーションをデバッグする場合はx64版をインストールしてください。
なお、クラシック版のWinDbgはインストールを必要とせず、フォルダーごとコピーするだけでも実行することができます。運用環境などでアプリケーションのインストールが制限されている場合は、一旦別のコンピューターにインストールしてC:\Program Files\Windows Kits\10\Debuggers\x64
やC:\Program Files\Windows Kits\10\Debuggers\x86
フォルダーをコピーして使用するとよいでしょう。
WinDbg最新バージョンの入手とインストールの方法
以下のいずれかの方法で入手できます。
- Microsoft Storeからインストールする。
- インストーラーパッケージをダウンロードしてインストールする。
- WinGetを使用してインストールする。
winget install Microsoft.WinDbg
特に事情がなければ、Microsoft Storeからインストールするのが最も簡単で便利です。インストール手順は以下の通りです。
-
スタートメニューから
Microsoft Store
を開きます。 -
検索ボックスに
WinDbg
と入力し、検索結果からWinDbg
を選択します。 -
入手
ボタンをクリックしてインストールを開始します。
参考情報
WinDbgの使用方法
WinDbgの使用方法は非常に多岐にわたりますが、公式のチュートリアルを確認して進めるのが最も確実です。また、本サイトでも具体的なヒープ破損などのシナリオのデバッグを解説する中でWinDbgを使用していますので、その中で具体的なコマンドや操作方法を学ぶことができます。
本ページでは、WinDbgを使用したデバッグの基本的な流れを説明します。
1. WinDbgを起動してプロセスにアタッチする
まずはWinDbgを起動して、デバッグ対象のプロセスにアタッチする方法を説明します。以下の手順で行います。
-
デバッグ対象アプリケーションとしてメモ帳(notepad.exe)を起動しておきます。
-
スタートメニューから
WinDbg
を開きます。 -
File
メニューを開きます。 -
Start debugging
メニューを選択し、Attach to process
を選択します。 -
表示されているプロセスの一覧リストからデバッグ対象のプロセス(ここではnotepad.exe)を選択して
Attach
ボタンをクリックします。一覧に表示されているプロセスが多い場合は、プロセス名またはプロセスIDを入力して絞り込むこともできます。
2. ブレークポイントを設定して関数呼び出し時に一時停止する
デバッグ対象のプロセスにアタッチしたら、自動的にプロセスの実行は一時停止した状態になります。この状態になると、WinDbgのコマンドを使用してデバッグ作業を行うことができます。WinDbgのコマンドは下図の赤枠の部分に入力してEnterキーを押すことで実行できます。"Debuggee isu running"と表示されている場合はプロセスが実行中であることを示しており、この状態ではコマンドを入力することはできません。一時停止したい場合は、Alt
キーとDel
キーを同時に押すか、Home
メニューの左端にあるBreak
を選択します。
デバッグ作業は例えば、特定の関数が呼び出されたときに実行を中断させるためのブレークポイントを設定したり、スタックトレースを取得して呼び出し元の関数を特定することから始めます。ここでは、メモ帳でファイルを保存する操作を行ったときにどの関数が呼び出されているかを確認するために、ブレークポイントを設定してみます。
-
シンボルの設定が行われていることを確認します。シンボルはデバッグ情報を提供し、関数名やクラス名を表示するために必要です。
.sympath
-
パスに
https://msdl.microsoft.co/download/symbols
のURLが表示されていない場合は、以下のコマンドでシンボルパスを設定します。.symfix+
-
ブレークポイントを設定します。ここではファイルを開くための
CreateFile
関数にブレークポイントを設定したいと思いますが、いくつか類似の関数名がありどれが使用されているか分からないため、ワイルドカードを指定してまとめて設定します。bm kernelbase!WriteFile*
-
実行を再開します。
g
-
メモ帳で適当な文字列を入力し、ファイルに保存する操作を行います。下図のようにBreakpointがヒットするはずです。
3. 呼び出し履歴と変数の値を確認する
デバッグの作業で典型的なものとして、関数の呼び出し元の特定や変数の状態確認が挙げられます。WinDbgでは、以下のコマンドを使用してこれらの情報を確認できます。
-
呼び出し履歴を確認します。以下のコマンドでスタックトレースを表示できます。
k
ここで
Call Site
の列にモジュール名(Notepad)の後で+
に続けて表示されている数字は、関数のオフセットです。関数名が表示されていない場合や、オフセットが非常に大きな値を示している場合は、シンボルが正しく読み込まれていない可能性があります。lmvm <モジュール名>
のコマンドで状態が確認できます。"no symbols"と表示されている場合は、何らかの理由でシンボルが読み込まれていないことを示しています。lmvm Notepad
残念ながら私が確認したWindows 11のバージョンではNotePad.exeのシンボルファイルはMicrosoftのシンボルサーバーに公開されていないようでした。シンボルが公開されている場合は、
.reload /f <モジュール名>.<拡張子>
のコマンドでシンボルファイルを再読み込みすることができます。 -
変数の値を確認します。以下のコマンドで特定の変数の値を表示できます。ただし、変数が最適化されている場合やシンボルが正しく読み込まれていない場合、あるいはシンボルからソースコードや変数の情報が削除されている場合は、正しい値が表示されないことがあります。
dv
-
今回はkernelbase.dllのプライベートシンボルが公開されていないため、kernelbase!WriteFile関数の変数は確認できません。このような場合は逆アセンブルが必要になる可能性がありますが、今回のようにパラメーターが公開されている関数の場合は、呼び出し規約とパラメーターの順序を理解していれば、変数の値を確認できる可能性があります。例えば、WriteFile関数の定義は以下のようになっています。
BOOL WriteFile(
HANDLE hFile,
LPCVOID lpBuffer,
DWORD nNumberOfBytesToWrite,
LPDWORD lpNumberOfBytesWritten,
LPOVERLAPPED lpOverlapped
);次に、x64呼び出し規約では第二引数はRDXレジスタに格納されることとなっています。レジスタは関数の処理の実行を通じて変更される可能性がありますが、今回は関数の先頭で一時停止した状態のため、第二引数のlpBufferの値はRDXレジスタに格納されていることが期待できます。以下のコマンドでRDXレジスタの値を確認できます。試しにRDXレジスタが指すアドレスのメモリをバイト単位で表示してみると、メモ帳に入力した文字が格納されていることが確認できます。(日本語を入力した場合は、Shift-JISやUTF-16などでエンコーディングされているため右側の文字列表示は確認できない可能性があります。)
db rdx
4. デバッグの終了
デバッグが完了したら、Home
メニューからStop Debugging
もしくはDetach
を選択してデバッグを終了します。Stop Debugging
を選択すると、デバッグ対象のプロセスも終了しますが、Detach
を選択するとプロセスはそのまま実行され続けます。