windbgのLogger Extensionと「bp」コマンドにおけるコマンド実行

windbg における 2 つの小ネタをメモしておきます。

この日記における windbg の実行環境は以下の通りです。User-Mode におけるデバックとなります。

コンポーネント バージョン情報
OS Windows XP SP3*1
windbg 6.12.0002.633

Logger Extension(logexts.dll)

windbg拡張機能には、Logger Extension(logexts.dll)があります。この機能を使うと、windbgデバッグするプログラムの Windows API 呼び出し(引数、戻り値を含む)を記録できます(すべての API 呼び出しを記録できるか分かりません)。以下に MSDN の文章を引用します。

Logger can monitor the actions of a user-mode target application and record all of its API calls. The resulting information can be displayed in the debugger, saved as a text file, or displayed in a powerful interactive format by the LogViewer tool.

Logger and LogViewer - Windows drivers | Microsoft Docs

試しに DLL Hijacking のブログ記事で使った Mozilla Firefox 3.5.8 を windbg で起動して、LoadLibrary 関数呼び出しを記録してみます。

LoadLibrary 関数呼び出しの記録

[File]メニューの[Open Executable]にて firefox.exe を起動します。

CommandLine: "C:\Program Files\Mozilla Firefox\firefox.exe"
Symbol search path is: SRV*c:\websymbols*http://msdl.microsoft.com/download/symbols
Executable search path is: 
ModLoad: 00400000 004e0000   firefox.exe
ModLoad: 7c940000 7c9df000   ntdll.dll
ModLoad: 7c800000 7c933000   C:\WINDOWS\system32\kernel32.dll
(略)
ModLoad: 770d0000 7715b000   C:\WINDOWS\system32\OLEAUT32.dll
ModLoad: 7c420000 7c4cf000   C:\Program Files\Mozilla Firefox\MOZCPP19.dll
ModLoad: 003e0000 003e7000   C:\Program Files\Mozilla Firefox\xpcom.dll
(874.864): Break instruction exception - code 80000003 (first chance)
eax=00191eb4 ebx=7ffdf000 ecx=00000003 edx=00000008 esi=00191f48 edi=00191eb4
eip=7c94120e esp=0012fb20 ebp=0012fc94 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
ntdll!DbgBreakPoint:
7c94120e cc              int     3


Logger Extension で LoadLibrary関数呼び出し(LoadLibrary だけではないけど)を記録する手順は、以下の通りです。各コマンドの詳細は、「!logexts.help」で確認できます。この手順では、windbg の実行結果を適宜編集しています。文頭が // の文は僕のコメントとなります。

// Logger Extension のロード
0:000> !load logexts

// テキストファイルへの出力の有効化 
0:000> !logo e t

Windows API Logging Extensions  v3.01
Parsing the manifest files...
Location: C:\Program Files\Debugging Tools for Windows (x86)\winext\manifest\main.h
   Parsing file "main.h" ...
   Parsing file "winerror.h" ...
   Parsing file "kernel32.h" ...
(略)
   Parsing file "d3d8caps.h" ...
   Parsing file "dsound.h" ...
Parsing completed.
  Debugger            Disabled
  Text file           Enabled
  Verbose log         Enabled

// LoadLibrary関数を含むカテゴリ「ProcessAndThreads」の記録のみ有効化
0:000> !logc d *
All categories disabled.
0:000> !logc e 19
 19 ProcessesAndThreads             Enabled

// Windows API 呼び出しの記録の開始(出力先:C:\windbg_logext_dir) 
0:000> !loge C:\windbg_logext_dir
Logexts injected. Output: "C:\windbg_logext_dir\LogExts\"
Logging enabled.

// ここで「g」コマンドを実行する。


windbgFirefox を実行すると、「C:\windbg_logext_dir\LogExts」フォルダに firefox.exe.txt と firefox.exe.lgv が生成されます。firefox.exe.txt には、以下のようなログが記録されています。この記録から LoadLibrary関数のみ抽出すると、こうなります。

Thrd 864 00401C44 GetCurrentProcessId() -> 0x00000874
Thrd 864 00401C4C GetCurrentThreadId() -> 0x00000864
Thrd 864 78133B81 GetModuleFileNameW( NULL 0x00000104) -> 0x0000002C ( "C:\Program Files\Mozilla Firefox\firefox.exe")
Thrd 864 7813E268 GetEnvironmentStringsW() -> "ALLUSERSPROFILE=C:\Documents and Settings\All Users"
Thrd 864 7813E2F7 FreeEnvironmentStringsW( "ALLUSERSPROFILE=C:\Documents and Settings\All Users") -> TRUE
Thrd 864 00401024 LoadLibraryExA( "ntdll.dll" NULL 0x00000000) -> 0x7C940000
(略)


ただ、Logger Extension ですべての Windows API 呼び出しを記録できるか疑問があります。Mozilla Firefox 3.6.8 には dwmapi.dll を呼び出すときに DLL Hijacking の問題がありますが、Logger Extension ではその LoadLibrary関数呼び出しを記録していませんでした。後述の「bp」コマンドにおけるコマンド実行、および「Process Monitor」のスタックトレース(下図)では LoadLibraryW 関数で dwmapi.dll を読み込んでいることを確認しています。

「bp」コマンドにおけるコマンド実行

