-
-
[分享]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编辑
,原因: 补充内容
赞赏
他的文章
- [分享]混淆、加密、反沙箱概念介绍 594
- [分享]手动映射sys到内核 681
- [分享]Minifilter简易文件访问监控 569
- [分享]内核逆向开发阶段评测方法 598
- [分享]游戏网络封包解析可行性分析 774
赞赏
雪币:
留言: