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

Windows下Mini-Filter驱动开发求助:实现文件监控与内容校验功能

Got it, let's build this Windows Mini-Filter driver to meet your requirements. I'll provide the complete code along with explanations so you can follow along and understand each part.

整体实现思路

We'll hook into the IRP_MJ_CREATE operation (which triggers when a file is opened) using a Mini-Filter callback. Here's the breakdown:

  • Every time a file is opened, we'll retrieve and print its name.
  • If the file is testme.txt, we'll open it in kernel mode, read its content, and check for the string "hello".
    • If "hello" is found, we'll block the open operation.
    • If not, we'll allow the operation and print a "clean" message.
完整代码实现
#include <fltKernel.h>
#include <dontuse.h>
#include <suppress.h>

#pragma prefast(disable:__WARNING_ENCODE_MEMBER_FUNCTION_POINTER, "Not valid for kernel mode drivers")

PFLT_FILTER gFilterHandle = NULL;

// 回调函数:处理文件打开操作
FLT_PREOP_CALLBACK_STATUS
PreCreateCallback(
    _Inout_ PFLT_CALLBACK_DATA Data,
    _In_ PCFLT_RELATED_OBJECTS FltObjects,
    _Out_opt_ PVOID *CompletionContext
)
{
    UNREFERENCED_PARAMETER(CompletionContext);
    NTSTATUS status = STATUS_SUCCESS;
    PFLT_FILE_NAME_INFORMATION nameInfo = NULL;
    UNICODE_STRING fileName;
    UNICODE_STRING targetFileName = RTL_CONSTANT_STRING(L"testme.txt");
    UNICODE_STRING searchString = RTL_CONSTANT_STRING(L"hello");
    HANDLE fileHandle = NULL;
    IO_STATUS_BLOCK ioStatus;
    PVOID buffer = NULL;
    ULONG bytesRead = 0;
    BOOLEAN foundHello = FALSE;

    // 获取文件名信息
    status = FltGetFileNameInformation(Data, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT, &nameInfo);
    if (!NT_SUCCESS(status)) {
        DbgPrint("Failed to get file name info, status: 0x%X\n", status);
        return FLT_PREOP_SUCCESS_NO_CALLBACK;
    }

    // 提取文件名(只取最后部分,比如"testme.txt"而不是完整路径)
    status = FltParseFileNameInformation(nameInfo);
    if (!NT_SUCCESS(status)) {
        DbgPrint("Failed to parse file name info, status: 0x%X\n", status);
        goto Cleanup;
    }

    fileName = nameInfo->Name;
    DbgPrint("File being opened: %wZ\n", &fileName);

    // 检查是否是目标文件testme.txt
    if (RtlCompareUnicodeString(&fileName, &targetFileName, TRUE) == 0) {
        // 打开文件用于读取
        OBJECT_ATTRIBUTES objAttr;
        InitializeObjectAttributes(&objAttr, &nameInfo->Name, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);

        status = ZwCreateFile(&fileHandle,
                              GENERIC_READ,
                              &objAttr,
                              &ioStatus,
                              NULL,
                              FILE_ATTRIBUTE_NORMAL,
                              FILE_SHARE_READ,
                              FILE_OPEN,
                              FILE_SYNCHRONOUS_IO_NONALERT,
                              NULL,
                              0);
        if (!NT_SUCCESS(status)) {
            DbgPrint("Failed to open testme.txt, status: 0x%X\n", status);
            goto Cleanup;
        }

        // 分配缓冲区读取文件内容(这里假设文件大小不超过4KB,实际可根据需求调整)
        buffer = ExAllocatePool2(POOL_FLAG_PAGED, 4096, 'TxtR');
        if (!buffer) {
            DbgPrint("Failed to allocate buffer for reading file\n");
            goto CleanupFile;
        }

        // 读取文件内容
        status = ZwReadFile(fileHandle,
                            NULL,
                            NULL,
                            NULL,
                            &ioStatus,
                            buffer,
                            4096,
                            NULL,
                            NULL);
        if (!NT_SUCCESS(status)) {
            DbgPrint("Failed to read testme.txt, status: 0x%X\n", status);
            goto CleanupBuffer;
        }

        bytesRead = (ULONG)ioStatus.Information;
        if (bytesRead > 0) {
            // 在读取的内容中查找"hello"字符串
            PWCHAR content = (PWCHAR)buffer;
            ULONG maxIndex = (bytesRead / sizeof(WCHAR)) - searchString.Length / sizeof(WCHAR);
            for (ULONG i = 0; i <= maxIndex; i++) {
                if (RtlCompareMemory(content + i, searchString.Buffer, searchString.Length) == searchString.Length) {
                    foundHello = TRUE;
                    break;
                }
            }
        }

        if (foundHello) {
            // 找到"hello",拒绝打开操作
            DbgPrint("Found 'hello' in testme.txt - blocking open operation\n");
            Data->IoStatus.Status = STATUS_ACCESS_DENIED;
            Data->IoStatus.Information = 0;
            status = STATUS_ACCESS_DENIED;
        } else {
            // 未找到,允许打开并打印提示
            DbgPrint("testme.txt is clean\n");
        }

CleanupBuffer:
        if (buffer) {
            ExFreePool(buffer);
        }
CleanupFile:
        if (fileHandle) {
            ZwClose(fileHandle);
        }
    }

Cleanup:
    if (nameInfo) {
        FltReleaseFileNameInformation(nameInfo);
    }

    return NT_SUCCESS(status) ? FLT_PREOP_SUCCESS_NO_CALLBACK : FLT_PREOP_COMPLETE;
}

// 回调函数表
const FLT_OPERATION_REGISTRATION Callbacks[] = {
    { IRP_MJ_CREATE, 0, PreCreateCallback, NULL },
    { IRP_MJ_OPERATION_END }
};

// 过滤驱动注册信息
const FLT_REGISTRATION FilterRegistration = {
    sizeof(FLT_REGISTRATION),
    FLT_REGISTRATION_VERSION,
    0,
    NULL,
    Callbacks,
    FltUnload,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL
};

// 驱动入口函数
NTSTATUS
DriverEntry(
    _In_ PDRIVER_OBJECT DriverObject,
    _In_ PUNICODE_STRING RegistryPath
)
{
    UNREFERENCED_PARAMETER(RegistryPath);
    NTSTATUS status;

    status = FltRegisterFilter(DriverObject, &FilterRegistration, &gFilterHandle);
    if (!NT_SUCCESS(status)) {
        return status;
    }

    status = FltStartFiltering(gFilterHandle);
    if (!NT_SUCCESS(status)) {
        FltUnregisterFilter(gFilterHandle);
    }

    return status;
}

// 驱动卸载函数
NTSTATUS
FltUnload(
    _In_ FLT_FILTER_UNLOAD_FLAGS Flags
)
{
    UNREFERENCED_PARAMETER(Flags);
    FltUnregisterFilter(gFilterHandle);
    return STATUS_SUCCESS;
}
关键部分解释
  • PreCreateCallback: This is our main handler for file open operations. We use FltGetFileNameInformation to retrieve the file name, then parse it to get the base name (like testme.txt).
  • Target File Check: We compare the parsed file name with testme.txt using RtlCompareUnicodeString (case-insensitive).
  • Kernel-Mode File Reading: For testme.txt, we use ZwCreateFile to open the file (with kernel handle) and ZwReadFile to read its content. Note that we allocate a paged pool buffer—make sure to adjust the buffer size if you expect larger files.
  • String Search: We loop through the read buffer to check for the "hello" string using RtlCompareMemory for efficient comparison.
  • Block Operation: If "hello" is found, we set Data->IoStatus.Status to STATUS_ACCESS_DENIED and return FLT_PREOP_COMPLETE to terminate the open request.
编译与测试注意事项
  • Environment: You'll need the Windows Driver Kit (WDK) installed to compile this driver. Create a Kernel Mode Driver project in Visual Studio and replace the template code with this.
  • Signing: Test-sign the driver (enable test signing mode in Windows with bcdedit /set testsigning on and reboot).
  • Loading/Unloading: Use these commands in an elevated command prompt:
    • Load: sc create FileMonitor type= kernel binPath= "C:\path\to\your\driver.sys" then sc start FileMonitor
    • Unload: sc stop FileMonitor then sc delete FileMonitor
  • Debug Output: Use DebugView (from Sysinternals) to see the DbgPrint messages. Make sure to enable "Capture Kernel" in DebugView.

内容的提问来源于stack exchange,提问作者Yousef

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 09:06:02