VS2013 WDK8.1驱动开发4(NT式驱动基本结构)

本系列博客为学习《Windows驱动开发技术详解》一书的学习笔记。

前言

VS2013 WDK8.1驱动开发1(最简单的NT驱动)一文中我们完成了一个最简单的NT驱动程序,今天我们详细的看看它的结构。

NT式驱动入口函数

在前文中我们完成了如下的驱动程序入口函数:

    /// @brief 驱动程序入口函数
    /// @param[in] pDriverObject 从I/O管理器中传进来的驱动对象
    /// @param[in] pRegPath 驱动程序在注册表中的路径
    /// @return 初始化驱动状态
    NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject, IN PUNICODE_STRING pRegPath)
    {
        NTSTATUS status = STATUS_SUCCESS;

        KdPrint(("Enter DriverEntry\n"));

        // 注册驱动调用函数入口
        // 这些函数不是由驱动程序本身负责调用, 而是由操作系统负责调用
        pDriverObject->DriverUnload = HelloNTDriverUnload;
        pDriverObject->MajorFunction[IRP_MJ_CREATE] = HelloNTDriverDispatchRoutine;
        pDriverObject->MajorFunction[IRP_MJ_CLOSE] = HelloNTDriverDispatchRoutine;
        pDriverObject->MajorFunction[IRP_MJ_WRITE] = HelloNTDriverDispatchRoutine;
        pDriverObject->MajorFunction[IRP_MJ_READ] = HelloNTDriverDispatchRoutine;

        // 创建驱动设备对象
        status = CreateDevice(pDriverObject);

        KdPrint(("Leave DriverEntry\n"));

        return status;
    }

入口函数的一个参数是pDriverObject,该参数是从I/O管理器中传递进来的,每个驱动都有一个唯一的驱动对象与之对应。我们看看驱动对象的数据结构:

    typedef struct _DRIVER_OBJECT {
        CSHORT Type;
        CSHORT Size;

        PDEVICE_OBJECT DeviceObject;
        ULONG Flags;

        PVOID DriverStart;
        ULONG DriverSize;
        PVOID DriverSection;
        PDRIVER_EXTENSION DriverExtension;

        UNICODE_STRING DriverName;

        PUNICODE_STRING HardwareDatabase;

        PFAST_IO_DISPATCH FastIoDispatch;

        PDRIVER_INITIALIZE DriverInit;
        PDRIVER_STARTIO DriverStartIo;
        PDRIVER_UNLOAD DriverUnload;
        PDRIVER_DISPATCH MajorFunction[IRP_MJ_MAXIMUM_FUNCTION + 1];

    } DRIVER_OBJECT;
    typedef struct _DRIVER_OBJECT *PDRIVER_OBJECT; 
  • DeviceObject:每个驱动程序都有一个或多个设备对象,每个设备对象都有一个指针指向下一个设备对象,最后一个设备对象指向空。
  • DriverName:驱动程序的名称。
  • HaedwareDatabase:设备的硬件数据库键名。
  • DriverUnload:驱动卸载派遣函数指针。
  • MajorFunction:主派遣函数指针数组。

入口函数的另一个参数是pRegPath,它是一个UNICODE字符串,它的内容为驱动设备服务键的键名字符串。

我们可以使用KdPrint查看以上的信息,改写后的入口函数如下:

    NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject, IN PUNICODE_STRING pRegPath)
    {
        NTSTATUS status = STATUS_SUCCESS;

        KdPrint(("Enter DriverEntry\n"));

        KdPrint(("DriverName: %ws\n", pDriverObject->DriverName.Buffer));
        KdPrint(("HardwareDatabase: %ws\n", pDriverObject->HardwareDatabase->Buffer));
        KdPrint(("RegPath: %ws\n", pRegPath->Buffer));

        UNREFERENCED_PARAMETER(pRegPath);

        // 注册驱动调用函数入口
        // 这些函数不是由驱动程序本身负责调用, 而是由操作系统负责调用
        pDriverObject->DriverUnload = HelloNTDriverUnload;
        pDriverObject->MajorFunction[IRP_MJ_CREATE] = HelloNTDriverDispatchRoutine;
        pDriverObject->MajorFunction[IRP_MJ_CLOSE] = HelloNTDriverDispatchRoutine;
        pDriverObject->MajorFunction[IRP_MJ_WRITE] = HelloNTDriverDispatchRoutine;
        pDriverObject->MajorFunction[IRP_MJ_READ] = HelloNTDriverDispatchRoutine;

        // 创建驱动设备对象
        status = CreateDevice(pDriverObject);

        KdPrint(("Leave DriverEntry\n"));

        return status;
    }

运行结果为:

创建设备对象

创建设备对象是由内核函数IoCreateDevice完成的。

    NTSTATUS
    IoCreateDevice(
        _In_     PDRIVER_OBJECT DriverObject,
        _In_     ULONG DeviceExtensionSize,
        _In_opt_ PUNICODE_STRING DeviceName,
        _In_     DEVICE_TYPE DeviceType,
        _In_     ULONG DeviceCharacteristics,
        _In_     BOOLEAN Exclusive,
        _Out_    PDEVICE_OBJECT *DeviceObject
        );
  • DriverObject:驱动对象指针。
  • DeviceExtensionSize:设备扩展的大小,I/O管理器会根据这个大小,在内存中创建设备扩展,并与驱动对象关联。
  • DeviceName:设备名称,我们必须按照"\Device\[设备名]"的形式。
  • DeviceType:设备类型,针对虚拟设备,我们一般设置为FILE_DEVICE_UNKNOWN
  • DeviceCharacteristics:设置设备对象的特征。
  • Exclusive:设置设备对象是否为内核模式下使用,一般设置为TRUE。
  • DeviceObject:保存设备对象地址。

