MiniFilter内核回调
字数 4119
更新时间 2026-03-19 12:03:09

MiniFilter 微过滤器内核回调技术教学文档

第一章:MiniFilter 基础概念与原理

1.1 微过滤器内核回调定义

微过滤器内核回调是专门的回调例程,用于在不同阶段监控和拦截文件系统操作,例如在创建文件阶段监控和拦截,在修改文件内容阶段进行监控和拦截。

1.2 EDR 中的应用

终端检测与响应(EDR)系统利用这些回调来捕获有关恶意文件系统活动的遥测数据,例如:

  • 检测攻击者何时将恶意文件落地到磁盘
  • 识别短时间内大量文件被加密的情况

第二章:MiniFilter 工作原理

2.1 回调注册机制

微过滤器回调通过 FltRegisterFilter API 函数进行注册。函数原型如下:

NTSTATUS FLTAPI FltRegisterFilter(
 [in] PDRIVER_OBJECT Driver,           // 程序对象的指针
 [in] const FLT_REGISTRATION *Registration, // 指向调用方分配的微过滤器驱动程序注册结构
 [out] PFLT_FILTER *RetFilter
);

2.2 FLT_REGISTRATION 结构体

这是微过滤器配置的核心结构,关键字段包括:

  • Callback:指向操作注册数组,定义了针对不同I/O操作(如创建、写入、删除、读取等)的回调函数
  • Unload:指向卸载例程的指针,当微过滤器被卸载时系统会调用该例程

2.3 CALLBACK_NODE 与双向链表

  1. 存储结构:微过滤器回调存储在由 CALLBACK_NODE 结构组成的双向链表中
  2. 实例管理:每一个微过滤器实例都维护着这样一个列表,用于处理不同的文件系统操作
  3. 回调类型
    • 操作前回调 (Pre-Operation Callback):在文件操作发生之前调用
    • 操作后回调 (Post-operation Callback):在文件系统操作完成之后调用

2.4 分层架构

微过滤器驱动程序采用分层架构来拦截和监控文件系统操作:

  • 分层原理:驱动程序在系统堆栈中垂直排列顺序
  • 处理流程:当文件操作请求(如"文件写入")从应用层发出时,会从最高海拔(Altitude)的过滤器依次向下流动
  • 拦截机制:每一层都有机会查看、修改或拦截这个请求

2.5 实例机制

每个微过滤器驱动可以拥有一个或多个实例(Instance),使其能够挂钩到不同的文件系统卷或操作:

  • 实例化示例:C盘和D盘分别对应 InstanceCInstanceD 实例
  • 回调触发:在C盘创建文件触发 Instance C 回调,在D盘创建文件触发 Instance D 回调

2.6 回调节点结构

在每一个实例内部,针对特定的文件系统操作注册了多个回调节点结构:

  • 专业化处理:一个回调节点处理一种特定的文件系统操作
    • Create 节点:处理创建文件操作
    • Read 节点:处理读取文件操作
  • 回调位置
    • 操作前内核回调:位于回调节点偏移 0x18
    • 操作后内核回调:位于回调节点偏移 0x20

第三章:静态与动态分析技术

3.1 IDA Pro 静态分析

  1. 定位关键函数:在驱动程序的导入表中搜索 FltRegisterFilter 函数
  2. 分析参数:重点关注第二个参数(包含回调函数的 FLT_REGISTRATION 结构体地址)
  3. 查看回调数组:通过 Callbacks 成员定位 FLT_OPERATION_REGISTRATION 数组
  4. 分析回调函数:查看针对不同I/O操作(CREATE、READ、WRITE等)的微过滤器操作前和操作后内核回调

3.2 Windbg 动态分析

3.2.1 查看已加载微过滤器

使用 !fltkd.filters 命令显示所有已注册的 FLT_FILTER 结构列表

