0%

使用MiniFilter实现文件保护(1)

可用的框架

文件保护可以使用框架有两种,一种是 sfilter 框架,另一种是 minifilter 框架。
微软推荐使用 minifilter 框架,因为它封装了 sfilter 中关于细节的处理,使用起来更简单。

框架的注册和卸载

在XPSP3系统中,也可以使用 minifilter 框架,其注册函数如下所示

1
2
3
4
5
NTSTATUS FltRegisterFilter(
_In_ PDRIVER_OBJECT Driver,
_In_ const FLT_REGISTRATION *Registration,
_Out_ PFLT_FILTER *RetFilter
);

注册完毕后,还需要使过滤器生效,才会开始工作。

1
2
3
NTSTATUS FltStartFiltering(
_In_ PFLT_FILTER Filter
);

如果驱动提供卸载功能,则需要在 DriverUnload 中卸载过滤器。

1
2
3
VOID FltUnregisterFilter(
_In_ PFLT_FILTER Filter
);

注册框架前的准备工作

本例子基于WDK源码中 src -> filesys -> miniFilter -> passThrough 工程作为基础。
在进行注册之前,需要初始化两个关键的结构体,作为全局变量,这里以最小实现为例进行初始化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
CONST FLT_OPERATION_REGISTRATION Callbacks[] = {
{ IRP_MJ_CREATE, 0, FilePreCreate, FilePostOperation },
{ IRP_MJ_SET_INFORMATION, 0, FilePreSetInformation, FilePostOperation },
{ IRP_MJ_WRITE, 0, FilePreWrite, FilePostOperation },
{ IRP_MJ_OPERATION_END } };

CONST FLT_REGISTRATION Registration = {
sizeof(FLT_REGISTRATION), // Size
FLT_REGISTRATION_VERSION, // Version
0, // Flags
NULL, // Context
Callbacks, // Operation callbacks
NULL, // MiniFilterUnload
FileInstanceSetup, // InstanceSetup
FileInstanceQueryTeardown, // InstanceQueryTeardown
FileInstanceTeardownStart, // InstanceTeardownStart
FileInstanceTeardownComplete, // InstanceTeardownComplete
NULL, // GenerateFileName
NULL, // GenerateDestinationFileName
NULL }; // NormalizeNameComponent

结构体数组 FLT_OPERATION_REGISTRATION 用来定义我们需要关注的操作,
这里只处理 IRP_MJ_CREATE IRP_MJ_SET_INFORMATION IRP_MJ_WRITE 三种操作,
结构体数组使用 IRP_MJ_OPERATION_END 作为结束标志,放到数组的最后。

以上每种操作分为 动作之前动作之后 两个时机,这里把 动作之后 都设置为不做任何操作,
注意在 动作之前 的函数中使用 FLT_PREOP_SUCCESS_NO_CALLBACK 返回值时,就可以
省略 动作之后 的函数,而如果返回 FLT_PREOP_SUCCESS_WITH_CALLBACK 时不能省略。

1
2
3
4
5
typedef FLT_PREOP_CALLBACK_STATUS (*PFLT_PRE_OPERATION_CALLBACK)(
_Inout_ PFLT_CALLBACK_DATA Data,
_In_ PCFLT_RELATED_OBJECTS FltObjects,
_Out_ PVOID *CompletionContext
); // 操作之前 函数类型
1
2
3
4
5
6
typedef FLT_POSTOP_CALLBACK_STATUS (*PFLT_POST_OPERATION_CALLBACK)(
_Inout_ PFLT_CALLBACK_DATA Data,
_In_ PCFLT_RELATED_OBJECTS FltObjects,
_In_opt_ PVOID CompletionContext,
_In_ FLT_POST_OPERATION_FLAGS Flags
); // 操作之后 函数类型

实例的处理

当MiniFilter在卷上处理实例时,会调用相关Instance回调函数,其中 InstanceSetup
管理准备附加的卷设备信息,可以返回 STATUS_FLT_DO_NOT_ATTACH 来跳过该卷设备,
InstanceQueryTeardown InstanceTeardownStart InstanceTeardownComplete
我们这里不做任何处理,直接返回成功

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
NTSTATUS FileInstanceSetup(
IN PCFLT_RELATED_OBJECTS FltObjects,
IN FLT_INSTANCE_SETUP_FLAGS Flags,
IN DEVICE_TYPE VolumeDeviceType,
IN FLT_FILESYSTEM_TYPE VolumeFilesystemType)
{
UNREFERENCED_PARAMETER(FltObjects);
UNREFERENCED_PARAMETER(Flags);
UNREFERENCED_PARAMETER(VolumeDeviceType); // 卷设备类型
// FILE_DEVICE_CD_ROM_FILE_SYSTEM
// FILE_DEVICE_DISK_FILE_SYSTEM
// FILE_DEVICE_NETWORK_FILE_SYSTEM
UNREFERENCED_PARAMETER(VolumeFilesystemType); // 文件系统类型
return STATUS_SUCCESS;
}

