Windows驱动. 驱动基础
驱动对象和设备对象
I/O管理器
I/O管理器(I/O Manager) 负责 I/O请求调度, 生命周期, IRP流转 的核心组件
所有的应用程序/内核 发起的I/O请求, 都会通过I/O管理器进行调度
无论对端口的读写, 还是文件的读写, 都统一为 IRP 的请求形式, 包含了操作的重要数据(读还是写, 操作多大内存, 读到哪里去)
IRP被传递到具体设备的驱动中, 由驱动处理这些IRP, 并将处理完成的IRP按原路返回用户模式下的应用程序中
总而言之, I/O管理器担当着用户模式代码和设备驱动程序之间的端口
驱动程序
I/O管理器接收到应用程序请求后, 创建对应IRP, 然后将请求传递给驱动, 由如下处理
根据IRP请求, 直接操作对应硬件, 完成此IRP, 返回
根据IRP请求, 将其转发给更底层的驱动, 等待底层驱动返回
接收到IRP请求后, 分配新的IRP发送给其他驱动程序中, 等待其他驱动程序返回
驱动对象 DRIVER_OBJECT *PDRIVER_OBJECT
每个驱动程序会有一个 唯一 的驱动对象与其对应
当 I/O 管理器调用驱动程序的 DriverEntry 例程时,它会提供驱动程序的驱动程序对象的地址。 驱动程序对象包含用于许多驱动程序标准例程的入口点的存储。 驱动程序负责填写这些入口点。
DRIVER_OBJECT 由I/O管理程序创建, 它为每个已安装和加载的驱动程序创建一个实例, 然后将实例的地址传递给 DriverEntry 的 pDriverObject
NTSTATUS DriverEntry(
IN PDRIVER_OBJECT pDriverObject, // 👈 I/O管理创建好的 DRIVER_OBJECT 从这里传入
IN PUNICODE_STRING pRegistryPath
)随后驱动程序要把自己写的函数地址注册进PDRIVER_OBJECT, 这样 I/O 管理器就可以调用这些函数
DriverObject->MajorFunction[IRP_MJ_CREATE] = MyDispatchCreate;
DriverObject->MajorFunction[IRP_MJ_READ] = MyDispatchRead;
DriverObject->MajorFunction[IRP_MJ_WRITE] = MyDispatchWrite;
DriverObject->DriverUnload = MyDriverUnload;| 类型 | 名称 | 描述 | 备注 |
|---|---|---|---|
CSHORT | Type | 驱动对象类型 | |
CSHORT | Size | 驱动对象大小 | |
PDEVICE_OBJECT | DeviceObject | 设备对象 | 每个驱动由一个或者多个设备对象组成, 此处为设备对象链表的第一个成员. 在卸载时, 需要手动删除所有设备对象 |
ULONG | Flags | 驱动对象标志 | |
PVOID | DriverStart | 驱动程序入口地址 | |
ULONG | DriverSize | 驱动程序大小 | |
PVOID | DriverSection | 驱动链表 | 整个系统里驱动的链表管理 |
PDRIVER_EXTENSION | DriverExtension | 驱动扩展 | |
UNICODE_STRING | DriverName | 驱动名 | 该字符串一般为 \Driver\[驱动名] |
UNICODE_STRING | HardwareDatabase | 硬件数据库 | |
PFAST_IO_DISPATCH | FastIoDispatch | 快速IO分发 | |
PDRIVER_INITIALIZE | DriverInit | 驱动初始化 | |
PDRIVER_STARTIO | DriverStartIo | 驱动开始IO | |
PDRIVER_UNLOAD | DriverUnload | 驱动卸载 | |
PDRIVER_DISPATCH[] | MajorFunction | 驱动主要功能 | 数组每个成员记录着一个指针, 每个指针指向一个函数, 即为IRP派遣函数 |
设备对象 DEVICE_OBJECT *PDEVICE_OBJECT
操作系统使用 DEVICE_OBJECT 结构来表示设备对象。 设备对象表示驱动程序处理 I/O 请求的逻辑、虚拟或物理设备。
这里的设备范围很广, 除了物理设备外, 系统资源(内存管理, 进程, 线程管理) 也可以视作设备对象
| 类型 | 名称 | 描述 | 备注 |
|---|---|---|---|
PDRIVER_OBJECT | DeviceObject | 驱动对象 | 同属一个驱动程序的驱动对象指向的是同一个驱动对象 |
PDEVICE_OBJECT | NextDevice | 下一个设备对象 | 设备对象链表的下一个成员 |
PDEVICE_OBJECT | AttachedDevice | 附加设备对象 | 如果有更高一层的驱动附加到这个驱动的话, 它只想更高一层驱动 |
PVOID | DeviceExtension | 设备扩展 | 每个设备都会指定一个设备扩展对象, 用于存储设备私有数据, 它是一个自定义结构体 |
DEVICE_TYPE | DeviceType | 设备类型 | 设备类型 |
在老的NT模型中, 不支持PnP概念, 设备对象 DEVICE_OBJECT 是在 DriverEntry 里直接进行创建的
在WDM模型中, 设备对象需要想系统提供一个 AddDevice 例程, 此例程由PNP管理器负责调用, 主要职责是创建对象
pDriverObject->DriverExtension->AddDevice = MyAddDevice;
/**
* @brief 创建设备对象
* @param pDriverObject 驱动对象
* @param pPhysicalDeviceObject 由PNP管理器传递进来的底层物理驱动设备对象
* @return NTSTATUS
*/
#pragma PAGEDCODE // 在分页内存中执行
NTSTATUS MyAddDevice(PDRIVER_OBJECT pDriverObject, PDEVICE_OBJECT pPhysicalDeviceObject)
{
PAGED_CODE(); // 是否在分页内存中断言
NTSTATUS status;
UNICODE_STRING uDevName = { 0 }; // 设备名(内核可见)
PDEVICE_OBJECT pDeviceObject = NULL;
// 1. 初始化设备名
RtlInitUnicodeString(&uDevName, L"\\Device\\MyDevice");
// 2. 创建设备对象(安全驱动关键:FILE_DEVICE_SECURE_OPEN 确保设备安全)
status = IoCreateDevice(
pDriverObject, // 驱动对象
0, // 无设备扩展
&uDevName, // 设备名
FILE_DEVICE_UNKNOWN, // 安全驱动无特定设备类型
FILE_DEVICE_SECURE_OPEN, // 安全属性:仅允许有权限的进程访问
FALSE, // 非独占(安全驱动通常允许多进程访问)
&pDeviceObject // 输出设备对象
);
if (!NT_SUCCESS(status))
{
KdPrint(("IoCreateDevice 失败,状态=0x%X\n", status));
return status;
}
// 3. 设置缓冲IO
pDeviceObject->Flags |= DO_BUFFERED_IO;
// 清除DO_DEVICE_INITIALIZING标志(设备初始化完成)
pDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
// 4. 创建符号链接(让应用层访问)
RtlInitUnicodeString(&g_uSymLink, L"\\??\\MyDevice");
status = IoCreateSymbolicLink(&g_uSymLink, &uDevName);
if (!NT_SUCCESS(status))
{
KdPrint(("IoCreateSymbolicLink 失败,状态=0x%X\n", status));
IoDeleteDevice(pDeviceObject);
return status;
}
KdPrint(("AddDevice 创建设备完成\n"));
return STATUS_SUCCESS;
}
设备名称必须是 \Device\[设备名] 的形式, Windows下所有设备都是这种命令, 例如C盘和D盘命名为 \Device\HarddiskVolume1 和 \Device\HarddiskVolume2
如果不指定设备名, 则会自动分配一个数字作为设备名, 例如 \Device\Device00000001
如果指定了设备名, 则可以被内核模式其他驱动是被, 但是无法被用户模式下应用识别 , 如果要让用户模式下程序识别设备, 可以通过 符号链接/设备接口 两种方式
在内核模式下, 符号链接都是以 \??\ 开头, 用户模式下则是以 \\.\ 开头
例如C盘: 内核: \??\C: , 用户模式: \\.\C:
设备扩展 DEVICE_EXTENSION *PDEVICE_EXTENSION
每个设备对象都会有一个设备扩展对象, 用于存储设备私有数据, 它是一个自定义结构体, 由 I/O管理器创建
typedef struct _CUSTOM_DEVICE_EXTENSION {
PDEVICE_OBJECT DeviceObject;
PVOID PrivateData;
} CUSTOM_DEVICE_EXTENSION, *PCUSTOM_DEVICE_EXTENSION;然后需要在创建设备时指定
status = IoCreateDevice(
pDriverObject, // 驱动对象
0, // 无设备扩展
sizeof(CUSTOM_DEVICE_EXTENSION), // 设备扩展大小
&uDevName, // 设备名
FILE_DEVICE_UNKNOWN, // 安全驱动无特定设备类型
FILE_DEVICE_SECURE_OPEN, // 安全属性:仅允许有权限的进程访问
FALSE, // 非独占(安全驱动通常允许多进程访问)
&pDeviceObject // 输出设备对象
);