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驱动开发3(手动加载NT驱动程序)
VS2013 WDK8.1驱动开发4(NT式驱动基本结构)
还没有人评论...