|
楼主 |
发表于 2022-10-29 01:10:41
|
显示全部楼层
本帖最后由 Grav1ty 于 2022-10-29 01:26 编辑
驱动2
串口过滤
大概逻辑:创建虚拟设备(IoCreateDevice)->绑定到真实对象上(IoAttachDeviceToDeviceStack)->分发函数(ccpDispatch)->如果irpsp.MajorFunction是写,则捕获,获取缓冲区及其长度并打印,否则直接放行->动态卸载
用到的重要库中函数:
- NTSTATUS IoAttachDeviceToDeviceStackSafe(
- [in] PDEVICE_OBJECT SourceDevice, //过滤设备
- [in] PDEVICE_OBJECT TargetDevice, //要被绑定的设备栈中的设备
- [out] PDEVICE_OBJECT *AttachedToDeviceObject //返回最终被绑定的设备
- );
复制代码- NTSTATUS IoCreateDevice(
- [in] PDRIVER_OBJECT DriverObject, //本驱动的驱动对象,由系统给
- [in] ULONG DeviceExtensionSize, //设备拓展,此处填0
- [in, optional] PUNICODE_STRING DeviceName, //设备名称(过滤设备一般不需要名称,故此处填null)
- [in] DEVICE_TYPE DeviceType, //设备类型,填被绑定的设备类型即可
- [in] ULONG DeviceCharacteristics, //设备特征,生成设备对象时一般为0
- [in] BOOLEAN Exclusive,
- [out] PDEVICE_OBJECT *DeviceObject
- );
复制代码 不知道为什么这个函数在代码块中显示不出来,那就放在正文里//通过设备名字获取设备对象 NTSTATUS IoGetDeviceObjectPointer( [in] PUNICODE_STRING ObjectName, //传入设备名称 [in] ACCESS_MASK DesiredAccess, //期望的权限 直接FILE_ALL_ACCESS即可 [out] PFILE_OBJECT *FileObject, //返回设备对象的同时会返回一个文件对象 [out] PDEVICE_OBJECT *DeviceObject //返回的文件对象 );
- void IoSkipCurrentIrpStackLocation(
- [in, out] PIRP Irp
- ); //跳过当前栈空间
复制代码- NTSTATUS IofCallDriver(
- PDEVICE_OBJECT DeviceObject, //接收irp的设备
- __drv_aliasesMem PIRP Irp //要发送的irp
- ); //把irp发给设备的函数
复制代码
irp结构中有三个地方描述缓冲区:
irp->MDLAddress 比较简单且不追求效率的情况,把R3的缓冲数据拷贝到内核空间
irp->UserBuffer 最追求效率的方案,应用层的缓冲区地址直接放在UserBuffer里,在内核空间中访问
irp->AssociatedIrp.SystemBuffer 把应用层的地址空间映射到内核空间,MDL翻译为“内存描述符链”。irp中的MDLAddress是一个MDL指针,这个MDL可以读出一个内存空间的虚拟地址,这就弥补了UserBuffer的不足,同时比SystemBuffer更轻量
获取缓冲区有多长,对于此处遇到的情况而言:
- ULONG len = irpsp->Parameters.Write.Length;
复制代码
- void IoDetachDevice(
- [in, out] PDEVICE_OBJECT TargetDevice
- ); //负责将绑定的设备解除绑定
复制代码- void IoDeleteDevice(
- [in] PDEVICE_OBJECT DeviceObject
- ); //删除设备释放内存
复制代码- NTSTATUS KeDelayExecutionThread(
- [in] KPROCESSOR_MODE WaitMode,
- [in] BOOLEAN Alertable,
- [in] PLARGE_INTEGER Interval
- ); //延时
复制代码- PVOID MmGetSystemAddressForMdlSafe(
- [in] PMDL Mdl,
- [in] ULONG Priority
- ); //返回指定 MDL 描述的缓冲区的非分页系统空间虚拟地址。
复制代码
完整源码:
comcap.c
- #include<ntddk.h>
- #include<ntstrsafe.h>
- #define NTSTRSAFE_LIB //为了使用静态的ntstrsafe静态库
- #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个设备
- //保存所有过滤设备指针
- static PDEVICE_OBJECT s_fltobj[CCP_MAX_COM_ID] = { 0 };
- //保存所有真实设备指针
- static PDEVICE_OBJECT s_nextobj[CCP_MAX_COM_ID] = { 0 };
- PDEVICE_OBJECT ccpOpenCom();
- NTSTATUS ccpAttachDevice();
- void ccpAttachAllComs();
- void ccpUnload();
- NTSTATUS ccpDispatch();
- NTSTATUS DriverEntry();
- //生成过滤设备并绑定
- NTSTATUS ccpAttachDevice(PDRIVER_OBJECT driver, PDEVICE_OBJECT oldobj, PDEVICE_OBJECT* fltobj, PDEVICE_OBJECT* next) {
- NTSTATUS status;
- PDEVICE_OBJECT topdev = NULL;
- //生成设备
- status = IoCreateDevice(
- driver,//本驱动的驱动对象,由系统给
- 0,//设备拓展,此处填0
- NULL,//设备名称(过滤设备一般不需要名称,故此处填null)
- oldobj->DeviceType,//设备类型,填被绑定的设备类型即可
- 0,//设备特征,生成设备对象时一般为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)->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;
- }
- //从名字获取设备对象
- PDEVICE_OBJECT ccpOpenCom(ULONG id, NTSTATUS* status) {
- //输入的是串口的id,需要改写成unicode_str的形式
- UNICODE_STRING name_str;
- static WCHAR name[32] = { 0 };
- PFILE_OBJECT fileobj = NULL;
- PDEVICE_OBJECT devobj = NULL;
- //根据id转换成串口的名字
- 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);
- 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);
- 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;
- }
- //动态卸载
- void ccpUnload(PDRIVER_OBJECT drv) {
- ULONG i;
- LARGE_INTEGER interval;
- //首先解除绑定
- for (i = 0; i < CCP_MAX_COM_ID; i++) {
- if (s_nextobj[i] != NULL) {
- 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;
- //所有看的分发函数都设置成一样的
- for (i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++) {
- driver->MajorFunction[i] = ccpDispatch;
- }
- //支持动态卸载
- driver->DriverUnload = ccpUnload;
- //绑定所有串口
- ccpAttachAllComs(driver);
- //直接返回成功即可
- return STATUS_SUCCESS;
- }
复制代码
sources
- TARGETNAME=comcap
- TARGETPATH=Release
- TARGETTYPE=DRIVER
- SOURCES=comcap.c
复制代码
安装后
|
|