Windows驱动开发(二)

1. NT和WDM式驱动

1. NT式驱动

传统的Windows系统驱动类型。NT式驱动的启动/停止/安装/卸载只能由 服务控制管理程序组件(SCM) 来完成的。
包括最简单的hello world,以及目前常用的文件过滤框架 minifilter 都是基于NT式实现的。
NT式驱动的最大特点即完全不依赖硬件支持即可工作在内核模式中。

VOID FilterUnload(PDRIVER_OBJECT DriverObject) {
    UNREFERENCED_PARAMETER(DriverObject);
}

VOID Initialize(PDRIVER_OBJECT pDriverObject) {
    pDriverObject->DriverUnload = FilterUnload;
    for (int i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++) {
        pDriverObject->MajorFunction[i] = DispatchAny;
    }
}

//
// DriverEntry
//
_Function_class_(DRIVER_INITIALIZE)
    _IRQL_requires_same_
    _IRQL_requires_(PASSIVE_LEVEL)
EXTERN_C NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT pDriverObject,
                              _In_ PUNICODE_STRING pusRegistryPath) {
    // Enable POOL_NX_OPTIN
    // Set NonPagedPool
    ExInitializeDriverRuntime(DrvRtPoolNxOptIn);

    //
    // Init global data
    //
    BOOLEAN fSuccess = FALSE;
    __try {
        Initialize(pDriverObject);
        LOGINFO3("Driver Load\n");
        fSuccess = true;

    } __finally {
        if (!fSuccess) {
            FilterUnload(pDriverObject);
        }
    }

    return STATUS_SUCCESS;
}

但是NT式驱动的缺点也正因如此,当需要用NT式驱动实现设备过滤时需要枚举每个需要的设备再附加到设备堆栈,例如早期的SFilter框架。

2.WDM驱动

支持即插即用(PNP)和电源管理功能的驱动类型,也是最常见的驱动类型,通常与硬件关联的驱动都是由WDM实现。
WDM驱动与NT式驱动的最大区别在于WDM驱动不支持SCM管理。虽然WDM驱动不支持SCM管理,但依然支持通过SCM启动,只是无法通过SCM停止。
在具体实现上,相比NT式驱动,WDM驱动必须额外注册AddDevice回调函数,此回调函数的作用是创建设备对象并由PNP管理器调用。
同时在IRP_MJ_POWERIRP_MJ_PNP中处理设备插拔和电源的IRP请求。

VOID FilterUnload(PDRIVER_OBJECT DriverObject) {
    UNREFERENCED_PARAMETER(DriverObject);
}

NTSTATUS DispatchPower(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
    NTSTATUS Status = STATUS_INVALID_DEVICE_OBJECT_PARAMETER;
    PDEVICE_EXTENSION DevExt = NULL;
    do {
        DevExt = (PDEVICE_EXTENSION)(DeviceObject->DeviceExtension);
        IoAcquireRemoveLock(&DevExt->RemoveLock, Irp);
#if (NTDDI_VERSION < NTDDI_VISTA)
        PoStartNextPowerIrp(Irp);
        IoSkipCurrentIrpStackLocation(Irp);
        Status = PoCallDriver(DevExt->TargetDevice, Irp);
#else
        IoSkipCurrentIrpStackLocation(Irp);
        Status = IoCallDriver(DevExt->TargetDevice, Irp);
#endif
        IoReleaseRemoveLock(&DevExt->RemoveLock, Irp);
    } while (false);
    return Status;
}

NTSTATUS DispatchPnp(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
    NTSTATUS Status = STATUS_INVALID_DEVICE_OBJECT_PARAMETER;
    PDEVICE_EXTENSION DevExt = NULL;
    PDEVICE_OBJECT TargetDevice = NULL;
    PIO_STACK_LOCATION IrpStack = NULL;
    bool LockState = true;
    do {
        DevExt = (PDEVICE_EXTENSION)(DeviceObject->DeviceExtension);
        IoAcquireRemoveLock(&DevExt->RemoveLock, Irp);
        TargetDevice = DevExt->TargetDevice;  //
        IrpStack = IoGetCurrentIrpStackLocation(Irp);
        switch (IrpStack->MinorFunction) {
            case IRP_MN_START_DEVICE:  // 处理设备启动请求
                break;
            // case IRP_MN_QUERY_REMOVE_DEVICE: // 安全移除
            // case IRP_MN_SURPRISE_REMOVAL: // 强制移除
            case IRP_MN_REMOVE_DEVICE:  // 处理设备移除请求
                IoReleaseRemoveLockAndWait(&DevExt->RemoveLock, Irp);
                IoDetachDevice(DevExt->TargetDevice);  //
                IoDeleteDevice(DeviceObject);          //
                LockState = false;
                break;
        }
        //
        IoSkipCurrentIrpStackLocation(Irp);
        Status = IoCallDriver(DevExt->TargetDevice, Irp);
        if (LockState) {
            IoReleaseRemoveLock(&DevExt->RemoveLock, Irp);
        }
    } while (false);
    return Status;
}

