Windows驱动编程之串口过滤杂谈
字数 1523 2025-08-24 23:51:13

Windows驱动编程之串口过滤技术详解

一、串口基础概念

1.1 串口定义与特点

串口(COM接口)是一种采用串行通信方式的扩展接口,具有以下特点:

  • 数据按bit位传输
  • 通信线路简单,只需一根传输线即可实现双向传输
  • 成本低廉
  • 适用于远距离通信
  • 传输速度相对较慢

重要区分:USB不是串口,两者是完全不同的接口技术。

1.2 主板架构与串口位置

  • 北桥芯片:处理高速信号(CPU、RAM、AGP等)
  • 南桥芯片:负责I/O总线直接通信(PCI、SATA、USB、LAN、音频、键盘控制等)

串口属于南桥芯片管理的I/O设备。

二、设备过滤原理

2.1 过滤概念

设备驱动过滤是指在原有设备驱动的基础上添加一层过滤设备,特点包括:

  • 不改变原有接口封装
  • 在数据传输过程中添加处理层
  • 类似于"层层过滤"的概念

2.2 设备栈模型

  • 设备栈采用分层结构,类似"蒸包子笼"一层盖一层
  • 过滤设备被添加到设备栈的最顶层
  • 一个设备可以被多个设备绑定
  • ReactOS系统中由PnP管理器处理设备拔插通知

三、关键API与实现步骤

3.1 创建过滤设备

使用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,
    _Outptr_ PDEVICE_OBJECT *DeviceObject
);

参数说明:

  • DriverObject:调用者的驱动程序对象指针
  • DeviceExtensionSize:扩展设备大小(无扩展传0)
  • DeviceName:设备名(过滤设备通常为NULL)
  • DeviceType:必须与绑定设备类型相同
  • DeviceObject:返回新创建的设备对象指针

示例代码:

PDEVICE_OBJECT fltobj = NULL;
status = IoCreateDevice(pDriver, 0, NULL, oldobj->DeviceType, 0, FALSE, &fltobj);
if (!NT_SUCCESS(status))
    return status;

3.2 绑定设备

两种绑定方式:

方式一:有设备名称绑定(IoAttachDevice)

NTSTATUS IoAttachDevice(
    _In_ PDEVICE_OBJECT SourceDevice,
    _In_ PUNICODE_STRING TargetDevice,
    _Out_ PDEVICE_OBJECT *AttachedDevice
);

方式二:无设备名称绑定(推荐使用IoAttachDeviceToDeviceStackSafe)

PDEVICE_OBJECT IoAttachDeviceToDeviceStack(
    PDEVICE_OBJECT SourceDevice,
    PDEVICE_OBJECT TargetDevice
);

3.3 获取串口驱动对象

使用IoGetDeviceObjectPointer()函数:

NTSTATUS IoGetDeviceObjectPointer(
    PUNICODE_STRING ObjectName,
    ACCESS_MASK DesiredAccess,
    PFILE_OBJECT *FileObject,
    PDEVICE_OBJECT *DeviceObject
);

示例代码:

RtlStringCchPrintfW(&name_str, sizeof name_str, L"\\Device\\Serial%d", id);
IoGetDeviceObjectPointer(&name_str, FILE_ALL_ACCESS, &fileobj, &devobj);

注意:该函数会使内核引用计数+2,可能导致设备被占用。

3.4 停止和卸载过滤

使用以下两个函数:

void IoDetachDevice(PDEVICE_OBJECT TargetDevice);
void IoDeleteDevice(PDEVICE_OBJECT DeviceObject);

四、串口数据获取与处理

4.1 IRP与派遣函数

IRP(I/O Request Packet)是处理I/O请求的核心数据结构:

  • 用户层通过系统I/O打开文件名
  • I/O管理器调用对象管理解析符号链接
  • 初始化分配内存IRP并创建IO_STACK_LOCATION数组
  • 通过IRP的I/O堆栈获取数据执行操作

派遣函数原型:

DRIVER_DISPATCH DriverDispatch;
NTSTATUS DriverDispatch(_DEVICE_OBJECT *DeviceObject, _IRP *Irp)

4.2 IRP处理流程

  1. 获取当前设备的IO栈:
PIO_STACK_LOCATION irpsp = IoGetCurrentIrpStackLocation(irp);
  1. 判断派遣函数处理的消息类型(如IRP_MJ_CREATE或IRP_MJ_WRITE)

  2. 获取数据(需考虑三种可能的通信方式):

// DO_DIRECT_IO方式
if(irp->MdlAddress != NULL) {
    buf = (PUCHAR)MmGetSystemAddressForMdlSafe(irp->MdlAddress, NormalPagePriority);
}

// 其他两种方式
// irp->AssociatedIrp.SystemBuffer
// irp->UserBuffer
  1. 处理数据(示例打印串口数据):
ULONG Writesize = irpsp->Parameters.Write.Length;
for(ULONG i = 0; i < Writesize; ++i) {
    KdPrint(("%2X ", buf[i]));
    if(!(Writesize % 16))
        KdPrint(("\r\n"))
}
  1. 继续处理或完成请求:
IoSkipCurrentIrpStackLocation(irp);
// 调用IoCallDriver或PoCallDriver处理
// 或进行敏感数据修改等操作
  1. 收尾工作:
irp->IoStatus.Information = 0;
irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
IoCompleteRequest(irp, IO_NO_INCREMENT);
return STATUS_ERROR;

4.3 IRQL(中断请求级别)

  • 保证进程优先级的机制
  • 主要级别:Dispatch、APC与Passive
  • 影响IRP处理过程(本文未详细展开)

五、完整实现示例

5.1 封装打开串口设备对象

NTSTATUS prOpenSataobj(PDEVICE_OBJECT *devobj) {
    NTSTATUS status;
    UNICODE_STRING name;
    PFILE_OBJECT fileobj = NULL;
    
    RtlInitUnicodeString(&name, L"\\Device\\Serial0");
    status = IoGetDeviceObjectPointer(&name, FILE_ALL_ACCESS, &fileobj, devobj);
    
    if(NT_SUCCESS(status))
        ObDereferenceObject(fileobj);
    
    return status;
}

5.2 封装过滤设备创建与绑定

PDEVICE_OBJECT prAttachDevobj(PDRIVER_OBJECT pDriver, PDEVICE_OBJECT *oldobj) {
    NTSTATUS status;
    PDEVICE_OBJECT fltobj;
    
    // 1. 创建过滤设备
    status = IoCreateDevice(pDriver, 0, NULL, oldobj->DeviceType, 0, FALSE, &fltobj);
    if(!NT_SUCCESS(status))
        return status;
    
    // 拷贝重要标志
    if(oldobj->Flags & DO_BUFFERED_IO)
        fltobj->Flags |= DO_BUFFERED_IO;
    if(oldobj->Flags & DO_DIRECT_IO)
        fltobj->Flags |= DO_DIRECT_IO;
    if(oldobj->Characteristics & FILE_DEVICE_SECURE_OPEN)
        fltobj->Characteristics |= FILE_DEVICE_SECURE_OPEN;
    
    // 2. 绑定设备驱动
    PDEVICE_OBJECT nrtobj = NULL;
    status = IoAttachDeviceToDeviceStackSafe(fltobj, oldobj, &nrtobj);
    if(!NT_SUCCESS(status)) {
        IoDeleteDevice(fltobj);
        fltobj = NULL;
        status = STATUS_UNSUCCESSFUL;
        return status;
    }
    
    // 设置启动状态
    fltobj->Flags = fltobj->Flags & ~DO_DEVICE_INITIALIZING;
    return nrtobj;
}

六、注意事项与进阶知识

  1. 通信协议兼容性:必须处理所有可能的通信方式(DO_DIRECT_IO、DO_BUFFERED_IO和UserBuffer)

  2. 引用计数管理:IoGetDeviceObjectPointer会使引用计数+2,需要特别注意

  3. IRP处理安全:索引不能设置为0,否则会导致系统崩溃

  4. 进阶知识(本文未详细展开):

    • IRP的详细调用过程
    • MDL映射机制
    • 内核通信机制
    • IRQL级别处理机制

