首页
社区
课程
招聘
[分享]Minifilter简易文件访问监控
发表于: 5天前 568

[分享]Minifilter简易文件访问监控

5天前
568

前言

Minifilter是 Windows文件系统过滤驱动框架(Filter Manager)的一种驱动类型,用于在 文件系统操作前后拦截 I/O 请求。常用于:文件监控、防病毒、DLP(数据防泄漏)、文件加密、行为审计等。

相比于 WFP 来说 Minifilter 会相对简单一些,效果:

图片描述这里显示\Device\HarddiskVolume3其实是 NT 内核路径而不是用户态看到的 DOS 路径

我们看到的C盘、D盘实际上只是一个符号链接映射

基本上内核产品都会直接使用 NT Path ,有一下优点:唯一、不依赖盘符、不受用户修改盘符影响、网络盘/挂载卷更稳定 图片描述

跑起来后由于打印日志系统会变卡,因为系统实际上一直不间断有大量文件操作,但正常的使用方式通常都是阻止文件打开或者重定向等其他功能,具体逻辑按需添加即可

原理

当 Windows 访问文件时流程是这样的:

User App
   │
   │  CreateFile / ReadFile / WriteFile
   ▼
I/O Manager
   ▼
Filter Manager
   ▼
Minifilter Drivers (多个)
   ▼
File System (NTFS/FAT)
   ▼
Disk Driver

Minifilter分别支持在操作前和操作后进行拦截,如果学过java的同学将会很容易理解,这个操作其实就是SpringBoot中AOP面向切面编程的前辈

例如可以在这些操作前后进行处理:

IRP_MJ_CREATE  -> 打开文件
IRP_MJ_READ    -> 读取文件
IRP_MJ_WRITE   -> 写文件
IRP_MJ_SET_INFORMATION -> 修改属性
IRP_MJ_CLOSE   -> 关闭文件

下面直接给出一个最小可运行逻辑的文件访问监控系统设计:

内核代码

安装Minifilter

//安装Minifilter
NTSTATUS InstallMinifilter(PDRIVER_OBJECT DriverObject)
{
    NTSTATUS status;

    // 注册过滤器
    status = FltRegisterFilter(
        DriverObject,
        &FilterRegistration,
        &g_FilterHandle
    );
    if (!NT_SUCCESS(status))
    {
        PZY_PRINT("Minifilter注册失败");
        return status;
    }

    // 启动过滤
    status = FltStartFiltering(g_FilterHandle);
    if (!NT_SUCCESS(status))
    {
        PZY_PRINT("Minifilter启动失败");
        FltUnregisterFilter(g_FilterHandle);
        g_FilterHandle = NULL;
        return status;
    }
    PZY_PRINT("Minifilter启动成功");
    return STATUS_SUCCESS;
}

卸载Minifilter

//卸载Minifilter
NTSTATUS FilterUnload(_In_ FLT_FILTER_UNLOAD_FLAGS Flags)
{
    PZY_PRINT("卸载 MiniFilter");
    if (g_FilterHandle)
    {
        FltUnregisterFilter(g_FilterHandle);
        g_FilterHandle = NULL;
    }
    return STATUS_SUCCESS;
}

注册结构

//Minifilter 注册结构
CONST FLT_REGISTRATION FilterRegistration =
{
    sizeof(FLT_REGISTRATION),           //  结构大小
    FLT_REGISTRATION_VERSION,           //  结构版本
    0,                                  //  过滤器标志
    NULL,
    Callbacks,                          //  注册回调函数集
    FilterUnload,                       //  驱动卸载函数
    InstallCallback,                    //  安装回调函数
    DestroyInstallCallback,             //  销毁回调函数
    UnbindBefore,                       //  解除绑定时触发
    UnbindAfter,                        //  解绑完成时触发
    NULL,
    NULL,
    NULL
};

Callbacks

CONST FLT_OPERATION_REGISTRATION Callbacks[] =
{
    // 创建时触发
    { IRP_MJ_CREATE, 0, PreCreate, PostOperation },
    // 读取时触发
    { IRP_MJ_READ, 0, PreRead, PostOperation },
    // 写入触发
    { IRP_MJ_WRITE, 0, PreWrite, PostOperation },
    // 设置时触发
    //{ IRP_MJ_SET_INFORMATION, 0, PreOperation, PostOperation },
    // 结束标志
    { IRP_MJ_OPERATION_END }
};

主要回调