3.2.2 分析实例结构

  1. 查看实例:使用 !instance <实例地址> 4 命令
  2. 理解实例关系:每个微过滤器可以包含一个或多个实例,代表附加到不同磁盘卷的过滤器实例
  3. 实例数量含义:例如,WdFilter 有五个实例表示它正在同时监控五个不同的卷

3.2.3 分析回调节点

  1. 查看CALLBACK_NODE结构:使用 dt _CALLBACK_NODE <Address> 命令
  2. 结构成员
    • CallbackLinks:指向回调节点双向链表中上一个和下一个节点的指针
    • Instance:实例指针,指向关联的 FLT_INSTANCE 结构
    • PreOperation:操作前回调函数指针
    • PostOperation:操作后回调函数指针

3.2.4 查看实例详细信息

使用 dt _FLT_INSTANCE <实例地址> 命令查看过滤器实例的详细信息

第四章:自定义微过滤器开发

4.1 基本框架代码

#include <fltKernel.h>

// 声明全局句柄
PFLT_FILTER gFilterHandle = NULL;

// 操作前回调函数
extern "C" FLT_PREOP_CALLBACK_STATUS PreOperationCallback(
 _Inout_ PFLT_CALLBACK_DATA Data,
 _In_ PCFLT_RELATED_OBJECTS FltObjects,
 _Flt_CompletionContext_Outptr_ PVOID* CompletionContext
)
{
 UNREFERENCED_PARAMETER(CompletionContext);
 UNREFERENCED_PARAMETER(FltObjects);
 
 // 获取执行文件系统操作的进程PID
 HANDLE processId = PsGetCurrentProcessId();
 // 获取执行文件系统操作的线程TID
 HANDLE threadId = PsGetCurrentThreadId();
 
 // 判断I/O请求的操作类型
 if (Data->Iopb->MajorFunction == IRP_MJ_READ) {
     DbgPrintEx(0, 0, "[Minifilter] Read Operation: PID: %p, TID: %p\n", processId, threadId);
 }
 else if (Data->Iopb->MajorFunction == IRP_MJ_WRITE) {
     DbgPrintEx(0, 0, "[Minifilter] Write Operation: PID: %p, TID: %p\n", processId, threadId);
 }
 else if (Data->Iopb->MajorFunction == IRP_MJ_SET_INFORMATION) {
     FILE_INFORMATION_CLASS fileInfoClass = Data->Iopb->Parameters.SetFileInformation.FileInformationClass;
     if (fileInfoClass == FileDispositionInformation || fileInfoClass == FileRenameInformation) {
         DbgPrintEx(0, 0, "[Minifilter] Delete/Rename: PID: %p\n", processId);
     }
 }

 return FLT_PREOP_SUCCESS_NO_CALLBACK;
}

// 操作后回调函数
extern "C" FLT_POSTOP_CALLBACK_STATUS PostOperationCallback(
 _Inout_ PFLT_CALLBACK_DATA Data,
 _In_ PCFLT_RELATED_OBJECTS FltObjects,
 _In_opt_ PVOID CompletionContext,
 _In_ FLT_POST_OPERATION_FLAGS Flags
)
{
 UNREFERENCED_PARAMETER(Data);
 UNREFERENCED_PARAMETER(FltObjects);
 UNREFERENCED_PARAMETER(CompletionContext);
 UNREFERENCED_PARAMETER(Flags);

 return FLT_POSTOP_FINISHED_PROCESSING;
}

// Unload例程
extern "C" NTSTATUS Unload(_In_ FLT_FILTER_UNLOAD_FLAGS Flags) {
 UNREFERENCED_PARAMETER(Flags);
 if (gFilterHandle != NULL) {
     FltUnregisterFilter(gFilterHandle);
     DbgPrintEx(0, 0, "[Minifilter] Unloaded Successfully\n");
 }
 return STATUS_SUCCESS;
}

