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
FltGetFileNameInformationto retrieve the file name, then parse it to get the base name (liketestme.txt). - Target File Check: We compare the parsed file name with
testme.txtusingRtlCompareUnicodeString(case-insensitive). - Kernel-Mode File Reading: For
testme.txt, we useZwCreateFileto open the file (with kernel handle) andZwReadFileto 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
RtlCompareMemoryfor efficient comparison. - Block Operation: If "hello" is found, we set
Data->IoStatus.StatustoSTATUS_ACCESS_DENIEDand returnFLT_PREOP_COMPLETEto 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 onand reboot). - Loading/Unloading: Use these commands in an elevated command prompt:
- Load:
sc create FileMonitor type= kernel binPath= "C:\path\to\your\driver.sys"thensc start FileMonitor - Unload:
sc stop FileMonitorthensc delete FileMonitor
- Load:
- Debug Output: Use DebugView (from Sysinternals) to see the
DbgPrintmessages. Make sure to enable "Capture Kernel" in DebugView.
内容的提问来源于stack exchange,提问作者Yousef
相关产品推荐
相关产品推荐

