Windowsデバイスドライバの基本動作を確認する (1)

はじめに

Windowsのデバイスドライバは、標準的なUSB機器など一般的なハードウェアを制御するものはOSに標準搭載されており、デバイスを接続するだけで自動的にインストールされて動作可能になるため、普段はあまり意識することがないかもしれません。特殊なハードウェアを動かす際は専用ドライバのインストールが必要ですが、インストール時にどのような操作や設定が行われ、どのような手順でドライバが起動されるかなど、細かい動きまで気にすることはないかと思います。

本記事では、簡単なデバイスドライバを自作して実際に動かしながら、基本的な動作を確認していきたいと思います。

ビルド環境

開発経験のある方はご存知のとおり、WindowsのデバイスドライバはWDK (Windows Driver Kit)を用いてビルドします。現在のWDKはVisual Studioと統合されており、ビルドだけでなく、ソースコードの作成からデバッグまでの一連の作業をVisual Studio上で行うことができますが、ここでは昔ながらの開発手法による基本的な流れを紹介するため、少し前のバージョンであるWDK 7.1.0 (Windows 7/Vista/XP向け)を使用します。

基本構造

Windowsでは、個々のデバイスドライバが独立したモジュール(.SYSファイル)になっており、必要なときにロードされて動作を開始します。Linuxなどのローダブルモジュールに相当しますが、Linuxのデバイスドライバはカーネルイメージに組み込んで静的にリンクすることもできるのに対して、Windowsでは全てがローダブルになります。各.SYSファイルは\Windows\system32\driversの下に格納されています。

全てのデバイスドライバは、DriverEntryという固定の名前の関数をもっており、ロード時に呼び出されます。DriverEntryでは、通常、引数で渡されるDriverObjectというデータ構造の中でドライバ内のコールバック関数を登録するなどの初期化処理を行いますが、今回は、以下のようにDbgPrint関数を用いてメッセージを出力した後、正常終了するだけで、他の機能は何も持たない単純なデバイスドライバを作成し、インストールと基本的な起動・停止動作を確認します。

ビルド手順

WDK 7.1.0を用いたデバイスドライバのビルドでは、sourcesという名前のファイルを用いてドライバの名前やソースファイルを指定します。(WDKのサンプルではsources以外にmakefileも存在しますが、経験上、sourcesだけがあればビルドできるようです)

今回作成したドライバのsourcesファイルを以下に示します。

ここでは、ビルドに最低限必要な以下の情報を指定しています。

  • TARGETNAME : ビルド対象のモジュール名
  • TARGETTYPE : ビルド対象の種類 (= カーネルモードドライバ)
  • SOURCES : ソースファイル名一覧

Sourcesとソースファイルを用意して、WDKのビルド環境上でbuildコマンドを実行すると、ドライバ(.SYSファイル)が生成されます。

インストールおよび起動手順

通常、デバイスドライバのインストールは、インストール対象や必要な設定情報を記述したINFファイルを用いて行いますが、ここでは必要最低限の情報のみを登録して動作させるため、Windows標準のSCというコマンドを用いて行います。SCコマンドは、カーネルモードドライバ用というわけではなく、一般にはユーザモードの常駐プロセス(サービス)の制御に用いられます。

インストールに必要な作業は以下の二つです。

  1. 生成した.SYSファイルを\Windows\system32\driversの下にコピーする。
  2. SCコマンドを用いてデバイスドライバの情報を登録する。
    sc create sample1 binPath= system32\drivers\sample1.sys type= kernel

SCコマンドでデバイスドライバ情報を登録すると、以下のレジストリキーが生成されて、SCコマンドで指定した情報が追加されます。

SCコマンドで指定していない情報についてはデフォルト値が設定されています。

  • ErrorControl : エラー制御方法 = 通常 (デフォルト)
  • ImagePath : ドライバイメージへの相対パス
  • Start : ドライバの動作開始モード = 開始指示を受けて起動する
    • OS起動時に自動的に開始するように指定することも可能
  • Type : サービス種別 = カーネルモードドライバ

この後、同じSCコマンドを用いてドライバの開始を指示することで、ドライバを起動することができます。
sc start sample1

DriverEntryの中で出力したメッセージは、カーネルデバッグを使用する際はデバッガの画面に表示されますが、ここではDebugViewというツールを用いて確認しました。

なお、通常はないと思いますが、DriverEntryの戻り値を正常終了ではなくエラーを表す値(例えば、STATUS_UNSUCCESSFUL)に変更すると、ドライバの起動に失敗して、SCコマンドがエラーメッセージを表示します。

ドライバの停止

動作中のドライバの停止もSCコマンドを用いて行うことができます。
sc stop sample1

しかし、上で作成したドライバを停止しようとすると、以下のようにエラーメッセージが表示され、うまく停止することができません。

先に説明した通り、通常のドライバはDriverObjectの中に各種コールバック関数を設定しますが、今回のドライバでは何も行っておらず、コールバック関数未設定のまま動作していることから、停止時の処理が適切に行われないためと推測されます。そこで、DriverObjectに設定するコールバック関数のうち、アンロード時に呼び出される関数 (DriverUnload) を設定するように修正したところ、正常に停止できるようになりました。

おわりに

今回は、単純なデバイスドライバを作成して、そのインストールおよび起動・停止の手順を確認しました。ここで紹介した手順は、SCコマンドを用いて手動でインストールおよび起動・停止を行うものでしたが、通常使用されるドライバの多くは、ハードウェアが接続または切断されたのをOSが検出して、プラグアンドプレイにより自動的に起動・停止が行われます。次回以降は、このあたりの動作についても確認していこうと思います。

 

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です