NTSTATUS FileInstanceQueryTeardown(
IN PCFLT_RELATED_OBJECTS FltObjects,
IN FLT_INSTANCE_QUERY_TEARDOWN_FLAGS Flags)
{
UNREFERENCED_PARAMETER(FltObjects);
UNREFERENCED_PARAMETER(Flags);
return STATUS_SUCCESS;
}

VOID FileInstanceTeardownStart(
IN PCFLT_RELATED_OBJECTS FltObjects,
IN FLT_INSTANCE_TEARDOWN_FLAGS Flags)
{
UNREFERENCED_PARAMETER(FltObjects);
UNREFERENCED_PARAMETER(Flags);
}

VOID FileInstanceTeardownComplete(
IN PCFLT_RELATED_OBJECTS FltObjects,
IN FLT_INSTANCE_TEARDOWN_FLAGS Flags)
{
UNREFERENCED_PARAMETER(FltObjects);
UNREFERENCED_PARAMETER(Flags);
}

操作的类型处理

删除文件时触发的操作,还包括创建或打开时设置 FILE_DELETE_ON_CLOSE 标志的方式,所以要在
IRP_MJ_CREATE 中检测是否存在 FILE_DELETE_ON_CLOSE 标志信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
FLT_PREOP_CALLBACK_STATUS FLTAPI FilePreCreate(
IN OUT PFLT_CALLBACK_DATA Data,
IN PCFLT_RELATED_OBJECTS FltObjects,
OUT PVOID *CompletionContext)
{
UNREFERENCED_PARAMETER(FltObjects);
UNREFERENCED_PARAMETER(CompletionContext);
// 本次操作对应的进程
FltGetRequestorProcessId(Data);
// 使用FltGetFileNameInformation获取文件信息
// 使用FltParseFileNameInformation转换文件路径
Data; // 文件信息
// 检查是否包含关闭文件后并删除的标志
if (Data->Iopb->Parameters.Create.Options & FILE_DELETE_ON_CLOSE)
{
// 设置拒绝本次操作
Data->IoStatus.Information = 0;
Data->IoStatus.Status = STATUS_ACCESS_DENIED;
// 已处理完毕,不再往下层发送
return FLT_PREOP_COMPLETE;
}
// 本层不做处理,继续发给下层过滤器
return FLT_PREOP_SUCCESS_NO_CALLBACK;
}

IRP_MJ_SET_INFORMATION 中,重命名文件时触发 FileRenameInformation 操作,删除文件时触发
FileDispositionInformation 操作,我们只关注这两种操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
FLT_PREOP_CALLBACK_STATUS FLTAPI FilePreSetInformation(
IN OUT PFLT_CALLBACK_DATA Data,
IN PCFLT_RELATED_OBJECTS FltObjects,
OUT PVOID *CompletionContext)
{
UNREFERENCED_PARAMETER(FltObjects);
UNREFERENCED_PARAMETER(CompletionContext);
// 本次操作对应的进程
FltGetRequestorProcessId(Data);
// 使用FltGetFileNameInformation获取文件信息
// 使用FltParseFileNameInformation转换文件路径
Data; // 文件信息
// 检查本次操作类型是不是删除文件操作
if (Data->Iopb->Parameters.SetFileInformation.FileInformationClass == FileDispositionInformation);
// 检查本次操作类型是不是重命名文件操作
if (Data->Iopb->Parameters.SetFileInformation.FileInformationClass == FileRenameInformation)
{
// 文件的新父路径,使用ZwQueryInformationFile查询
((PFILE_RENAME_INFORMATION)(Data->Iopb->Parameters.SetFileInformation.InfoBuffer))->RootDirectory;
// 文件的新子路径,WCHAR字符串,不含L'\0'结尾
((PFILE_RENAME_INFORMATION)(Data->Iopb->Parameters.SetFileInformation.InfoBuffer))->FileName;
((PFILE_RENAME_INFORMATION)(Data->Iopb->Parameters.SetFileInformation.InfoBuffer))->FileNameLength;
}
// 本层不做处理,继续发给下层过滤器
return FLT_PREOP_SUCCESS_NO_CALLBACK;
}

IRP_MJ_WRITE 中,我们处理文件内容的写入操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
FLT_PREOP_CALLBACK_STATUS FLTAPI FilePreWrite(
IN OUT PFLT_CALLBACK_DATA Data,
IN PCFLT_RELATED_OBJECTS FltObjects,
OUT PVOID *CompletionContext)
{
UNREFERENCED_PARAMETER(FltObjects);
UNREFERENCED_PARAMETER(CompletionContext);
// 本次操作对应的进程
FltGetRequestorProcessId(Data);
// 使用FltGetFileNameInformation获取文件信息
// 使用FltParseFileNameInformation转换文件路径
Data; // 文件信息
// 本层不做处理,继续发给下层过滤器
return FLT_PREOP_SUCCESS_NO_CALLBACK;
}

通用的 操作之后 函数,不做任何处理。

1
2
3
4
5
6
7
8
9
10
11
12
FLT_POSTOP_CALLBACK_STATUS FilePostOperation(
IN OUT PFLT_CALLBACK_DATA Data,
IN PCFLT_RELATED_OBJECTS FltObjects,
IN 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;
}