你的MiniFilter安全吗?

简介

筛选器管理器 (FltMgr.sys)是Windows系统提供的内核模式驱动程序, 用于实现和公开文件系统筛选器驱动程序中通常所需的功能; 第三方文件系统筛选器开发人员可以使用FltMgr的功能可以更加简单的编写文件过滤驱动, 这种驱动我们通常称为MiniFilter, 下面是MiniFilter的基本框架:
你的MiniFilter安全吗?_第1张图片
简单的说就是我们可以通过微软提供的一些接口, 可以使我们方便的在内核层监控文件的操作(创建, 删除, 读写等), 例如杀毒软件利用MiniFilter监控文件的创建, 在文件创建时对文件进行病毒查杀; 又例如安全产品利用MiniFilter, 阻止第三方程序在自己的目录下面写文件等…

MiniFilter

由于在MiniFilter中我们不用关心IRP的处理工作, 这些都可以交给 Filter Manager处理, 所以我们要编写一个MiniFilter是很简单的, 只有调用几个API函数, 并在参数中填写我们关心的或者需要处理的结构就可以了;

FltRegisterFilter

我们使用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;
}

.inf文件

MiniFilter驱动加载到系统时, 和常规的驱动加载不太一样, 需要写注册表项, 定义一些字段, 例如Altitude, 这是最重要的一个字段之一;
因为不同的高度意味在不同的加载顺序和分组, 高度越高越会被先执行, 具体的高度以及分组可以参考微软的文档:
你的MiniFilter安全吗?_第2张图片
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

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回调当中;

Flt函数

通过以上的基础知识, 我们知道了在实际的应用当中我们可以通过MiniFilter来监控文件操作, 并且杀毒软件也是通过这种机制来监控文件的创建等事件, 对我们落盘的文件进行查杀的; 同时安全软件软件EDR产品等也会通过这种方式来对自己的安装目录进行保护, 禁止第三方软件等往自己的目录写入文件, 例如我们往一个受保护的目录中创建一个新文件, 会被阻止:
你的MiniFilter安全吗?_第3张图片
如果我们在自己的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驱动AB, A的高度也就是Altitude是370030, B的高度是180000; 如果在B中调用FltCreateFileEx函数并且第二个参数设置为非 NULL, 这个时候对文件的操作在驱动A中就收不到相关的回调, 因为A的高度比B的高, 所以文件操作的请求发不到A去;
同样的, 在FltWriteFile, FltReadFile等函数中, 也有这种机制:
你的MiniFilter安全吗?_第4张图片
你的MiniFilter安全吗?_第5张图片
如果有这样的MiniFilter驱动, 满足了这两个条件:

  1. Altitude比杀软或者EDR的Altitude低;
  2. 存在文件读写的接口;

那我们是不是就可以绕过杀毒软件或者EDR的自保了呢:

Wcifs.sys

在Windows中有一个驱动, 是处理容器相关的, 叫做wcifs.sys:
你的MiniFilter安全吗?_第6张图片
这个驱动注册了MiniFilter, 并且Altitude也比较低, 远远小于常规EDR注册的MiniFilter的高度:
你的MiniFilter安全吗?_第7张图片
在这个驱动中还注册了与应用层之间的通信接口:
你的MiniFilter安全吗?_第8张图片
WcPortMessage函数中, MessageCode == 4时, 有一个WcCopyFileHandler函数
你的MiniFilter安全吗?_第9张图片
WcCopyFileHandler函数中利用FltWriteFileFltReadFile实现了一个文件拷贝的功能:
你的MiniFilter安全吗?_第10张图片
并且文件打开时是用的FltCreateFileEx2函数, 并且Instance参数并不是为NULL:
你的MiniFilter安全吗?_第11张图片
这就导致如果使用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可以阻止第三方文件的写入, 应该做一些额外的验证, 确保自保目录的安全;

你可能感兴趣的:(安全)