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);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值