MiniFilter 是目前杀毒软件用来实现“文件系统自我保护”和“文件实时监控”的方法。
由于 MiniFilter 模型简单,开发快捷,通用性好,以前用 FSD HOOK 或者标准过滤驱动来实现相关功能的杀软纷纷改用 MiniFilter,比如卡巴斯基。不过,枚举 MiniFilter 就跟之前枚举回调的方法不太相同了,因为 MiniFilter 的框架不在 NTOSKRNL 里,自成一套系统,有专用的 API。不过“自成一套系统,有专用 API”的好处就是,无需我们自己通过特征码来定位数组或者链表,直接使用 MiniFilter 提供的 API 就行。
枚举MiniFilter主要使用FltEnumerateFilters这个API,它会返回过滤器对象(FLT_FILTER)的地址,然后根据过滤器对象的地址,加上一个偏移,获得记录过滤器 PreCall、PostCall、IRP 等信息的结构体指针(PFLT_OPERATION_REGISTRATION)。上文之所以说要加上偏移,是因为 FLT_FILTER 的定义在每个系统都不同,比如 WIN7X64 中的定义为:
nt!DbgBreakPointWithStatus:
fffff800`03e74f60 cc int 3
kd> dt fltmgr!_FLT_FILTER
+0x000 Base : _FLT_OBJECT
+0x020 Frame : Ptr64 _FLTP_FRAME
+0x028 Name : _UNICODE_STRING
+0x038 DefaultAltitude : _UNICODE_STRING
+0x048 Flags : _FLT_FILTER_FLAGS
+0x050 DriverObject : Ptr64 _DRIVER_OBJECT
+0x058 InstanceList : _FLT_RESOURCE_LIST_HEAD
+0x0d8 VerifierExtension : Ptr64 _FLT_VERIFIER_EXTENSION
+0x0e0 VerifiedFiltersLink : _LIST_ENTRY
+0x0f0 FilterUnload : Ptr64 long
+0x0f8 InstanceSetup : Ptr64 long
+0x100 InstanceQueryTeardown : Ptr64 long
+0x108 InstanceTeardownStart : Ptr64 void
+0x110 InstanceTeardownComplete : Ptr64 void
+0x118 SupportedContextsListHead : Ptr64 _ALLOCATE_CONTEXT_HEADER
+0x120 SupportedContexts : [6] Ptr64 _ALLOCATE_CONTEXT_HEADER
+0x150 PreVolumeMount : Ptr64 _FLT_PREOP_CALLBACK_STATUS
+0x158 PostVolumeMount : Ptr64 _FLT_POSTOP_CALLBACK_STATUS
+0x160 GenerateFileName : Ptr64 long
+0x168 NormalizeNameComponent : Ptr64 long
+0x170 NormalizeNameComponentEx : Ptr64 long
+0x178 NormalizeContextCleanup : Ptr64 void
+0x180 KtmNotification : Ptr64 long
+0x188 Operations : Ptr64 _FLT_OPERATION_REGISTRATION
+0x190 OldDriverUnload : Ptr64 void
+0x198 ActiveOpens : _FLT_MUTEX_LIST_HEAD
+0x1e8 ConnectionList : _FLT_MUTEX_LIST_HEAD
+0x238 PortList : _FLT_MUTEX_LIST_HEAD
+0x288 PortLock : _EX_PUSH_LOCK
FLT_OPERATION_REGISTRATION 的结构体定义是不变的:
枚举代码如下:
#include <Fltkernel.h> ULONG FltFilterOperationsOffset=0x188; //WIN7 OFFSET of fltmgr!_FLT_FILTER->PFLT_OPERATION_REGISTRATION //typedef struct _FLT_OPERATION_REGISTRATION //{ //UCHARMajorFunction; //ULONGFlags; //PVOIDPreOperation; //PVOIDPostOperation; //PVOIDReserved1; //} FLT_OPERATION_REGISTRATION, *PFLT_OPERATION_REGISTRATION; typedef struct _FLT_FILTER { UCHAR buffer[1024]; } FLT_FILTER, *PFLT_FILTER; //NTSTATUS //__fastcall //FltEnumerateFilters //( // PFLT_FILTER *FilterList, // ULONG FilterListSize, // PULONG NumberFiltersReturned //); // //NTSTATUS //__fastcall //FltObjectDereference //( // PVOID FltObject //); ULONG EnumMiniFilter() { long ntStatus; ULONG uNumber; PVOID pBuffer = NULL; ULONG uIndex = 0, DrvCount = 0; PVOID pCallBacks = NULL, pFilter = NULL; PFLT_OPERATION_REGISTRATION pNode; do { if(pBuffer != NULL) { ExFreePool(pBuffer); pBuffer = NULL; } ntStatus = FltEnumerateFilters(NULL, 0, &uNumber); if(ntStatus != STATUS_BUFFER_TOO_SMALL) break; pBuffer = ExAllocatePoolWithTag(NonPagedPool, sizeof(PFLT_FILTER) * uNumber, 'mnft'); if(pBuffer == NULL) { ntStatus = STATUS_INSUFFICIENT_RESOURCES; break; } ntStatus = FltEnumerateFilters(pBuffer, uNumber, &uNumber); } while (ntStatus == STATUS_BUFFER_TOO_SMALL); if(! NT_SUCCESS(ntStatus)) { if(pBuffer != NULL) ExFreePool(pBuffer); return 0; } DbgPrint("MiniFilter Count: %ld\n",uNumber); DbgPrint("------\n"); __try { while(DrvCount<uNumber) { pFilter = (PVOID)(*(PULONG64)((PUCHAR)pBuffer + DrvCount * 8)); pCallBacks = (PVOID)((PUCHAR)pFilter + FltFilterOperationsOffset); pNode = (PFLT_OPERATION_REGISTRATION)(*(PULONG64)pCallBacks); __try { while(pNode->MajorFunction != 0x80) //IRP_MJ_OPERATION_END { if(pNode->MajorFunction<28) //MajorFunction id is 0~27 { DbgPrint("Object=%p\tPreFunc=%p\tPostFunc=%p\tIRP=%d\n", pFilter, pNode->PreOperation, pNode->PostOperation, pNode->MajorFunction); } pNode++; } } __except(EXCEPTION_EXECUTE_HANDLER) { FltObjectDereference(pFilter); DbgPrint("[EnumMiniFilter]EXCEPTION_EXECUTE_HANDLER: pNode->MajorFunction\n"); ntStatus = GetExceptionCode(); ExFreePool(pBuffer); return uIndex; } DrvCount++; FltObjectDereference(pFilter); DbgPrint("------\n"); } } __except(EXCEPTION_EXECUTE_HANDLER) { FltObjectDereference(pFilter); DbgPrint("[EnumMiniFilter]EXCEPTION_EXECUTE_HANDLER\n"); ntStatus = GetExceptionCode(); ExFreePool(pBuffer); return uIndex; } if(pBuffer != NULL) { ExFreePool(pBuffer); ntStatus=STATUS_SUCCESS; } return uIndex;}
执行结果:
不过对抗 MiniFilter 似乎就只有两种方法了:
1.把记录的函数地址改为自己设置的空函数;
2.把处理函数头改为 RET 直接返回。 为什么不能把 直接把 MiniFilter 对象 反注册呢?因为MSDN 对 对 FltUnregisterFilter 的 用途 给出了 这样的解释 :A minifilter driver can only callFltUnregisterFilter to unregister itself, not another minifilter driver。据我测试,如果第三方驱动强制使用此函数注销一个 MiniFilter,轻则无效,重则蓝屏。
把 MINIFILTER 的处理函数禁用掉之后,卡巴斯基 2013 在 WIN64 系统上的文件保护就彻底失效了,可以直接使用最简单的方法来删除卡巴斯基文件夹内的文件,国内那些采用同样方法实现文件自我保护的杀毒软件(****)同理。
宋孖健,13