如何在Windows Minifilter中区分文件创建与文件打开操作?
问题描述
我想要实现类似Sysinternals Sysmon的文件创建追踪功能,基于微软nullfilter示例修改了Minifilter驱动代码,注册了IRP_MJ_CREATE回调,通过判断createDisp值来识别文件/文件夹创建操作,但目前代码同时追踪了文件打开与创建操作。我查阅了相关技术讨论,尝试了提及的方法仍无法精准区分,请问是否在底层不存在该区分,或是我找错了处理位置?
核心修改代码
nullfilter.c
// ... FLT_POSTOP_CALLBACK_STATUS FLTAPI CheckPathLengthPost( _Inout_ PFLT_CALLBACK_DATA Data, _In_ PCFLT_RELATED_OBJECTS FltObjects, _In_ PVOID CompletionContext, _In_ FLT_POST_OPERATION_FLAGS Flags ); // ... CONST FLT_OPERATION_REGISTRATION callback[] = { { IRP_MJ_CREATE, 0, CheckPathLengthPre, CheckPathLengthPost }, {IRP_MJ_OPERATION_END} }; CONST FLT_REGISTRATION FilterRegistration = { sizeof( FLT_REGISTRATION ), // Size FLT_REGISTRATION_VERSION, // Version 0, // Flags NULL, // Context callback, // Operation callbacks NullUnload, // FilterUnload NULL, // InstanceSetup NullQueryTeardown, // InstanceQueryTeardown NULL, // InstanceTeardownStart NULL, // InstanceTeardownComplete NULL, // GenerateFileName NULL, // GenerateDestinationFileName NULL // NormalizeNameComponent }; // ... FLT_PREOP_CALLBACK_STATUS FLTAPI CheckPathLengthPre( _Inout_ PFLT_CALLBACK_DATA Data, _In_ PCFLT_RELATED_OBJECTS FltObjects, _Flt_CompletionContext_Outptr_ PVOID* CompletionContext ) { UNREFERENCED_PARAMETER(FltObjects); UNREFERENCED_PARAMETER(CompletionContext); if (Data->RequestorMode == KernelMode) return FLT_PREOP_SUCCESS_NO_CALLBACK; PFLT_FILE_NAME_INFORMATION info = NULL; NTSTATUS status = FltGetFileNameInformation(Data, FLT_FILE_NAME_OPENED | FLT_FILE_NAME_QUERY_DEFAULT | FLT_FILE_NAME_DO_NOT_CACHE, &info); if (!(NT_SUCCESS(status))) { DbgPrint("nullfilter|CheckPathLengthPre: FltGetFileNameInformation failed: %#x", status); return FLT_PREOP_SUCCESS_NO_CALLBACK; } ULONG createDisp = (Data->Iopb->Parameters.Create.Options >> 24) & 0x000000FF; BOOLEAN isCreation = ((createDisp == FILE_CREATE) || (createDisp == FILE_SUPERSEDE) || (createDisp == FILE_OVERWRITE) || (createDisp == FILE_OVERWRITE_IF) || (createDisp == FILE_OPEN_IF)); // File or folder is being created if ((Data->Iopb->Parameters.Create.Options) & FILE_NON_DIRECTORY_FILE) { if (isCreation == 1) { DbgPrint("nullfilter: isFile: %d, isDir: %d, createDisp: <%08x>, Path: %wZ", 1, 0, createDisp, info->Name); } } else if ((Data->Iopb->Parameters.Create.Options) & FILE_DIRECTORY_FILE) { if (isCreation == 1) { DbgPrint("nullfilter: isFile: %d, isDir: %d, createDisp: <%08x>, Path: %wZ", 0, 1, createDisp, info->Name); } } return FLT_PREOP_SUCCESS_WITH_CALLBACK; }
nullfilter.inf
[Version] Signature = "$Windows NT$" Class = "Bottom" ;This is determined by the work this filter driver does ClassGuid = {21D41938-DAA8-4615-86AE-E37344C18BD8} ;This value is determined by the Class Provider = %ProviderString% DriverVer = 06/16/2007,1.0.0.0 CatalogFile = nullfilter.cat ; ... [NullFilter.Service] DisplayName = %ServiceName% Description = %ServiceDescription% ServiceBinary = %12%\%DriverName%.sys ;%windir%\system32\drivers\ Dependencies = "FltMgr" ServiceType = 2 ;SERVICE_FILE_SYSTEM_DRIVER StartType = 3 ;SERVICE_DEMAND_START ErrorControl = 1 ;SERVICE_ERROR_NORMAL LoadOrderGroup = "FSFilter Bottom" AddReg = NullFilter.AddRegistry ; ... [Strings] ; ... DefaultInstance = "Null Instance" Instance1.Name = "Null Instance" Instance1.Altitude = "47777" Instance1.Flags = 0
解决方案
底层可以精准区分文件创建和打开操作,你当前的问题出在两个关键位置:
1. 错误在PRE回调中判断createDisp
createDisp对应的是请求的意图,而非实际执行结果。比如发起FILE_OPEN_IF请求时,实际可能是打开已有文件,也可能是创建新文件,只有在POST回调中才能拿到最终的执行结果。
需要把判断逻辑移到CheckPathLengthPost回调中,同时结合操作的NTSTATUS结果验证:
FLT_POSTOP_CALLBACK_STATUS FLTAPI CheckPathLengthPost( _Inout_ PFLT_CALLBACK_DATA Data, _In_ PCFLT_RELATED_OBJECTS FltObjects, _In_ PVOID CompletionContext, _In_ FLT_POST_OPERATION_FLAGS Flags ) { UNREFERENCED_PARAMETER(FltObjects); UNREFERENCED_PARAMETER(CompletionContext); UNREFERENCED_PARAMETER(Flags); if (Data->RequestorMode == KernelMode) return FLT_POSTOP_FINISHED_PROCESSING; // 仅处理操作成功的场景 if (!NT_SUCCESS(Data->IoStatus.Status)) return FLT_POSTOP_FINISHED_PROCESSING; PFLT_FILE_NAME_INFORMATION info = NULL; NTSTATUS status = FltGetFileNameInformation(Data, FLT_FILE_NAME_OPENED | FLT_FILE_NAME_QUERY_DEFAULT | FLT_FILE_NAME_DO_NOT_CACHE, &info); if (!NT_SUCCESS(status)) { DbgPrint("nullfilter|CheckPathLengthPost: FltGetFileNameInformation failed: %#x", status); return FLT_POSTOP_FINISHED_PROCESSING; } ULONG createDisp = (Data->Iopb->Parameters.Create.Options >> 24) & 0xFF; BOOLEAN isCreation = FALSE; // 仅保留实际会创建新对象的操作 switch (createDisp) { case FILE_CREATE: case FILE_SUPERSEDE: case FILE_OVERWRITE: case FILE_OVERWRITE_IF: isCreation = TRUE; break; case FILE_OPEN_IF: // FILE_OPEN_IF需结合状态判断:STATUS_CREATED是创建,STATUS_SUCCESS是打开 isCreation = (Data->IoStatus.Status == STATUS_CREATED); break; default: isCreation = FALSE; break; } if (isCreation) { if (Data->Iopb->Parameters.Create.Options & FILE_NON_DIRECTORY_FILE) { DbgPrint("nullfilter: 新建文件, createDisp: <%08x>, Path: %wZ", createDisp, info->Name); } else if (Data->Iopb->Parameters.Create.Options & FILE_DIRECTORY_FILE) { DbgPrint("nullfilter: 新建文件夹, createDisp: <%08x>, Path: %wZ", createDisp, info->Name); } } FltReleaseFileNameInformation(info); return FLT_POSTOP_FINISHED_PROCESSING; }
2. 错误将FILE_OPEN_IF纳入创建判断
FILE_OPEN_IF的语义是“文件存在则打开,不存在则创建”,它不必然对应创建操作。必须在POST回调中通过Data->IoStatus.Status区分:结果为STATUS_CREATED时是创建,STATUS_SUCCESS时是打开。
额外注意事项
- 调用
FltReleaseFileNameInformation释放文件名信息,避免内存泄漏(你的PRE回调中遗漏了这一步)。 - 可根据需求过滤内核模式操作、文件系统临时操作等场景,减少误触发。
内容的提问来源于stack exchange,提问作者Tyler Montney
相关产品推荐
相关产品推荐