设备对象的数据结构如下:

    typedef struct _DEVICE_OBJECT {
        CSHORT Type;
        USHORT Size;
        LONG ReferenceCount;
        struct _DRIVER_OBJECT *DriverObject;
        struct _DEVICE_OBJECT *NextDevice;
        struct _DEVICE_OBJECT *AttachedDevice;
        struct _IRP *CurrentIrp;
        PIO_TIMER Timer;
        ULONG Flags;                                // See above:  DO_...
        ULONG Characteristics;                      // See ntioapi:  FILE_...
        __volatile PVPB Vpb;
        PVOID DeviceExtension;
        DEVICE_TYPE DeviceType;
        CCHAR StackSize;

        ...


    } DEVICE_OBJECT;
  • DriverObject:驱动对象指针。
  • NextDevice:下一个设备对象指针,所有的设备对象会在水平方向组成一个链表,最后一个设备对象的NextDevice指向空。
  • DeviceExtension:设备扩展的地址。

这次我们在CreateDevice函数中创建两个设备对象,改写后的函数如下:

    /// @brief 创建设备对象
    /// @param[in] pDriverObject 驱动对象
    /// @return 状态值
    NTSTATUS CreateDevice(IN PDRIVER_OBJECT pDriverObject)
    {
        UNICODE_STRING devName1;
        UNICODE_STRING devName2;
        UNICODE_STRING symLinkName1;
        UNICODE_STRING symLinkName2;
        PDEVICE_OBJECT pDevObj1 = NULL;
        PDEVICE_OBJECT pDevObj2 = NULL;
        PDEVICE_EXTENSION pDevExt1 = NULL;
        PDEVICE_EXTENSION pDevExt2 = NULL;
        NTSTATUS status;

        // 创建设备对象1
        RtlInitUnicodeString(&devName1, L"\\Device\\HelloNTDriverDevice1");
        status = IoCreateDevice(
            pDriverObject,
            sizeof(DEVICE_EXTENSION),
            &devName1,
            FILE_DEVICE_UNKNOWN,
            0,
            TRUE,
            &pDevObj1);
        if (!NT_SUCCESS(status))
        {
            return status;
        }

        pDevObj1->Flags |= DO_BUFFERED_IO;
        pDevExt1 = (PDEVICE_EXTENSION)pDevObj1->DeviceExtension;
        pDevExt1->PDeviceObject = pDevObj1;
        pDevExt1->DeviceName = devName1;

        RtlInitUnicodeString(&symLinkName1, L"\\??\\HelloNTDriver1");
        pDevExt1->SymLinkName = symLinkName1;
        status = IoCreateSymbolicLink(&symLinkName1, &devName1);
        if (!NT_SUCCESS(status))
        {
            IoDeleteDevice(pDevObj1);
            return status;
        }


        // 创建设备对象2
        RtlInitUnicodeString(&devName2, L"\\Device\\HelloNTDriverDevice2");
        status = IoCreateDevice(
            pDriverObject,
            sizeof(DEVICE_EXTENSION),
            &devName2,
            FILE_DEVICE_UNKNOWN,
            0,
            TRUE,
            &pDevObj2);
        if (!NT_SUCCESS(status))
        {
            return status;
        }

        pDevObj2->Flags |= DO_BUFFERED_IO;
        pDevExt2 = (PDEVICE_EXTENSION)pDevObj2->DeviceExtension;
        pDevExt2->PDeviceObject = pDevObj2;
        pDevExt2->DeviceName = devName2;

        RtlInitUnicodeString(&symLinkName2, L"\\??\\HelloNTDriver2");
        pDevExt2->SymLinkName = symLinkName2;
        status = IoCreateSymbolicLink(&symLinkName2, &devName2);
        if (!NT_SUCCESS(status))
        {
            IoDeleteDevice(pDevObj2);
            return status;
        }


        return STATUS_SUCCESS;

    }

运行我们的驱动程序后就可以使用DeviceTree工具可以观察驱动对象和设备对象,该工具需要读者自行在互联网上下载,也可以在CSDN上下载猛戳我

我们先看一下驱动对象:

可以看到驱动对象中存在我们注册的IRP派遣函数以及我们创建的两个设备对象,驱动对象的地址是0xFFFFA803153BA10

创建的两个设备对象:

我们看到设备对象2的NextDevice地址是0xFFFFFA803336A0D0和设备对象1的设备地址相同。

后话

本文完整工程和代码托管在GitHub上猛戳我

其他章节链接

VS2013 WDK8.1驱动开发1(最简单的NT驱动)

VS2013 WDK8.1驱动开发2(最简单的WDM驱动)

VS2013 WDK8.1驱动开发3(手动加载NT驱动程序)

VS2013 WDK8.1驱动开发4(NT式驱动基本结构)

VS2013 WDK8.1驱动开发5(WDM驱动基本结构)

VS2013 WDK8.1驱动开发6(内存管理)

VS2013 WDK8.1驱动开发7(派遣函数)

VS2013 WDK8.1驱动开发8(设备读写操作)

VS2013 WDK8.1驱动开发9(IO设备控制操作)


分享给朋友阅读吧


您还未登录,登录微博账号发表精彩评论

 微博登录


最新评论

    还没有人评论...

 

 

刘杰

28岁, 现居苏州

微博:

微信:

BurnellLIU

邮箱:

burnell_liu@outlook.com

Github:

https://github.com/BurnellLiu

简介:

努力做一个快乐的程序员, good good study, day day up!

友情链接: Will Mao

苏ICP备16059872号. Copyright © 2016. http://www.burnelltek.com. All rights reserved.