ホーム ざれごと ワシントン州 ツール NT豆知識 Win32プログラミングノート 私的用語 ジョーク いろいろ ゲーム雑記 Favorites 掲示板 Mail
NTネイティブなデバッガのテクニック。
デバッガは日々変更されつつあるので、ここで紹介したテクニックが使えなかったらごめん、ということで。
現在、おもいっきり工事中。
ntsd(あるいはcdb)は、ユーザモードで動作するデバッガです。 一方、kd はカーネルモードデバッガ。システムのほとんど全ての機能をデバッグすることができます。
別の言い方をすると、ntsd はアプリケーションデベロッパのためのデバッガ。 kd はシステムデベロッパ(ドライバなど)のためのデバッガ、とでも言えるでしょう。
ここでは、 MSDN などのような網羅的な解説は行いません。 知っていると便利、デバッグ生活をちょっと楽にしてくれるような、小手先テクニックを紹介していきます。
スタックトレースの表示は、基本中の基本でしょう。
スタックトレース表示のコマンドは、 k です。
k に b をつけて kb とすると、引数も含めてスタックトレースを表示します。
k に v をつけて kv とすると、引数、トラップフレームなど詳細情報を含めてスタックトレースを表示します。
r
単に r と叩くと、全てのレジスタを表示し、次に実行するアセンブリラインを表示します。
0:000> r eax=01a05b40 ebx=25af0d2d ecx=0006fc7c edx=00000000 esi=0006fefc edi=77d43ca4 eip=7ffe0304 esp=0006febc ebp=0006fed8 iopl=0 nv up ei pl nz na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=0038 gs=0000 efl=00000202 *SharedUserSystemCall+c: 7ffe0304 c3 ret
特定のレジスタだけ表示したい場合は、 r <レジスタ名> です。 x86の場合、レジスタ名は次のようになります。
汎用レジスタ |
eax, ebx, ecx, edx, esi, edi
|
インストラクションポインタ |
eip
|
スタック |
esp, ebp
|
セグメントレジスタ |
cs, ss, ds, es, fs, gs |
d コマンドです。タイプを後ろにくっつけられます。
d[type] [range]
こんな感じ。
0:000> dd 6fefc l4 0006fefc 01080856 00000100 0000007b 00580001 0:000> dc 6fefc l4 0006fefc 01080856 00000100 0000007b 00580001 V.......{.....X.
b |
byte
|
w |
word
|
d |
dword
|
ds |
dword (or qword for 64bit) with symbols
|
a |
ascii
|
c |
dword and char
|
u |
unicode
|
q |
qword |
~スレッド番号s
スレッド番号は、
~
と叩けば一覧が取得可。
プロセスの情報は、
|
と叩けばよい。
0:001> ~ 0 id: 6a8.2e4 Suspend: 1 Teb 7ffde000 Unfrozen . 1 id: 6a8.804 Suspend: 1 Teb 7ffdd000 Unfrozen 0:001> | . 0 id: 6a8 create name: notepad.exe
.trap <アドレス>
↓のエクセプションの項目を見てください。
ba <e|r|w|i> <1|2|4> <アドレス>
たぶんよく使うのは、ba w 4 の形式。 指定されたアドレスに書き込みがなされると、デバッガへ落ちる。
セッションに属するページには、ソフトウェアブレークポイントがしかけられない(kd)。 そのため、ハードウェアブレークポイントを使わなければならない。
g $ra
CPU のタイプに関わらず実行可能。
$ra は、リターンアドレスを示す仮想レジスタ。
x <シンボル>
ワイルドカードを含むことができる。
相手が関数なら、そのタイプも表示可(Stripされていないシンボルが必要?)。
変数なら、場合によっては型・中身まで表示してくれる。
特定のイベント発生時のデバッガブレークのイネーブル・ディスエーブル。 sxe [event] でイネーブル、sxd [event] でディスエーブル。 sx で一覧表示。
モジュールの新規ロード時にブレーク。
.lines
行番号の表示モードをトグルします。
ちょっとうっとうしいので、
必要以外はオフにしているのが吉かも。
デバッガから
Ctrl+C
を叩くと、実行を中断させてデバッガのプロンプトに落ちます。
ntsd では、デバッグ中のアプリケーションをフォアグラウンドに持ってきて
F12
を押すと デバッガのプロンプトに落ちます。
ntsd の場合、"-g" オプションをつけなければイニシャルブレークポイントで止まります。 kd の場合、 Ctrl+K で、OS のスタート時に止まるかどうかをトグルします。
当然 ntsd オンリですが、起動時オプションに "-d" をつけると、kd から ntsd を操作可能になります。
起動オプションに、"-p <process-id>" を付けます。
ntsd の場合、 .restart で実行を最初からやり直すことができることがあります。 kd で OS をリブートさせるには、 .reboot です。
エクセプションから得られる情報には、エクセプションレコードと、コンテクストレコードがあります。 エクセプションレコードには、例外の種別・アクセスしたアドレスなどが収められています。 例外の種類によっては、収められている情報が異なるかもしれません。
コンテクストレコードには、例外が発生したときのCPUのコンテクストが収められています。
デバッガが例外をキャッチしたときには、上記のエクセプションレコード・コンテクストレコードのアドレスを表示してくれると思います。 それらのアドレスを、それぞれ .exr と .cxr に渡すことで、カレントコンテクストをセットすることができます。 あとは、r なり kv なりなんなりと。 コンテクストを元に戻す場合には、引数なしで .cxr とタイプします。
例外のキャッチは、sx で設定できます。 ファーストチャンスエクセプションは、例外が最初に起こったとき。 例外ハンドラに処理を渡す前に、デバッガに落ちます。 セカンドチャンスエクセプションは、例外がどのハンドラにも処理されなかったとき。 セカンドチャンスの方はデフォルトでオン、ファーストチャンスはデフォルトでオフだったと思います。
kd> .exr 0x6ff3e8 ExceptionAddress: 75b42ef7 (CSRSRV!CsrUnhandledExceptionFilter) ExceptionCode: c0000006 ExceptionFlags: 00000010 NumberParameters: 3 Parameter[0]: 00000000 Parameter[1]: 75b42ef7 Parameter[2]: c000009a Inpage operation failed at 75b42ef7, due to I/O error c000009a
.breakin
このコマンドで、ntsd から kd へ落ちます。
ntsd へ戻る場合は、kd のプロンプトから、
g
でおっけ。
なお、kd へ落ちたときにはデバッガのコンテクストがカレントなので、必要に応じてコンテクストを切り替えます。
!peb で、Process Environment Block の情報が表示されます。ロードされている DLL の一覧も出てきます。 もう少し詳細を知りたい場合は、 !dlls でおっけ。
lml
!teb
TEB (Thread Environment Block) を表示させると、その中に LastError が格納されています。
TBD
!error エラーコード
を使えば、謎に満ちたステータスコードの正体がわかります。
TBD
単にプロセス一覧を見たい場合、
!process 0 0
加えて、非常にconciseなスレッド情報を見るには、
!process 0 4
。
他に、!stacks とか。
!process 0 0 csrss.exe
Sid で表示されるのが、セッションID。
.process -p <EPROCESSのアドレス>
元のコンテクストに戻る場合は、引数をつけずに .process でおっけ。
TBD
目的のセッションコンテクストで走っているプロセスを見つけて、プロセスコンテクストを切り替える。
!vm
基本状況に加え、それぞれのプロセスごとのメモリ使用状況も表示します。
簡単にすませたい場合は、
!vm 1
でおっけ。
kd> !vm 1 *** Virtual Memory Usage *** Physical Memory: 81805 ( 327220 Kb) Paging File Name paged out Current: 491520Kb Free Space: 433312Kb Minimum: 491520Kb Maximum: 983040Kb Available Pages: 528 ( 2112 Kb) ResAvail Pages: 89 ( 356 Kb) ********** Running out of physical memory ********** Locked IO Pages: 103 ( 412 Kb) Free System PTEs: 25798 ( 103192 Kb) Free NP PTEs: 7218 ( 28872 Kb) Free Special NP: 0 ( 0 Kb) Modified Pages: 9450 ( 37800 Kb) NonPagedPool Usage: 24984 ( 99936 Kb) NonPagedPool Max: 32236 ( 128944 Kb) PagedPool 0 Usage: 3711 ( 14844 Kb) PagedPool 1 Usage: 529 ( 2116 Kb) PagedPool 2 Usage: 519 ( 2076 Kb) PagedPool Usage: 4759 ( 19036 Kb) PagedPool Maximum: 64512 ( 258048 Kb) Shared Commit: 1315 ( 5260 Kb) Special Pool: 0 ( 0 Kb) Shared Process: 1955 ( 7820 Kb) PagedPool Commit: 4759 ( 19036 Kb) Driver Commit: 822 ( 3288 Kb) Committed pages: 110319 ( 441276 Kb) Commit limit: 198282 ( 793128 Kb)
!poolused
オプションはヘルプを見よ。
!poolfind
オプションはヘルプを見よ。
!pool <アドレス>
プールブロックのバリデーションも行ってくれます(スモールプールの場合)。 なお、ベリファイアでスペシャルプールの指定を行っている場合は、アロケーションの大きさに関わらず1ページ割り当てられるので、びっくりしないでください。
!verifier 1
でベリファイアの設定を確認。
userexts / userkdx は、ウィンドウマネージャのデバッグエクステンションです。 userexts がユーザモード、userkdx がカーネルモード用。 なおこれらのコマンドを使うには、stripされていないシンボルが必要です。
!dti, !dt, !dp, !dpi
!dw
0: kd> !dw -vf bc6809d8 s 0 et 0x81e05020 t 0x99e8feb0 q 0xe2080760 ppi 0xb967fe70 i 4d8.7e4 dwwin.exe pwnd = 0xbc6809d8 pti 0x99e8feb0 handle 0x004501be spwndNext 0xbc672d90 "" spwndPrev 0x00000000 "" spwndParent 0xbc6706e8 " " spwndChild 0xbc680ae8 "&Close" spwndOwner 0xbc6808e8 "OfficeWatson" rcWindow (110,122)-(529,321) 419x199 rcClient (113,151)-(526,318) 413x167 lpfnWndProc 0x77d67459 (user32!DefDlgProcW) Unicode pcls 0xbc677d28 (V):0x8002 (NV):0x8002 Name:"#32770" hrgnUpdate 0x00000000 spwndLastActive 0xbc6809d8 "Microsoft Office" ppropList 0xbc680ad0 pSBInfo 0x00000000 spmenuSys 0x00000000 spmenu/id 0x00000000 hrgnClip 0x00000000 pName "Microsoft Office" dwUserData 0x00a4ff34 state 0x00030048 state2 0x80000300 ExStyle 0x00010109 style 0x84c000c4 fnid 0x000002a4 hImc 0x00000000 bFullScreen 0y0 hModule 0x314c0000 pStackTrace 0x00000000 pActCtx 0x00000000 BFBITMAP bc6809f8:80 BFICON bc6809f8:40 CBFAUTOHSCROLL bc6809f8:40 CBFOEMCONVERT bc6809f8:80 DF3DLOOK bc6809f8:04 EFAUTOHSCROLL bc6809f8:80 EFAUTOVSCROLL bc6809f8:40 EFMULTILINE bc6809f8:04 SBFSIZEBOXBOTTOMRIGHT bc6809f8:04 SFNOPREFIX bc6809f8:80 SFREALSIZECONTROL bc6809f8:40 WEFCONTROLPARENT bc6809f6:01 WEFDLGMODALFRAME bc6809f4:01 WEFTOPMOST bc6809f4:08 WEFWINDOWEDGE bc6809f5:01 WFBORDER bc6809fa:80 WFCAPTION bc6809fa:c0 WFCLIPSIBLINGS bc6809fb:04 WFCPRESENT bc6809ec:08 WFDIALOGWINDOW bc6809ee:01 WFDLGFRAME bc6809fa:40 WFFRAMEON bc6809ec:40 WFICONICPOPUP bc6809fb:c0 WFPOPUP bc6809fb:80 WFTITLESET bc6809ee:02 WFTOPLEVEL bc6809fa:40 WFWIN31COMPAT bc6809f1:01 WFWIN40COMPAT bc6809f1:02
!dcls
!dws
!ddesk
!dmon
!dy
!dsi。 -m オプションによる SYSTEMMETRICS は、XPではThemeによりオーバーライドされている可能性あり、要注意。
!dq, !dmq
!dsms
!dhot
!dti で表示される
-->
ささっているウィンドウメッセージが分かっていれば、!dsms -m msg で調べます。
それらしいものが見つかれば、Recv の et で示される値を !thread に渡してやり、スタックトレースを調べます。
必要に応じて ntsd をターゲットプロセスにアタッチし、ユーザモードで調べることも必要かもしれません。
例えば、ターゲットスレッドがクリティカルセクションを待っていれば、!lock や !critsec でオーナースレッドを調べ、さらに追跡します。
Sender が分かっていれば、t あるいは Win32Thread で示される値を !dsms -s に渡してやると、滞留している SendMessage の一覧が取得できます。 それらしいものを見つけて、あとは以下同文。
ありがちな例としては、
!ddesk でデスクトップの一覧を表示するとき、グローバルフックも同時に表示してくれます。 スレッド毎のフックは、!dhk -a。 個別のスレッドを見たい場合は、!dhk に、!process 0 4 で表示される Win32Thread の値を渡します。
ホーム ざれごと ワシントン州 ツール NT豆知識 Win32プログラミングノート 私的用語 ジョーク いろいろ ゲーム雑記 Favorites 掲示板 Mail