Minfilter说白了就是基于一个结构 FLT_REGISTRATION 的东西。
我们在驱动中把这个结构定义成全局的。在通过三个函数就可以简单操作Minfilter了
他们分别是
FltRegisterFilter();注册minifilter 函数
FltStartFiltering();开启minifilter 函数
FltUnregisterFilter();停止卸载 minifilter 函数
下面介绍 FLT_REGISTRATION 这个重要的结构体
typedef struct _FLT_REGISTRATION {
USHORT Size; //FLT_REGISTRATION结构的大小(以字节为单位)必须将此成员设置为 sizeof(FLT_REGISTRATION)。
USHORT Version; //FLT_REGISTRATION结构的版本。必须将此成员设置为FLT_REGISTRATION_VERSION。
FLT_REGISTRATION_FLAGS Flags; //这里是个标志位一般填写为0即可
CONST FLT_CONTEXT_REGISTRATION *ContextRegistration; //(标红)这里是minifilter的一个上下文。什么意思呢。也就是这里是一段缓存空间,可以用FltAllocateContext()进行分配之后存一些额外数据 (可以为null)
CONST FLT_OPERATION_REGISTRATION *OperationRegistration; //(标红)这里是minfilter的灵魂 这里是一组回调函数,一个回调对应一个Irp
PFLT_FILTER_UNLOAD_CALLBACK FilterUnloadCallback; //这里是minfilter的卸载函数,可以做一些收尾工作。可以为null,填写null 则这个minfilter就不能卸载。
PFLT_INSTANCE_SETUP_CALLBACK InstanceSetupCallback; //这里也是一个回调,minfilter在首个create操作上调用它,来通知说有一个可用卷
PFLT_INSTANCE_QUERY_TEARDOWN_CALLBACK InstanceQueryTeardownCallback; //这里也是一个回调,手工解除绑定,在实例被销毁时被调用。可以设置成NULL
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 中最重要的成员 是 OperationRegistration 是一个结构体,为什么说他重要呢,因为我们可以设置他 在开启minfilter之后,从而拦截处理我们所关心的irp,所以一般这里需要写成数组。
也就是结构体数组。
我们来看一下这个结构
typedef struct _FLT_OPERATION_REGISTRATION {
UCHAR MajorFunction; //需要捕捉irp的类型
FLT_OPERATION_REGISTRATION_FLAGS Flags; //标志位,可以设置 忽略掉一些io操作。
PFLT_PRE_OPERATION_CALLBACK PreOperation; //当此irp请求准备做事情之前会被我们拦截到,用此回调来通知我们,我们在此处理这个irp,可以为null
PFLT_POST_OPERATION_CALLBACK PostOperation; //当此irp请求做完事情之后,会被我们拦截到,用此回调来通知我们,我们在这个时间在进行处理,可以为null
PVOID Reserved1; //系统保留值,必须为null
} FLT_OPERATION_REGISTRATION, *PFLT_OPERATION_REGISTRATION;
观此结构,我们可以发现。只要我们把这个结构体数组 填上我们需要关心的irp 并且提供相应的回调,我们就能处理此次irp 请求。达到我们的需要,比如加解密,查毒等
注意这个结构体数组的最后一个元素必须为 { IRP_MJ_OPERATION_END } ,不然minfilter 分不清这个数组有多大。
我们以下称 当此irp请求准备做事情之前的回调 称为Pre操作。 当此irp请求做完事情之后的回调 称为Post操作。
他们的总体结构如图所示:
他们的定义分别如下
Pre操作 回调
FLT_PREOP_CALLBACK_STATUS
Pre (
_Inout_ PFLT_CALLBACK_DATA Data, //这是我们的irp 已经被重组称CallBackData包了。本质上还是irp
_In_ PCFLT_RELATED_OBJECTS FltObjects, //这里是和我们minfilter相关的一些对象 ,有哪些呢(1)卷设备对象(2)我们的实例对象(3)目标文件的内核对象(4)目标文件所在的设备对象
_Flt_CompletionContext_Outptr_ PVOID *CompletionContext
)
{
//业务逻辑
return 下面讲解;
}
FLT_POSTOP_CALLBACK_STATUS
FsFilter1PostOperation (
_Inout_ PFLT_CALLBACK_DATA Data, //这是我们的irp 已经被重组称CallBackData包了。本质上还是irp
_In_ PCFLT_RELATED_OBJECTS FltObjects, //这里是和我们minfilter相关的一些对象 ,有哪些呢(1)卷设备对象(2)我们的实例对象(3)目标文件的内核对象(4)目标文件所在的设备对象
_In_opt_ PVOID CompletionContext,
_In_ FLT_POST_OPERATION_FLAGS Flags
)
{
//业务逻辑
return 下面讲解;
}
他们的不同 (1)他们的返回类值不一样。(2)他们的参数不一样,这点上面有说明
他们的返回值都是返回给minfilter管理器看的
对于Pre操作来说,一般有
FLT_PREOP_SUCCESS_WITH_CALLBACK 意思是说把这个操作继续往下发。结束之后需要再次调用我们的Post回调函数来通知我们,我们好进一步处理。
FLT_PREOP_SUCCESS_NO_CALLBACK 是说把这个操作继续往下发,结束之后不用再调用我们的Post 回调了。
FLT_PREOP_COMPLETE 是告诉minfilter 这个操作不下发了。就在当前为止。这里如果要真正拒绝,我们把第一个参数Data->IoStatus.Status= 设置成 STATUS_ACCESS_DENIED。
对于Post操作来说,一般有
FLT_POSTOP_FINISHED_PROCESSING 意思是说操作已经完成
同学问:既然minfilter这么好,是不是先加载的minfilter 就优先处理 PFLT_CALLBACK_DATA(irp)操作了呢?
答案是否定的,minfilter有个设备栈 不管谁先加载 在栈中的顺序都是固定的。取决于他的高度值,高度越高的,越优先处理这个 PFLT_CALLBACK_DATA(irp)。
高度值范围为20000—429999
高度值详细信息:https://docs.microsoft.com/en-us/windows-hardware/drivers/ifs/load-order-groups-and-altitudes-for-minifilter-drivers
下面给出最简单minfilter代码 注意代码中的注释
#define NULL_FILTER_FILTER_NAME L"NullFilter" typedef struct _NULL_FILTER_DATA { PFLT_FILTER FilterHandle; } NULL_FILTER_DATA, *PNULL_FILTER_DATA; NULL_FILTER_DATA NullFilterData; CONST FLT_REGISTRATION FilterRegistration = { sizeof( FLT_REGISTRATION ), // Size FLT_REGISTRATION_VERSION, // Version 0, // Flags NULL, // Context NULL, // Operation callbacks NullUnload, // FilterUnload NULL, // InstanceSetup NullQueryTeardown, // InstanceQueryTeardown NULL, // InstanceTeardownStart NULL, // InstanceTeardownComplete NULL, // GenerateFileName NULL, // GenerateDestinationFileName NULL // NormalizeNameComponent }; NTSTATUS DriverEntry ( __in PDRIVER_OBJECT DriverObject, __in PUNICODE_STRING RegistryPath ) { NTSTATUS status; status = FltRegisterFilter( DriverObject, &FilterRegistration, &NullFilterData.FilterHandle );//这里的第三个参数是返回Minfilter的句柄。我们很多地方需要用到这个句柄 所以需要定义成全局变量 if (NT_SUCCESS( status )) { status = FltStartFiltering( NullFilterData.FilterHandle ); if (!NT_SUCCESS( status )) { FltUnregisterFilter( NullFilterData.FilterHandle ); } } DbgPrint("Minifilter started\n"); return status; } NTSTATUS NullUnload ( __in FLT_FILTER_UNLOAD_FLAGS Flags ) { UNREFERENCED_PARAMETER( Flags ); PAGED_CODE(); FltUnregisterFilter( NullFilterData.FilterHandle ); DbgPrint("Minifilter unloaded\n"); return STATUS_SUCCESS; } NTSTATUS NullQueryTeardown ( __in PCFLT_RELATED_OBJECTS FltObjects, __in FLT_INSTANCE_QUERY_TEARDOWN_FLAGS Flags ) { UNREFERENCED_PARAMETER( FltObjects ); UNREFERENCED_PARAMETER( Flags ); PAGED_CODE(); return STATUS_SUCCESS; }
R3主动-R0,R0主动-R3 通讯
对于他们的通讯机制是基于端口的。
用到的函数或者宏也很简单。
R0:
InitializeObjectAttributes();//初始化一些信息
FltCreateCommunicationPort();//创建通讯端口 注意,此函数参数非常主要。
FltSendMessage();//r0主动发消息联系r3
R3:
FilterGetMessage();
FilterReplyMessage();
FilterSendMessage();
这里得特别说一下FltCreateCommunicationPort();这个函数。我们在使用这个函数创建出来端口之后 可以设置三个回调
FltCreateCommunicationPort( g_pFilter,
&g_pServerPort,
&oa,//设置PORT的名字
NULL,
(标红)fnConnectFromClient, //R3开始链接我们的时候,这个回调会被调用,可以用于获得链接我们的R3的pid和Eprocess
(标红)fnDisconnectFromClient,//R3断开链接的时候,这个回调会被调用
(标红)fnMessageFromClient, //R3建立链接之后 R3发消息给minfilter时,这个函数会被调用
1 );
const PWSTR ScannerPortName = L"\\Scannerport"; SCANNER_DATA ScannerData; /*---------------------------------------------------------------------------------------------------------------------------- 以下为DriverEntry ------------------------------------------------------------------------------------------------------------------------------*/ NTSTATUS DriverEntry ( __in PDRIVER_OBJECT DriverObject, __in PUNICODE_STRING RegistryPath ) { OBJECT_ATTRIBUTES oa; UNICODE_STRING uniString; PSECURITY_DESCRIPTOR sd; NTSTATUS status; status = FltRegisterFilter( DriverObject, &FilterRegistration, &ScannerData.Filter );//注册minfilter if (!NT_SUCCESS( status )) { return status; } RtlInitUnicodeString( &uniString, ScannerPortName ); status = FltBuildDefaultSecurityDescriptor( &sd, FLT_PORT_ALL_ACCESS );//需要一个安全描述符 if (NT_SUCCESS( status )) { InitializeObjectAttributes( &oa, &uniString, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, sd );//初始化一些信息 status = FltCreateCommunicationPort( ScannerData.Filter, &ScannerData.ServerPort, &oa, NULL, ScannerPortConnect, //R3开始链接我们的时候,这个回调会被调用,可以用于获得链接我们的R3的pid和Eprocess ScannerPortDisconnect, //R3断开链接的时候,这个回调会被调用 fnMessageFromClient, //R3建立链接之后 R3发消息给minfilter时,这个函数会被调用 1 );创建端口链接注册回调函数 FltFreeSecurityDescriptor( sd );//释放掉安全描述符 if (NT_SUCCESS( status )) { status = FltStartFiltering( ScannerData.Filter ); if (NT_SUCCESS( status )) { return STATUS_SUCCESS; } FltCloseCommunicationPort( ScannerData.ServerPort ); } } FltUnregisterFilter( ScannerData.Filter ); return status; } /*---------------------------------------------------------------------------------------------------------------------------- 以下为ScannerPortConnect R3开始链接我们的时候的回调 ------------------------------------------------------------------------------------------------------------------------------*/ NTSTATUS ScannerPortConnect ( __in PFLT_PORT ClientPort, __in_opt PVOID ServerPortCookie, __in_bcount_opt(SizeOfContext) PVOID ConnectionContext, __in ULONG SizeOfContext, __deref_out_opt PVOID *ConnectionCookie ) { PAGED_CODE(); ScannerData.UserProcess = PsGetCurrentProcess(); ScannerData.ClientPort = ClientPort; //各种业务逻辑 return STATUS_SUCCESS; } /*---------------------------------------------------------------------------------------------------------------------------- 以下为ScannerPortDisconnect R3断开链接的时候的回调 ------------------------------------------------------------------------------------------------------------------------------*/ VOID ScannerPortDisconnect( __in_opt PVOID ConnectionCookie ) { PAGED_CODE(); //各种业务逻辑 FltCloseClientPort( ScannerData.Filter, &ScannerData.ClientPort ); ScannerData.UserProcess = NULL; } /*---------------------------------------------------------------------------------------------------------------------------- 以下为fnMessageFromClient R3建立链接之后处理请求会被调用 ------------------------------------------------------------------------------------------------------------------------------*/ NTSTATUS fnMessageFromClient( IN PVOID PortCookie, IN PVOID InputBuffer OPTIONAL, IN ULONG InputBufferLength, OUT PVOID OutputBuffer OPTIONAL, IN ULONG OutputBufferLength, OUT PULONG ReturnOutputBufferLength ) { __try { ProbeForRead(InputBuffer, InputBufferLength, sizeof(ULONG));//地址合法性校验 ProbeForWrite(OutputBuffer, OutputBufferLength, sizeof(ULONG));//地址合法性校验 } __except(EXCEPTION_EXECUTE_HANDLER) { return STATUS_NOT_IMPLEMENTED; } return STATUS_SUCCESS; }
R3:
FilterConnectCommunicationPort( ScannerPortName/*与R0的名字一致*/, 0, NULL, 0, NULL, &Port );//尝试链接R0的 minfilter completion = CreateIoCompletionPort( port,NULL,0,1);//处理从R0来的请求,即R0调用FltSendMessage的请求 即是R0-R3 ,R3也能-R0 FilterGetMessage( Port, &message->MessageHeader, FIELD_OFFSET( SANDBOX_MESSAGE, Ovlp ), &message->Ovlp ); while(1) { GetQueuedCompletionStatus( lpContext->Completion, &outSize, &key, &pOvlp, INFINITE ); 。。。 FilterReplyMessage(Port, (PFILTER_REPLY_HEADER) &replyMessage, sizeof( replyMessage ) ); FilterGetMessage( Port, &message->MessageHeader, FIELD_OFFSET( SANDBOX_MESSAGE, Ovlp ), &message->Ovlp ); }
后记:这篇文章出自我学习minfilter笔记整理,话说写一篇文章 真的,不是很容易。如果你愿意教我编辑帖子,或者使用论坛的发帖编辑系统我会很开心,欢迎批评指正与交流
[招聘]苏州极光无限公司!——高薪招聘逆向、移动、漏洞挖掘工程师!
最后于 9小时前 被骄阳a编辑 ,原因: