WindowsAPI编程核心技术-Minifilter文件监控 这节不讲Minifilter,只讲实现,入门Minifilter看驱动篇。
拦截IRP请求 本节在FLT_OPERATION_REGISTRATION中设置IRP_MJ_CREATE、IRP_MJ_READ、IRP_MJ_WRITE、IRP_MJ_SET_INFORMATION,分别对应文件创建、读取、写入、属性修改等。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 CONST FLT_OPERATION_REGISTRATION Callbacks[] = { { IRP_MJ_CREATE, 0 , Minifilter_FileMonitor_TestPreOperation, Minifilter_FileMonitor_TestPostOperation }, { IRP_MJ_READ, 0 , Minifilter_FileMonitor_TestPreOperation, Minifilter_FileMonitor_TestPostOperation }, { IRP_MJ_WRITE, 0 , Minifilter_FileMonitor_TestPreOperation, Minifilter_FileMonitor_TestPostOperation }, { IRP_MJ_SET_INFORMATION, 0 , Minifilter_FileMonitor_TestPreOperation, Minifilter_FileMonitor_TestPostOperation }, { IRP_MJ_OPERATION_END } };
文件保护 注册过滤器FltRegisterFilter
和开启过滤器FltStartFiltering
俩保持框架不变即可。
Minifilter注册启动后,插入到FltMgr实例队列中,关联需要过滤的卷。例如有3个Minifilter从上到下挂载到FltMgr实例上,一旦某个Minifilter返回FLT_PREOP_COMPLETE,表明这个I/O由它完成了,立即按照相反顺序调用相应操作后函数,不再继续下调。否则返回FLT_PREOP_SUCCESS_WITH_CALLBACK,文件操作继续执行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 FLT_PREOP_CALLBACK_STATUS Minifilter_FileMonitor_TestPreOperation (_Inout_ PFLT_CALLBACK_DATA Data, _In_ PCFLT_RELATED_OBJECTS FltObjects, _Flt_CompletionContext_Outptr_ PVOID* CompletionContext) { NTSTATUS status; UNREFERENCED_PARAMETER (FltObjects); UNREFERENCED_PARAMETER (CompletionContext); PT_DBG_PRINT (PTDBG_TRACE_ROUTINES, ("Minifilter_FileMonitor_Test!Minifilter_FileMonitor_TestPreOperation: Entered\n" )); UCHAR MajorFunction = Data->Iopb->MajorFunction; PFLT_FILE_NAME_INFORMATION lpNameInfo = NULL ; status = FltGetFileNameInformation (Data, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT, &lpNameInfo); if (NT_SUCCESS (status)) { status = FltParseFileNameInformation (lpNameInfo); if (NT_SUCCESS (status)) { if (IRP_MJ_CREATE == MajorFunction) { if (IsProtectionFile (lpNameInfo)) { KdPrint (("[IRP_MJ_CREATE]%wZ" , &lpNameInfo->Name)); return FLT_PREOP_COMPLETE; }; } else if (IRP_MJ_READ == MajorFunction) { if (IsProtectionFile (lpNameInfo)) { KdPrint (("[IRP_MJ_READ]%wZ" , &lpNameInfo->Name)); return FLT_PREOP_COMPLETE; }; } else if (IRP_MJ_WRITE == MajorFunction) { if (IsProtectionFile (lpNameInfo)) { KdPrint (("[IRP_MJ_WRITE]%wZ" , &lpNameInfo->Name)); return FLT_PREOP_COMPLETE; }; } else if (IRP_MJ_SET_INFORMATION == MajorFunction) { if (IsProtectionFile (lpNameInfo)) { KdPrint (("[IRP_MJ_SET_INFORMATION]%wZ" , &lpNameInfo->Name)); return FLT_PREOP_COMPLETE; }; }; }; }; return FLT_PREOP_SUCCESS_WITH_CALLBACK; };
判断文件路径时,要用ExAllocatePool
申请非分页内存,因为FltGetFileNameInformation
获取的路径信息存储在分页内存中,直接在回调函数中使用变量会蓝屏。
反Minifilter文件监控 前置芝士 FltEnumerateFilters 列举系统中所有注册的Minifilter驱动程序。
1 2 3 4 5 NTSTATUS FltEnumerateFilters ( _Out_opt_ PFLT_FILTER* FilterList, _In_opt_ ULONG FilterListSize, _Out_ PULONG NumberFilterReturned )
基本原理 别忘了包含头文件fltKernel.h并导入FltMgr.lib。
首先用FltEnumerateFilters
获取系统上所有注册成功的Minifilter过滤器对象指针数组PFLT_FILTER*
,这个数据结构在不同系统定义不同。
例如在Windows 10 x64上用WinDBG获取FLT_FILTER结构体定义,注意Operations成员:
1 2 3 4 dt fltmgr!_FLT_FILTER ... +0x1A8 Operations : Ptr64 _FLT_OPERATION_REGISTRATION ...
不同系统中FLT_FILTER结构中Operations成员偏移大小:
Windows 7
Windows 8.1
Windows 10
x86
CC
D4
E4
x64
188
198
1A8
结构FLT_OPERATION_REGISTRATION是固定的,定义:
1 2 3 4 5 6 7 typedef struct _FLT_OPERATION_REGISTRATION { UCHAR MajorFunction; FLT_OPERATION_REGISTRATION_FLAGS Flags; PFLT_PRE_OPERATION_CALLBACK PreOperation; PFLT_POST_OPERATION_CALLBACK PostOperation; PVOID Reserved1; } FLT_OPERATION_REGISTRATION, * PFLT_OPERATION_REGISTRATION;
枚举Minifilter系统回调函数实现代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 BOOLEAN EnumCallback (VOID) { NTSTATUS status = STATUS_SUCCESS; ULONG ulFilterListSize = 0 ; PFLT_FILTER* ppFilterList = NULL ; ULONG i = 0 ; LONG lOperationsOffset = 0 ; PFLT_OPERATION_REGISTRATION pFltOperationRegistration = NULL ; FltEnumerateFilters (NULL , 0 , &ulFilterListSize); ppFilterList = (PFLT_FILTER*)ExAllocatePool (NonPagedPool, ulFilterListSize * sizeof (PFLT_FILTER)); if (NULL == ppFilterList) { DbgPrint ("ExAllocatePool Error!\n" ); return FALSE; }; status = FltEnumerateFilters (ppFilterList, ulFilterListSize, &ulFilterListSize); if (!NT_SUCCESS (status)) { DbgPrint ("FltEnumerateFilters Error![0x%X]\n" , status); return FALSE; }; DbgPrint ("ulFilterListSize=%d\n" , ulFilterListSize); lOperationsOffset = GetOperationsOffset (); if (0 == lOperationsOffset) { DbgPrint ("GetOperationsOffset Error\n" ); return FALSE; }; __try { for (i = 0 ; i < ulFilterListSize; i++) { pFltOperationRegistration = (PFLT_OPERATION_REGISTRATION)(*(PVOID*)((PUCHAR)ppFilterList[i] + lOperationsOffset)); __try { DbgPrint ("-------------------------------------------------------------------------------\n" ); while (IRP_MJ_OPERATION_END != pFltOperationRegistration->MajorFunction) { if (IRP_MJ_MAXIMUM_FUNCTION > pFltOperationRegistration->MajorFunction) DbgPrint ("[Filter=%p]IRP=%d, PreFunc=0x%p, PostFunc=0x%p\n" , ppFilterList[i], pFltOperationRegistration->MajorFunction, pFltOperationRegistration->PreOperation, pFltOperationRegistration->PostOperation); pFltOperationRegistration = (PFLT_OPERATION_REGISTRATION)((PUCHAR)pFltOperationRegistration + sizeof (FLT_OPERATION_REGISTRATION)); }; DbgPrint ("-------------------------------------------------------------------------------\n" ); } __except (EXCEPTION_EXECUTE_HANDLER) { DbgPrint ("[2_EXCEPTION_EXECUTE_HANDLER]\n" ); }; }; } __except (EXCEPTION_EXECUTE_HANDLER) { DbgPrint ("[1_EXCEPTION_EXECUTE_HANDLER]\n" ); }; ExFreePool (ppFilterList); ppFilterList = NULL ; return TRUE; };
在自身驱动程序中可以用FltUnregisterFilter
,但现在显然不在一个驱动程序中删除回调函数。只有两种方法:
修改FLT_OPERATION_REGISTRATION中操作前回调函数和操作后回调函数地址,指向自定义空回调函数地址。
修改回调函数内存前几字节为RET。
这里用第一种方法,分别声明操作前回调函数、操作后回调函数,这里直接看源代码。
源代码 Driver.h:
1 2 3 4 5 6 7 8 9 10 11 12 #ifndef _DRIVER_H_ #define _DRIVER_H_ #include <ntddk.h> VOID DriverUnload (PDRIVER_OBJECT pDriverObject) ;NTSTATUS DriverDefaultHandle (PDEVICE_OBJECT pDevObj, PIRP pIrp) ;#endif
EnumRemove.h:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 #ifndef _ENUM_REMOVE_H_ #define _ENUM_REMOVE_H_ #include <fltKernel.h> BOOLEAN EnumCallback () ;NTSTATUS RemoveCallback (PFLT_FILTER pFilter) ;LONG GetOperationsOffset () ;FLT_PREOP_CALLBACK_STATUS New_MiniFilterPreOperation ( _Inout_ PFLT_CALLBACK_DATA Data, _In_ PCFLT_RELATED_OBJECTS FltObjects, _Flt_CompletionContext_Outptr_ PVOID *CompletionContext ) ;FLT_POSTOP_CALLBACK_STATUS New_MiniFilterPostOperation ( _Inout_ PFLT_CALLBACK_DATA Data, _In_ PCFLT_RELATED_OBJECTS FltObjects, _In_opt_ PVOID CompletionContext, _In_ FLT_POST_OPERATION_FLAGS Flags ) ;#endif
Driver.c:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 #include "EnumRemove.h" #include "Driver.h" NTSTATUS DriverEntry (PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegPath) { DbgPrint ("Enter DriverEntry\n" ); NTSTATUS status = STATUS_SUCCESS; pDriverObject->DriverUnload = DriverUnload; for (ULONG i = 0 ; i < IRP_MJ_MAXIMUM_FUNCTION; i++) { pDriverObject->MajorFunction[i] = DriverDefaultHandle; } EnumCallback (); DbgPrint ("Leave DriverEntry\n" ); return status; } VOID DriverUnload (PDRIVER_OBJECT pDriverObject) {} NTSTATUS DriverDefaultHandle (PDEVICE_OBJECT pDevObj, PIRP pIrp) { NTSTATUS status = STATUS_SUCCESS; pIrp->IoStatus.Status = status; pIrp->IoStatus.Information = 0 ; IoCompleteRequest (pIrp, IO_NO_INCREMENT); return status; }
EnumRemove.c:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 #include "EnumRemove.h" VOID ShowError (PCHAR lpszText, NTSTATUS ntStatus) { DbgPrint ("%s Error[0x%X]\n" , lpszText, ntStatus); } BOOLEAN EnumCallback () { NTSTATUS status = STATUS_SUCCESS; ULONG ulFilterListSize = 0 ; PFLT_FILTER *ppFilterList = NULL ; ULONG i = 0 ; LONG lOperationsOffset = 0 ; PFLT_OPERATION_REGISTRATION pFltOperationRegistration = NULL ; FltEnumerateFilters (NULL , 0 , &ulFilterListSize); ppFilterList = (PFLT_FILTER *)ExAllocatePool (NonPagedPool, ulFilterListSize *sizeof (PFLT_FILTER)); if (NULL == ppFilterList) { DbgPrint ("ExAllocatePool Error!\n" ); return FALSE; } status = FltEnumerateFilters (ppFilterList, ulFilterListSize, &ulFilterListSize); if (!NT_SUCCESS (status)) { DbgPrint ("FltEnumerateFilters Error![0x%X]\n" , status); return FALSE; } DbgPrint ("ulFilterListSize=%d\n" , ulFilterListSize); lOperationsOffset = GetOperationsOffset (); if (0 == lOperationsOffset) { DbgPrint ("GetOperationsOffset Error\n" ); return FALSE; } __try { for (i = 0 ; i < ulFilterListSize; i++) { pFltOperationRegistration = (PFLT_OPERATION_REGISTRATION)(*(PVOID *)((PUCHAR)ppFilterList[i] + lOperationsOffset)); __try { DbgPrint ("-------------------------------------------------------------------------------\n" ); while (IRP_MJ_OPERATION_END != pFltOperationRegistration->MajorFunction) { if (IRP_MJ_MAXIMUM_FUNCTION > pFltOperationRegistration->MajorFunction) { DbgPrint ("[Filter=%p]IRP=%d, PreFunc=0x%p, PostFunc=0x%p\n" , ppFilterList[i], pFltOperationRegistration->MajorFunction, pFltOperationRegistration->PreOperation, pFltOperationRegistration->PostOperation); } pFltOperationRegistration = (PFLT_OPERATION_REGISTRATION)((PUCHAR)pFltOperationRegistration + sizeof (FLT_OPERATION_REGISTRATION)); } DbgPrint ("-------------------------------------------------------------------------------\n" ); } __except (EXCEPTION_EXECUTE_HANDLER) { DbgPrint ("[2_EXCEPTION_EXECUTE_HANDLER]\n" ); } } } __except (EXCEPTION_EXECUTE_HANDLER) { DbgPrint ("[1_EXCEPTION_EXECUTE_HANDLER]\n" ); } ExFreePool (ppFilterList); ppFilterList = NULL ; return TRUE; } NTSTATUS RemoveCallback (PFLT_FILTER pFilter) { LONG lOperationsOffset = 0 ; PFLT_OPERATION_REGISTRATION pFltOperationRegistration = NULL ; pFltOperationRegistration = (PFLT_OPERATION_REGISTRATION)(*(PVOID *)((PUCHAR)pFilter + lOperationsOffset)); __try { while (IRP_MJ_OPERATION_END != pFltOperationRegistration->MajorFunction) { if (IRP_MJ_MAXIMUM_FUNCTION > pFltOperationRegistration->MajorFunction) { pFltOperationRegistration->PreOperation = New_MiniFilterPreOperation; pFltOperationRegistration->PostOperation = New_MiniFilterPostOperation; DbgPrint ("[Filter=%p]IRP=%d, PreFunc=0x%p, PostFunc=0x%p\n" , pFilter, pFltOperationRegistration->MajorFunction, pFltOperationRegistration->PreOperation, pFltOperationRegistration->PostOperation); } pFltOperationRegistration = (PFLT_OPERATION_REGISTRATION)((PUCHAR)pFltOperationRegistration + sizeof (FLT_OPERATION_REGISTRATION)); } } __except (EXCEPTION_EXECUTE_HANDLER) { DbgPrint ("[EXCEPTION_EXECUTE_HANDLER]\n" ); } return STATUS_SUCCESS; } LONG GetOperationsOffset () { RTL_OSVERSIONINFOW osInfo = { 0 }; LONG lOperationsOffset = 0 ; RtlGetVersion (&osInfo); if (6 == osInfo.dwMajorVersion) { if (1 == osInfo.dwMinorVersion) { #ifdef _WIN64 lOperationsOffset = 0x188 ; #else lOperationsOffset = 0xCC ; #endif } else if (2 == osInfo.dwMinorVersion) { #ifdef _WIN64 #else #endif } else if (3 == osInfo.dwMinorVersion) { #ifdef _WIN64 lOperationsOffset = 0x198 ; #else lOperationsOffset = 0xD4 ; #endif } } else if (10 == osInfo.dwMajorVersion) { #ifdef _WIN64 lOperationsOffset = 0x1A8 ; #else lOperationsOffset = 0xE4 ; #endif } return lOperationsOffset; } FLT_PREOP_CALLBACK_STATUS New_MiniFilterPreOperation ( _Inout_ PFLT_CALLBACK_DATA Data, _In_ PCFLT_RELATED_OBJECTS FltObjects, _Flt_CompletionContext_Outptr_ PVOID *CompletionContext ) { UNREFERENCED_PARAMETER (FltObjects); UNREFERENCED_PARAMETER (CompletionContext); DbgPrint ("[New_MiniFilterPreOperation]\n" ); return FLT_PREOP_SUCCESS_WITH_CALLBACK; } FLT_POSTOP_CALLBACK_STATUS New_MiniFilterPostOperation ( _Inout_ PFLT_CALLBACK_DATA Data, _In_ PCFLT_RELATED_OBJECTS FltObjects, _In_opt_ PVOID CompletionContext, _In_ FLT_POST_OPERATION_FLAGS Flags ) { UNREFERENCED_PARAMETER (Data); UNREFERENCED_PARAMETER (FltObjects); UNREFERENCED_PARAMETER (CompletionContext); UNREFERENCED_PARAMETER (Flags); DbgPrint ("[New_MiniFilterPostOperation]\n" ); return FLT_POSTOP_FINISHED_PROCESSING; }