// 注册列表
CONST FLT_OPERATION_REGISTRATION CallbackList[] = {
 { IRP_MJ_READ, 0, PreOperationCallback, PostOperationCallback }, 
 { IRP_MJ_WRITE, 0, PreOperationCallback, PostOperationCallback },
 { IRP_MJ_SET_INFORMATION, 0, PreOperationCallback, PostOperationCallback },
 { IRP_MJ_OPERATION_END }
};

// 注册结构
CONST FLT_REGISTRATION FilterRegister = {
 sizeof(FLT_REGISTRATION),           // 结构体大小
 FLT_REGISTRATION_VERSION,           // 微过滤器注册版本
 0, 
 NULL,
 CallbackList,                       // 指向操作回调数组的指针
 Unload,                            // 卸载回调
 NULL, NULL, NULL, NULL, NULL, NULL, NULL
};

// 驱动程序入口点
extern "C" NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath) {
 UNREFERENCED_PARAMETER(RegistryPath);

 // 注册微过滤器
 NTSTATUS status = FltRegisterFilter(DriverObject, &FilterRegister, &gFilterHandle);
 if (NT_SUCCESS(status)) {
     status = FltStartFiltering(gFilterHandle);
     if (!NT_SUCCESS(status)) {
         FltUnregisterFilter(gFilterHandle);
     }
 }
 return status;
}

4.2 遥测数据收集

微过滤器内核回调可收集的关键遥测数据包括:

  • 文件系统执行的操作类型(创建、写入、删除文件等)
  • 发起操作的进程PID
  • 发起操作的线程TID

4.3 注册表配置

加载微过滤器驱动前需要在Windows注册表中手动配置:

# 在 Services(服务)下创建主键 MiniFilterDriver
$MiniFilterDriverPath = "HKLM:\SYSTEM\CurrentControlSet\Services\MiniFilterDriver"
New-Item -Path $MiniFilterDriverPath -Force

# 设置驱动文件路径、类型、启动方式和错误控制
Set-ItemProperty -Path $MiniFilterDriverPath -Name "ImagePath" -Value "\??\C:\Users\Public\MiniFilterDriver.sys"
Set-ItemProperty -Path $MiniFilterDriverPath -Name "Type" -Value 0x00000001
Set-ItemProperty -Path $MiniFilterDriverPath -Name "Start" -Value 0x00000003
Set-ItemProperty -Path $MiniFilterDriverPath -Name "ErrorControl" -Value 0x00000001

# 创建 Instances(实例)键及其子键
$instancesPath = "$MiniFilterDriverPath\Instances"
New-Item -Path $instancesPath -Force

# 在 Instances 键中设置默认实例名称
Set-ItemProperty -Path $instancesPath -Name "DefaultInstance" -Value "MiniFilterDriver Instance"

# 在 Instances 下创建具体的 "MiniFilterDriver Instance" 键
$instancePath = "$instancesPath\MiniFilterDriver Instance" 
New-Item -Path $instancePath -Force

# 在该实例键下设置海拔高度 (Altitude) 和标志位 (Flags)
Set-ItemProperty -Path $instancePath -Name "Altitude" -Value "370000"
Set-ItemProperty -Path $instancePath -Name "Flags" -Value 0x00000000

关键参数说明

  • Altitude:海拔高度,决定微过滤器驱动何时接收到I/O请求,数值越大层级越高,接收请求越早
  • ImagePath:驱动程序文件路径

4.4 加载与测试

  1. 查看已加载过滤器:使用 fltmc 命令
  2. 加载自定义过滤器fltmc load <过滤器名称>
  3. 查看调试输出:在Windbg中查看DbgPrintEx输出的遥测数据
  4. 验证过滤器注册:使用 !fltkd.filter 命令

第五章:回调节点移除技术

5.1 双向链表结构

每个回调节点都是回调链表结构的一部分,类型为 LIST_ENTRY,包含 FlinkBlink 字段。

