Windowsドライバ開発(1)-ドライバ構造


Windowsドライバ開発(1)-ドライバ構造
1、NT式
1.1ヘッダファイル
  Driver.hヘッダファイルには、NT式駆動の開発に必要なNTDDKが含む.h、さらに、関数および変数がページングメモリに割り当てられるか、非ページングメモリに割り当てられるかを示すフラグもいくつか定義される.Windowsドライバのエントリ関数はDriverEntry関数です.WDM式のドライバがインポートするヘッダファイルはWDMである.h. 説明:1)C++プログラミングを採用しているので、extern「C」を使う必要があります.Cの関数のシンボルテーブルをインポートしているからです.2)ドライバで使用される変数または関数は、ページングメモリまたは非ページングメモリに割り当てることを指定する必要があります.ページングメモリは物理メモリが不足している場合に交換される可能性があります.高IRQLが必要なインスタンスでは、ページを交換することはできません.したがって、非ページングメモリとして定義する必要があります.3)DriverEntryはINITフラグのメモリに入れる必要があります.
1.2ドライバのエントリ関数
#pragma INITCODE
extern "C" NTSTATUS DriverEntry (
    IN PDRIVER_OBJECT pDriverObject,
    IN PUNICODE_STRING pRegistryPath )
{
NTSTATUS status;
KdPrint(("Enter DriverEntry
"
)); // pDriverObject->DriverUnload = HelloDDKUnload; pDriverObject->MajorFunction[IRP_MJ_CREATE] = HelloDDKDispatchRoutine; pDriverObject->MajorFunction[IRP_MJ_CLOSE] = HelloDDKDispatchRoutine; pDriverObject->MajorFunction[IRP_MJ_WRITE] = HelloDDKDispatchRoutine; pDriverObject->MajorFunction[IRP_MJ_READ] = HelloDDKDispatchRoutine; // status = CreateDevice(pDriverObject); KdPrint(("DriverEntry end
"
)); return status; }

