Hello World Windows ドライバー

マイクロソフトの「[Hello World Windows ドライバー](https://docs.microsoft.com/ja-jp/windows-hardware/drivers/gettingstarted/writing-a-very-small-kmdf--driver)」のページを見ながら、環境を設定し、デバックまで行き着いた記録です。元の文章が英語の直訳なのか、よくわからないところもあり、あれこれ探りながら進めています。

このページだけでは無く、下のページを参照しています。
- [KDNET ネットワーク カーネル デバッグの手動設定](https://docs.microsoft.com/ja-jp/windows-hardware/drivers/debugger/setting-up-a-network-debugging-connection)
- [KDNET ネットワーク カーネル デバッグの自動設定](https://docs.microsoft.com/ja-jp/windows-hardware/drivers/debugger/setting-up-a-network-debugging-connection-automatically)
- [Visual Studio を使って実行時にドライバーをテストする方法](https://docs.microsoft.com/ja-jp/windows-hardware/drivers/develop/testing-a-driver-at-runtime)
- [ドライバーの展開およびテストのためのコンピューターのプロビジョニング (WDK 10)](https://docs.microsoft.com/ja-jp/windows-hardware/drivers/gettingstarted/provision-a-target-computer-wdk-8-1)

## 環境・準備
パソコンを２台準備します。１台をホスト、もう一台をターゲットとします。２台のマシンは同じOSで無いとややこしいので、同じOSとします。私の環境はWindows10 32bitのマシンを使いました。

### ホスト
ホスト側ではドライバーのコンパイルと、デバックのホストの役割を持ちます。

- [Visual Studio Community](https://visualstudio.microsoft.com/ja/downloads/?rr=https%3A%2F%2Fdocs.microsoft.com%2Fja-jp%2Fvisualstudio%2Finstall%2Finstall-visual-studio%3Fview%3Dvs-2019) をインストールします。
- [Windows SDK](https://developer.microsoft.com/ja-jp/windows/downloads/windows-10-sdk/) をVisual Studioに追加します。Windows SDKは、Visual Studio Installerからでもインストールできます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58417/592d9141-82f4-0251-1f7f-16290dc388a1.png)
- [Windows Driver Kit (WDK)](https://docs.microsoft.com/ja-jp/windows-hardware/drivers/download-the-wdk) をインストールします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58417/9b68f66c-0f40-b2af-0cff-a68c6eea6e97.png)
- デバックツールのパスを環境変数PATHに追加しておくと便利です。
c:\Program Files\Windows Kits\10\Debuggers\x86

### ターゲット

ターゲットでは実際にドライバーを動作させます。以下インストールするSDKとWDKはマスター側と同じです。

- [Windows 10 SDK](https://developer.microsoft.com/ja-jp/windows/downloads/windows-10-sdk/)をインストールします。

- [WDK for Windows 10](https://docs.microsoft.com/ja-jp/windows-hardware/drivers/download-the-wdk)をインストールします。

- デバックツールのパスを環境変数PATHに追加しておくと便利です。
c:\Program Files\Windows Kits\10\Debuggers\x86
c:\Program Files\Windows Kits\10\Tools\x86

### ネットワークデバッグ

- kdnet.exeコマンドを使って、コンピューターにサポートされているネットワークアダプターがあることを確認します。コマンドウィンドウを開きます。下記のディレクトリへチェンジディレクトリを行わないとエラーになります。

```
> cd C:\Program Files\Windows Kits\10\Debuggers\x86
> kdnet

Network debugging is supported on the following NICs:
busparams=3.0.0, Realtek PCIe GbE Family Controller, Not plugged in.
```
- 以下はエラーの例です。エラーの出るマシンはホスト、ターゲットのいずれにも使えません。（使う手があるのかもしれませんがわかりませんでした。）

```
Network debugging is not supported on any of the NICs in this machine.
KDNET supports NICs from Intel, Broadcom, Realtek, Atheros, Emulex, Mellanox
and Cisco.

KDNET initialization failed.  Status = 0xC000000E.
NIC hardware initialization failed.
NIC hardware ID = 0x0.
```

- ホスト、ターゲットのipを調べます。

```
>ipconfig

Windows IP 構成

イーサネット アダプター イーサネット 2:

   接続固有の DNS サフィックス . . . . .:
   リンクローカル IPv6 アドレス. . . . .: xxxx::xxxx:xxxx:xxxx:xxxx%YY
   IPv4 アドレス . . . . . . . . . . . .: 192.168.XX.YY
   サブネット マスク . . . . . . . . . .: 255.255.255.0
   デフォルト ゲートウェイ . . . . . . .: 192.168.XX.1
```

- ホスト側にIPv4を 192.168.XX.YY, ターゲット側を 192.168.AA.BB とします。
- ホストとターゲットの間でpingを通してみます。

```
> ping 192.168.AA.BB

192.168.AA.BB に ping を送信しています 32 バイトのデータ:
192.168.AA.BB からの応答: バイト数 =32 時間 =46ms TTL=128
192.168.AA.BB からの応答: バイト数 =32 時間 =1ms TTL=128
192.168.AA.BB からの応答: バイト数 =32 時間 =1ms TTL=128
192.168.AA.BB からの応答: バイト数 =32 時間 =1ms TTL=128

192.168.AA.BB の ping 統計:
    パケット数: 送信 = 4、受信 = 4、損失 = 0 (0% の損失)、
ラウンド トリップの概算時間 (ミリ秒):
    最小 = 1ms、最大 = 46ms、平均 = 12ms
```

- pingが通らない場合があります。その時には、ファイアーウォールの設定を見直します。
- コントロール パネル > システムとセキュリティ > Windows Defender ファイアウォール > 設定のカスタマイズ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58417/720a644c-25a9-a6b7-70d5-58422b8fb812.png)


## プログラム

― ホスト側でプログラムを作成します。
- プログラムは、「[Hello World Windows ドライバー](https://docs.microsoft.com/ja-jp/windows-hardware/drivers/gettingstarted/writing-a-very-small-kmdf--driver)」に書かれているものと同じです。
- Visual Studio 2019を立ち上げ、新しいプロジェクトの作成　> C++ > Windows > Drivers > Kernal Mode Driver,Empty(KMDF)を選びます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58417/e31ca821-2b2d-ead0-31a7-c3ef5fa4facf.png)

- 「新しいプロジェクトを構成します」のページで「<b>ソリューションのディレクトリを作成</b>」のチェックボックスをオンにします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58417/fd6e51be-c0c1-2112-cabe-3f3500c088c9.png)

― ソリューションエクスプローラで<b>kmdfHelloWorld</b>を右クリックし、「新しい項目の追加 > C++ファイル」で「名前」をDriver.cなるソースファイルを作成します。ファイルの拡張子は<b>.c</b>です。内容は以下の通りです。

``` Driver.c
#include <ntddk.h>
#include <wdf.h>
DRIVER_INITIALIZE DriverEntry;
EVT_WDF_DRIVER_DEVICE_ADD KmdfHelloWorldEvtDeviceAdd;

NTSTATUS
DriverEntry(
    _In_ PDRIVER_OBJECT     DriverObject,
    _In_ PUNICODE_STRING    RegistryPath
)
{
    // NTSTATUS variable to record success or failure
    NTSTATUS status = STATUS_SUCCESS;

    // Allocate the driver configuration object
    WDF_DRIVER_CONFIG config;

    // Print "Hello World" for DriverEntry
    KdPrintEx((DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "KmdfHelloWorld: DriverEntry\n"));

    // Initialize the driver configuration object to register the
    // entry point for the EvtDeviceAdd callback, KmdfHelloWorldEvtDeviceAdd
    WDF_DRIVER_CONFIG_INIT(&config,
        KmdfHelloWorldEvtDeviceAdd
    );

    // Finally, create the driver object
    status = WdfDriverCreate(DriverObject,
        RegistryPath,
        WDF_NO_OBJECT_ATTRIBUTES,
        &config,
        WDF_NO_HANDLE
    );
    return status;
}

NTSTATUS
KmdfHelloWorldEvtDeviceAdd(
    _In_    WDFDRIVER       Driver,
    _Inout_ PWDFDEVICE_INIT DeviceInit
)
{
    // We're not using the driver object,
    // so we need to mark it as unreferenced
    UNREFERENCED_PARAMETER(Driver);

    NTSTATUS status;

    // Allocate the device object
    WDFDEVICE hDevice;

    // Print "Hello World"
    KdPrintEx((DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "KmdfHelloWorld: KmdfHelloWorldEvtDeviceAdd\n"));

    // Create the device object
    status = WdfDeviceCreate(&DeviceInit,
        WDF_NO_OBJECT_ATTRIBUTES,
        &hDevice
    );
    return status;
}
```

- ソリューションエクスプローラは以下のように構成されています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58417/9a791911-c2de-ac22-57b2-a50c5f8c686e.png)

― ビルドの前にプロパティを設定します。ソリューションエクスプローラで<b>kmdfHelloWorld</b>を右クリックし、
― [Wpp Tracing] > [General] > [Run Wpp tracing] を”いいえ”に設定
- [C/C++] > [コード生成] > [Spectre軽減策] を”無効”に設定

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58417/2c223df5-26c1-0668-22b5-c2f60a750ac3.png)


- <b>ビルド</b>メニューから<b>ソリューションのビルド</b>を選択します。

```
========== ビルド: 1 正常終了、0 失敗、0 更新不要、0 スキップ ==========
```

と表示が出ればOKです。Debugフォルダの、kmdfhelloworldフォルダの中に、kmdfhelloworld.cat, kmdfhelloworld.sys, kmdfhelloworld.inf の３つのファイルができています。
- 次のようなエラーが表示されることがあります。

```
エラー	MSB8040	Spectre 軽減のライブラリは、このプロジェクトに必要です。
使用されているツールセットとアーキテクチャについては、
Visual Studio インストーラー (個々のコンポーネント タブ) からインストールします。
```
- この場合には、「プロパティ > C/C++ > コード生成 > Spectre軽減策」を無効を再確認します。

## デバックの準備

- ドライバーはターゲット側のマシンで動作します。ターゲットマシンへドライバを転送するための準備をします。
- ターゲット側は立ち上げたままとします。
- 転送先を設定します。「プロパティ > Driver Install > Development」

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58417/5b58563f-6591-bfec-dbf3-734ab2e0d6e6.png)

- <b>Target Device Name</b> の右のボタンをクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58417/c24697f7-2ce4-0019-e5ff-b62abce822ad.png)

- <b>Add New Device</b> をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58417/ef9ff827-e166-c17c-ede5-16594ca638e1.png)

- Add New Device のWindowが開きますので、Display nameとNewwork Host nameを入力します。
- Newwork Host nameには、ターゲットマシンのIPを入れます。
- 「次へ」をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58417/22729533-900d-7d15-6607-ae3a5dd5f8da.png)

- Host IPには、ターゲットマシンのIPを入れます。
-　「次ぎ」へをクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58417/3aee9fda-1def-0ba7-1dbf-de5362297bb4.png)

-　ターゲットマシンにいろいろ表示が出て、ターゲットマシンが再起動し以下のウィンドウが表示されます。
-　完了表示が出るまでに数分かかります。 
- 完了を押します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58417/7052d5d9-ba01-a26d-c12f-eef74463dc2f.png)

- OK を押します。
- Remove previous driver versions before development にチェックを入れます。
- Hardware ID Driver Uodate にチェックを入れます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58417/39805d12-e0b4-9075-50b0-e4eec59d02df.png)

- OK を押します。
- <b>ビルド</b>メニューから「ソリューションの配置」を選びます。

```
[10:31:14:915]: Result Summary: Total=1, Passed=1, Failed=0, Blocked=0, Warned=0, Skipped=0
[10:31:14:918]: Task "Driver Post Install Actions" completed successfully
Driver Deployment Task Failed: Driver Removal
Driver Deployment Task Failed: Driver Preparation
```

- と出力に表示されます。Faildが２つ表示されていますが、無視します。
- ターゲットマシンの c:\DriverTest\Drivers の下に kmdfHelloWorld.sys, kmdfHelloWorld.inf, kmdfHelloWorld.sys.cer, kmdfHelloWorld.cat の４つのファイルが転送されています。 

## デバック
- まず、<b>ターゲット側</b>でコマンドを入力します。
- コマンドプロンプトを「管理者として実行します」

```
> bcdedit /debug on
この操作を正しく終了しました。
> bcdedit /dbgsettings NET HOSTIP:192.168.XX.YY PORT:50000 KEY:1.2.3.4
key=1.2.3.4
````

- HOSTIPはホストマシンのIPアドレスです。PORTとKEYはこのままで良いです。

- ホスト側で、コマンドプロンプトを開き次のコマンドを入力します。

```
>windbg -k net:port=50000,key=1.2.3.4
```

- waiting reconnet と表示され待ち状態になります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58417/61e47f43-4e21-d08b-4f61-78a1679283fd.png)

- <b>ターゲットマシンを再起動します。</b>
- デバッガ―に次のように表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58417/e91eca6b-dfba-ef9b-7dfb-88cbfb5fb636.png)


- <b>debug</b>メニューから<b>breakします。</b>
- ターゲットマシンがブレイクで止まります。（ターゲットマシンは直接には操作できなくなります）
 
```
*******************************************************************************
*                                                                             *
*   You are seeing this message because you pressed either                    *
*       CTRL+C (if you run console kernel debugger) or,                       *
*       CTRL+BREAK (if you run GUI kernel debugger),                          *
*   on your debugger machine's keyboard.                                      *
*                                                                             *
*                   THIS IS NOT A BUG OR A SYSTEM CRASH                       *
*                                                                             *
* If you did not intend to break into the debugger, press the "g" key, then   *
* press the "Enter" key now.  This message might immediately reappear.  If it *
* does, press "g" and "Enter" again.                                          *
*                                                                             *
*******************************************************************************
nt!RtlpBreakWithStatusInstruction:
83168fa4 cc              int     3
```

- 下の <b>kd</b>にコマンドを入力することができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58417/0d56ee49-e3da-e609-c2d6-b6a65da04523.png)

- kmdfHelloWorld ドライバからのデバックプリントを表示できるようにマスクの設定をします。
- その後、<b>g</b> コマンドでターゲット側をrun状態に戻します。

````
kd> ed kd_IHVDRIVER_Mask 0xFFFF
kd > g
````

- ターゲット側でコマンドプロンプトを「管理者として実行します」。
- ドライバーをインストールします。
- root/ は半角バックスラッシュでなければなりません。

```
> cd c:\DriverTest\Drivers
> C:\DriverTest\Drivers>devcon install kmdfhelloworld.inf root/kmdHelloWorld
Device node created. Install is complete when drivers are installed...
Updating drivers for root/kmdHelloWorld from C:\DriverTest\Drivers\kmdfhelloworld.inf.
Drivers installed successfully.
```

- ホスト側のデバッカで、kmdfHelloWorld ドライバからのデバック出力の表示を見ることができます。

```
KmdfHelloWorld: KmdfHelloWorldEvtDeviceAdd
KmdfHelloWorld: KmdfHelloWorldEvtDeviceAdd
KmdfHelloWorld: KmdfHelloWorldEvtDeviceAdd
```

- 再びbreak の後、lm コマンド、x コマンドでターゲット側のドライバの状況を見ることができます。

```
0: kd> lm
start    end        module name
77e40000 77fd9000   ntdll      (pdb symbols)          C:\ProgramData\dbg\sym\ntdll.pdb\428AA04B59155EA5C79465CF1806F3A71\ntdll.pdb
82f95000 83000000   hal        (deferred)             
83000000 83755000   nt         (pdb symbols)          C:\ProgramData\dbg\sym\ntkrpamp.pdb\7E8A99EEA98E25629FF1839EA8411A171\ntkrpamp.pdb
86400000 8644a000   kd_02_10ec   (deferred)             
8644a000 8648d000   kdcom      (deferred)             
...
944d0000 944e0000   bam        (deferred)             
944e0000 9451c000   ahcache    (deferred)             
94520000 9452d000   CompositeBus   (deferred)             
94530000 94536000   kmdfHelloWorld   (private pdb symbols)  C:\ProgramData\dbg\sym\kmdfHelloWorld.pdb\527655F6163C4ED0A6BEE2B09C179BE92\kmdfHelloWorld.pdb
94540000 9454a000   kdnic      (pdb symbols)          C:\ProgramData\dbg\sym\kdnic.pdb\27EBA0B22C6C63C3F86D707CB500ACFD1\kdnic.pdb
94550000 94561000   umbus      (deferred)             
94570000 9457f000   CAD        (deferred)             
94580000 945ad000   intelppm   (deferred)             
945b0000 945c2000   monitor    (deferred```
```

```
0: kd> x kmdfHelloWorld!
94533264          kmdfHelloWorld!WdfDriverGlobals = 0x9238cdc0
94533268          kmdfHelloWorld!WdfDriverStubDriverObject = 0x8e6e5058 Driver "\Driver\kmdfHelloWorld"
94533024          kmdfHelloWorld!__security_cookie_complement = 0x6c34a475
9453326c          kmdfHelloWorld!WdfDriverStubDisplacedDriverUnload = 0x86a84498
94533000          kmdfHelloWorld!WdfBindInfo = struct _WDF_BIND_INFO
```

- ターゲット側のマシンは

```
/dbgedit /debug off
```

の後再起動することでテストモードを抜けることができます。