首页
论坛
专栏
课程

[原创] Minfilter

2019-8-1 17:53 2492

[原创] Minfilter

2019-8-1 17:53
2492



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 );



给出代码片段R0


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笔记整理,话说写一篇文章 真的,不是很容易。如果你愿意教我编辑帖子,或者使用论坛的发帖编辑系统我会很开心,欢迎批评指正与交流




[公告]安全服务和外包项目请将项目需求发到看雪企服平台:https://qifu.kanxue.com

最后于 2019-8-1 18:35 被骄阳a编辑 ,原因:
最新回复 (2)
mlgbwoai 1 2019-8-2 09:50
2
0
C:\WinDDK\7600.16385.1\src\filesys\miniFilter\scanner\filter
mlgbwoai 1 2019-8-2 09:54
3
0
F:\Windows-driver-samples-master\Windows-driver-samples-master\filesys\miniFilter\scanner
游客
登录 | 注册 方可回帖
返回