home ホーム search 検索 -  login ログイン  | reload edit datainfo version cmd icon diff delete  | help ヘルプ

技術/Windows/WinDbgメモ

技術/Windows/WinDbgメモ

技術 / Windows / WinDbgメモ
id: 706 所有者: msakamoto-sf    作成日: 2010-07-13 23:53:23
カテゴリ: WinDBG Windows プログラミング 

WinDBG参考図書:

※"AWD"サンプルコードのシンボルパスについて
本書中で紹介されているURLは2011年1月の時点でアクセス不能になっている。

http://www.advancedwindowsdebugging.com/symbols/symstore.pri

このため、"AWD"公式HPからダウンロードしたプライベートシンボルファイルをローカルPC上に展開し、そちらへのパスをシンボルパスに設定する必要がある。


WinDBGやWindowsデバッグ関連参考サイト:



sympath, _NT_SYMBOL_PATHの注意点

本やWeb記事からのコピペで済ませると後々「シンボルキャッシュが複数箇所にコピーされてしまう」「VC++側でデバッグ実行時にやたら遅くなる」などトラブルにつながるので注意。

シンボルサーバーを使うための基本:

symsrv*ServerDLL*DownstreamStore*Servers

ショートカット:

sym*DownstreamStore*Servers
=
symsrv*symsrv.dll*DownstreamStore*Servers

"Servers"にはセミコロンで区切って一つ以上のシンボルサーバー・シンボルストアのPATHを指定できる。
"DownstreamStore"には"Servers"からダウンロードしたシンボルファイルをキャッシュする場所を指定する。アスタリスクで区切ることで「カスケード」できる。

以下、設定のバリエーションと落とし穴を紹介。

ローカルキャッシュ(DownstreamStore)無し

・Microsoftのシンボルサーバーから都度DL、ローカルキャッシュ無し。一番シンプルで一番非効率。

srv*http://msdl.microsoft.com/download/symbols

・Microsoftのシンボルサーバー+自社イントラネット内の共有フォルダをシンボルストアとして指定。

srv*http://msdl.microsoft.com/download/symbols;\\MyCorpShare\SymStore

落とし穴1:シンボルストアを複数指定する場合はセミコロンで区切ること。アスタリスクで区切ってしまうと、後述のDownstreamStoreのカスケード設定になってしまい、シンボルキャッシュが複数箇所にコピーされてしまうトラブルにつながる。

ローカルキャッシュ(DownstreamStore)有り

・MicrosoftのシンボルサーバーからDLして C:\localsymcache にキャッシュ。

srv*C:\localsymcache*http://msdl.microsoft.com/download/symbols

・↑+共有フォルダからもキャッシュ。

srv*C:\localsymcache*http://msdl.microsoft.com/download/symbols;\\MyCorpShare\SymStore

・デバッガのデフォルトのローカルキャッシュを使う。

srv**http://msdl.microsoft.com/download/symbols

→アスタリスク2つでデフォルトのローカルキャッシュを使うようになる。デフォルトのローカルキャッシュは、デバッガのホームディレクトリのsymディレクトリ。ホームディレクトリはデフォルトではデバッガのインストールディレクトリになっており、"!homedir"コマンドで変更できる。

ローカルキャッシュのカスケード

例:

srv*C:\localsymcache*\\MyCorpShare\SymCache*http://msdl.microsoft.com/download/symbols
  1. "C:\localsymcache"でシンボル発見 → ロード
  2. "C:\localsymcache"にシンボル無し → "\\MyCorpShare\SymCache" を探索
    1. "\\MyCorpShare\SymCache"でシンボル発見 → ロード + "C:\localsymcache"にコピー
    2. "\\MyCorpShare\SymCache"にシンボル無し → Microsoftを検索
      1. Microsoftでシンボル発見 → ロード + "C:\localsymcache", "\\MyCorpShare\SymCache" の両方にコピー
      2. Microsoftにシンボル無し → 失敗

落とし穴2:キャッシュとして使うことを意図していないシンボルストアを間違ってカスケードしてしまうと、意図しないシンボルのコピーによりディスク領域の消費や、あるいは書き込み権限絡みのエラーが発生する可能性がある。

