筛选器管理器 (FltMgr.sys)是Windows系统提供的内核模式驱动程序, 用于实现和公开文件系统筛选器驱动程序中通常所需的功能; 第三方文件系统筛选器开发人员可以使用FltMgr
的功能可以更加简单的编写文件过滤驱动, 这种驱动我们通常称为MiniFilter
, 下面是MiniFilter
的基本框架:
简单的说就是我们可以通过微软提供的一些接口, 可以使我们方便的在内核层监控文件的操作(创建, 删除, 读写等), 例如杀毒软件利用MiniFilter
监控文件的创建, 在文件创建时对文件进行病毒查杀; 又例如安全产品利用MiniFilter
, 阻止第三方程序在自己的目录下面写文件等…
由于在MiniFilter
中我们不用关心IRP的处理工作, 这些都可以交给 Filter Manager处理, 所以我们要编写一个MiniFilter
是很简单的, 只有调用几个API函数, 并在参数中填写我们关心的或者需要处理的结构就可以了;
我们使用FltRegisterFilter
来注册一个过滤器:
NTSTATUS FLTAPI FltRegisterFilter(
[in] PDRIVER_OBJECT Driver,
[in] const FLT_REGISTRATION *Registration,
[out] PFLT_FILTER *RetFilter
);
第一个参数和DriverEntry
的第一个参数一样, 第三参数RetFilter
作为输出, 主要用于后续调用FltUnregisterFilter
时注销MiniFilter:
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING reg_path)
{
NTSTATUS status;
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "DriverEntry Entry!\n");
ExInitializeResourceLite(&GlobalResource);
status = FltRegisterFilter(DriverObject, &FilterRegistration, &gFilterHandle);
if (NT_SUCCESS(status)) {
status = FltStartFiltering(gFilterHandle);
if (!NT_SUCCESS(status)) {
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "FltUnregisterFilter status: %lx\n", status);
FltUnregisterFilter(gFilterHandle);
}
else {
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "FltRegisterFilter Start!\n");
}
}
else {
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "status: %lx\n", status);
}
DriverObject->DriverUnload = UnDriver;
return STATUS_SUCCESS;
}
最重要的是第两个参数Registration
, 这个参数中定义了一系列的结构, 用于注册监控文件操作的回调等;
typedef struct _FLT_REGISTRATION {
USHORT Size;
USHORT Version;
FLT_REGISTRATION_FLAGS Flags;
const FLT_CONTEXT_REGISTRATION *ContextRegistration;
const FLT_OPERATION_REGISTRATION *OperationRegistration;
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;
PFLT_TRANSACTION_NOTIFICATION_CALLBACK TransactionNotificationCallback;
PFLT_NORMALIZE_NAME_COMPONENT_EX NormalizeNameComponentExCallback;
PFLT_SECTION_CONFLICT_NOTIFICATION_CALLBACK SectionNotificationCallback;
} FLT_REGISTRATION, *PFLT_REGISTRATION;
可以看到FLT_REGISTRATION
有很多字段, 但是我们用到的通常是:
CONST FLT_REGISTRATION FilterRegistration = {
sizeof(FLT_REGISTRATION), // Size
FLT_REGISTRATION_VERSION, // Version
0, // Flags
NULL, // Context
Callbacks, // Operation callbacks
PtUnload, // MiniFilterUnload
PtInstanceSetup, // 实例绑定回调函数,可以决定绑定哪些卷
PtInstanceQueryTeardown, // InstanceQueryTeardown
PtInstanceTeardownStart, // InstanceTeardownStart
PtInstanceTeardownComplete, /** 过滤管理器在发送的I/O请求都被完成的时候,调用这个函数, 在这个函数中,微过滤驱动关闭所有还被打开的文件*/
NULL, // GenerateFileName
NULL, // GenerateDestinationFileName
NULL // NormalizeNameComponent
};
在FLT_OPERATION_REGISTRATION
这个结构中我们定义我们关心的文件操作:
const FLT_OPERATION_REGISTRATION Callbacks[] = {
{
IRP_MJ_CREATE,
0,
NPPreCreate, // 生成预操作回调函数
NPPostCreate // 生成后操作回调函数
},
{ IRP_MJ_OPERATION_END }
};
IRP_MJ_CREATE
, IRP_MJ_SET_INFORMATION
等是IPR请求信息, 操作系统发送 IRP_MJ_CREATE 请求,以打开文件对象或设备对象的句柄。 例如,当驱动程序调用 ZwCreateFile 时,操作系统会发送 IRP_MJ_CREATE 请求以执行实际打开操作; 每个FLT_OPERATION_REGISTRATION
都必须以IRP_MJ_OPERATION_END
结尾;
所以我们这里注册了有关IRP_MJ_CREATE
的回调, 当有IRP_MJ_CREATE
请求时, 就会触发我们的回调, 会进入我们的NPPreCreate
回调函数, 我们可以在这个函数中处理文件, 例如阻止test1.exe
的操作:
FLT_PREOP_CALLBACK_STATUS NPPreCreate(
PFLT_CALLBACK_DATA Data,
PCFLT_RELATED_OBJECTS FltObjects,
PVOID *ComletionContext
) {
char Filename[256] = { "X:" };
NTSTATUS status;
PFLT_FILE_NAME_INFORMATION nameinfo;
UNREFERENCED_PARAMETER(FltObjects);
UNREFERENCED_PARAMETER(ComletionContext);
PAGED_CODE();
__try {
status = FltGetFileNameInformation(Data,
FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT,
&nameinfo);
if (NT_SUCCESS(status)) {
FltParseFileNameInformation(nameinfo);
if (NPUnicodeStringToChar(&nameinfo->Name, Filename)) {
if (strstr(Filename, "test1.exe") > 0) {
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "[NPPreCreate] Filename :%s\n", Filename);
Data->IoStatus.Status = STATUS_ACCESS_DENIED;
Data->IoStatus.Information = 0;
FltReleaseFileNameInformation(nameinfo);
return FLT_PREOP_COMPLETE;
}
}
FltReleaseFileNameInformation(nameinfo);
}
}
__except (EXCEPTION_EXECUTE_HANDLER) {
DbgPrint("NPPreCreate EXCEPTION_EXECUTE_HANDLER");
}
return FLT_PREOP_SUCCESS_WITH_CALLBACK;
}
MiniFilter驱动加载到系统时, 和常规的驱动加载不太一样, 需要写注册表项, 定义一些字段, 例如Altitude, 这是最重要的一个字段之一;
因为不同的高度意味在不同的加载顺序和分组, 高度越高越会被先执行, 具体的高度以及分组可以参考微软的文档:
inf驱动安装文件:
;;;
;;; MiniFile
;;;
;;;
;;; Copyright (c) 1999 - 2002, Microsoft Corporation
;;;
[Version]
Signature = "$Windows NT$"
Class = "ActivityMonitor" ;This is determined by the work this filter driver does
ClassGuid = {b86dff51-a31e-4bac-b3cf-e8cfe75c9fc2} ;This value is determined by the Class
Provider = %ProviderString%
DriverVer = 06/16/2007,1.0.0.0
CatalogFile = MiniFile.cat
[DestinationDirs]
DefaultDestDir = 12
MiniFile.DriverFiles = 12
;;
;; Default install sections
;;
[DefaultInstall]
OptionDesc = %ServiceDescription%
CopyFiles = MiniFile.DriverFiles
[DefaultInstall.Services]
AddService = %ServiceName%,,MiniFile.Service
;;
;; Default uninstall sections
;;
[DefaultUninstall]
DelFiles = MiniFile.DriverFiles
[DefaultUninstall.Services]
DelService = %ServiceName%,0x200 ;Ensure service is stopped before deleting
;
; Services Section
;
[MiniFile.Service]
DisplayName = %ServiceName%
Description = %ServiceDescription%
ServiceBinary = %12%\%DriverName%.sys
Dependencies = "FltMgr"
ServiceType = 2 ;SERVICE_FILE_SYSTEM_DRIVER
StartType = 3 ;SERVICE_DEMAND_START
ErrorControl = 1 ;SERVICE_ERROR_NORMAL
LoadOrderGroup = "FSFilter Activity Monitor"
AddReg = MiniFile.AddRegistry
;
; Registry Modifications
;
[MiniFile.AddRegistry]
HKR,,"SupportedFeatures",0x00010001,0x3
HKR,"Instances","DefaultInstance",0x00000000,%DefaultInstance%
HKR,"Instances\"%Instance1.Name%,"Altitude",0x00000000,%Instance1.Altitude%
HKR,"Instances\"%Instance1.Name%,"Flags",0x00010001,%Instance1.Flags%
;
; Copy Files
;
[MiniFile.DriverFiles]
%DriverName%.sys
[SourceDisksFiles]
MiniFile.sys = 1,,
[SourceDisksNames]
1 = %DiskId1%,,,
;;
;; String Section
;;
[Strings]
ProviderString = "TODO-Set-Provider"
ServiceDescription = "MiniFile mini-filter driver"
ServiceName = "MiniFile"
DriverName = "MiniFile"
DiskId1 = "MiniFile Device Installation Disk"
;Instances specific information.
DefaultInstance = "Null Instance"
Instance1.Name = "Null Instance"
Instance1.Altitude = "370030"
Instance1.Flags = 0 ; Suppress automatic attachments
FltCreateCommunicationPort
函数可以创建一个通信服务器端口,微筛选器驱动程序可在该端口上接收来自用户模式应用程序的连接请求; 也通过这个函数注册就是可以让ring3的程序直接和MiniFilter
驱动进行通信, FltCreateCommunicationPort
函数原型如下:
NTSTATUS FLTAPI FltCreateCommunicationPort(
[in] PFLT_FILTER Filter,
[out] PFLT_PORT *ServerPort,
[in] POBJECT_ATTRIBUTES ObjectAttributes,
[in, optional] PVOID ServerPortCookie,
[in] PFLT_CONNECT_NOTIFY ConnectNotifyCallback,
[in] PFLT_DISCONNECT_NOTIFY DisconnectNotifyCallback,
[in, optional] PFLT_MESSAGE_NOTIFY MessageNotifyCallback,
[in] LONG MaxConnections
);
处理与应用层程序通信的逻辑主要就是就在MessageNotifyCallback
回调当中;
通过以上的基础知识, 我们知道了在实际的应用当中我们可以通过MiniFilter
来监控文件操作, 并且杀毒软件也是通过这种机制来监控文件的创建等事件, 对我们落盘的文件进行查杀的; 同时安全软件软件EDR产品等也会通过这种方式来对自己的安装目录进行保护, 禁止第三方软件等往自己的目录写入文件, 例如我们往一个受保护的目录中创建一个新文件, 会被阻止:
如果我们在自己的MiniFilter
驱动中调用FltCreateFileEx
, FltWriteFile
, FltReadFile
等函数去操作文件, 会不会被其他MiniFilter
拦截呢?
我们来看看微软的文档中对FltCreateFileEx
的描述, 原型是这样的:
NTSTATUS FLTAPI FltCreateFileEx(
[in] PFLT_FILTER Filter,
[in, optional] PFLT_INSTANCE Instance,
[out] PHANDLE FileHandle,
[out] PFILE_OBJECT *FileObject,
[in] ACCESS_MASK DesiredAccess,
[in] POBJECT_ATTRIBUTES ObjectAttributes,
[out] PIO_STATUS_BLOCK IoStatusBlock,
[in, optional] PLARGE_INTEGER AllocationSize,
[in] ULONG FileAttributes,
[in] ULONG ShareAccess,
[in] ULONG CreateDisposition,
[in] ULONG CreateOptions,
[in, optional] PVOID EaBuffer,
[in] ULONG EaLength,
[in] ULONG Flags
);
在这这些参数中, 微软对第二个参数Instance
有这样的描述:
[in, optional] Instance
创建请求要发送到的微筛选器驱动程序实例的不透明实例指针。
实例必须附加到文件或目录所在的卷。
此参数是可选的,可以为 NULL。
如果此参数为 NULL,则请求将发送到卷的文件系统驱动程序堆栈顶部的设备对象。
如果为非 NULL,则请求仅发送到附加到指定实例下方的微型筛选器驱动程序实例。
这里有一个问题, 假如我们有两个MiniFilter
驱动A
和B
, A
的高度也就是Altitude是370030, B
的高度是180000; 如果在B
中调用FltCreateFileEx
函数并且第二个参数设置为非 NULL
, 这个时候对文件的操作在驱动A
中就收不到相关的回调, 因为A
的高度比B
的高, 所以文件操作的请求发不到A
去;
同样的, 在FltWriteFile
, FltReadFile
等函数中, 也有这种机制:
如果有这样的MiniFilter
驱动, 满足了这两个条件:
那我们是不是就可以绕过杀毒软件或者EDR的自保了呢:
在Windows中有一个驱动, 是处理容器相关的, 叫做wcifs.sys
:
这个驱动注册了MiniFilter
, 并且Altitude也比较低, 远远小于常规EDR注册的MiniFilter
的高度:
在这个驱动中还注册了与应用层之间的通信接口:
在WcPortMessage
函数中, MessageCode == 4
时, 有一个WcCopyFileHandler
函数
在WcCopyFileHandler
函数中利用FltWriteFile
和FltReadFile
实现了一个文件拷贝的功能:
并且文件打开时是用的FltCreateFileEx2
函数, 并且Instance
参数并不是为NULL
:
这就导致如果使用wcifs.sys
提供的文件拷贝功能, 那么常规的EDR或者杀软的MiniFilter
根本就监控不到, 因为wcifs.sys
的高度更低, 这就导致我们可以绕过EDR或者杀软的自保;
调用驱动需要的结构体:
struct WcifsPortMessageCopyFileHandler
{
/*0*/ DWORD MessageVersionOrCode;
/*4*/ DWORD MessageSize;
/*8*/ wchar_t InstanceName[50];
/*108*/ DWORD InstanceNameLength;
/*112*/ DWORD ReparseTag;
/*116*/ DWORD OffsetToSourceContainerRootId;
/*120*/ DWORD SizeOfSourceContainerRootId;
/*124*/ DWORD OffsetToTargetContainerRootId;
/*128*/ DWORD SizeOfTargetContainerRootId;
/*132*/ DWORD OffsetToSourceFileRelativePath;
/*136*/ DWORD SizeOfSourceFileRelativePath;
/*140*/ DWORD OffsetToTargetFileRelativePath;
/*144*/ DWORD SizeOfTargetFileRelativePath;
/*148*/ char UnionData[]; // 2*ContainerRootId + source & target relative paths
};
wcifs.sys
是系统自带的, 并且默认就会加载, 所以利用该驱动的接口来拷贝文件对于绕过一些文件过滤驱动是很方便的, 同时作为EDR或者杀软产品也不能绝对的相信自己的MiniFilter
可以阻止第三方文件的写入, 应该做一些额外的验证, 确保自保目录的安全;