5.2 断链操作原理

  1. 链表结构:双向链表包含前向指针(Flink)和后向指针(Blink
  2. 断链目标:从链表中断开特定节点的连接
  3. 操作步骤
    • 让目标节点的前一个节点的 Flink 指向目标节点的下一个节点
    • 让目标节点的下一个节点的 Blink 指向目标节点的前一个节点
    • 将目标节点自身的 FlinkBlink 指向自身

5.3 具体操作命令

// 假设当前节点地址: 0xffff8f8d`b31802c0
// 前一个节点地址: 0xffff8f8d`b30e32d0
// 后一个节点地址: 0xffff8f8d`b5768f00

// 第一步:前一个节点的Flink指向后一个节点
eq 0xffff8f8d`b30e32d0 0xffff8f8d`b5768f00

// 第二步:后一个节点的Blink指向前一个节点
eq 0xffff8f8d`b5768f00+0x8 0xffff8f8d`b30e32d0

// 第三步:当前节点的Flink和Blink指向自身
eq 0xffff8f8d`b31802c0 0xffff8f8d`b31802c0
eq 0xffff8f8d`b31802c0+0x8 0xffff8f8d`b31802c0

5.4 移除Sysmon微过滤器内核回调实战

5.4.1 准备工作

  1. 安装Sysmon工具:参考相关安装文档
  2. 验证日志记录:打开事件查看器查看Sysmon操作日志
  3. 定位Sysmon过滤器:在Windbg中使用 !fltkd.filters 查找 SysmonDrv

5.4.2 操作步骤

  1. 查看实例:针对所有Sysmon实例执行操作
  2. 定位CREATE节点:找到与文件创建操作相关的回调节点
  3. 获取指针信息:列出节点的 FlinkBlink 指针
  4. 执行断链操作:对每个实例的CREATE节点执行上述断链操作
  5. 验证效果:尝试创建新文件,验证Sysmon是否不再记录相关事件

5.4.3 安全影响

通过断开回调连接,可以使恶意文件直接落地到磁盘而不被EDR检测,因为回调无法感知文件操作。

第六章:系统微过滤器枚举技术

6.1 核心API函数

6.1.1 FltEnumerateFilters函数

NTSTATUS FltEnumerateFilters(
 [out] PFLT_FILTER *FilterList,        // 指向缓冲区数组的指针,存放微过滤器句柄
 [in] ULONG FilterListSize,            // 缓冲区能容纳的句柄数量
 [out] PULONG NumberFiltersReturned    // 实际存入缓冲区的句柄数量
);

使用模式:需要调用两次

  1. 第一次调用:获取系统中实际存在的过滤器总数
  2. 第二次调用:填充FilterList缓冲区

6.1.2 FltGetFilterInformation函数

NTSTATUS FltGetFilterInformation(
 [in] PFLT_FILTER Filter,              // 目标过滤器的句柄
 [in] FILTER_INFORMATION_CLASS InformationClass, // 请求的信息类型
 [out] PVOID Buffer,                   // 接收数据的缓冲区
 [in] ULONG BufferSize,                // 缓冲区大小
 [out] PULONG BytesReturned            // 实际写入或需要的字节数
);

使用模式:需要调用两次

  1. 第一次调用:返回过滤器信息所需的字节数
  2. 第二次调用:真正获取微过滤器的基本信息

6.2 数据结构定义

6.2.1 自定义数据结构

typedef struct _MinifilterData {
    WCHAR FilterName[256];     // 存储过滤器名称的宽字符数组
    WCHAR FilterAltitude[256]; // 存储过滤器海拔(Altitude)的宽字符数组
} MinifilterData, *PMinifilterData;

6.2.2 系统结构体

typedef struct _FILTER_AGGREGATE_BASIC_INFORMATION {
    ULONG NextEntryOffset;
    ULONG Flags;
    union {
        struct {
            ULONG FrameID;
            ULONG NumberOfInstances;
            USHORT FilterNameLength;
            USHORT FilterNameBufferOffset;
            USHORT FilterAltitudeLength;
            USHORT FilterAltitudeBufferOffset;
        } MiniFilter;
        struct {
            USHORT FilterNameLength;
            USHORT FilterNameBufferOffset;
        } LegacyFilter;
    } Type;
} FILTER_AGGREGATE_BASIC_INFORMATION, *PFILTER_AGGREGATE_BASIC_INFORMATION;

关键字段说明

  • FilterNameLength:过滤器名称的长度
  • FilterNameBufferOffset:名称字符串偏移地址
  • FilterAltitudeLength:海拔高度字符串的长度
  • FilterAltitudeBufferOffset:海拔高度字符串偏移地址

6.3 驱动程序层实现

6.3.1 内存分配与枚举

// 第一次调用:获取过滤器总数
status = FltEnumerateFilters(NULL, bufferSize, &filterCount);
if (status != STATUS_BUFFER_TOO_SMALL) {
    return status;
}

// 计算所需缓冲区大小
bufferSize = filterCount * sizeof(PFLT_FILTER);
filterList = (PFLT_FILTER*)ExAllocatePool2(POOL_FLAG_NON_PAGED, bufferSize, DRIVER_TAG);

// 第二次调用:填充过滤器句柄列表
status = FltEnumerateFilters(filterList, bufferSize, &filterCount);
if (!NT_SUCCESS(status)) {
    ExFreePool(filterList);
    return status;
}

6.3.2 获取过滤器信息

// 遍历所有微过滤器
for (ULONG i = 0; i < filterCount; i++) {
    PFLT_FILTER filter = filterList[i];
    PFILTER_AGGREGATE_BASIC_INFORMATION filterInfo = NULL;
    ULONG filterInfoSize = 0;
    ULONG filterInfoBufferSize = 0;
    
    // 第一次调用:获取所需缓冲区大小
    status = FltGetFilterInformation(filter, FilterAggregateBasicInformation, 
                                     NULL, 0, &filterInfoBufferSize);
    
    // 分配内存
    filterInfo = (PFILTER_AGGREGATE_BASIC_INFORMATION)ExAllocatePool2(
        POOL_FLAG_NON_PAGED, filterInfoBufferSize, DRIVER_TAG);
    
    if (!filterInfo) {
        continue;
    }
    
    // 第二次调用:获取实际数据
    status = FltGetFilterInformation(filter, FilterAggregateBasicInformation, 
                                     filterInfo, filterInfoBufferSize, &filterInfoSize);
    
    if (!NT_SUCCESS(status)) {
        ExFreePool(filterInfo);
        continue;
    }
    
    // 通过偏移量获取名称和海拔高度
    PWCHAR filterNameAddr = (PWCHAR)((PCHAR)filterInfo + filterInfo->Type.MiniFilter.FilterNameBufferOffset);
    PWCHAR filterAltitudeAddr = (PWCHAR)((PCHAR)filterInfo + filterInfo->Type.MiniFilter.FilterAltitudeBufferOffset);
    
    // 复制到输出缓冲区
    wcsncpy(filtersData[i].FilterName, filterNameAddr, 
            min(filterInfo->Type.MiniFilter.FilterNameLength / sizeof(WCHAR), 255));
    filtersData[i].FilterName[min(filterInfo->Type.MiniFilter.FilterNameLength / sizeof(WCHAR), 255)] = L'\0';
    
    wcsncpy(filtersData[i].FilterAltitude, filterAltitudeAddr, 
            min(filterInfo->Type.MiniFilter.FilterAltitudeLength / sizeof(WCHAR), 255));
    filtersData[i].FilterAltitude[min(filterInfo->Type.MiniFilter.FilterAltitudeLength / sizeof(WCHAR), 255)] = L'\0';
    
    ExFreePool(filterInfo);
}

6.4 用户层实现

6.4.1 打开设备句柄

HANDLE Hdevice = CreateFileA("\\\\.\\LCKDriver",
    GENERIC_READ | GENERIC_WRITE,
    FILE_SHARE_WRITE,
    NULL,
    OPEN_EXISTING,
    0,
    NULL);

6.4.2 发送IOCTL请求

// 在堆栈分配64KB内存
DWORD lpBytesReturned = 0;
BYTE buffer[1 << 16]; // 分配64KB缓冲区
memset(buffer, 0, sizeof(buffer));

BOOL success = DeviceIoControl(
    Hdevice,
    IOCTL_LIST_MINIFILTERS, // 控制码,需与驱动程序匹配
    NULL, 0,                 // 输入缓冲区
    buffer, sizeof(buffer),  // 输出缓冲区
    &lpBytesReturned,        // 实际返回长度
    NULL
);

6.4.3 处理并显示结果

if (success) {
    DWORD count = lpBytesReturned / sizeof(MinifilterData);
    MinifilterData* data = (MinifilterData*)buffer;
    
    wprintf(L"\n成功获取到 %lu 个微过滤器信息:\n", count);
    wprintf(L"--------------------------------------------------\n");
    wprintf(L"%-4s | %-20s | %s\n", L"ID", L"Filter Name", L"Altitude");
    wprintf(L"--------------------------------------------------\n");
    
    for (DWORD i = 0; i < count; i++) {
        wprintf(L"[%02lu] | %-20ls | %ls\n",
                i,
                data[i].FilterName,
                data[i].FilterAltitude);
    }
    wprintf(L"--------------------------------------------------\n");
} else {
    printf("IOCTL 请求失败,错误代码: %lu\n", GetLastError());
}

第七章:调试技巧与问题解决

7.1 Windbg调试技巧

  1. 强制开启符号解析.symopt- 100 命令解决符号文件找不到的问题
  2. 查看链表结构:使用 dt nt!_LIST_ENTRY <地址> 命令
  3. 实例分析:使用 !instance <实例地址> 4 查看详细信息
  4. 结构解析:使用 dt 命令解析各种内核结构

7.2 常见问题处理

  1. 符号问题:确保正确加载符号文件
  2. 内存访问:注意指针偏移计算
  3. 权限问题:确保以管理员权限运行相关工具
  4. 注册表配置:确保Altitude值设置合理,避免冲突

第八章:安全应用与防御

8.1 攻击视角应用

  1. 绕过EDR检测:通过移除特定微过滤器的回调节点,使恶意文件操作不被监控
  2. 持久化隐藏:修改微过滤器链表,隐藏特定文件操作
  3. 权限提升:通过控制微过滤器实现权限绕过

8.2 防御视角应用

  1. 检测回调移除:监控微过滤器链表的完整性
  2. 完整性校验:定期检查关键微过滤器的回调函数
  3. 行为监控:检测异常的微过滤器操作
  4. 深度防御:采用多层微过滤器防护

8.3 最佳实践

  1. 最小权限原则:微过滤器只监控必要的操作
  2. 错误处理:完善的错误处理机制
  3. 性能考虑:回调函数应尽可能高效
  4. 兼容性测试:确保与系统和其他过滤器的兼容性
  5. 安全审计:定期审计微过滤器配置和实现

总结

本教学文档详细介绍了MiniFilter微过滤器内核回调技术的完整知识体系,从基础概念、工作原理、开发实现到高级攻防技术。重点内容包括:

  1. 核心机制:回调注册、双向链表、分层架构
  2. 分析技术:静态分析与动态调试方法
  3. 开发实践:自定义微过滤器开发与部署
  4. 攻防技术:回调节点移除与防御检测
  5. 系统枚举:完整的内核与用户态实现

通过掌握这些技术,安全研究人员可以深入理解Windows文件系统过滤机制,开发安全监控工具,或进行相关的安全测试与研究。在实际应用中,应遵守相关法律法规,仅将技术用于合法的安全研究和防御目的。

相似文章
相似文章
 全屏