"cache"によるローカルキャッシュ

srv*...;cache*localsymcache;srv*...

といった書式でより細かいローカルキャッシュ制御が可能。ただし _NT_SYMBOL_PATH に組み込んでVC++でデバッグ実行をすると、数分以上のwaitが発生する。

参考:C言語系/memos/VC++/13, _NT_SYMBOL_PATH環境変数に注意

落とし穴3:上記の通り、_NT_SYMBOL_PATH環境変数 + VC++の併用ケースでは実用上の問題で非推奨となる。

まとめ

  • Servers と ローカルキャッシュ(DownstreamStore) とその区切り文字を混同しない。
    • Serversの区切り:セミコロン
    • DownstreamStoreの区切り:アスタリスク
  • _NT_SYMBOL_PATH環境変数とVC++を併用するときは"cache"を使わない。
  • "Debugging Tools for Windows" ヘルプファイルの "Advanced SymSrv Use" をよく読みましょう。

簡単なBATファイルを作っておくと便利

デバッグ対象のEXEのあるディレクトリなど、適当なディレクトリにそのプロジェクト専用のWinDbg起動用BATファイルを作っておくと便利かもしれない。"_NT_"で始まる実行イメージやソース、シンボルディレクトリの環境変数を定義したり、windbgのコマンドラインオプションを埋め込んでおく。

windbg.bat:

@echo off
cd /d %~dp0

set PATH=C:\WinDDK\7600.16385.1\Debuggers;%PATH%
set _NT_SYMBOL_PATH=srv*C:\temp\symcache*http://msdl.microsoft.com/download/symbols;.
set _NT_EXECUTABLE_IMAGE_PATH=.
set _NT_SOURCE_PATH=.
set _NT_DEBUG_LOG_FILE_APPEND=windbg.log

start windbg.exe -srcpath %_NT_SOURCE_PATH% -loga %_NT_DEBUG_LOG_FILE_APPEND% -WF foo.wew debugee.exe arg1 arg2 ...

"_NT_SYMBOL_PATH"については各自の環境に応じて調整する。
わざわざ"-srcpath"を指定しているのは、環境変数で"_NT_SOURCE_PATH"を指定してもGUI("File" -> "Source File Path ...")に反映ず、"-srcpath"で指定すればちゃんと反映されたため。

"-WF"でWorkspace設定を読み込ませるようにしているが、WinDbg終了時に"File" -> "Save Workspace"を実行しておかないと保存されない=意味無しので注意。終了時に自動保存してくれると楽なんだけど。

VMware上のゲストWindowsOSのカーネルデバッグ接続(Named Pipe)

ホストOS側での設定
VMwareマシンの設定でシリアルポートを追加する。「接続」で「名前付きパイプを使用する」を設定。「この端末はサーバです。」「接続先はアプリケーションです。」の組み合わせに設定する。
ゲストOS側での設定
技術/Windows/WinDbgメモ を参考にゲストOS側を COM1 でカーネルデバッガの接続を有効にして起動する。
接続

ホストOS側:

windbg -k com:pipe,port=\\.\pipe\xxxxyyyy

カーネルデバッグ中にEXEファイルのシンボルをロードしたい

AWDの第三章では、プロセスが例外やDebugBreakし、カーネルデバッガに制御が渡るシナリオが紹介されています。
サンプルとして提示されているコマンド実行結果では、バックトレースの中に例外を発行した02sample.exeのシンボルが表示されています。

実際に試してみると、カーネルデバッガにブレークした時点では特定のプロセスのEXEファイル(今回なら02sample.exe)のシンボルをロード出来ませんでした。こんなエラーになっちゃいます:

kd> !reload -f 02sample.exe

"02sample.exe" was not found in the image list.
Debugger will attempt to load "02sample.exe" at given base 00000000.

Please provide the full image name, including the extension (i.e. kernel32.dll)
for more reliable results.Base address and size overrides can be given as
.reload <image.ext>=<base>,<size>.
DBGENG:  02sample.exe - Partial symbol image load missing image info
...
Unable to add module at 00000000

