Windows PE で IOCTL サンプルを動作させる方法についてご紹介します。
今回は、Windows PE で IOCTL サンプルを動作させる方法についてご紹介します。
Windows PE でご自身のドライバがうまく動作しない場合の、比較の一助になれば幸いです。
前提
今回は、以前ご案内した以下のブログと同じ環境がすでにあることを前提とします。
上記で使用している ISO を上書きすることになるため、仮想マシンはシャットダウン (電源 OFF でもよいです) し、カーネルデバッガも終了しておきます。また、今回はあくまでもテスト目的のため、通常必要となる以下の方法での INF ファイルのドライバインストール方法を使っていない点にご留意ください。
今回使用する IOCTL のサンプルにも、運用環境で使用してはいけない旨記載されております。
- IOCTL サンプル サイト 抜粋:
[!CAUTION] This sample driver is not a Plug and Play driver. This is a minimal driver meant to demonstrate a feature of the operating system. Neither this driver nor its sample programs are intended for use in a production environment. Instead, they are intended for educational purposes and as a skeleton driver.
手順
(1) IOCTL サンプルを Visual Studio 2019 で x64 / Debug でビルドします。
1-1. サンプルの入手
IOCTL サンプルは、以下のサイトの右側の緑色の [Code] ボタンを押すと表示される [Download ZIP] ボタンで Windows-driver-samples-master.zipをダウンロードすると、Windows-driver-samples-master\general\ioctl\wdm のフォルダにあります。
https://github.com/Microsoft/Windows-driver-samples1-2. サンプルのビルド
このフォルダの ioctl.sln を、Visual Studio 2019 で開きます。Exe フォルダの下にはユーザーモードアプリケーションである ioctlapp のプロジェクト、Sys フォルダの下にはカーネルモードドライバである sioctl のプロジェクトがあることを確認できます。
[ソリューション ‘ioctl’] を右クリックして [構成マネージャー] をクリックします。
今回は、[アクティブソリューション構成] を **[Debug]**、[アクティブ ソリューション プラットフォーム] を [x64] とします。
また、ioctlapp のプロパティを開き、[構成プロパティ]-[C/C++]-[コード生成] の [ランタイム ライブラリ] は [マルチスレッド デバッグ (/MTd)] にしておきます。
[ソリューション ‘ioctl’] を右クリックして [ソリューションのリビルド] をクリックします。
これで、ioctlapp.exe, sioctl.sys ができます。次のステップに必要なファイルと場所は以下です。
ファイル 場所 ioctlapp.exe ioctl\wdm\exe\x64\Debug sioctl.sys ioctl\wdm\sys\x64\Debug
(2) 管理者権限で起動された [展開およびイメージング ツール環境] で、以下のコマンドを実行します。(作業用ディレクトリは、前回の記事の前提のまま D:\WinPE_amd64 とします。)
2-1. テスト署名が利用可能になるようにします。
1
> bcdedit /store d:\WinPE_amd64\media\EFI\Microsoft\Boot\BCD /set {default} testsigning on
2-2. 上述のアプリケーションとドライバのファイルを WinPE のイメージにコピーするために、WinPE のイメージをマウントします。
1
> Dism /Mount-Image /ImageFile:"D:\WinPE_amd64\media\sources\boot.wim" /index:1 /MountDir:"D:\WinPE_amd64\mount"
2-3. マウントした WinPE のイメージに、上述のアプリケーションとドライバのファイルをコピーします。ここでは例として \Windows\Ioctl というフォルダにコピーするとします。
1
2
3> xcopy D:\develop\blog\ioctl\wdm\sys\x64\Debug\sioctl.sys "D:\WinPE_amd64\mount\Windows\Ioctl"
> xcopy D:\develop\blog\ioctl\wdm\exe\x64\Debug\ioctlapp.exe "D:\WinPE_amd64\mount\Windows\Ioctl"2-4. WinPE イメージのマウントを解除し、変更をコミットします。
1
> Dism /Unmount-Image /MountDir:"D:\WinPE_amd64\mount" /commit
2-5. 上記が完了したら、以下のコマンドで Windows PE の ISO ファイルを作成します。
1
> makewinpemedia /iso d:\WinPE_amd64\winpe_x64_debug.iso
(3) 「Windows PE でのネットワーク経由のカーネルデバッガ接続方法」の記事で作成した仮想マシンを起動すると、上記の ISO ファイルで起動します。
(4) カーネルデバッガ側は、以下のコマンドを実行することで Windows PE のターゲットにデバッガ接続し、ブレークインできます。
1
> windbg.exe -k net:port=50005,key=5.5.5.5
(5) カーネルデバッガ側では、Symbol Search Path に上記 1-2. の sioctl.sys と ioctlapp.exe のシンボルファイル (sioctl.pdb と ioctlapp.pdb) の存在するフォルダへのフルパスを追記しておきます。
(6) カーネルデバッガの Commands ウィンドウで以下のコマンドを実行して、sioctl.sys の DriverEntry にブレークポイントを貼ってから g を実行します。
1
> bp sioctl!DriverEntry
(7) 仮想マシン上で開いているコマンドプロンプト上で、以下を実行します。
7-1. ioctlapp.exe のある \Windows\Ioctl フォルダに移動します。
1
> cd \Windows\Ioctl
7-2. ioctlapp.exe をオプションなしで実行します。
1
> ioctlapp.exe
(8) カーネルデバッガ側で sioctl!DriverEntry にブレークインします。自動的に ioctl\wdm\sys\sioctl.c が開かない場合には開きます。
(9) Commands ウィンドウで p などを入力して、161 行目の「return status;」までステップ実行します。ここで、!drvobj sioctl 2 と実行すれば、132 行目で「DriverObject->MajorFunction[IRP_MJ_CREATE] = SioctlCreateClose;」を実行している通り、以下のように、IOCTL_MJ_CREATE のコールバックに SIoctl!SioctlCreateClose がセットされていることがわかります。
1 | !drvobj sioctl 2 |
(10) カーネルデバッガの Commands ウィンドウで以下のコマンドを実行して、sioctl.sys の SioctlCreateClose にブレークポイントを貼ってから g を実行します。
1 | > bp sioctl!SioctlCreateClose |
(11) カーネルデバッガ側で SIoctl!SioctlCreateClose にブレークインします。k を実行すると、確かに ioctlapp.exe の main 関数の CreateFile からオープンされたことが確認できます。
1 | k |
Testapp.c の 106 行目のコードは、確かに以下の通り CreateFile() を実行しています。
これでオープンできているのは、sioctl.sys で以下のように、IoCreateDevice() の第 3 引数 ntUnicodeString で \Device\SIOCTL という NT Device Name をセットしており、かつ、これに対するシンボリックリンクとして、Win32 Name である \DosDevices\IoctlTest を IoCreateSymbolicLink() で作成しているためです。
参考:
Introduction to MS-DOS Device Names
ただ、上記のドキュメントにも以下の抜粋の様にある通り、上記の方法を取るのは、一般には非 WDM ドライバです。WDM ドライバは一般的にはデバイスインターフェースを登録して使用します。
抜粋:
A named device object that is created by a non-WDM driver typically has an MS-DOS device name. An MS-DOS device name is a symbolic link in the object manager with a name of the form \DosDevices\DosDeviceName.
WDM drivers do not usually supply MS-DOS device names for their devices. Instead, WDM drivers use the IoRegisterDeviceInterface routine to register a device interface. The device interface specifies devices by their capabilities, rather than by a particular naming convention. For more information, see Device Interface Classes.
KMDF など WDF については、以下のドキュメントもご参考ください。
Using Device Interfaces
(12) 最終的に仮想マシン上のコマンドプロンプトには、ioctlapp.exe の実行結果として、以下が表示されて、問題なく動作できていることがわかります。(各 IOCTL の動作を確認したい場合は、上記を参考に SIoctl!SioctlDeviceControl にブレークポイントを貼ってみてください。)
以上の内容がお役に立てば幸いです。
変更履歴
2020/07/29 created by Tsuda
※ 本記事は 「jpwdkblog について」 の留意事項に準じます。
※ 併せて 「ホームページ」 および 「記事一覧」 もご参照いただければ幸いです。