学习串口过滤驱动

在学习Windows内核编程的时候,参考了《Windows内核开发》书籍

首先我们先固定的过滤一个串口,来简化代码,先易后难,后续再扩充代码,过滤所有串口。

 

基础知识:在注册表中添加串口项

在windows 7 x86中 在注册表HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\SERIALCOMM下 添加项,使用串口工具枚举串口时会显示自己添加的,如下:

 

 

串口3   \Device\Serial2  对应COM3  是手动添加的

串口4   \Device\Serial3 对应COM4   是手动添加的

在上图中使用cmd 往串口写数据 COM1和COM2都可以写入,COM3和COM4提示系统找不到对应的文件,那是因为不存在这个串口,在驱动中创建此设备就可以打开。

也就是说,如果要虚拟串口,我们要在注册表中添加上面的信息,然后还要在驱动创建对应的设备,保证可以打开。

 

下面代码可以监控对串口1写的操作,并打印数据。

#include <ntddk.h>
#define NTSTRSAFE_LIB
#include <ntstrsafe.h>

PDEVICE_OBJECT gloFltObj = NULL;
PDEVICE_OBJECT gloTopDev = NULL;
PDEVICE_OBJECT gloCOM5 = NULL;
void DriverUnload(PDRIVER_OBJECT pDriver)
{
  // 首先解除绑定
  if (NULL != gloTopDev)
  {
    IoDetachDevice(gloTopDev);
    gloTopDev = NULL;
  }
  if (NULL != gloFltObj)
  {
    IoDeleteDevice(gloFltObj);
    gloFltObj = NULL;
  }

  // 睡眠5秒。等待所有irp处理结束
  //interval.QuadPart = (5 * 1000 * DELAY_ONE_MILLISECOND);
  //KeDelayExecutionThread(KernelMode, FALSE, &interval);

  return;
}


NTSTATUS ccpDispatch(PDEVICE_OBJECT device, PIRP irp)
{
  PIO_STACK_LOCATION irpsp = IoGetCurrentIrpStackLocation(irp);
  NTSTATUS status;
  ULONG i, j;

  
  if (gloFltObj == device)
  {
    // 所有电源操作,全部直接放过。
    if (irpsp->MajorFunction == IRP_MJ_POWER)
    {
      // 直接发送,然后返回说已经被处理了。
      PoStartNextPowerIrp(irp);
      IoSkipCurrentIrpStackLocation(irp);
      return PoCallDriver(gloTopDev, irp);
    }
    // 此外我们只过滤写请求。写请求的话,获得缓冲区以及其长度。
    // 然后打印一下。
    if (irpsp->MajorFunction == IRP_MJ_WRITE)
    {
      // 如果是写,先获得长度
      ULONG len = irpsp->Parameters.Write.Length;
      // 然后获得缓冲区
      PUCHAR buf = NULL;
      if (irp->MdlAddress != NULL)
        buf =(PUCHAR)MmGetSystemAddressForMdlSafe(irp->MdlAddress, NormalPagePriority);
      else
        buf = (PUCHAR)irp->UserBuffer;
      if (buf == NULL)
        buf = (PUCHAR)irp->AssociatedIrp.SystemBuffer;

      // 打印内容
      for (j = 0; j<len; ++j)
      {
        DbgPrint("comcap: Send Data: %2x\r\n",buf[j]);
      }
    }

    // 这些请求直接下发执行即可。我们并不禁止或者改变它。
    IoSkipCurrentIrpStackLocation(irp);
    return IoCallDriver(gloTopDev, irp);
  }

  // 如果根本就不在被绑定的设备中,那是有问题的,直接返回参数错误。
  irp->IoStatus.Information = 0;
  irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
  IoCompleteRequest(irp, IO_NO_INCREMENT);
  return STATUS_SUCCESS;
}

void createFilterDevice(PDRIVER_OBJECT pDriver)
{
  UNICODE_STRING name_str;
  PFILE_OBJECT fileobj = NULL;
  PDEVICE_OBJECT portdevobj = NULL;
  PDEVICE_OBJECT topDev = NULL;
  RtlInitUnicodeString(&name_str, L"\\Device\\Serial1");
  PDEVICE_OBJECT pFltObj = NULL;
  NTSTATUS status = STATUS_SUCCESS;
  __try
  {
    // 打开设备对象
    status = IoGetDeviceObjectPointer(&name_str, FILE_ALL_ACCESS, &fileobj, &portdevobj);
    if (STATUS_SUCCESS != status)
      __leave;
    // 生成设备,然后绑定之。
    status = IoCreateDevice(pDriver, 0, NULL, portdevobj->DeviceType, 0, FALSE, &pFltObj);

    if (status != STATUS_SUCCESS)
      __leave;
    // 拷贝重要标志位。
    if (portdevobj->Flags & DO_BUFFERED_IO)
      pFltObj->Flags |= DO_BUFFERED_IO;
    if (portdevobj->Flags & DO_DIRECT_IO)
      pFltObj->Flags |= DO_DIRECT_IO;
    
    if (portdevobj->Characteristics & FILE_DEVICE_SECURE_OPEN)
      pFltObj->Characteristics |= FILE_DEVICE_SECURE_OPEN;
    pFltObj->Flags |= DO_POWER_PAGABLE;
    // 绑定一个设备到另一个设备上
    topDev = IoAttachDeviceToDeviceStack(pFltObj, portdevobj);
    if (topDev == NULL)
    {
      __leave;
    }
    gloTopDev = topDev;
    // 设置这个设备已经启动。
    pFltObj->Flags = pFltObj->Flags & ~DO_DEVICE_INITIALIZING;
    gloFltObj = pFltObj;
    status = STATUS_SUCCESS;
  }
  __finally
  {
    if (NULL != fileobj)
    {
      ObDereferenceObject(fileobj);
      fileobj = NULL;
    }
    if (NULL == topDev && NULL != pFltObj)
    {
      // 如果绑定失败了,销毁设备,重新来过。
      IoDeleteDevice(pFltObj);
      pFltObj = NULL;
      status = STATUS_UNSUCCESSFUL;
    }
  }
  return;
}
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING pRegistryPath)
{
  __try
  {
    __asm int 3
  }
  __except (EXCEPTION_EXECUTE_HANDLER)
  {

  }
  
  if (pRegistryPath)
  {
    KdPrint(("%wZ\r\n", pRegistryPath));
  }

  //打开串口 过滤 添加dispatch函数
  if (NULL != pDriver)
  {
    pDriver->DriverUnload = DriverUnload;
    size_t i;
    // 所有的分发函数都设置成一样的
    for (i = 0; i<IRP_MJ_MAXIMUM_FUNCTION; i++)
    {
      pDriver->MajorFunction[i] = ccpDispatch;
    }
  }
  createFilterDevice(pDriver);
  return STATUS_SUCCESS;
}

 

创建虚拟串口的代码如下:

  UNICODE_STRING name_COM5;
  RtlInitUnicodeString(&name_COM5, L"\\Device\\Serial4");  //串口5  这个应该还需要修改注册表  才可以被搜索到
  NTSTATUS status = IoCreateDevice(pDriver, 0, &name_COM5, FILE_DEVICE_UNKNOWN, 0, FALSE, &gloCOM5);
  if (STATUS_SUCCESS == status)
  {
    UNICODE_STRING sym_name_COM5;
    RtlInitUnicodeString(&sym_name_COM5, L"\\??\\COM5");
    status = IoCreateSymbolicLink(&sym_name_COM5, &name_COM5);
  }

 

posted @ 2022-09-30 15:11  psj00  阅读(427)  评论(0)    收藏  举报