色々試行錯誤してみたところ、".thread"メタコマンドに"/p"オプションを付けてコンテキストを切り替えると上手くいきました。
多分、特定プロセスのコンテキストに切り替わったためにEXEのメモリイメージを認識し、シンボルファイルを検索できるようになったんだと思います。

例:"AWD"第二章で使われている02sample.exeのシンボルファイルをKD上でロードしてみます。

1.まずスレッドアドレスを見つけます。

kd> !process 0 4 02sample.exe
PROCESS 81e4c020  SessionId: 0  Cid: 0474    Peb: 7ffdf000  ParentCid: 0278
    DirBase: 09bc0420  ObjectTable: e1837878  HandleCount:   7.
    Image: 02sample.exe

        THREAD 8203a810  Cid 0474.0ee0  Teb: 7ffde000 Win32Thread: 00000000 WAIT
        ^^^^^^^^^^^^^^^

2.".thread /p"でコンテキストを切り替えます。ついでに"/r"も付けて、ロード出来るシンボルファイルはロードさせちゃいます。

kd> .thread /p /r 8203a810
Implicit thread is now 8203a810
Implicit process is now 81e4c020
.cache forcedecodeuser done
Loading User Symbols
....
(ntdll.dllのシンボルファイルのロードメッセージ。
 "!sym noisy"に設定してたから出力されたのかも。)

3.バックトレースの表示をトリガーにして、02sample.exeのシンボルをロードしてみる。

kd> k
  *** Stack trace for last set context - .thread/.cxr resets it
ChildEBP RetAddr  
b1f5a42c 80502cf0 nt!KiSwapContext+0x2e
b1f5a438 804fbd72 nt!KiSwapThread+0x46
b1f5a460 8063afc4 nt!KeWaitForSingleObject+0x1c2
b1f5a540 8063c099 nt!DbgkpQueueMessage+0x17c
b1f5a564 8063c1cb nt!DbgkpSendApiMessage+0x45
b1f5a5f0 804feccc nt!DbgkForwardException+0x8f
b1f5a9b0 8050245b nt!KiDispatchException+0x37e
b1f5ad34 80542ec1 nt!KiRaiseException+0x175
b1f5ad50 8053f668 nt!NtRaiseException+0x31
b1f5ad50 7c812afb nt!KiFastCallEntry+0xf8
(kernel32.dllのシンボルロードメッセージ)
0006ff04 77bd272c kernel32!RaiseException+0x53
(02sample.exeのシンボルロードメッセージ)
0006ff44 010016eb msvcrt!_CxxThrowException+0x36
0006ff58 01001d63 02sample!RaiseCPP+0x1b [c:\awd\chapter2\sample.cpp @ 50]
0006ff70 01001c7b 02sample!AppInfo::Loop+0xb3 [c:\awd\common\menu.h @ 47]
0006ff7c 01002038 02sample!wmain+0x1b [c:\awd\chapter2\sample.cpp @ 228]
0006ffc0 7c817077 02sample!__wmainCRTStartup+0x102 [d:\vistartm\base\crts\crtw32\dllstuff\crtexe.c @ 711]
0006fff0 00000000 kernel32!BaseProcessStart+0x23

02sampleのシンボルがロードされシンボル解決出来たことが分かりました。

オンライン(=Live)カーネルデバッグでユーザープロセス・スレッドにコンテキストを切り替えたい

基本の流れ:

  1. "!session"コマンドで現在ログイン中のセッションを確認
  2. "!sprocess" or "!process"コマンドでプロセスを検索
  3. ".process"コマンドでプロセスコンテキストを切り替え
  4. ".thread"コマンドでスレッドコンテキストを切り替え
  5. スタック、レジスタの確認、ブレークポイント設定など

例1:AWDの第二章サンプル(02sample.exe)のプロセスにコンテキストを切り替えてみる:

######## ログインセッションは一つしか無かったので、"!process"コマンドで02sample.exeを検索
kd> !process 0 0 02sample.exe
PROCESS 81cfb268  SessionId: 0  Cid: 0b68    Peb: 7ffda000  ParentCid: 09cc
    DirBase: 09c40360  ObjectTable: e226bd20  HandleCount:   9.
    Image: 02sample.exe

