具体的介绍可以看https://www.lyshark.com/post/6f58192d.html,这个是比较复杂的,不像监控进程,线程那些,只需要一个api+一个回调函数。
我们自己写的文件过滤驱动为这个
现在我们去windbg去找一下关于文件过滤的一系列内核数据结构。
参考https://github.com/V-i-x-x/kernel-callback-removal/tree/main/MiniFilterFileCallbackKernelBypass
这个项目很好,但是需要手动进行修改一些偏移,不然直接蓝屏。
我们可以通过以下导出函数 FLTMGR!FltEnumerateFilters进行二分查找获得该FLTMGR!FltGlobals地址
1 | u FLTMGR!FltEnumerateFilters L50 |
根据特征码找到此位置,然后获取这个地址减去0x58即可。
1 | 0: kd> dt FLTMGR!_GLOBALS fffff801`7ead9600 |
+0x058 FrameList : _FLT_RESOURCE_LIST_HEAD
1 | 0: kd> dx -id 0,0,ffffbb088d28a040 -r1 (*((FLTMGR!_FLT_RESOURCE_LIST_HEAD *)0xfffff8017ead9658)) |
1 | 0: kd> dt FLTMGR!_FLTP_FRAME 0xffffbb088dacc030 |
跟着链表就能找到我们的驱动
开头第一个就是这个
比如我们看到是0,那么就是代表IRP_MJ_CREATE操作,然后pre和post就是回调FSCreatePreCallback和FSCreatePostCallback
1 | static std::unordered_map<BYTE, const char*> g_IrpMjMap{ |
这边加0x20,看下一个IRP。
终止条件要注意:数组最后一个 entry 的 MajorFunction 是 IRP_MJ_OPERATION_END (0x80),遍历时碰到 0x80 就停止,这是 FltMgr 的约定标志。
那么下一个0x4,就是IRP_MJ_WRITE。继续遍历即可
每个帧也连接到卷。你可以把卷作为设备名称。
文件过滤器可以设置为仅激活并监控特定卷的 I/O 操作。
1 | 0: kd> dt FLTMGR!_FLTP_FRAME 0xffffbb088dacc030 |
需要从我们获得的卷地址中减去 10,才能到达卷结构 FLTMGR!_FLT_VOLUME 的起点
每个卷将包含一个回调列表,按其mj+22进行索引。
意思就是从22开始,对应我们的g_IrpMjMap,比如22就是{0, “IRP_MJ_CREATE”}
1 | dt FLTMGR!_CALLBACK_NODE |
那么我们之前看到的,0x0的回调,跟这个是对应的,pre post都是一样的。
所以这个寻找的逻辑,就是遍历这些卷,然后进行找Callbacks,从50个链表的第22个开始,然后遍历我们之前获取到的指定驱动的mj几种类型的。
比如这是第21个,就是0xff {((UCHAR)-1), “IRP_MJ_ACQUIRE_FOR_SECTION_SYNCHRONIZATION”},都对上了。
该函数将具体操作如下:
过滤器(GetOperationsForFilter)得到的操作。主要函数(步骤 1 操作)+ 22。操作 entry 将包含为该卷设置的所有回调意思就是从那50个里面,每一个都是一个链表,如果进去链表看里面数据FLTMGR!_CALLBACK_NODE我们循环对应上了这个pre和post,那么就进行这个从这个链表里面摘除。比如刚刚我们看的,这个就是第21个的链表。这个只有一个节点,如果我们摘除了。
1 | 0: kd> dx -id 0,0,ffffbb088d28a040 -r1 (*((FLTMGR!_LIST_ENTRY *)0xffffbb088da9e280)) |
此时链表空了