windbg にはブレイクポイントを設定する「bp」コマンドがあります。この「bp」コマンドではブレイクすると同時に特定のコマンドを実行する使用方法があります。以下に MSDN の文章を引用します。

User-Mode
[~Thread] bp[ID] [Options] [Address [Passes]] ["CommandString"]
(略)
CommandString
Specifies a list of commands that are executed every time that the breakpoint is encountered the specified number of times. You must enclose the CommandString parameter in quotation marks. Use semicolons to separate multiple commands.


Debugger commands in CommandString can include parameters. You can use standard C-control characters (such as \n and \"). Semicolons that are contained in second-level quotation marks (\") are interpreted as part of the embedded quoted string.


The CommandString commands are executed only if the breakpoint is reached while the application is executing in response to a g (Go) command. The commands are not executed if you are stepping through the code or tracing past this point.


Any command that resumes program execution after a breakpoint (such as g or t) ends the execution of the command list.

bp, bu, bm (Set Breakpoint) - Windows drivers | Microsoft Docs

Logger Extension と同様に、「bp」コマンドを使って Mozilla Firefox 3.5.8 における LoadLibrary 関数呼び出しを記録してみます。

LoadLibrary 関数呼び出しの記録

Logger Extension の場合と同様に、windbgfirefox.exe を起動します。「bp」コマンドを設定して実行すると、以下のような結果が得られます。

0:000> bp kernel32!LoadLibraryA ".printf \"++++++++++ LoadLibraryA\\n+++ StackTrace\\n\"; kb; .printf \"\\n+++ First Argument(dump string)\\n\"; dW poi(esp+4); .printf \"\\n++++++++++\\n\";"

// ここで「g」コマンドを実行する。
0:000> g
ModLoad: 60740000 60749000   C:\WINDOWS\system32\LPK.DLL
ModLoad: 5b5d0000 5b5d8000   C:\WINDOWS\system32\rdpsnd.dll
ModLoad: 762b0000 762c0000   C:\WINDOWS\system32\WINSTA.dll
ModLoad: 59250000 592a5000   C:\WINDOWS\system32\NETAPI32.dll
ModLoad: 76ba0000 76bab000   C:\WINDOWS\system32\PSAPI.DLL
++++++++++ LoadLibraryA
+++ StackTrace
ChildEBP RetAddr  Args to Child              
0012f940 73f9e23f 73f81840 73f9e439 7c9657e5 kernel32!LoadLibraryA
0012f9e8 73f9e40d 0012fa10 7c94118a 73f80000 USP10!UspProcessAttach+0x1f
0012f9f0 7c94118a 73f80000 00000001 0012fd30 USP10!_DllMain+0x1d
0012fa10 7c95b5d2 73f9e439 73f80000 00000001 ntdll!LdrpCallInitRoutine+0x14
0012fb18 7c95fbdc 0012fd30 7ffde000 7ffdf000 ntdll!LdrpRunInitializeRoutines+0x344
0012fc94 7c95fad7 0012fd30 7c940000 0012fce0 ntdll!LdrpInitializeProcess+0x114b
0012fd1c 7c94e457 0012fd30 7c940000 00000000 ntdll!_LdrpInitialize+0x183
00000000 00000000 00000000 00000000 00000000 ntdll!KiUserApcDispatcher+0x7

+++ First Argument(dump string)
73f81840  6467 3369 2e32 6c64 006c 0000 0000 0000  gdi32.dll.......
73f81850  6e69 7469 656d 6964 6966 616e 7369 6c6f  initmedifinaisol
73f81860  696c 6167 736d 7465 0404 0404 0404 0404  ligamset........
73f81870  0404 0404 0404 0404 0404 0404 0404 0404  ................
73f81880  0404 0404 0404 0404 0404 0202 0202 0201  ................
73f81890  0201 0101 0101 0201 0202 0102 0101 0101  ................
73f818a0  0101 0401 0404 0404 0101 0101 0101 0101  ................
73f818b0  0102 0801 0808 0808 0808 0808 0808 0404  ................

++++++++++
eax=00005b9d ebx=73f9e439 ecx=0012f99c edx=7c94e514 esi=0012fa04 edi=00000001
eip=7c801d7b esp=0012f944 ebp=0012f9e8 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
kernel32!LoadLibraryA:
7c801d7b 8bff            mov     edi,edi


この「bp」コマンドでは、CommandString として ".printf \"++++++++++ LoadLibraryA\\n+++ StackTrace\\n\"; kb; .printf \"\\n+++ First Argument(dump string)\\n\"; dW poi(esp+4); .printf \"\\n++++++++++\\n\";" を設定します。
この CommandString では、以下の 5 つのコマンドを連続で実行します。この CommandString ではコマンドを実行した後にブレイクしますが、「g」コマンドを連結することで CommandString 実行後すぐにデバッグを再開できます。

  1. .printf」コマンドによる文字列の出力
  2. kb」コマンドによるスタックトレースの出力
  3. 「.printf」コマンドによる文字列の出力
  4. dW」コマンドによる LoadLibrary関数の第一引数のメモリ内容の出力
    • poi」コマンドで、レジスタESP値+4 の DWORD 値を参照する。
  5. 「.printf」コマンドによる文字列の出力

参考情報

*1:2011年12月29日時点の最新パッチを適用済み。VMware Player 4.0.0 上で動作させています。