[内核安全1]串口的过滤

it2023-12-19  76

串口的过滤是通过编写一个过滤驱动, 将其加载到计算机上,通过附着到所有串口设备的驱动上来实现对串口设备通信的监控。由于Windows上的串口设备名都是形如:

\\Device\\Serial1 \\Device\\Serial2 \\Device\\Serial3 .... \\Device\\SerialN

所以可以通过枚举所有的串口设备名来把新的过滤驱动附着到上面。先来看一下下面的代码:

PDEVICE_OBJECT ccpOpenCom(ULONG id, NTSTATUS *status) { UNICODE_STRING name_str; static WCHAR name[32] = { 0 }; PFILE_OBJECT fileobj = NULL; PDEVICE_OBJECT devobj = NULL; memset(name, 0, sizeof(WCHAR) * 32); RtlStringCchPrintfW(name, 32, L"\\Device\\Serial%d", id); // 获取串口的设备名 RtlInitUnicodeString(&name_str, name); *status = IoGetDeviceObjectPointer(&name_str, FILE_ALL_ACCESS, &fileobj, &devobj); // 串口设备名对应的文件指针和设备对象指针 if (*status == STATUS_SUCCESS) ObDereferenceObject(fileobj); return(devobj); }

这段代码获取了特定号码的串口设备名并获取了该串口设备对应的设备对象指针,注意别忘记解引用文件对象指针。接下去就是以该段代码作为基础来遍历所有的串口设备并获取所有串口设备的设备对象指针, 例如下面:

void ccpAttachAllComs(PDRIVER_OBJECT driver) { ULONG i; PDEVICE_OBJECT com_ob; NTSTATUS status; for (i = 0; i < CCP_MAX_COM_ID; ++i) { com_ob = ccpOpenCom(i, &status); // 打开0~31号串口设备 if (com_ob == NULL) // 如果打开失败就继续打开下一个 continue; // 打开成功就绑定 ccpAttachDevice(driver, com_ob, &s_fltobj[i], &s_nextobj[i]); } }

这段代码中调用了ccpAttachDevice函数代码如下:

NTSTATUS ccpAttachDevice(PDRIVER_OBJECT driver, PDEVICE_OBJECT oldobj, PDEVICE_OBJECT *fltobj, PDEVICE_OBJECT *next) { NTSTATUS status; PDEVICE_OBJECT topdev = NULL; status = IoCreateDevice(driver, 0, NULL, oldobj->DeviceType, 0, FALSE, fltobj); if (status != STATUS_SUCCESS) 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)->Flags |= FILE_DEVICE_SECURE_OPEN; (*fltobj)->Flags |= DO_POWER_PAGABLE; topdev = IoAttachDeviceToDeviceStack(*fltobj, oldobj); if (topdev == NULL) { IoDeleteDevice(*fltobj); *fltobj = NULL; status = STATUS_UNSUCCESSFUL; return(status); } *next = topdev; (*fltobj)->Flags = (*fltobj)->Flags & ~DO_DEVICE_INITIALIZING; return(STATUS_SUCCESS); }

上面ccpAttachDevice做的工作就是把如下图所示的Filter Driver附着到Serial Driver上,并且通过指针参数返回过滤驱动对象指针和过滤驱动附着的设备对象指针。其还会把过滤驱动的Flags标志以及Characteristic设成与串口设备一致。

所以上面的ccpAttachAllComs函数所做的工作就是遍历所有的串口设备,并确定是否有串口设备,如果有就把过滤驱动附着到上面。最后就是派遣函数了,所有的IRP请求都被送到派遣函数ccpDispatch中处理, 处理代码如下:

NTSTATUS ccpDispatch(PDEVICE_OBJECT device, PIRP irp) { PIO_STACK_LOCATION irpsp = IoGetCurrentIrpStackLocation(irp); NTSTATUS status; ULONG i, j; for (i = 0; i < CCP_MAX_COM_ID; ++i) { if (s_fltobj[i] == device) { if (irpsp->MajorFunction == IRP_MJ_POWER) { PoStartNextPowerIrp(irp); // 跳过这个IRP,通过电源管理处理下一个电源IRP IoSkipCurrentIrpStackLocation(irp); return(PoCallDriver(s_nextobj[i], irp)); // 把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->AssociatedIrp.SystemBuffer; for (j = 0; j < len; ++j) DbgPrint("comcap: SendData: %2x\r\n", buf[j]); } // 除了写请求外全部下发 IoSkipCurrentIrpStackLocation(irp); return(IoCallDriver(s_nextobj[i], irp)); } } // 根本没在绑定的设备中 irp->IoStatus.Information = 0; irp->IoStatus.Status = STATUS_INVALID_PARAMETER; IoCompleteRequest(irp, IO_NO_INCREMENT); return(STATUS_SUCCESS); }

由于IRP在设备栈中的传递顺序是自顶向下,所以必定先碰到高层的过滤设备,当IRP请求被送到cchDispatch派遣函数时首先确定是否是附着的过滤设备,如果是的话确定具体IRP类型,如果是电源方面的IRP那就直接放过不管,并把该IRP顺着设备栈往下传递,如果是写操作的IRP,那么把所有的数据通过MDL的方式获取并将其打印出来。其他的IRP都忽略直接送到下一层设备(当然你想要处理的话也可以处理,随意),这里仅仅是将写操作IRP请求的内容打印。

运行结果如下:

来看一下完整的代码:

#include <ntddk.h> #include <ntstrsafe.h> #define CCP_MAX_COM_ID 32 static PDEVICE_OBJECT s_fltobj[CCP_MAX_COM_ID] = {0}; static PDEVICE_OBJECT s_nextobj[CCP_MAX_COM_ID] = {0}; PDEVICE_OBJECT ccpOpenCom(ULONG id, NTSTATUS *status) { UNICODE_STRING name_str; static WCHAR name[32] = { 0 }; PFILE_OBJECT fileobj = NULL; PDEVICE_OBJECT devobj = NULL; memset(name, 0, sizeof(WCHAR) * 32); RtlStringCchPrintfW(name, 32, L"\\Device\\Serial%d", id); // 获取串口的设备名 RtlInitUnicodeString(&name_str, name); *status = IoGetDeviceObjectPointer(&name_str, FILE_ALL_ACCESS, &fileobj, &devobj); // 串口设备名对应的文件指针和设备对象指针 if (*status == STATUS_SUCCESS) ObDereferenceObject(fileobj); return(devobj); } // 创建一个与老设备相同的无名新设备用于过滤老设备,将其附在老设备上 NTSTATUS ccpAttachDevice(PDRIVER_OBJECT driver, PDEVICE_OBJECT oldobj, PDEVICE_OBJECT *fltobj, PDEVICE_OBJECT *next) { NTSTATUS status; PDEVICE_OBJECT topdev = NULL; status = IoCreateDevice(driver, 0, NULL, oldobj->DeviceType, 0, FALSE, fltobj); if (status != STATUS_SUCCESS) 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)->Flags |= FILE_DEVICE_SECURE_OPEN; (*fltobj)->Flags |= DO_POWER_PAGABLE; topdev = IoAttachDeviceToDeviceStack(*fltobj, oldobj); if (topdev == NULL) { IoDeleteDevice(*fltobj); *fltobj = NULL; status = STATUS_UNSUCCESSFUL; return(status); } *next = topdev; (*fltobj)->Flags = (*fltobj)->Flags & ~DO_DEVICE_INITIALIZING; return(STATUS_SUCCESS); } void ccpAttachAllComs(PDRIVER_OBJECT driver) { ULONG i; PDEVICE_OBJECT com_ob; NTSTATUS status; for (i = 0; i < CCP_MAX_COM_ID; ++i) { com_ob = ccpOpenCom(i, &status); // 打开0~31号串口设备 if (com_ob == NULL) // 如果打开失败就继续打开下一个 continue; // 打开成功就绑定 ccpAttachDevice(driver, com_ob, &s_fltobj[i], &s_nextobj[i]); } } NTSTATUS ccpDispatch(PDEVICE_OBJECT device, PIRP irp) { PIO_STACK_LOCATION irpsp = IoGetCurrentIrpStackLocation(irp); NTSTATUS status; ULONG i, j; for (i = 0; i < CCP_MAX_COM_ID; ++i) { if (s_fltobj[i] == device) { if (irpsp->MajorFunction == IRP_MJ_POWER) { PoStartNextPowerIrp(irp); // 跳过这个IRP,通过电源管理处理下一个电源IRP IoSkipCurrentIrpStackLocation(irp); return(PoCallDriver(s_nextobj[i], irp)); // 把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->AssociatedIrp.SystemBuffer; for (j = 0; j < len; ++j) DbgPrint("comcap: SendData: %2x\r\n", buf[j]); } // 除了写请求外全部下发 IoSkipCurrentIrpStackLocation(irp); return(IoCallDriver(s_nextobj[i], irp)); } } // 根本没在绑定的设备中 irp->IoStatus.Information = 0; irp->IoStatus.Status = STATUS_INVALID_PARAMETER; IoCompleteRequest(irp, IO_NO_INCREMENT); return(STATUS_SUCCESS); } #define DELAY_ONE_MICROSECOND (-10) #define DELAY_ONE_MILLISECOND (DELAY_ONE_MICROSECOND*1000) #define DELAY_ONE_SECOND (DELAY_ONE_MILLISECOND*1000) VOID Unload(IN PDRIVER_OBJECT pDriverObject) { KdPrint(("卸载驱动!\n")); ULONG i; LARGE_INTEGER interval; for (i = 0; i < CCP_MAX_COM_ID; ++i) { if (s_nextobj[i] != NULL) IoDetachDevice(s_nextobj[i]); } interval.QuadPart = (5 * 1000 * DELAY_ONE_MILLISECOND); KeDelayExecutionThread(KernelMode, FALSE, &interval); for (int i = 0; i < CCP_MAX_COM_ID; ++i) { if (s_fltobj[i] != NULL) IoDeleteDevice(s_fltobj[i]); } } NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegistryPath) { pDriverObject->DriverUnload = Unload; ULONG i; KdPrint(("加载驱动!\n")); for (i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++) pDriverObject->MajorFunction[i] = ccpDispatch; ccpAttachAllComs(pDriverObject); return(STATUS_SUCCESS); }

(完)

最新回复(0)