ReactOS開発の手引き

by 片山博文MZ

2023年2月28日

移転しました: https://katahiromz.fc2.page/reactos/

概要

Windows互換OS、ReactOSの開発者になるための知識について解説する。

必須条件

準備1

現在利用できるフリーの仮想マシンには、 QemuVirtualBoxVMWare があります。

準備2 (Visual Studioを使わない場合)

準備2 (Visual Studioを使う場合)

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をご覧下さい。

Win2003評価版のデバッグ方法

※1: リンクが間違っていたので修正しました。

Win2003のデバッグの練習

では、実際にデバッグを試してみよう。今回は、explorer.exeSHELL32!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のデバッグが可能になる。

ReactOSのデバッグ方法 (Visual Studioを使わない場合)

おおざっぱに言うと、デバッグ方法は次のようになる。

1. VirtualBoxにReactOSにインストールした後、電源OFFの状態でVirtualBoxの「設定」でシリアルポートを有効化し、適当な場所のRawファイルに出力するようにする。

2. ReactOSを起動し、起動メニューで「ReactOS (Debug)」を選ぶ。

3. するとデバッグモードで起動し、Rawファイルにデバッグ情報が出力される。

ReactOSでは、ソースコードの中でDPRINT1文(もしくはERR文)をprintfのように使えばデバッグ出力されるようになっている。

キーボードのTab+Kを押せば、デバッガに強制的に移行する。 デバッガに入ると、VirtualBoxの画面の動きが止まるが、 気にせず、Rawファイルを監視しながら、画面をアクティブにした状態でキーボードでデバッガに入力できる。

こちらも参照:Debugging - ReactOS Wiki

ReactOSのデバッグの練習

今回は、「explorer.exe」プロセスの中の「kernel32.dll」にある CreateDirectoryWkernel32!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: 7C5DA9D0
CreateDirectoryWのアドレスは0x7c5da9d0だとわかる。これを使って後でブレークポイントを設定する。

4. Tab+K でデバッガに入る。


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)

ReactOSのデバッグ方法 (Visual Studio + WinDbgを使う場合)

  1. VirtualBoxで新しい仮想マシン (Other Windows (32-bit)) を作成し、そこにVSでビルドしたbootcd.isoを使ってReactOSをインストールする。
  2. 仮想マシンの状態を保存するために、VirtualBoxを操作して仮想マシンのスナップショットを作成する。
  3. 仮想マシンをいったん終了し、仮想マシンのシリアルポートの設定をする。シリアルポートCOM1にパイプ(\\.\pipe\com1)を設定する。「シリアルポート」の「シリアルポートを有効化」にチェックを入れ、「ポートモード」を「ホストにパイプ」に変更して、「パス/アドレス」に「\\.\pipe\com1」を指定する。チェックボックス「存在するパイプ/ソケットに接続」のチェックをはずす。
  4. WinDbgを起動し、「Settings」の「Debugging settings」で「Default symbol path」にシンボルの場所(msvc_pdbフォルダの場所)を設定。「ファイル」メニューから「Attach to Kernel」を選択し、「Pipe」「Reconnect」にチェックを入れ、「Port」に「\\.\pipe\com1」を設定し、「OK」ボタンを押す。
  5. 仮想マシンを起動する。うまくいけばReactOSをWinDbgでデバッグできる。

日本語WinXPのデバッグ方法 (WinDbgを使う場合)

  1. 日本語XPモードの配布ファイル「WindowsXPMode_ja-jp.exe」をどこかから拾ってくる(Wayback Machineに配布元URLを入力して下さい)。2022年現在、配布元( https://www.microsoft.com/ja-jp/download/details.aspx?id=8002 )からは直接はダウンロードできないようだ。
  2. 「WindowsXPMode_ja-jp.exe」を7-Zipで展開する。ファイル「xpminstl32.msi」ができる。
  3. さらに「msiexec.exe /a xpminstl32.msi targetdir="%~dp0xpmode"」という内容のバッチファイルをxpminstl32.msiと同じ場所に置き、実行する。
  4. しばらく待つと"Windows XP Mode base.vhd"が展開される。このファイルのプロパティの「読み込み専用」を解除。
  5. 期限切れのため、システム時計を過去(2008/1/1)に戻して仮想マシンのWinXPにログオンする。
  6. WinXPの電源を切る前に管理者権限のコマンドプロンプトで「bootcfg /debug ON /PORT COM1 /ID 1」を実行する。また、VirtualBox Guest Additionsも忘れずインストールする。 ※ただし、「許可されていないプログラムから……保護する」はチェックを外すこと。
  7. 仮想マシンの状態を保存するために、VirtualBoxを操作して仮想マシンのスナップショットを作成する。
  8. 仮想マシンをいったん終了し、仮想マシンのシリアルポートの設定をする。シリアルポートCOM1にパイプ(\\.\pipe\com1)を設定する。「シリアルポート」の「シリアルポートを有効化」にチェックを入れ、「ポートモード」を「ホストにパイプ」に変更して、「パス/アドレス」に「\\.\pipe\com1」を指定する。チェックボックス「存在するパイプ/ソケットに接続」のチェックをはずす。
  9. WinDbgを起動し、「Settings」の「Debugging settings」で「Default symbol path」に「srv*」を設定。「ファイル」メニューから「Attach to Kernel」を選択し、「Pipe」「Reconnect」にチェックを入れ、「Port」に「\\.\pipe\com1」を設定し、「OK」ボタンを押す。
  10. 仮想マシンを起動する。うまくいけばWinXPをWinDbgでデバッグできる。

※ COM1に空きがないとか、COM1とは別のポートを使いたい人は、COM2などを代わりに使ったりできるようです。
※ Default symbol pathは、複数のパスが指定できます。「srv*」はインターネットから読み込まれるパスのようです。 シンボルファイルはフォルダとタイムスタンプなどで厳密に管理されているので、バイナリと合わない間違ったシンボルファイルが読み込まれる恐れはないようです。
※ ネット接続が遅い環境では、シンボルのダウンロードに時間がかかります。
※ うまくいかなかったときは、必ずご報告下さい。

開発を始める

結び

ReactOSの開発について解説した。

分からないことがあれば、エラーメッセージで検索するか、 katayama.hirofumi.mz@gmail.com にご連絡を。

inserted by FC2 system