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_POWER
和IRP_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);