You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 18:24:59