//文件创建/打开
FLT_PREOP_CALLBACK_STATUS CreateFileCallback(
    PFLT_CALLBACK_DATA Data,
    PCFLT_RELATED_OBJECTS FltObjects,
    PVOID* CompletionContext
)
{
    //获取pid
    HANDLE pid = PsGetCurrentProcessId();

    // 初始值设置为 "未知"
    UNICODE_STRING filePath = gUnknownPath;
    // 尝试获取真实路径
    GetFileFullPath(Data, &filePath);

    PZY_PRINT("[CreateFile][%d] FilePath=%wZ", pid, filePath);
   
    return FLT_PREOP_SUCCESS_NO_CALLBACK;
}

//文件读取
FLT_PREOP_CALLBACK_STATUS ReadFileCallback(
    PFLT_CALLBACK_DATA Data,
    PCFLT_RELATED_OBJECTS FltObjects,
    PVOID* CompletionContext
)
{
    //获取pid
    HANDLE pid = PsGetCurrentProcessId();

    // 读取长度
    ULONG length = Data->Iopb->Parameters.Read.Length;

    // 初始值设置为 "未知"
    UNICODE_STRING filePath = gUnknownPath;
    // 尝试获取真实路径
    GetFileFullPath(Data, &filePath);

    PZY_PRINT("[ReadFile][%d] Length=%d FilePath=%wZ", pid, length, filePath);

    return FLT_PREOP_SUCCESS_NO_CALLBACK;
}

//文件写入
FLT_PREOP_CALLBACK_STATUS WriteFileCallback(
    PFLT_CALLBACK_DATA Data,
    PCFLT_RELATED_OBJECTS FltObjects,
    PVOID* CompletionContext
)
{
    //获取pid
    HANDLE pid = PsGetCurrentProcessId();

    // 写入长度
    ULONG length = Data->Iopb->Parameters.Write.Length;

    // 初始值设置为 "未知"
    UNICODE_STRING filePath = gUnknownPath;
    // 尝试获取真实路径
    GetFileFullPath(Data, &filePath);

    PZY_PRINT("[WriteFile][%d] Length=%d FilePath=%wZ", pid, length, filePath);

    return FLT_PREOP_SUCCESS_NO_CALLBACK;
}

路径读取

