学习串口过滤驱动
在学习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);
}

浙公网安备 33010602011771号