PROCESS 820a7340  SessionId: 0  Cid: 0bec    Peb: 7ffde000  ParentCid: 09cc
    DirBase: 09c40280  ObjectTable: e1c44c98  HandleCount:   9.
    Image: 02sample.exe

######## Process Address (EPROCESS) 0x820a7340 に切り替えてみる。
kd> .process 820a7340  
Implicit process is now 820a7340
kd> .context
User-mode page directory base is 9c40280

######## Process Environment Block (PEB)を見てみる。ちゃんと02sample.exeになってる。
kd> !peb
PEB at 7ffde000
    InheritedAddressSpace:    No
    ReadImageFileExecOptions: No
    BeingDebugged:            No
    ImageBaseAddress:         01000000
    Ldr                       00181e90
    Ldr.Initialized:          Yes
    Ldr.InInitializationOrderModuleList: 00181f28 . 00182070
    Ldr.InLoadOrderModuleList:           00181ec0 . 00182060
    Ldr.InMemoryOrderModuleList:         00181ec8 . 00182068
            Base TimeStamp                     Module
         1000000 472187dc Oct 26 15:23:24 2007 C:\awdbin\WinXP.x86.chk\02sample.exe

######## "!process"コマンドで直接EPROCESSを指定して確認してみる。
kd> !process 820a7340 2
PROCESS 820a7340  SessionId: 0  Cid: 0bec    Peb: 7ffde000  ParentCid: 09cc
    DirBase: 09c40280  ObjectTable: e1c44c98  HandleCount:   9.
    Image: 02sample.exe

        THREAD 81c35b90  Cid 0bec.082c  Teb: 7ffdd000 ...
            81c35d84  Semaphore Limit 0x1

kd> !thread 81c35b90  
THREAD 81c35b90  Cid 0bec.082c  Teb: 7ffdd000 ...
    81c35d84  Semaphore Limit 0x1
Waiting for reply to LPC MessageId 00009d87:
Current LPC port e113ec50
Not impersonating
DeviceMap                 e1884720

######## Thread Address (ETHREAD) 0x81c35b90 に切り替えてみる。
kd> .thread 81c35b90  
Implicit thread is now 81c35b90

######## スタックトレースを確認:ちゃんと02sample.exeのスタックになっている。
kd> k
  *** Stack trace for last set context - .thread/.cxr resets it
ChildEBP RetAddr  
b1946c68 80502cf0 nt!KiSwapContext+0x2e
...
0006ff70 01001c7b 02sample!AppInfo::Loop+0x70 [c:\awd\common\menu.h @ 38]
0006ff7c 01002038 02sample!wmain+0x1b [c:\awd\chapter2\sample.cpp @ 228]
...

例2:手っ取り早くスタックトレースを確認してみる。

########### まず02sample.exeのEPROCESS, ETHREADを確認。
kd> !process 0 2 02sample.exe
PROCESS 81cfb268  SessionId: 0  Cid: 0b68    Peb: 7ffda000  ParentCid: 09cc
    DirBase: 09c40360  ObjectTable: e226bd20  HandleCount:   9.
    Image: 02sample.exe

        THREAD 81be5b80  Cid 0b68.0b6c  Teb: 7ffdf000 Win32Thread: 00000000 WAIT: (WrLpcReply) UserMode Non-Alertable
            81be5d74  Semaphore Limit 0x1


PROCESS 820a7340  SessionId: 0  Cid: 0bec    Peb: 7ffde000  ParentCid: 09cc
    DirBase: 09c40280  ObjectTable: e1c44c98  HandleCount:   9.
    Image: 02sample.exe

        THREAD 81c35b90  Cid 0bec.082c  Teb: 7ffdd000 Win32Thread: 00000000 WAIT: (WrLpcReply) UserMode Non-Alertable
            81c35d84  Semaphore Limit 0x1

########## ";" でつなげて一気にプロセス・スレッド両コンテキストを切り替える。
kd> .process /p 820a7340; .thread /p 81c35b90 ; .context
Implicit process is now 820a7340
.cache forcedecodeuser done
Implicit thread is now 81c35b90
Implicit process is now 820a7340
.cache forcedecodeuser done
User-mode page directory base is 9c40280

