
文/图 李旭昇
文件系统微过滤驱动(File System Mini-Filter ,简称MiniFilter )是微软为了简化文件过滤驱动开发过程而设计的新一代文件过滤框架。MiniFilter 通过向过滤管理器(Filter Manager ,简称FltMgr )注册想要过滤的I/O 操作来间接地附加到文件系统设备栈上。FltMgr 是一个传统的文件过滤驱动,运行在内核模式,向第三方MiniFilter 提供文件过滤驱动的常用功能。如图1所示是一个简化的I/O 设备栈,其中有一个FltMgr 和三个MiniFilter 。
图1 简化的I/O 设备栈
针对每一种I/O 操作,MiniFilter 只需注册预处理回调(pre-operation callback )和后处理回调(post-operation callback )即可。FltMgr 会恰当的处理IRP 并在I/O 操作的前、后调用上述两个回调。 与传统过滤驱动相比,MiniFilter 优势明显。首先,MiniFilter 代码十分简洁,开发迅速。除去一些必要的注册工作,我们只需提供两个回调就可以完成对一种I/O 操作的过滤(甚至可以只提供一个,将另一个设为NULL )。对于我们不感兴趣的I/O 操作,FltMgr 将完成基本的处理并继续传递。其次,MiniFilter 是微软文档化的方法,具有良好的稳定性与跨平台性,一份代码不需修改便可以在不同系统上工作。另外,FltMgr 还提供了许多通用的函数,帮助我们获得文件名等有用的信息。 下面我们动手编写一个MiniFilter 。DriverEntry 中通过FltRegisterFilter 注册MiniFilter ,再通过FltStartFiltering 开始过滤。
extern "C" NTSTATUS DriverEntry(
_In_ PDRIVER_OBJECT DriverObject , _In_ PUNICODE_STRING RegistryPath ) {
DriverObject ->DriverUnload=Unload;
黑
客防线 w w w .h a c k
e r .c o m .c
n
转载请注明出处
DbgPrint("Load!\\n");
NTSTATUS status;
//注册MiniFilter
status=FltRegisterFilter(DriverObject ,&FilterRegistration,&Filte rHandle);
if (!NT_SUCCESS (status)) {
DbgPrint("Fail to Register.\\n");
DbgPrint("%d\\n
//开始过滤
status=FltStartFiltering(FilterHandle); if (!NT_SUCCESS (status))
{
//启动失败,取消注册,返回
FltUnregisterFilter(FilterHandle); DbgPrint("Fail to Start.\\n"); return STATUS_SUCCESS ; }
DbgPrint("MiniFilter Started!!!\\n"); return STATUS_SUCCESS ; }
FltRegisterFilter 的原型为: NTSTATUS FltRegisterFilter(
_In_ PDRIVER_OBJECT Driver,
_In_ const FLT_REGISTRATION *Registration, _Out_ PFLT_FILTER *RetFilter );
第一个参数为驱动对象,即DriverEntry 中传入的参数;第二个参数指向FLT_REGISTRATION 结构,我们稍后详细介绍;第三个参数是返回的MiniFilter 句柄,需要保存在全局变量中,因为函数FltStartFiltering 和FltUnregisterFilter 都要用到它。注册成功后,调用FltStartFiltering 开始过滤,如果启动失败,就调用FltUnregisterFilter 取消注册。
FLT_REGISTRATION 结构定义如下:
typedef struct _FLT_REGISTRATION {
USHORT Size; USHORT Version; FLT_REGISTRATION_FLAGS Flags;
const FLT_CONTEXT_REGISTRATION *ContextRegistration; const FLT_OPERATION_REGISTRATION *OperationRegistration;
黑
客防线 w w w .h a c k
e r .c o m .c
n
转载请注明出处
PFLT_FILTER_UNLOAD_CALLBACK FilterUnloadCallback; PFLT_INSTANCE_SETUP_CALLBACK InstanceSetupCallback; PFLT_INSTANCE_QUERY_TEARDOWN_CALLBACK InstanceQueryTeardownCallback;
PFLT_INSTANCE_TEARDOWN_CALLBACK InstanceTeardownStartCallback; PFLT_INSTANCE_TEARDOWN_CALLBACK InstanceTeardownCompleteCallback;
PFLT_GENERATE_FILE_NAME GenerateFileNameCallback; PFLT_NORMALIZE_NAME_COMPONENT NormalizeNameComponentCallback;
PFLT_NORMALIZE_CONTEXT_CLEANUP NormalizeContextCleanupCallback;
} FLT_REGISTRATION, *PFLT_REGISTRATION;
Size 为结构的大小,按部就班的设为sizeof(FLT_REGISTRATION)即可;Version 为版本,必须设为FLT_REGISTRATION_VERSION ;FilterUnloadCallback 在MiniFilter 卸载时被调用,不提供该函数将导致MiniFilter 不能卸载,会带来许多不便。我们将其设为FileFilterUnload ,代码很简单,只需用FltUnregisterFilter 取消MiniFilter 的注册即可。这里需要注意,在DriverUnload 例程中,我们不能再次调用FltUnregisterFilter ,这是因为FltMgr 已经调用过FilterUnloadCallback ,MiniFilter 已经被取消注册,再次取消注册将导致蓝屏。
OperationRegistration 为一组FLT_OPERATION_REGISTRATION 结构,其结束标志为IRP_MJ_OPERATION_END 。FLT_OPERATION_REGISTRATION 结构的定义如下:
typedef struct _FLT_OPERATION_REGISTRATION {
UCHAR MajorFunction; FLT_OPERATION_REGISTRATION_FLAGS Flags;
PFLT_PRE_OPERATION_CALLBACK PreOperation; PFLT_POST_OPERATION_CALLBACK PostOperation; PVOID Reserved1;
} FLT_OPERATION_REGISTRATION, *PFLT_OPERATION_REGISTRATION; 其中MajorFunction 指定了我们感兴趣的I/O 操作,PreOperation 和PostOperation 为相应的预处理回调和后处理回调。Flags 设为0即可。我们只对IRP_MJ_CREATE 感兴趣,并预处理回调FileFilterPreCreate 和后处理回调FileFilterPostCreate 。
const FLT_OPERATION_REGISTRATION Callbacks[] = { {IRP_MJ_CREATE , 0,
(PFLT_PRE_OPERATION_CALLBACK )FileFilterPreCreate, (PFLT_POST_OPERATION_CALLBACK )FileFilterPostCreate}, {IRP_MJ_OPERATION_END } }; 读者可以自行修改Callbacks 来添加对其他I/O 操作的过滤。我们的后处理回调FileFilterPostCreate 只是返回了FLT_POSTOP_FINISHED_PROCESSING ,没有额外的操作。预处理回调FileFilterPreCreate 的逻辑也不复杂,首先判断当前操作的文件的后缀名是否为virus ,
黑
客防线 w w w .h a c k
e r .c o m .c
n
转载请注明出处
如果是则拒绝操作并打印相关信息,否则就放过该操作。代码如下:
FLT_PREOP_CALLBACK_STATUS FileFilterPreCreate( _Inout_ PFLT_CALLBACK_DATA Data ,
_In_ PCFLT_RELATED_OBJECTS FltObjects , _In_opt_ PVOID CompletionContext ){
NTSTATUS status;
PFLT_FILE_NAME_INFORMATION NameInfo;
//获取文件信息
status=FltGetFileNameInformation(Data ,FLT_FILE_NAME_NORMALIZED |F LT_FILE_NAME_QUERY_DEFAULT ,&NameInfo);
if (!NT_SUCCESS (status)) return FLT_PREOP_SUCCESS_WITH_CALLBACK ;
//解析文件信息
FltParseFileNameInformation(NameInfo);
UNICODE_STRING HiddenExt=RTL_CONSTANT_STRING (L "virus");
if (RtlEqualUnicodeString(&NameInfo->Extension,&HiddenExt,FALSE )) {
Data ->IoStatus.Status=STATUS_ACCESS_DENIED ; Data ->IoStatus.Information=0;
DbgPrint("DENIED Access: %wZ
}
//释放NameInfo
FltReleaseFileNameInformation(NameInfo); return FLT_PREOP_SUCCESS_WITH_CALLBACK ; }
FileFilterPreCreate 的第一个参数Data 中有非常详细的信息,包括文件名称、操作的各种参数等。该结构在MSDN 中有详细的定义,这里不再赘述。我们并不直接操作Data ,而是利用FltMgr 提供的FltGetFileNameInformation 函数获取文件的信息,接着用FltParseFileNameInformation 解析得到的信息。得到的FLT_FILE_NAME_INFORMATION 结构内有丰富的信息供我们利用。
typedef struct _FLT_FILE_NAME_INFORMATION { USHORT Size;
FLT_FILE_NAME_PARSED_FLAGS NamesParsed; FLT_FILE_NAME_OPTIONS Format; UNICODE_STRING Name; UNICODE_STRING Volume; UNICODE_STRING Share; UNICODE_STRING Extension; UNICODE_STRING Stream;
黑
客防线 w w w .h a c k
e r .c o m .c
n
转载请注明出处
UNICODE_STRING FinalComponent; UNICODE_STRING ParentDir;
} FLT_FILE_NAME_INFORMATION, *PFLT_FILE_NAME_INFORMATION;
其中Name 为文件名,Extension 是后缀名。如果当前操作的文件后缀名为virus ,就设置IoStatus.Status 为STATUS_ACCESS_DENIED ,IoStatus.Information 为0,即拒绝访问。最后需要用FltReleaseFileNameInformation 释放NameInfo 指向的内存,否则将会造成内存泄露,而文件系统的I/O 操作十分频繁,这将导致内存迅速耗尽。读者可以根据自己的需要修改该函数。
下面讨论一下MiniFilter 的安装。DDK 中所带的例子采用INF 文件进行安装,每次编译之后都要重新安装并重启,非常麻烦。但如果用工具加载驱动就会导致FltRegisterFilter 注册失败,错误为STATUS_OBJECT_NAME_NOT_FOUND 。查阅MSDN 后发现,MiniFilter 需要指定Altitude 等参数,以确定MiniFilter 加载与过滤的先后顺序。本文将INF 文件中的注册表操作转换成一个reg 文件,在加载MiniFilter 之前双击导入即可。
如图2所示,我们新建一个test.virus ,在其中随便输入一些内容,此时用记事本就可以打开test.virus 并查看其中的内容。接下来我们加载驱动,并再次尝试打开它。此时记事本提示Access is denied ,并可在DBGView 中看到相应的输出,如图3所示。
图2 新建test.virus 并输入一些内容
图3 加载驱动后,访问被拒绝
黑
客防线 w w w .h a c k
e r .c o m .c
n
转载请注明出处
以上代码在Win7 x86/x VS2012下测试通过,并可移植到其他版本系统中。本文为笔者关于MiniFilter 的一点心得,如有不当,还望指正。
黑
客防线 w w w .h a c k
e r .c o m .c
n
转载请注明出处