七、参考资料

  1. 《Windows驱动开发技术详解》
  2. 《寒江独钓-Windows内核安全编程》
  3. ReactOS系统源码
  4. Windows WDK文档
Windows驱动编程之串口过滤技术详解 一、串口基础概念 1.1 串口定义与特点 串口(COM接口)是一种采用串行通信方式的扩展接口,具有以下特点: 数据按bit位传输 通信线路简单,只需一根传输线即可实现双向传输 成本低廉 适用于远距离通信 传输速度相对较慢 重要区分 :USB不是串口,两者是完全不同的接口技术。 1.2 主板架构与串口位置 北桥芯片 :处理高速信号(CPU、RAM、AGP等) 南桥芯片 :负责I/O总线直接通信(PCI、SATA、USB、LAN、音频、键盘控制等) 串口属于南桥芯片管理的I/O设备。 二、设备过滤原理 2.1 过滤概念 设备驱动过滤是指在原有设备驱动的基础上添加一层过滤设备,特点包括: 不改变原有接口封装 在数据传输过程中添加处理层 类似于"层层过滤"的概念 2.2 设备栈模型 设备栈采用分层结构,类似"蒸包子笼"一层盖一层 过滤设备被添加到设备栈的最顶层 一个设备可以被多个设备绑定 ReactOS系统中由PnP管理器处理设备拔插通知 三、关键API与实现步骤 3.1 创建过滤设备 使用 IoCreateDevice() 函数: 参数说明: DriverObject :调用者的驱动程序对象指针 DeviceExtensionSize :扩展设备大小(无扩展传0) DeviceName :设备名(过滤设备通常为NULL) DeviceType :必须与绑定设备类型相同 DeviceObject :返回新创建的设备对象指针 示例代码: 3.2 绑定设备 两种绑定方式: 方式一:有设备名称绑定(IoAttachDevice) 方式二:无设备名称绑定(推荐使用IoAttachDeviceToDeviceStackSafe) 3.3 获取串口驱动对象 使用 IoGetDeviceObjectPointer() 函数: 示例代码: 注意 :该函数会使内核引用计数+2,可能导致设备被占用。 3.4 停止和卸载过滤 使用以下两个函数: 四、串口数据获取与处理 4.1 IRP与派遣函数 IRP(I/O Request Packet)是处理I/O请求的核心数据结构: 用户层通过系统I/O打开文件名 I/O管理器调用对象管理解析符号链接 初始化分配内存IRP并创建IO_ STACK_ LOCATION数组 通过IRP的I/O堆栈获取数据执行操作 派遣函数原型: 4.2 IRP处理流程 获取当前设备的IO栈: 判断派遣函数处理的消息类型(如IRP_ MJ_ CREATE或IRP_ MJ_ WRITE) 获取数据(需考虑三种可能的通信方式): 处理数据(示例打印串口数据): 继续处理或完成请求: 收尾工作: 4.3 IRQL(中断请求级别) 保证进程优先级的机制 主要级别:Dispatch、APC与Passive 影响IRP处理过程(本文未详细展开) 五、完整实现示例 5.1 封装打开串口设备对象 5.2 封装过滤设备创建与绑定 六、注意事项与进阶知识 通信协议兼容性 :必须处理所有可能的通信方式(DO_ DIRECT_ IO、DO_ BUFFERED_ IO和UserBuffer) 引用计数管理 :IoGetDeviceObjectPointer会使引用计数+2,需要特别注意 IRP处理安全 :索引不能设置为0,否则会导致系统崩溃 进阶知识 (本文未详细展开): IRP的详细调用过程 MDL映射机制 内核通信机制 IRQL级别处理机制 七、参考资料 《Windows驱动开发技术详解》 《寒江独钓-Windows内核安全编程》 ReactOS系统源码 Windows WDK文档