NTSTATUS AddDevice(PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT PDO) {
    NTSTATUS Status = STATUS_SUCCESS;
    PDEVICE_EXTENSION DevExt = NULL;
    PDEVICE_OBJECT FilterDevice = NULL;
    Status = IoCreateDevice(
        DriverObject, sizeof(DEVICE_EXTENSION),
        NULL, PDO->DeviceType,
        PDO->Characteristics, FALSE, &FilterDevice);
    if (!NT_SUCCESS(Status)) {
        LOGERROR(Status, "IoCreateDevice failed\n");
        return Status;
    }

    FilterDevice->Flags |= PDO->Flags;
    DevExt = (PDEVICE_EXTENSION)(FilterDevice->DeviceExtension);
    RtlZeroMemory(DevExt, sizeof(DEVICE_EXTENSION));
    IoInitializeRemoveLock(&DevExt->RemoveLock, c_nRmLockTag, 60, 0);
    DevExt->PDO = PDO;

    Status = IoAttachDeviceToDeviceStackSafe(
        FilterDevice,            //
        PDO,                     //
        &DevExt->TargetDevice);  //
    if (!NT_SUCCESS(Status)) {
        LOGERROR(Status, "IoAttachDeviceToDeviceStackSafe failed\n");
        IoDeleteDevice(FilterDevice);
        return Status;
    }

    FilterDevice->Flags |= DevExt->TargetDevice->Flags & (DO_DIRECT_IO | DO_BUFFERED_IO);
    FilterDevice->Flags &= ~DO_DEVICE_INITIALIZING;
    return STATUS_SUCCESS;
}

VOID Initialize(PDRIVER_OBJECT pDriverObject) {
    pDriverObject->DriverUnload = FilterUnload;
    for (int i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++) {
        pDriverObject->MajorFunction[i] = DispatchAny;
    }

    pDriverObject->MajorFunction[IRP_MJ_POWER] = DispatchPower;
    pDriverObject->MajorFunction[IRP_MJ_PNP] = DispatchPnp;
    pDriverObject->DriverExtension->AddDevice = AddDevice;
}

//
// DriverEntry
//
_Function_class_(DRIVER_INITIALIZE)
    _IRQL_requires_same_
    _IRQL_requires_(PASSIVE_LEVEL)
EXTERN_C NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT pDriverObject,
                              _In_ PUNICODE_STRING pusRegistryPath) {
    // Enable POOL_NX_OPTIN
    // Set NonPagedPool
    ExInitializeDriverRuntime(DrvRtPoolNxOptIn);

    //
    // Init global data
    //
    BOOLEAN fSuccess = FALSE;
    __try {
        Initialize(pDriverObject);
        LOGINFO3("Driver Load\n");
        fSuccess = true;
    } __finally {
        if (!fSuccess) {
            FilterUnload(pDriverObject);
        }
    }

    return STATUS_SUCCESS;
}

从上述的示例代码中可以看出,WDM驱动与NT式驱动的区。

2. 设备堆栈和IRP派遣

上图是USB存储设备的设备堆栈示例图,我们从下往上来看。
最下层是USB控制控制总线,由pci驱动创建设备的PDO(Physical Device Object)。
上一层是USB根控制器,由usbuhci驱动创建PDO,同时附加到下层的控制总线上。
更上层则是USB存储设备,由usbhub驱动创建PDO,附加到USB控制器。
最上层即实际的USB磁盘设备,由usbstor驱动创建PDO,附加到下一层的存储设备。
继续向上追溯的话,还有由disk驱动创建的磁盘分区,这里就不一一赘述了。
而所有的IRP请求都是由系统分发给最顶层的驱动程序,然后调用IoCallDriver向下分发,等待下层的执行的结果。
当然,实际的IO请求实际上是从卷设备开始分发的,而具体如何由卷设备派遣给磁盘设备的逻辑下次再说了。

  • 如何由卷设备追溯顶级的USB设备

1)通过IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS请求获取物理磁盘的编号。

bool GetPhysicalDrive(PDEVICE_OBJECT DeviceObject, PWCH pPhysicalDrive, ULONG size) {
    VOLUME_DISK_EXTENTS vde;
    NTSTATUS status = IoControl(DeviceObject, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, NULL, 0, &vde, sizeof(vde));
    swprintf_s(pPhysicalDrive, size, L"\\??\\PhysicalDrive%lu", vde.Extents[0].DiskNumber);
    return true;
}

2)由物理磁盘的编号获取文件系统设备对象