UNICODE_STRING gUnknownPath = RTL_CONSTANT_STRING(L"未知");
// 获取文件完整路径 outName 必须由调用者释放
NTSTATUS GetFileFullPath(
    PFLT_CALLBACK_DATA Data,
    PUNICODE_STRING outName
)
{
    NTSTATUS status;
    PFLT_FILE_NAME_INFORMATION nameInfo = NULL;

    //
    // 获取文件名信息
    //
    status = FltGetFileNameInformation(
        Data,
        FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT,
        &nameInfo
    );

    if (!NT_SUCCESS(status))
    {
        return status;
    }

    //
    // 解析文件路径
    //
    status = FltParseFileNameInformation(nameInfo);

    if (!NT_SUCCESS(status))
    {
        FltReleaseFileNameInformation(nameInfo);
        return status;
    }

    //
    // 为路径分配新的内存
    //
    outName->Length = nameInfo->Name.Length;
    outName->MaximumLength = nameInfo->Name.Length + sizeof(WCHAR);

    outName->Buffer = ExAllocatePoolWithTag(
        NonPagedPool,
        outName->MaximumLength,
        'pthF'
    );

    if (!outName->Buffer)
    {
        FltReleaseFileNameInformation(nameInfo);
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    //
    // 复制路径
    //
    RtlCopyMemory(
        outName->Buffer,
        nameInfo->Name.Buffer,
        nameInfo->Name.Length
    );

    outName->Buffer[outName->Length / sizeof(WCHAR)] = L'\0';

    //
    // 释放 FilterManager 结构
    //
    FltReleaseFileNameInformation(nameInfo);

    return STATUS_SUCCESS;
}

//释放路径内存
VOID FreeFileFullPath(PUNICODE_STRING name)
{
    if (!name) return;
    if (!name->Buffer)return;
    ExFreePoolWithTag(name->Buffer, 'pthF');
    name->Buffer = NULL;
}

其他回调


// 当实例被安装时触发
NTSTATUS InstallCallback(_In_ PCFLT_RELATED_OBJECTS FltObjects, _In_ FLT_INSTANCE_SETUP_FLAGS Flags, _In_ DEVICE_TYPE VolumeDeviceType, _In_ FLT_FILESYSTEM_TYPE VolumeFilesystemType)
{
    PZY_PRINT("安装MiniFilter");
    return STATUS_SUCCESS;
}

// 当实例被销毁时触发
NTSTATUS DestroyInstallCallback(_In_ PCFLT_RELATED_OBJECTS FltObjects, _In_ FLT_INSTANCE_QUERY_TEARDOWN_FLAGS Flags)
{
    PZY_PRINT("销毁MiniFilter");
    return STATUS_SUCCESS;
}

// 实例解除绑定时触发
VOID UnbindBefore(_In_ PCFLT_RELATED_OBJECTS FltObjects, _In_ FLT_INSTANCE_TEARDOWN_FLAGS Flags)
{
    PZY_PRINT("MiniFilter解绑前");
    return STATUS_SUCCESS;
}

// 实例解绑完成时触发
VOID UnbindAfter(_In_ PCFLT_RELATED_OBJECTS FltObjects, _In_ FLT_INSTANCE_TEARDOWN_FLAGS Flags)
{
    PZY_PRINT("MiniFilter解绑后");
    return STATUS_SUCCESS;
}

// 后操作回调函数 (在执行过滤之后运行此处)
FLT_POSTOP_CALLBACK_STATUS PostOperation(_Inout_ PFLT_CALLBACK_DATA Data, _In_ PCFLT_RELATED_OBJECTS FltObjects, _In_opt_ PVOID CompletionContext, _In_ FLT_POST_OPERATION_FLAGS Flags)
{
    return FLT_POSTOP_FINISHED_PROCESSING;
}

安装代码

由于Minifilter在注册驱动时和普通驱动还有些区别,这里给出有区别的位置

添加Minifilter注册表依赖

//创建 Minifilter 必需注册表
void CreateMiniFilterRegistry(const char* driverName)
{
    HKEY hKey;
    char keyPath[512];

    sprintf(keyPath,
        "SYSTEM\\CurrentControlSet\\Services\\%s\\Instances",
        driverName);

    RegCreateKeyA(
        HKEY_LOCAL_MACHINE,
        keyPath,
        &hKey);

    // DefaultInstance
    const char* instanceName = "DefaultInstance";

    RegSetValueExA(
        hKey,
        "DefaultInstance",
        0,
        REG_SZ,
        (BYTE*)"MyInstance",
        strlen("MyInstance") + 1);

    RegCloseKey(hKey);

    // Instance 子键
    sprintf(keyPath,
        "SYSTEM\\CurrentControlSet\\Services\\%s\\Instances\\MyInstance",
        driverName);

    RegCreateKeyA(
        HKEY_LOCAL_MACHINE,
        keyPath,
        &hKey);

    const char* altitude = "370000";
    DWORD flags = 0;

    RegSetValueExA(
        hKey,
        "Altitude",
        0,
        REG_SZ,
        (BYTE*)altitude,
        strlen(altitude) + 1);

    RegSetValueExA(
        hKey,
        "Flags",
        0,
        REG_DWORD,
        (BYTE*)&flags,
        sizeof(DWORD));

    RegCloseKey(hKey);
}

删除Minifilter注册表依赖

//卸载时增加删除注册表
void DeleteMiniFilterRegistry(const char* driverName)
{
    char keyPath[512];

    sprintf(
        keyPath,
        "SYSTEM\\CurrentControlSet\\Services\\%s\\Instances",
        driverName);

    RegDeleteTreeA(
        HKEY_LOCAL_MACHINE,
        keyPath);
}

加载驱动的变化

CreateServiceA中的第五项驱动类型要选择SERVICE_FILE_SYSTEM_DRIVER

CreateServiceA(hServiceMgr,
        lpszDriverName,				//驱动程序的在注册表中的名字  
        lpszDriverName,				// 注册表驱动程序的 DisplayName 值  
        SERVICE_START,				// 加载驱动程序的访问权限  SERVICE_START 或者 SERVICE_ALL_ACCESS
        SERVICE_FILE_SYSTEM_DRIVER,	//Minifilter项目需要改//SERVICE_KERNEL_DRIVER,	// 表示加载的服务是驱动程序  
        SERVICE_DEMAND_START,		// 注册表驱动程序的 Start 值   //指定当进程调用StartService函数时由服务控制管理器启动的服务。
        SERVICE_ERROR_NORMAL,		//SERVICE_ERROR_IGNORE, // 注册表驱动程序的 ErrorControl 值  
        szDriverImagePath,			// GetFullPathNameA szDriverImagePath 注册表驱动程序的路径 如: C:\\222\1.sys
        "FSFilter Activity Monitor",// LoadOrderGroup HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\GroupOrderList
        NULL,
        "FltMgr",					// 依赖 Filter Manager
        NULL,
        NULL);

传播安全知识、拓宽行业人脉——看雪讲师团队等你加入!

最后于 5天前 被mb_binusgki编辑 ,原因: 补充内容
收藏
免费 1
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回