2016/04/03(日)RAMDISKドライバを作る

RAMDISKドライバを作る

ほんとは、これがやりたくてオレオレ認証局とか作ってたんだけど、
調べたところによると、Windowsで使えるドライバ用のコード証明書のCAは決まってて
そればかりか、Windows 10のカーネルモードのドライバのコード署名はEV証明書でないといけないみたいで、
結局、この記事でやってるようにドライバ署名の検証をスルーしないと動かせなかった。
趣味レベルでカーネルモードドライバをちゃんと作ってリリースする道はもはや絶望的だなー……。

この記事では、

Visual Studioでのドライバの開発に必要なものをインストールして、
サンプルドライバをビルド・インストールしてみてから、
RAMDISKの容量が1GBになるよう、ソースコードを改造してみます。

制限

この記事の範疇ではFAT16なので、2GB(一説では4GB?)よりも大きいドライブは作れないはず。
FAT32への対応はすぐできるんだと思うけど、あんまり調べてない。

上記の通りドライバにコード署名できないので、ドライブを使うには、
毎回ドライバ署名の検証をスルーできる状態でWindowsを起動する必要がある。

ドライバのビルド環境を用意する

Windows 10 でサンプル ドライバーをビルドするまで
https://blogs.msdn.microsoft.com/jpwdkblog/2015/08/21/windows-10/

Visual Studio 2015 を入れる。 https://www.visualstudio.com/products/visual-studio-community-vs
Windows10 SDKを入れる https://dev.windows.com/ja-jp/downloads/windows-10-sdk
Windows Driver Kit 10 を入れる https://msdn.microsoft.com/en-us/windows/hardware/hh852365.aspx
サンプルドライバをチェックアウトしてくる https://github.com/Microsoft/Windows-driver-samples

サンプルのRAMDISKドライバをビルドしてみる

storage\\ramdisk\\randisk.sln があるので、これを開いて
x64 Release にしてビルドする。

ツールチェインがないといわれる場合

SDKかWDKのどっちか入れ忘れてるか、Visual Studioのバージョンがあってないかもです。

Inf2Cat, signability test failed. と出る場合、

WdfRamdiskプロジェクトのプロパティ→「構成プロパティ」→「Inf2Cat」→「General」の「Use Local Time」を「はい」にします。
これをしないと、日本では 午前0時から午前9時の間、UTCと年月日が未来にズレるので、署名できません。
夜更かしさん。

ビルドしたドライバをインストールする

事前にこの記事でやってるように、コード署名の検証をスルーできる状態でWindowsを起動します
デバイスマネージャを開いて、ツリー最上層のホスト名を右クリックし「レガシハードウェアの追加」
「一覧から選択したハードウェアをインストールする(詳細)」「次へ」
「すべてのデバイスを表示」「次へ」
「ディスク使用」さっきビルドしたファイルがある場所を指定「OK」
「モデル」から「WDF Sample RAM disk Driver」を選んで「次へ」
ハードウェアをインストールする準備ができました。「次へ」
ドライバーソフトウェアの発行元を検証できません「このドライバー ソフトウェアをインストールします」
次のハードウェアがインストールされました「完了」

これでR:ドライブができています。
が、エクスプローラで開こうとしても「アクセスが拒否されました。」と出るばかりで中がのぞけません。
サンプルドライバを何も変更せずにインストールすると、管理者権限が必要になってしまうのです。
管理者権限のあるコマンドプロンプトなどからであれば R: ドライブにアクセスできます。

C:\\WINDOWS\\system32>r:
R:\\>copy con aaa
^Z
        1 個のファイルをコピーしました。

R:\\>dir
 ドライブ R のボリューム ラベルは MS-RAMDRIVE です
 ボリューム シリアル番号は 1234-5678 です

 R:\\ のディレクトリ

2016/04/03  07:55                 0 aaa
               1 個のファイル                   0 バイト
               0 個のディレクトリ       1,030,144 バイトの空き領域

容量は1MB。

容量を増やしてみる

ramdisk.c を開いてみると、
RamDiskQueryDiskRegParameters 関数の中で、レジストリから設定データを拾い、
RamDiskFormatDisk 関数の中で、ブートセクタを作ってるみたいなので、これをいじってみます。

randisk.inx を開いてデフォルトのDiskSizeを変更します。

HKR, "Parameters", "DiskSize",          %REG_DWORD%, 0x40000000

FAT16のbootSectorでは、65536個以上のセクタを持つディスクでは、
bsSectors領域ではなくbsHugeSectorsを使う決まりになっているらしいので、それを使ってるところ変えます。
http://www.ntfs.com/fat-partition-sector.htm

	bootSector->bsSectors     = 0;
	bootSector->bsHugeSectors = (devExt->DiskRegInfo.DiskSize /
                                         devExt->DiskGeometry.BytesPerSector);
    fatEntries =
        (bootSector->bsHugeSectors - bootSector->bsResSectors -
            bootSector->bsRootDirEnts / DIR_ENTRIES_PER_SECTOR) /
                bootSector->bsSecPerClus + 2;

計算過程の変数サイズもかえる。

    ULONG        fatEntries;     // Number of cluster entries in FAT
    ULONG        fatSectorCnt;   // Number of sectors for FAT
    bootSector->bsFATsecs       = (USHORT)fatSectorCnt;
管理者じゃなくてもアクセスできるようにする。

RamDiskEvtDeviceAdd関数の中で、セキュリティを設定する。
infファイル側に書くべきみたいな文書がちょいちょい引っかかるのでその方が正しいんだと思いますが、よくわかってない。

wdmsec.h を include して、$(DDK_LIB_PATH)\\ntstrsafe.lib をリンクする。

RamDiskEvtDeviceAdd関数で、セキュリティを設定する。

	UNICODE_STRING lsa;
    WdfDeviceInitSetDeviceType(DeviceInit, FILE_DEVICE_DISK);
    WdfDeviceInitSetIoType(DeviceInit, WdfDeviceIoDirect);
    WdfDeviceInitSetExclusive(DeviceInit, FALSE);
    // の後で...
    // SYSTEMさんがGENERIC_ALLで、EveryoneさんがGENERIC_ALLでアクセスしてよい 的な意味の文字列を用意する
    RtlUnicodeStringInit(&lsa, L"D:P(A;;GA;;;SY)(A;;GA;;;WD)");
    WdfDeviceInitAssignSDDLString(DeviceInit, &lsa); // ← それを、セット

ビルド。

ドライバを更新

デバイスマネージャを開いて
インストールしたデバイス「Sample Device/WDF Sample RAM disk Driver」を右クリックして、
「ドライバーソフトウェアの更新」から、ドライバを更新します。

再起動を求められた場合は、いったんキャンセルして、
コード署名の検証をスルーできる状態でWindowsを再起動します。

これで、1GBのRドライブができてるはずなので、ブラウザキャッシュにするなりTEMP置き場にするなり。
詳しく調べてないけど、FAT32にするには、FAT32のbootSectorを用意して書き込めばいいのかな、たぶん。

また、起動時に毎回ドライバ署名の検証を強制しないモードで起動するのはめんどくさいので、
bcdedit でテストモードにしてしまう手もある。

bcdedit /set testsigning on

おわり。