一、设备绑定的内核API
二、生成过滤设备并绑定 与 从名字获得设备对象
三、绑定所有串口
四、请求的区分
五、请求的结局
六、写请求的数据
答案 一、设备绑定的内核API
二、生成过滤设备并绑定 与 从名字获得设备对象
三、绑定所有串口
四、请求的区分
五、请求的结局
六、写请求的数据
三个基本概念 一、过滤的概念 过滤的概念本质就是在一个驱动中对每个串口生成一个过滤设备,将每个过滤设备附加到对应的设备栈中。 消息自上而下发送,在到达串口前必须经过过滤设备,这样来对此进行过滤。 二、设备栈的概念 拿一个函数来举个例子 NTSTATUS IoAttachDeviceToDeviceStackSafe( IN PDEVICE_OBJECT SourceDevice, IN PDEVICE_OBJECT TargetDevice, OUT PDEVICE_OBJECT *AttachedToDeviceObject ); 其中之所以会输出 AttchedToDeviceObject,是因为当将过滤设备SourceDevice卸载时需要这个参数。 三、设备、驱动与IRP请求的概念 如图,一个驱动对象可以只绑定一个分发函数来处理所有设备的请求。 因为分发函数的第一个参数就是对应的设备对象,我们可以通过该成员来进行区分是来自该驱动对象的哪个设备的。 利用过滤设备实现监控的代码 该代码实现了一个驱动过滤的例子。 一、实验效果 使用超级终端,端口2来建立连接(windbg往往使用端口1,再测试时无法接收信息,后来端口2则可以收到请求) 二、源代码 /********* 作者:OneTrianee 编写时间:2019/12/8 编译环境:Win10 vs2019 "Empty WDM Driver" 代码作用:生成过滤设备,绑定端口监视输入数据 参考资料:《Windows内核安全与驱动开发》 注意事项: 1. 虚拟机中windbg往往会占用COM1,此时通信应该使用COM2(如果没有,关闭去虚拟机设置中开一个。) 2. 开机时绑定端口2成功,但是卸载之后再绑定就只能绑定端口1,暂时不清楚原因(接触绑定没发现问题..) 效果(DebugView): 分发函数接收到请求了!! comcap: Send Data: 61 分发函数接收到请求了!! comcap: Send Data: 73 ***********/ #include <ntddk.h> #include <ntstrsafe.h> // sleep 时用到的宏 #define DELAY_ONE_MICROSECOND (-10) #define DELAY_ONE_MILLISECOND (DELAY_ONE_MICROSECOND*1000) #define DELAY_ONE_SECOND (DELAY_ONE_MILLISECOND*1000) #define CCP_MAX_COM_ID 32 // 假设有32个端口 // // 函数声明 // NTSTATUS ccpDispatch(PDEVICE_OBJECT device, PIRP irp); // 设备分发函数 void ccpUnload(PDRIVER_OBJECT drv); // 动态卸载驱动函数 void ccpAttachAllComs(PDRIVER_OBJECT driver); // 绑定所有串口函数 PDEVICE_OBJECT ccpOpenCom(ULONG id, NTSTATUS* status); // 打开端口设备对象 NTSTATUS ccpAttachDevice(PDRIVER_OBJECT driver, PDEVICE_OBJECT oldobj, PDEVICE_OBJECT* fltobj, PDEVICE_OBJECT* next ); // 将设备与驱动对象进行绑定 // // 设备栈: // // IRP消息 ↓ // ---------- // |过滤设备| // ---------- // |端口设备| // ---------- // static PDEVICE_OBJECT s_fltobj[CCP_MAX_COM_ID] = { 0 }; // 过滤设备 static PDEVICE_OBJECT s_nextobj[CCP_MAX_COM_ID] = { 0 }; // 端口设备(绑定后会返回) /* 函数名:ccpDispatch 函数作用:分发函数,分发设备的irp请求 参数1 - PDEVICE_OBJECT device:发送请求的目标设备 参数2 - PIRP irp: IRP请求内容 */ NTSTATUS ccpDispatch(PDEVICE_OBJECT device, PIRP irp) { PIO_STACK_LOCATION irpsp = IoGetCurrentIrpStackLocation(irp); NTSTATUS status; ULONG i, j; // // 判断是发送给哪个过滤设备 // 如果设备存在,则打印出设备内容 // DbgPrint("分发函数接收到请求了!!\n"); for (i = 0; i < CCP_MAX_COM_ID; i ) { if (s_fltobj[i] == device) { // 所有电源操作,全部直接放过。 if (irpsp->MajorFunction == IRP_MJ_POWER) { // 直接发送,然后返回说已经被处理了。 PoStartNextPowerIrp(irp); IoSkipCurrentIrpStackLocation(irp); return PoCallDriver(s_nextobj[i], 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(s_nextobj[i], irp); } } // 如果根本就不在被绑定的设备中,那是有问题的,直接返回错误参数。 irp->IoStatus.Information = 0; irp->IoStatus.Status = STATUS_INVALID_PARAMETER; IoCompleteRequest(irp, IO_NO_INCREMENT); return STATUS_SUCCESS; } /* 函数名:ccpAttchAllComs 函数功能:生成过滤设备, 参数1 - dirver:生成过滤设备需要指明其在哪个驱动中。 */ 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); if (com_ob == NULL) // 获取失败,继续获取下一个 continue; else DbgPrint("绑定端口号%u成功!", i); // 在这里绑定,并不管绑定是否成功 ccpAttachDevice(driver, com_ob, &s_fltobj[i], &s_nextobj[i]); } } /* 函数名:ccpOpenCom 函数功能:打开驱动端口设备 参数1 - ULONG id:打开的端口号ID,函数内自动给转换为设备名,然后打开 参数2 - NTSTATUS* status:操作状态 返回值 - PDEVICE_OBJECT:如果打开成功,返回端口设备句柄指针 */ PDEVICE_OBJECT ccpOpenCom(ULONG id, NTSTATUS* status) { UNICODE_STRING name_str; static WCHAR name[32] = { 0 }; PFILE_OBJECT fileobj = NULL; PDEVICE_OBJECT devobj = NULL; // // 将 ID 转换为相应的端口设备名UNICODE_STRING // memset(name, 0, sizeof(WCHAR) * 32); RtlStringCchPrintfW( (NTSTRSAFE_PWSTR)name, 32, (NTSTRSAFE_PWSTR)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; } /* 函数名: 函数功能:生成过滤设备,并绑定串口设备,保存过滤设备fltobj和原栈顶设备next 参数1 - PDRIVER_OBJECT driver:生成过滤设备时所在的驱动对象 参数2 - PDEVICE_OBJECT oldobj:已生成的端口设备 参数3 - PDEVICE_OBJECT* fltobj:生成的过滤设备(保存在数组中) 参数4 - PDEVICE_OBJECT* next:原设备栈顶的设备 */ 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->Flags & DO_BUFFERED_IO) (*fltobj)->Flags |= DO_BUFFERED_IO; if (oldobj->Characteristics & FILE_DEVICE_SECURE_OPEN) (*fltobj)->Characteristics |= 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; } /* 函数名:ccpUnload 函数作用:动态卸载驱动对象 参数1 - PDRIVER_OBJECT drv:需要卸载的驱动对象指针 */ void ccpUnload(PDRIVER_OBJECT drv) { ULONG i; LARGE_INTEGER interval; // 首先解除绑定 for (i = 0; i < CCP_MAX_COM_ID; i ) { if (s_nextobj[i] != NULL) /* |--------| |过滤设备| |--------| |next设备| // 将该层上面的过滤设备给卸载掉 |--------| |xxxx设备| |--------| |端口设备| |--------| */ IoDetachDevice(s_nextobj[i]); } // 睡眠5秒,等待所有irp处理结束 interval.QuadPart = (5 * 1000 * DELAY_ONE_MILLISECOND); KeDelayExecutionThread(KernelMode, FALSE, &interval); // 删除这些设备 for (i = 0; i < CCP_MAX_COM_ID; i ) { if (s_fltobj[i] != NULL) { IoDeleteDevice(s_fltobj[i]); } } } NTSTATUS DriverEntry(PDRIVER_OBJECT driver, PUNICODE_STRING reg_path) { size_t i; DbgPrint("过滤设备安装成功!"); // 所有分发函数都设置成一样的 for (i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i ) { driver->MajorFunction[i] = ccpDispatch; } // 支持动态卸载 driver->DriverUnload = ccpUnload; // 绑定所有串口 ccpAttachAllComs(driver); // 直接返回成功即可 return STATUS_SUCCESS; }来源:https://www./content-3-589701.html |
|