PFILE_OBJECT FileObj;
NTSTATUS status = IoGetDeviceObjectPointer(&usDiskWin32Name, FILE_READ_DATA, &FileObj, &DevObj);
ObDereferenceObject(FileObj);
// Find FileSystem From Device Stack
UNICODE_STRING usFileSystemDriver = RTL_CONSTANT_STRING(L"\\FileSystem\\RAW");
DevObj = GetTargetDeviceFromStackByDriver(DevObj, &usFileSystemDriver);

3)由文件系统设备对象获取磁盘设备对象

IoGetDiskDeviceObject(DevObj, &DiskObj);

4)由磁盘设备对象获取磁盘驱动器对象

PDEVICE_OBJECT GetTargetDeviceFromStackByDriver(PDEVICE_OBJECT DeviceObject, PUNICODE_STRING DriverName) {
    PDEVICE_OBJECT TargetDevice;
    TargetDevice = IoGetLowerDeviceObject(DeviceObject);
    while (TargetDevice) {
        if (0 == RtlCompareUnicodeString(&TargetDevice->DriverObject->DriverName, DriverName, TRUE)) {
            break;
        }

        DeviceObject = TargetDevice;
        TargetDevice = IoGetLowerDeviceObject(DeviceObject);
        ObDereferenceObject(DeviceObject);
    }
    return TargetDevice;
}

// Find USBSTOR From Device Stack
UNICODE_STRING usUsbstorDriver = RTL_CONSTANT_STRING(L"\\Driver\\USBSTOR");
DevObj = GetTargetDeviceFromStackByDriver(DiskObj, &usUsbstorDriver);
【适合小白入门】深入掌握Windows操作系统原理,提升程序开发水平,为学习驱动开发,内核安全打下基础。学完本课程可以轻松的理解Windows内核,开阔思路,对没有底层开发基础的人起到有非常好的指导作用。在此基础上可以开发出有趣且功能强大的软件。 课程目录: 第1章windows驱动基础 第一课 认识windows驱动课 在虚拟机里安装windows操作系统 第三课 windows操作系统基本概念 第四课 操作系统的分层结构 第2章windowsw驱动编译环境配置、安装及调试 第五课 安装驱动开发环境 第六课 安装驱动开发环境 第七课 实战:编写驱动程序加载器 第3章驱动程序的基本结构 第八课 复习c语言的指针和数据结构 第九课 windows驱动程序的基本结构 第十课 编程实战-创建设备对象 第4章windows内存管理 第十一课 内存管理操作 第十驱动开发中使用链表 第十三课 驱动开发中使用快查表 第十四课 在驱动中使用c++中内存管理操作-newdelet 第十六课 驱动开发中宏与断言的使用 第5章应用程序与驱动程序通信 第十六课 irp与派遣函数 第十七课 缓冲区读写操作 第十五课 在驱动中使用结构化异常处理 第十八课 缓冲区读写操作 第十九课 模拟文件 第三十课 直接方式与其它方式读写操作 第三十一课 io设备控制操作 第三十课 io设备控制操作 第6章windows内核函数 第十七课 内核模式下的字符串操作1 第十八课 内核模式下的字符串操作 第十九课 内核模式下的字符串操作 第十课 内核模式下的文件操作 第十一课 内核模式下的文件操作 第课 内核模式下注册表操作 第十三课 内核模式下注册表操作 第十四课 内核模式下注册表操作 第十五课 内核模式下注册表操作 第7章驱动程序的同步处理 第三十三课 内核模式下的同步与异步操作 第三十四课 用户模式下的同步对象1_事件 第三十五课 用户模式下的同步对象2_线程信号量与互斥体 第三十六课 内核模式下的同步对象3_系统线程创建与普 第三十七课 内核模式下的同步对象4_信号量与互斥体 第三十八课 内核模式与用户模式间的同步操作 第三十九课 其它内核同步要素 第8章IRP的同步与异步 第四十课 应用程序的对文件同步与异步操作 第四十一课 irp异步完成 第四十课 irp的取消与startio函数 第四十三课 自定义startio函数 第9章定时器54分钟3节 第四十四课 io定时器与dpc定时器 第四十五课 内核模式下的等待操作 第四十六课 时间函数与irp超时处理 第10章驱动程序调用驱动程序1小时3节 第四十七课 通过设备句柄调用驱动程序 第四十八课 设备指针调用驱动程序 第四十九课 自定义irp与ObReferenceObject 第11章分层过滤驱动 第五十课 分层驱动:枚举设备栈上的设备对象 第五十一课 编写过滤驱动程序 第五十课 irp完成函数 第12章驱动程序开发高级技能 第五十三课 驱动程序的兼容性 第五十五课 驱动调试之windbg与vmware 第五十六课 驱动调试vs vmware 第五十四课 驱动签名证书原理及制作 第五十七课 驱动调试神器virtualkd 第五十八课 汇编语言编写驱动之环境搭建 第五十九课 用汇编语言开发32与64位驱动程序
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值