移転しました: https://katahiromz.fc2.page/reactos/
Windows互換OS、ReactOSの開発者になるための知識について解説する。
現在利用できるフリーの仮想マシンには、 Qemu と VirtualBox と VMWare があります。
WinDbgでReactOSをデバッグしたい場合は、Visual Studioでビルドして、シンボルファイルを生成する必要があります。
※ Visual C++をインストールしたVisual Studio 2019を想定しています。
※2018.12.20 PATH設定とconfigureのコマンドラインを少し修正しました。日本語Visual Studioでもビルドできるはず。できないときは連絡してね。
※RosBEのCMakeは少しハックされたCMakeです。オリジナルのCMakeとはちょっと違います。興味がある人は、https://github.com/reactos/RosBEをご覧下さい。
※1: リンクが間違っていたので修正しました。
では、実際にデバッグを試してみよう。今回は、explorer.exeのSHELL32!SHFileOperationWをデバッグする。
1. 「ファイル」メニューから「Attach to Kernel」を選択し、「OK」をクリックする。
2. デバッガが再接続待ちになる。デバッグを開始するためにWin2003を起動する。
3. 「Break」ボタンを押してブレークする。これでデバッガコマンドが入力できる状態になる。
4. デバッガで「!process 0 0」コマンドを実行。プロセスのリストが表示される。そのリストの中からexplorer.exeを探す。
... PROCESS 81dec8b0 SessionId: 0 Cid: 06f4 Peb: 7ffdf000 ParentCid: 06d0 DirBase: 14d28000 ObjectTable: e1937640 HandleCount: 350. Image: explorer.exe ...
5. 「explorer.exe」という項目に「PROCESS 81dec8b0」と書いてあるから、これを使ってexplorer.exeのプロセスを
アタッチする。「.process 81dec8b0」コマンドを実行。さらに「.reload /user」コマンドを実行してユーザモードのデバッグを可能にする。
6.「LM」コマンドを実行して、モジュールのリストを表示する。
kd> LM ... 7c800000 7c8c2000 ntdll (pdb symbols) C:\symbols\ntdll.pdb\4613B105DA224E789F26051F952BE5A82\ntdll.pdb 7c8d0000 7d0cf000 SHELL32 (deferred) 7d1e0000 7d27c000 ADVAPI32 (deferred) ...
7. モジュールにSHELL32があるのが分かる。さらにシンボルを検索するために「x SHELL32!SHFile*」を実行する。
kd> x SHELL32!SHFile* 7c9a1cde SHELL32!SHFileOperationW (_SHFileOperationW@4) 7c9a1fc6 SHELL32!SHFileOperationA (_SHFileOperationA@4) 7c959535 SHELL32!SHFileSysBindToStorage (_SHFileSysBindToStorage@24)
8. ここでシンボル「SHELL32!SHFileOperationW」にデバッグブレークを設定するために「bp SHELL32!SHFileOperationW」を実行する。
9. 「g」コマンドでWin2003のデバッグ実行を再開する。
10. Win2003を操作して、ファイルをコピーして貼り付けしようとすると、ブレークが発生する。
kd> g Breakpoint 0 hit SHELL32!SHFileOperationW: 001b:7c9a1cde 8bff mov edi,edi
11. 「kp」コマンドを実行すると呼び出し履歴が得られる。
kd> kp # ChildEBP RetAddr 00 01aef86c 7ca05f85 SHELL32!SHFileOperationW 01 01aefce0 7ca062a3 SHELL32!CFSDropTarget::_MoveCopy+0x1ff 02 01aeff38 7ca06349 SHELL32!CFSDropTarget::_DoDrop+0x270 03 01aeff54 77da3f12 SHELL32!CFSDropTarget::_DoDropThreadProc+0x46 04 01aeffb8 77e6482f SHLWAPI!WrapperThreadProc+0x94 05 01aeffec 00000000 kernel32!BaseThreadStart+0x34
これはSHFileOperationW関数がCFSDropTarget::_MoveCopy 関数から呼び出されているのが分かる。
12. ステップ実行すれば、SHELL32!SHFileOperationWのデバッグが可能になる。
おおざっぱに言うと、デバッグ方法は次のようになる。
1. VirtualBoxにReactOSにインストールした後、電源OFFの状態でVirtualBoxの「設定」でシリアルポートを有効化し、適当な場所のRawファイルに出力するようにする。
2. ReactOSを起動し、起動メニューで「ReactOS (Debug)」を選ぶ。
3. するとデバッグモードで起動し、Rawファイルにデバッグ情報が出力される。
ReactOSでは、ソースコードの中でDPRINT1文(もしくはERR文)をprintfのように使えばデバッグ出力されるようになっている。
キーボードのTab+Kを押せば、デバッガに強制的に移行する。 デバッガに入ると、VirtualBoxの画面の動きが止まるが、 気にせず、Rawファイルを監視しながら、画面をアクティブにした状態でキーボードでデバッガに入力できる。
こちらも参照:Debugging - ReactOS Wiki
今回は、「explorer.exe」プロセスの中の「kernel32.dll」にある CreateDirectoryW(kernel32!CreateDirectoryW)をデバッグする。
1. CreateDirectoryWのアドレスが欲しいので "dll/win32/kernel32/client/file/dir.c" の CreateDirectoryWの関数の中に次の行を追加してリビルドする。
DPRINT1("CreateDirectoryW address: %p\n", CreateDirectoryW);
2. ReactOSにGuest Additionsをインストールした後、デバッグモードで起動する。
3. デスクトップにフォルダを作成すると「CreateDirectoryW address: ...」のような行がデバッグ出力されるはずだ。
(dll/win32/kernel32/client/file/dir.c:102) CreateDirectoryW address: 7C5DA9D0CreateDirectoryWのアドレスは0x7c5da9d0だとわかる。これを使って後でブレークポイントを設定する。
4. Tab+K でデバッガに入る。
[7h Entered debugger on embedded INT3 at 0x0008:0x80958ae8.
5. 「proc list」でプロセスの一覧を得る。
kdb:> proc list PID State Filename 0x00000004 In Memory System 0x00000068 In Memory smss.exe 0x00000098 In Memory csrss.exe 0x000000ac In Memory winlogon.exe 0x000000c4 In Memory services.exe ... 0x000001d8 In Memory explorer.exe ...
6. 一覧の中から「explorer.exe」を探すと
0x000001d8 In Memory explorer.exe
という行が見つかるので、「explorer.exe」プロセスにアタッチするために「proc attach 0x1d8」を実行する。
kdb:> proc attach 0x1d8 Attached to process 0x000001d8, thread 0x000001db.
7. CreateDirectoryWのアドレスは0x7c5da9d0だった。 これを使ってブレークポイントを設定する。
kdb:> bpx 0x7c5da9d0 Breakpoint 0 inserted. Breakpoint 0 enabled. kdb:> set condition INT3 first always
8. 「cont」コマンドで実行を再開し、再びフォルダを作ってみる。ブレークポイントで停止する。
kdb:> cont Entered debugger on breakpoint #0: EXEC 0x001b:0x7c5da9d0 kdb:>
9. 「bt」コマンドで呼び出し履歴を取得してみる。
kdb:> bt Eip: <kernel32.dll:2a9d0> Frames: <shell32.dll:55876> <shell32.dll:55db3> <shell32.dll:570a7> <shell32.dll:48654> <shell32.dll:4985e> <shell32.dll:49f70> <shell32.dll:29af8> <shell32.dll:70d9b> <shell32.dll:71e40> <shell32.dll:71edc> <shell32.dll:6a456> <shell32.dll:6a9cf> ...
ここで「kernel32.dll:2a9d0」やら「shell32.dll:55876」など、わけのわからないものが 書かれているが、これらは、RosBEの「raddr2line」というコマンドラインツールを使えば、 ソースコードの行番号として解読できる。
解読した結果は次の通り。
<kernel32.dll:2a9d0>: dll/win32/kernel32/client/file/dir.c:92 (CreateDirectoryW) <shell32.dll:55876>: dll/win32/shell32/shlfileop.cpp:1310 (copy_dir_to_dir) <shell32.dll:55db3>: dll/win32/shell32/shlfileop.cpp:1502 (copy_files) <shell32.dll:570a7>: dll/win32/shell32/shlfileop.cpp:1966 (SHFileOperationW) <shell32.dll:48654>: dll/win32/shell32/droptargets/CFSDropTarget.cpp:90 (CFSDropTarget::_CopyItems) <shell32.dll:4985e>: dll/win32/shell32/droptargets/CFSDropTarget.cpp:647 (CFSDropTarget::_DoDrop) <shell32.dll:49f70>: dll/win32/shell32/droptargets/CFSDropTarget.cpp:442 (CFSDropTarget::Drop) <shell32.dll:29af8>: dll/win32/shell32/CShellLink.cpp:3167 (CShellLink::Drop) <shell32.dll:70d9b>: dll/win32/shell32/CSendToMenu.cpp:91 (CSendToMenu::DoDrop) <shell32.dll:71e40>: dll/win32/shell32/CSendToMenu.cpp:439 (CSendToMenu::DoSendToItem) <shell32.dll:71edc>: dll/win32/shell32/CSendToMenu.cpp:499 (CSendToMenu::InvokeCommand) <shell32.dll:6a456>: dll/win32/shell32/CDefaultContextMenu.cpp:1020 (CDefaultContextMenu::InvokeShellExt) <shell32.dll:6a9cf>: dll/win32/shell32/CDefaultContextMenu.cpp:1195 (CDefaultContextMenu::InvokeCommand) <shell32.dll:5e0ee>: dll/win32/shell32/CDefView.cpp:1391 (CDefView::InvokeContextMenuCommand) <shell32.dll:5e61b>: dll/win32/shell32/CDefView.cpp:1539 (CDefView::OnContextMenu) <shell32.dll:d1e84>: dll/win32/shell32/CDefView.cpp:321 (CDefView::ProcessWindowMessage) <shell32.dll:d1a3e>: sdk/lib/atl/atlwin.h:1565 (CDefView::WindowProc)
※ COM1に空きがないとか、COM1とは別のポートを使いたい人は、COM2などを代わりに使ったりできるようです。
※ Default symbol pathは、複数のパスが指定できます。「srv*」はインターネットから読み込まれるパスのようです。
シンボルファイルはフォルダとタイムスタンプなどで厳密に管理されているので、バイナリと合わない間違ったシンボルファイルが読み込まれる恐れはないようです。
※ ネット接続が遅い環境では、シンボルのダウンロードに時間がかかります。
※ うまくいかなかったときは、必ずご報告下さい。
ReactOSの開発について解説した。
分からないことがあれば、エラーメッセージで検索するか、 katayama.hirofumi.mz@gmail.com にご連絡を。