########## スタックトレースの確認→02sample.exeのものになっている。
kd> k
  *** Stack trace for last set context - .thread/.cxr resets it
ChildEBP RetAddr  
b1946c68 80502cf0 nt!KiSwapContext+0x2e
...
0006ff70 01001c7b 02sample!AppInfo::Loop+0x70 [c:\awd\common\menu.h @ 38]
0006ff7c 01002038 02sample!wmain+0x1b [c:\awd\chapter2\sample.cpp @ 228]
...

".process", ".thread" で "/p" オプションがあったりなかったりしますが、WinDBGのヘルプを見てみると、オンラインデバッグの場合は"/p"オプションを付けたほうが良いようです。例ではEPROCESSやETHREAD・スタックトレースを確認出来れば良いので、付けたり付けなかったりしてます。

オンライン(=Live)カーネルデバッグでユーザープロセスにブレークポイント設定→ステップ実行

指定のプロセス・スレッドに切り替えるところまでは上と同じです。一点違うのは、".process"コマンドに"/i"オプションを付けることです。これが無いと、ユーザープロセスがブレークポイントを踏んだ時にカーネルデバッガがbreakしてくれませんでした。

kd> !process 0 2 02sample.exe
PROCESS 81bd1020  SessionId: 0  Cid: 0ff0    Peb: 7ffdf000  ParentCid: 09cc
    DirBase: 09c40360  ObjectTable: e20bfad8  HandleCount:   7.
    Image: 02sample.exe

        THREAD 81d1e020  Cid 0ff0.0ff4  Teb: 7ffde000 ...
            81d1e214  Semaphore Limit 0x1


PROCESS 820c29f0  SessionId: 0  Cid: 03d4    Peb: 7ffdf000  ParentCid: 09cc
    DirBase: 09c40280  ObjectTable: e1d34bb0  HandleCount:   7.
    Image: 02sample.exe

        THREAD 81cff450  Cid 03d4.01d8  Teb: 7ffde000 ...
            81cff644  Semaphore Limit 0x1

kd> .process /i 820c29f0; .thread 81cff450; .context
You need to continue execution (press 'g' <enter>) for the context
to be switched. When the debugger breaks in again, you will be in
the new process context.
Implicit thread is now 81cff450
User-mode page directory base is b1f000
kd> k
  *** Stack trace for last set context - .thread/.cxr resets it
ChildEBP RetAddr  
b1a56c68 80502cf0 nt!KiSwapContext+0x2e
...
0006ff70 01001c7b 02sample!AppInfo::Loop+0x70 [c:\awd\common\menu.h @ 38]
0006ff7c 01002038 02sample!wmain+0x1b [c:\awd\chapter2\sample.cpp @ 228]
...

02sample.exeのOutputDebug関数にブレークポイントを設定してみます。"/p"と"/t"でProcessAddress, ThreadAddress両方を指定する必要がありました。

kd> x 02sample!OutputDebug
01001c30 02sample!OutputDebug (void)

kd> bp /p 820c29f0 /t 81cff450 02sample!OutputDebug

kd> bl
 0 e 01001c30     0001 (0001) 02sample!OutputDebug
     Match thread data 81cff450  Match process data 820c29f0

kd> g
########### ".process /i"オプションにより一旦breakしますので、再度"g"で再開させます。

Break instruction exception - code 80000003 (first chance)
nt!RtlpBreakWithStatusInstruction:
80529c0c cc              int     3
kd> g

これで、指定されたプロセス・スレッドがOutputDebug関数に入るとカーネルデバッガにbreakします。あとは"t"や"p"コマンドなど一般的なデバッグコマンドを使ってユーザープロセスをデバッグできます。



プレーンテキスト形式でダウンロード
現在のバージョン : 4
更新者: msakamoto-sf
更新日: 2011-04-02 16:20:23
md5:45bde9e32d583d5dad8a02e26cb1c23a
sha1:33130105fb38edf22f06b9ce54c2ff6eb7b063fe
コメント
コメントを投稿するにはログインして下さい。