WOW64 上でのデバッグ [2/2] ~ .effmach コマンドによるコンテキストのスイッチ
WOW64 上のデバッグ
前のページでは WOW64 とは何か、ということを説明しました。
ここでは実例を挙げて、プログラムのデバッグを実践してみましょう。
テストプログラムは次のようなプログラムです。エディット・ボックスとボタンがあります。エディットボックスに何か文字を入力し、ボタンを押下します。
すると、エディット・ボックスに入力した文字がメッセージボックスに表示される。ただ、それだけのプログラムです。
ちなみに、このプログラムはターゲットを x86 としてビルドしてあります。ですから、前のページで見たようにこのプロセスは WOW64 上で走っているはずです。 このような単純なプログラムで、メッセージボックスに渡した文字がデバッガで確認できるかどうか試してみましょう。
引数、戻り値、エラーコードの確認。これらはデバッガ操作の基本ですからね。
これが出来ないとデバッグをすることは非常に難しいです。
さて、上記のプログラムは dlgwin32.exe というプログラムです。
これをタスクマネージャで見てみましょう。
タスクマネージャでは 32 ビットコードを実行しているプロセスには、*32 というマークが付きます。 下のスクリーンショットのように、確かに dlgwin32.exe にも *32 というマークが付いています。
現在、メッセージボックスを表示して停止している状態なので、この状態のままデバッグできるかどうか試してみましょう。
タスクマネージャからプロセス ID (PID) が 3064 であることもわかったので、PID = 3064 のプロセスにデバッガをアタッチします。 CDB をアタッチするには次のようにします。
> cdb -p 3064
スクリーンショットは次のようになります。
シンボルパスが設定されていなかったので、シンボルパスを設定します。
0:003> .sympath srv*C:\websymbols*http://msdl.microsoft.com/download/symbols Symbol search path is: srv*C:\websymbols*http://msdl.microsoft.com/download/symbols Expanded Symbol search path is: srv*c:\websymbols*http://msdl.microsoft.com/download/symbols
シンボルがわからないという人は、当サイト内の デバッグにあると便利なシンボルとは? などをご覧ください。
シンボルパスを設定したら、シンボルをリロードします。
0:003> .reload Reloading current modules ...................................
このままの状態でスタックバックトレースを見てみます。
0:003> ~*kn 100 0 Id: bf8.660 Suspend: 1 Teb: 00000000`7efdb000 Unfrozen # Child-SP RetAddr Call Site 00 00000000`0008ebf8 00000000`72ed2d92 wow64cpu!CpupSyscallStub+0x9 01 00000000`0008ec00 00000000`72f4d07e wow64cpu!Thunk0Arg+0x5 02 00000000`0008ecc0 00000000`72f4c549 wow64!RunCpuSimulation+0xa 03 00000000`0008ed10 00000000`76e984c8 wow64!Wow64LdrpInitialize+0x429 04 00000000`0008f260 00000000`76e97623 ntdll!LdrpInitializeProcess+0x17e2 05 00000000`0008f760 00000000`76e8308e ntdll! ?? ::FNODOBFM::`string'+0x2bea0 06 00000000`0008f7d0 00000000`00000000 ntdll!LdrInitializeThunk+0xe 1 Id: bf8.1074 Suspend: 1 Teb: 00000000`7efad000 Unfrozen # Child-SP RetAddr Call Site 00 00000000`01c5f0f8 00000000`72ed282c wow64cpu!CpupSyscallStub+0x9 01 00000000`01c5f100 00000000`72f4d07e wow64cpu!WaitForMultipleObjects32+0x32 02 00000000`01c5f1c0 00000000`72f4c549 wow64!RunCpuSimulation+0xa 03 00000000`01c5f210 00000000`76ecd177 wow64!Wow64LdrpInitialize+0x429 04 00000000`01c5f760 00000000`76e8308e ntdll! ?? ::FNODOBFM::`string'+0x2bfe4 05 00000000`01c5f7d0 00000000`00000000 ntdll!LdrInitializeThunk+0xe 2 Id: bf8.15ec Suspend: 1 Teb: 00000000`7efa1000 Unfrozen # Child-SP RetAddr Call Site 00 00000000`055cf0f8 00000000`72ed2bcd wow64cpu!CpupSyscallStub+0x9 01 00000000`055cf100 00000000`72f4d07e wow64cpu!Thunk0ArgReloadState+0x1a 02 00000000`055cf1c0 00000000`72f4c549 wow64!RunCpuSimulation+0xa 03 00000000`055cf210 00000000`76ecd177 wow64!Wow64LdrpInitialize+0x429 04 00000000`055cf760 00000000`76e8308e ntdll! ?? ::FNODOBFM::`string'+0x2bfe4 05 00000000`055cf7d0 00000000`00000000 ntdll!LdrInitializeThunk+0xe # 3 Id: bf8.f7c Suspend: 1 Teb: 00000000`7efaa000 Unfrozen # Child-SP RetAddr Call Site 00 00000000`01d3fc78 00000000`76f48638 ntdll!DbgBreakPoint 01 00000000`01d3fc80 00000000`76eb39cb ntdll!DbgUiRemoteBreakin+0x38 02 00000000`01d3fcb0 00000000`00000000 ntdll!RtlUserThreadStart+0x25
残念ながらここには、ntdll と wow64*.dll の呼び出ししか見えておらず、windlg32.exe は全く見えてきません。 現在、メッセージボックスを表示中のはずなのですが、メッセージボックスに関わる何かも全く見えません。
このままでは、デバッグは困難です。
ここで役に立つのが、.effmach コマンドです。
WOW64 エミュレーションモードでは、それぞれのスレッドは二つのコンテキストを保持しています。 ひとつはネイティブコンテキストで、もうひとつは 32 ビットコンテキストです。(それぞれのスレッドに TEB も二つあり、 WOW64 プロセスの PEB も二つになります. これらは !wow64exts.straddr コマンドでみれます)
.effmach コマンドを利用すると 32 ビットのコンテキストにスイッチすることができるのです。
では、実際に .effmach x86 コマンドの後に、スタックバックトレースを確認してみましょう。
0:003> .effmach x86 Effective machine: x86 compatible (x86) 0:003:x86> ~*kbn 100 0 Id: bf8.660 Suspend: 1 Teb: 7efdb000 Unfrozen # ChildEBP RetAddr Args to Child 00 0018f528 74f12674 00480764 00000000 00000001 USER32!NtUserWaitMessage+0x15 01 0018f564 74f1288a 006f034e 00480764 00000000 USER32!DialogBox2+0x222 02 0018f590 74f4f8d0 74ee0000 0027a740 00480764 USER32!InternalDialogBox+0xe5 03 0018f644 74f4fbac 00000040 00000000 00401000 USER32!SoftModalMessageBox+0x757 04 0018f79c 74f4fcaf 0018f7a8 00000028 00480764 USER32!MessageBoxWorker+0x269 05 0018f808 74f4fea5 00480764 0018f860 0041219c USER32!MessageBoxTimeoutW+0x52 06 0018f828 74f4fee7 00480764 0018f860 0041219c USER32!MessageBoxExW+0x1b 07 0018f844 0040119d 00480764 0018f860 0041219c USER32!MessageBoxW+0x18 08 0018fa68 004010d9 00480764 000003e9 00140c3a dlgwin32!OnCommand+0x6d 09 0018fa8c 74ef6238 00480764 00000111 000003e9 dlgwin32!DialogProc+0xd9 0a 0018fab8 74f212a1 00401000 00480764 00000111 USER32!InternalCallWinProc+0x23 0b 0018fb34 74f210e2 00000000 00401000 00480764 USER32!UserCallDlgProcCheckWow+0x10f 0c 0018fb84 74f244b1 008209c0 00000000 00000111 USER32!DefDlgProcWorker+0xb7 0d 0018fbc0 74efcd81 008209c0 00000000 76e739e8 USER32!SendMessageWorker+0x42f 0e 0018fbe4 74f35f4b 00480764 00000111 000003e9 USER32!SendMessageW+0x7f 0f 0018fbfc 74f3608c 00749e10 00000000 00000000 USER32!xxxButtonNotifyParent+0x66 10 0018fc24 74f22eec 0024e070 00000000 00000001 USER32!xxxBNReleaseCapture+0x138 11 0018fcc0 74f37042 00749e10 00000000 00000202 USER32!ButtonWndProcWorker+0xa07 12 0018fce8 74ef6238 00140c3a 00000202 00000000 USER32!ButtonWndProcW+0x54 13 0018fd14 74ef68ea 74f36fee 00140c3a 00000202 USER32!InternalCallWinProc+0x23 14 0018fd8c 74ef7d31 00000000 76ed939c 00140c3a USER32!UserCallWinProcCheckWow+0x109 15 0018fdec 74ef7dfa 76ed939c 00000000 0018fe28 USER32!DispatchMessageWorker+0x3bc 16 0018fdfc 74f12292 0018fe44 00000001 008209c0 USER32!DispatchMessageW+0xf 17 0018fe28 74f12715 00480764 00000000 00000000 USER32!IsDialogMessageW+0x5f6 18 0018fe6c 74f1288a 00480764 00000000 00000000 USER32!DialogBox2+0x15f 19 0018fe98 74f127b8 00400000 00417060 00000000 USER32!InternalDialogBox+0xe5 1a 0018feb8 74f12aa1 00400000 00417060 00000000 USER32!DialogBoxIndirectParamAorW+0x37 1b 0018fedc 00401118 00400000 00000065 00000000 USER32!DialogBoxParamW+0x3f 1c 0018fef8 00401370 00400000 00000000 00234e33 dlgwin32!WinMain+0x18 1d 0018ff88 768c3677 7efde000 0018ffd4 77069d72 dlgwin32!__tmainCRTStartup+0x113 1e 0018ff94 77069d72 7efde000 4e8b3da0 00000000 KERNEL32!BaseThreadInitThunk+0xe 1f 0018ffd4 77069d45 004013db 7efde000 00000000 ntdll_77030000!__RtlUserThreadStart+0x70 20 0018ffec 00000000 004013db 7efde000 00000000 ntdll_77030000!_RtlUserThreadStart+0x1b 1 Id: bf8.1074 Suspend: 1 Teb: 7efad000 Unfrozen # ChildEBP RetAddr Args to Child 00 0214fdf4 77091dab 00000003 00262728 00000001 ntdll_77030000!NtWaitForMultipleObjects+0x15 01 0214ff88 768c3677 00000000 0214ffd4 77069d72 ntdll_77030000!TppWaiterpThread+0x33d 02 0214ff94 77069d72 002626f8 4c873da0 00000000 KERNEL32!BaseThreadInitThunk+0xe 03 0214ffd4 77069d45 77091c7f 002626f8 00000000 ntdll_77030000!__RtlUserThreadStart+0x70 04 0214ffec 00000000 77091c7f 002626f8 00000000 ntdll_77030000!_RtlUserThreadStart+0x1b 2 Id: bf8.15ec Suspend: 1 Teb: 7efa1000 Unfrozen # ChildEBP RetAddr Args to Child 00 056cfec8 74c10816 000000e8 00000000 056cff10 ntdll_77030000!NtWaitForSingleObject+0x15 01 056cff34 768c1184 000000e8 00000bb8 00000000 KERNELBASE!WaitForSingleObjectEx+0x98 02 056cff4c 768c1138 000000e8 00000bb8 00000000 KERNEL32!WaitForSingleObjectExImplementation+0x75 03 056cff60 72932b44 000000e8 00000bb8 00000000 KERNEL32!WaitForSingleObject+0x12 04 056cff80 72932b88 056cff94 768c3677 02194958 imjp10k!IImeConvert::SearchDicThread+0x1aa 05 056cff88 768c3677 02194958 056cffd4 77069d72 imjp10k!SearchDicThreadFunc+0x19 06 056cff94 77069d72 02194958 4bff3da0 00000000 KERNEL32!BaseThreadInitThunk+0xe 07 056cffd4 77069d45 72932b6f 02194958 00000000 ntdll_77030000!__RtlUserThreadStart+0x70 08 056cffec 00000000 72932b6f 02194958 00000000 ntdll_77030000!_RtlUserThreadStart+0x1b # 3 Id: bf8.f7c Suspend: 1 Teb: 7efaa000 Unfrozen # ChildEBP RetAddr Args to Child 00 0497fff4 00000000 00000000 00000000 00000000 ntdll_77030000!RtlUserThreadStart
確かに 32 ビット上のスタックバックトレースと同様の結果が表示されました。
ここから、メッセージボックスの文字が本当に拾えるか確認してみましょう。
スレッド 0 にて、dlgwin32 のコードから MessageBoxW を呼び出しています。
MessageBoxW の第二引数はメッセージテキストですよね?ですから、それを持ってきて表示しましょう。
MessageBox の W ですから、それは Unicode のはずですから、du コマンドで表示します。
0:003:x86> du 0018f860
0018f860 "テスト"
確かに 「テスト」 という文字が出てきました。
このようにして、WOW64 上のコードを調べれば良いのです。
尚、32 ビットにスイッチした後、元のモードに戻るには .effmach . (ドット) とすれば OK です。
0:003:x86> .effmach . Effective machine: x64 (AMD64) 0:003>
おまけ ~ !wow64exts デバッガエクステンションのコマンドを利用する
他の方法として、!wow64exts デバッガエクステンションを利用することも出来ます。!wow64exts.k コマンドを使って次のようにスタックを見ることも出来ます。
0:003> ~0s wow64cpu!CpupSyscallStub+0x9: 00000000`72ed2dd9 c3 ret 0:000> !wow64exts.k Walking 64bit Stack... Child-SP RetAddr Call Site 00000000`0008ebf8 00000000`72ed2d92 wow64cpu!CpupSyscallStub+0x9 00000000`0008ec00 00000000`72f4d07e wow64cpu!Thunk0Arg+0x5 00000000`0008ecc0 00000000`72f4c549 wow64!RunCpuSimulation+0xa 00000000`0008ed10 00000000`76e984c8 wow64!Wow64LdrpInitialize+0x429 00000000`0008f260 00000000`76e97623 ntdll!LdrpInitializeProcess+0x17e2 00000000`0008f760 00000000`76e8308e ntdll! ?? ::FNODOBFM::`string'+0x2bea0 00000000`0008f7d0 00000000`00000000 ntdll!LdrInitializeThunk+0xe Walking 32bit Stack... ChildEBP RetAddr 0018f528 74f12674 USER32!NtUserWaitMessage+0x15 0018f564 74f1288a USER32!DialogBox2+0x222 0018f590 74f4f8d0 USER32!InternalDialogBox+0xe5 0018f644 74f4fbac USER32!SoftModalMessageBox+0x757 0018f79c 74f4fcaf USER32!MessageBoxWorker+0x269 0018f808 74f4fea5 USER32!MessageBoxTimeoutW+0x52 0018f828 74f4fee7 USER32!MessageBoxExW+0x1b 0018f844 0040119d USER32!MessageBoxW+0x18 0018fa68 004010d9 dlgwin32!OnCommand+0x6d 0018fa8c 74ef6238 dlgwin32!DialogProc+0xd9 0018fab8 74f212a1 USER32!InternalCallWinProc+0x23 0018fb34 74f210e2 USER32!UserCallDlgProcCheckWow+0x10f 0018fb84 74f244b1 USER32!DefDlgProcWorker+0xb7 0018fbc0 74efcd81 USER32!SendMessageWorker+0x42f 0018fbe4 74f35f4b USER32!SendMessageW+0x7f 0018fbfc 74f3608c USER32!xxxButtonNotifyParent+0x66 0018fc24 74f22eec USER32!xxxBNReleaseCapture+0x138 0018fcc0 74f37042 USER32!ButtonWndProcWorker+0xa07 0018fce8 74ef6238 USER32!ButtonWndProcW+0x54 0018fd14 74ef68ea USER32!InternalCallWinProc+0x23 0:000>
また 32 ビットモードにスイッチする、!wow64exts.sw コマンドというのもあります。
0:000> !wow64exts.sw Switched to 32bit mode 0:000:x86> ~*kbn . 0 Id: bf8.660 Suspend: 1 Teb: 7efdb000 Unfrozen # ChildEBP RetAddr Args to Child 00 0018f528 74f12674 00480764 00000000 00000001 USER32!NtUserWaitMessage+0x15 01 0018f564 74f1288a 006f034e 00480764 00000000 USER32!DialogBox2+0x222 02 0018f590 74f4f8d0 74ee0000 0027a740 00480764 USER32!InternalDialogBox+0xe5 03 0018f644 74f4fbac 00000040 00000000 00401000 USER32!SoftModalMessageBox+0x757 ... (同様なので省略) 0:000:x86>
.effmach も内部的には !wow64exts.sw でモードを切り替えているので結果は同様になります。