1)コンソールプログラムにmain,Win 32プログラムにWinMain,DLLプログラムにDllMainが必要なように,ドライバにも独自のエントリポイント,すなわちDriverEntryがある.DriverEntryはINITメモリ領域にロードする必要があります.これにより、ドライバがアンインストールされた後、メモリを終了することができます.2)DriverEntryはカーネル内のI/Oマネージャによって呼び出され、2つのパラメータDriverObjectとRegistryPathがあります(もちろん、パラメータの名前は自分で変更できます).ここでDriverObjectはI/Oマネージャによって渡されたドライバオブジェクトであり、RegistryPathはこのドライバが担当するレジストリを指します.3)DriverEntryはまずいくつかの変数を定義し、IoCreateDeviceを呼び出してデバイスオブジェクトを作成し、IoCreateSymbolicLinkを呼び出してシンボルリンクを作成することを見ることができます.HelloDDKdispatchRoutineなどは、ドライバがWindowsのI/Oマネージャにコールバック関数を登録しています.上のコードの意味は、ドライバがアンインストールされると自動的にHelloDDKUNloadルーチンが呼び出されます.ドライバがIRP_を受信するとMJ_CREATEではHelloDDKdispatchRoutineが自動的に呼び出されます.4)KdPrintはマクロであり、出力に用いられる.MFCのTRACEと似ています.5)#pragma INITCODEは、この関数がINITメモリ関数にロードされることを示す.6)ドライバオブジェクトDriverObjectには、関数ポインタ配列MajorFunctionがあります.各要素には関数のアドレスが対応するIRPが記録されています.この配列を簡単に設定することで、IRPを対応する派遣関数に関連付けることができます.
1.3デバイスインスタンスの作成(関数)
#pragma INITCODE
NTSTATUS CreateDevice (IN PDRIVER_OBJECT pDriverObject)
{
    NTSTATUS status;
    PDEVICE_OBJECT pDevObj;
    PDEVICE_EXTENSION pDevExt;

    //      
    UNICODE_STRING devName;
    RtlInitUnicodeString(&devName,L"\\Device\\MyDDKDevice");

    //    
    status = IoCreateDevice( pDriverObject,
                    sizeof(DEVICE_EXTENSION),
                    &(UNICODE_STRING)devName,
                    FILE_DEVICE_UNKNOWN,
                    0, TRUE,
                    &pDevObj );

    if (!NT_SUCCESS(status))
    return status;
    pDevObj->Flags |= DO_BUFFERED_IO;
    pDevExt = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;
    pDevExt->pDevice = pDevObj;
    pDevExt->ustrDeviceName = devName;

    //      
    UNICODE_STRING symLinkName;
    RtlInitUnicodeString(&symLinkName,L"\\??\\HelloDDK");
    pDevExt->ustrSymLinkName = symLinkName;
    status = IoCreateSymbolicLink( &symLinkName,&devName );
    if (!NT_SUCCESS(status))
    {
        IoDeleteDevice( pDevObj );
        return status;
    }
    return STATUS_SUCCESS;
}
  • RtlInitUnicodeString
  • IoCreateDevice
  • NTSTATUS IoCreateDevice
    (
        IN PDRIVER_OBJECT DriverObject,
        IN ULONG DeviceExtensionSize,
        IN PUNICODE_STRING DeviceNameOPTIONAL,
        IN DEVICE_TYPE DeviceType,
        IN ULONG DeviceCharacteristics,
        IN BOOLEAN Exclusive,
        OUT PDEVICE_OBJECT *DeviceObject
    );
  • DriverObject:関数を呼び出すドライバオブジェクトを指す.各ドライバは、そのDriverEntryプロセスでそのドライバオブジェクトを受信する.
  • DeviceExtensionSize:ドライバがデバイス拡張オブジェクトとして定義構造体のサイズを指定する.
  • DeviceName:(オプションのパラメータ)は、Unicode文字列を含むゼロで終わるバッファを指し、このデバイスの名前であり、この文字列は完全なデバイスパス名でなければならない.
  • DeviceType:システムによって定義されたFILEを指定します.DEVICE_XXX定数は、このデバイスのタイプ
  • を示しています.
  • DeviceCharacteristics:1つ以上のシステム定義定数を指定し、接続し、ドライバに関するデバイスのその他の情報を提供する.可能なデバイス特徴情報については、DEVICE_を参照してください.OBJECT構造体
  • Exclusive:指定デバイスが排他的である場合、ほとんどのドライバはこの値をFALSE、排他的であればTRUE、非排他的でFALSEとする.
  • DeviceObject:DEVICE_を指すOBJECT構造体ポインタのポインタです.これは、DEVICE_を受信するためのポインタです.OBJECT構造体のポインタ1)前に作成したデバイスオブジェクトには、デバイス名を指定するパラメータがありますが、このデバイス名はカーネル状態でしか表示されません.つまり、ring 3のアプリケーション層プログラムは見えないので、ドライバはring 3にシンボルリンクを公開する必要があります.このリンクは本当のデバイス名を指します.ring 3のアプリケーションは、このシンボルリンクを介してドライバを見つけて通信することができます.実際によく言われるCディスク、Dディスクは、カーネル内の真のデバイスオブジェクトが「DeviceHarddisknolume 1」と「DeviceHarddisknolume 2」であるシンボルリンクです.カーネルモードでは、シンボルリンクは「??」(または「DosDevices」)で始まり、Cディスクが「??:」、ユーザーモードでは「.」で始まり、Cディスクが「.C:」である.

  • 1.4ドライバルーチンのアンインストール
    #pragma PAGEDCODE
    VOID HelloDDKUnload (IN PDRIVER_OBJECT pDriverObject)
    {
        PDEVICE_OBJECT pNextObj;
        KdPrint(("Enter DriverUnload
    "
    )); pNextObj = pDriverObject->DeviceObject;// while (pNextObj != NULL) { PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION) pNextObj->DeviceExtension; // UNICODE_STRING pLinkName = pDevExt->ustrSymLinkName; IoDeleteSymbolicLink(&pLinkName); // pNextObj = pNextObj->NextDevice; IoDeleteDevice( pDevExt->pDevice ); } }

    アンインストールドライバルーチンは、ドライバがアンインストールされたときにI/Oマネージャが呼び出しを担当するDriverEntryで独自に定義されており、主にスキャン処理を行います.KdPrint:ドライバはカーネル状態で動作するため、コンソールのプログラムのようにprintfを使用していくつかの情報を出力することはできません.Win 32プログラムのようにMessageBoxを通じてダイアログボックスをポップアップすることはできません.いくつかの情報を出力するには、DbgPrint関数を呼び出す必要があります.この関数を超えて出力される情報は直接見ることはできません.DbgView(KmdManager)など、専門的なツールが必要です.一部のコンテンツはデバッグ版で出力し、リリース版では無視したいため、DDKではリリース版でコンパイルされず、デバッグ版でのみ実行されるマクロKdPrintが定義されています.KdPrintは次のように定義されています.
    #define KdPrint(_x_) DbgPrint _x_ //                。

    1.5派遣ルーチン
    #pragma PAGEDCODE
    NTSTATUS HelloDDKDispatchRoutine(IN PDEVICE_OBJECT pDevObj,IN PIRP pIrp)
    {
        KdPrint(("Enter HelloDDKDispatchRoutine
    "
    )); NTSTATUS status = STATUS_SUCCESS; // IRP pIrp->IoStatus.Status = status; pIrp->IoStatus.Information = 0; // bytes xfered IoCompleteRequest( pIrp, IO_NO_INCREMENT ); KdPrint(("Leave HelloDDKDispatchRoutine
    "
    )); return status; }

    派遣ルーチンはIRPを処理する.
    1.6コンパイル
  • DDK方式
  • VC方式:『VS 2013+WDK 7.6構築駆動開発環境』
  • 参照
    1.7駆動取付
    DriverStudioのツール:DriverMonitorを使用します.
    2、WDM式駆動
    #pragma INITCODE
    extern "C" NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject,
    IN PUNICODE_STRING pRegistryPath)
    {
        KdPrint(("Enter DriverEntry
    "
    )); pDriverObject->DriverExtension->AddDevice = HelloWDMAddDevice; // NT, PNP 。 ... }
    #pragma PAGEDCODE
    NTSTATUS HelloWDMAddDevice(IN PDRIVER_OBJECT DriverObject,
    IN PDEVICE_OBJECT PhysicalDeviceObject)
    {
        PAGED_CODE();
        KdPrint(("Enter HelloWDMAddDevice
    "
    )); ... }
  • PAGED_CODE();//マクロ、checkバージョンのみ有効、このインスタンスが存在する割り込み要求がAPC_を超える場合LEVELの場合、断言が発生します.
  • ペアIRP_MN_REMOVE_DEVICEの処理は、NT式駆動のアンロードルーチンと同様であり、WDM式駆動ではアンロードルーチンはほとんど処理されない.

  • 2.1コンパイル:
    同じNT
    2.2インストール:
    EzDriverInstallでハードウェアをインストールまたはコントロールする.
    3、まとめ
    実際、一般的なWindowsドライバは、プラグアンドプレイ機能をサポートしないNTドライバと、プラグアンドプレイをサポートするWDMドライバの2つに分類できます.NT式駆動のインストールはサービスに基づいており、レジストリを変更することによって行うこともできるし、CreateServiceのようなサービス関数によって直接インストールすることもできる.しかし、WDMドライブは異なり、インストール時にinfファイルを作成することで制御する必要があります.これに加えて、NTドライバは「ntddk.h」というヘッダファイルをインポートする必要があり、WDMドライバは「wdm.h」ヘッダファイルを必要とします.学習中に記述されたものの多くはNTドライバに属し,WDMドライバの記述を考慮する必要がない限り,独自のデバイスサポートプラグアンドプレイを必要としない.