Minifilter文件系统过滤框架

原理

正常的IRP流程是R3 API调用时,会将请求封装成一个IRP经过IO管理器到达文件系统,然后在发往磁盘存储系统,最后到达硬件。

使用MiniFilter后会在IO栈中添加MiniFilter管理器。当IRP到达文件系统之前时,首先会被该管理器拦截。管理器会将IRP封装成CALLBACK_DATA,由管理器中存在的多个minifilter驱动依次处理。这些驱动位置是固定的,位置由altitude属性决定。处理结束后把结果返回给管理器,管理器根据结果决定是否把IRP继续下发。

MiniFilter可以有多个祯【多个管理器,每个管理器有多个minifilter驱动】IRP发给帧1,帧1处理后继续下发到Legacy Filter驱动【例如Sfilter】然后继续下发到帧0,最后下发到文件系统、磁盘存储系统最后到达硬件。

Minifilter管理器处理CALLBACK_DATA时会依次获取高altitude值的minifilter驱动的处理结果然后根据结果决定是否继续下发到低altitude值的minifilter驱动。

每一个minifilter驱动必须有Altitude这个唯一标识符。在加载时值越小相对于其他minifilter驱动在I/O栈中的位置越低。Altitude值取值范围在20000到429999。在320000到329999组中包含了在文件I/O期间探测杀毒的过滤驱动,140000到149999组中包含了在文件I/O期间加解密的过滤驱动。

使用

将Minfilter驱动往Minfilter框架中注册前需要先对驱动的结构体fileMonitorRegistration初始化,结构体类型为FLT_REGISTRATION。成员描述的有结构体大小、版本号、Flags值、各种函数等,其中ContextRegistration指向上下文管理的函数,fileMonitorCallbacks指向回调函数结构体数组【存放关于每个IRP处理的组函数】fileMonUnload指向用于卸载驱动的函数,fileMonInstanceSetup指向的函数在驱动绑定到卷设备时会被调用【驱动在启动时会遍历卷设备并为每个卷设备绑定自身的实例】,fileMonInstanceTeardownStart指向的函数在驱动从卷设备卸载时会被调用。

在fileMonitorCallbacks中根据每个IRP可注册一组【pre(处理前)post(处理后)】回调函数,不注册的由Minfilter管理器处理。结构体如下:

const FLT_OPERATION_REGISTRATION 
fileMonitorCallbacks[] =
{
    { 
        IRP_MJ_CREATE,
        FLTFL_OPERATION_REGISTRATION_SKIP_PAGING_IO,
        HOOK_PreNtCreateFile,
        HOOK_PostNtCreateFile
    },
    { 
        IRP_MJ_CLEANUP,
        0,
        HOOK_PreNtCleanup,
        NULL
    },
    {
        IRP_MJ_WRITE,
        0,
        HOOK_PreNtWriteFile,
        HOOK_PostNtWriteFile
    },
    {  
        IRP_MJ_SET_INFORMATION,
        0,
        HOOK_PreNtSetInformationFile,
        HOOK_PostNtSetInformationFile
    },
    { 
        IRP_MJ_OPERATION_END
    }
    ... ...
};

注册的函数参数基本类似,关于CreateFile的一组函数如下:

//______________________________pre:
FLT_PREOP_CALLBACK_STATUS
HOOK_PreNtCreateFile (
PFLT_CALLBACK_DATA Data,//IRP封装
PCFLT_RELATED_OBJECTS FltObjects,//相关对象,例如文件、卷设备、实例和设备对象
PVOID *CompletionContext//上下文对象,可用于让pre分配传给post操作 
//分配的一个context资源
)
{
    //sandbox?,主防?,杀毒引擎?,加解密?
    return XXX;
}

//______________________________post:
FLT_POSTOP_CALLBACK_STATUS
HOOK_PostNtCreateFile (
PFLT_CALLBACK_DATA Data,
PCFLT_RELATED_OBJECTS FltObjects,
PVOID CompletionContext, 
    //在PRE-OP里返回          //FLT_PREOP_SUCCESS_WITH_CALLBACK
    //时获取里面的上下文,并最后释放
FLT_POST_OPERATION_FLAGS Flags
)
{
    return XXX;
}
//_______________________________end



函数返回值用于返回给MiniFilter管理器控制其操作
PRE的返回值有:
FLT_PREOP_SUCCESS_WITH_CALLBACK//继续下发,而且调用对应的post函数
FLT_PREOP_SUCCESS_NO_CALLBACK//继续下发,不需要调用post函数
FLT_PREOP_COMPLETE//终止下发,具体执行成功还是失败在CALLBACK_DATA中设置。例如成功处理:
//Data->IoStatus.Status=STATUS_ACCESS_DENIED;
//Data->IoStatus.Information=0;
//return FLT_PREOP_COMPLETE;
PRE其他的返回值还有:
FLT_PREOP_PENDING,
FLT_PREOP_DISALLOW_FASTIO,
FLT_PREOP_SYNCHRONIZE

POST的返回值有:
FLT_POSTOP_FINISHED_PROCESSING//无更多处理
FLT_POSTOP_MORE_PROCESSING_REQUIRED//需要做更多的处理,例如需要等待线程结束

读写缓冲区

Data->lopb->Parameters.Read/Write.MdlAddress/ReadBuffer/Length;

由于Data不仅处理IRP还处理FASTIO和文件过滤驱动,可以使用三个宏分别判定:

FLT_IS_IRP_OPERATION
FLT_IS_FASTIO_OPERATION
FLT_IS_FS_FILTER_OPERATION

安装与卸载

安装Minifilter过滤驱动有两种方式,.inf文件安装和代码动态加载

.inf文件安装需要准备一个与MiniFilter驱动关联的.inf文件,然后通过右键安装或SetupCopyOEMinf安装。之后系统会将驱动sys文件拷贝到C:\WINDOWS\SYSTEM32\Dirver目录下并在注册表中创建驱动服务(不会触发DirverEntry)这时需要在CMD中使用”net start/stop 驱动名称”启动或卸载驱动服务。

另一种就是通过动态代码安装。这种方式与NT驱动的驱动安装大同小异,不同在于CreateService设置Group为”FSFilter Acticity Moniter”,DependOnService值为”Fltmgr”,还要在SYSTEM\CurrentControlSet\Services\DriverName\Instances子健并在其下面建立驱动名称子健,再在新建的子健下设置Altitude的值。

初始化fileMonitorRegistration结构体后在DriverEntry中调用FltRegisterFilter注册并获取Minfilter的句柄然后使用FltStartFiltering启动即可:

FltRegisterFilter( DriverObject,&fileMonitorRegistration,&g_pFilter );
FltStartFiltering( g_pFilter );

FltUnregisterFilter( g_pFilter );//DirverUnload中使用该函数注销过滤设备

框架提供的操作方法

路径获取

//在postCreate中使用
PFLT_FILE_NAME_INFORMATION pNameInfo=NULL;//定义一个文件信息指针

FltGetFileNameInformation(Data,FLT_FILE_NAME_NORMALIZEDI|FLT_FILE_NAME_QUERY_DEFAULT,&pNameInfo);
//函数内部申请了内存并设置了引用计数,将文件信息保存到文件信息指针中。

FltParseFileNameInformation(pNameInfo);//解析文件信息,解析后文件地址依然为盘符。需要进一步转换
pNameInfo->Name;pNameInfo->Volume;

FltReleaseFileNameInformation(pNameInfo);//引用计数减一,最后释放内存


//IRP_MJ_SETINFO中重命名获取:
PFLT_FILE_NAME_INFORMATION pNameInfo=(PFLT_FILE_NAME_INFORMATION)Data->lopb->Parameters.SetFileInformation.InfoBuffer;
//或使用:
FltGetDestinationFileNameInformation//重命名获取

Parameters.SetFile.FileInformationClass == FileRenameInformation//判断setInformation为修改名称操作

监控进程创建

IRP_MJ_ACQUIRE_FOR_SECTION_SYNCHRONIZATION//该IRP会在进程创建时调用
Data->Iopb->Parameters.AcquireForSectionSynchronization.PageProtection == PAGE_EXECUTE//创建进程

文件操作

//与Zw系列函数对应,例如:
FltCreateFile、FltReadFile、FltWriteFile、FltClose、FltQueryXxx、FltSetXxx、FltGetXxx、FltPerformXxx

//该系列函数比Zw系列多了头两个参数:参1、参2为实例【MiniFilter实例或在FltObject->XXX实例,看情况选择】

在MiniFilter中不能使用Zw函数,否则可能的导致重入【再次调用zw函数,无限循环】

MiniFilter上下文

FltAllocateContext//申请
FltReleaseContext//释放【内部使用了引用计数】

//分配的类别
FltGet/Stream/StreamHandle/Instance/Volume/File Context
FltSet/Stream/StreamHandle/Instance/Volume/File Context
//分别为流【FCB】、流句柄【FO】、实例、卷、文件【vista以上】上下文。Get获取,Set设置。

Instance上下文例子:

typedef struct _INSTANCE_CONTEXT {
…//自己定义结构体内容
} INSTANCE_CONTEXT, *PINSTANCE_CONTEXT;
PINSTANCE_CONTEXT pContext = NULL;
//分配与设置
ntStatus = FltGetInstanceContext(FltObjects->Instance, & pContext);
if(NT_SUCCESS(Status) == FALSE)//如果还未分配上下文就申请一个
{
    ntStatus = FltAllocateContext(g_pFilter,FLT_INSTANCE_CONTEXT,
        sizeof(INSTANCE_CONTEXT),
        PagedPool,& pContext);
    if(NT_SUCCESS(Status) == FALSE)
    {
        return ntStatus ;
    }
    RtlZeroMemory(pContext, sizeof(INSTANCE_CONTEXT));
}
pContext->xxx = xxx;;//给自定义的结构成员赋值

FltSetInstanceContext(FltObjects->Instance,FLT_SET_CONTEXT_REPLACE_IF_EXISTS,pContext,NULL);//将内存设置到上下文中
if (pContext)
{
    FltReleaseContext(pContext);//这里不使用了,所以直接减引用
}
//获取访问
PINSTANCE_CONTEXT pContext = NULL;
Status = FltGetInstanceContext(FltObjects->Instance,&pContext);
pContext->xxx = xxx;

注册卸载上下文的函数在驱动注册结构体fileMonitorRegistration的ContextRegistration成员中。内部保存了每个类型的上下文的清理函数数组。

R3R0端口通信
R3主动:

//首先R0创建端口,R3通过端口名称与R0通信。

FltCreateCommunicationPort( g_pFilter,
&g_pServerPort,&oa,//设置PORT的名字
NULL,//安全描述符,可传入&oa
fnConnectFromClient, //获得R3端口g_pClientPort等【R3请求链接时调用,函数内可获取R3端口以及R3进程信息】
fnDisconnectFromClient,//【断开链接时调用】
fnMessageFromClient, //处理从R3 FilterSendMessage的请求【R3发送数据后处理数据时调用】
1 );//R0创建端口

FltBuildDefaultSecurityDescriptor(&sd,FLT_PORT_ALL_ACCESS);//建立只允许管理员和系统访问的安全描述符
InitializeObjectAttributes(&oa,&portName,OBJ_CASE_INSENSITIVE|OBJ_KERNEL_HANDLE,NULL,sd)//关联到oa
FltFreeSecurityDescriptor(sd);//使用后释放描述符

FltCloseClientPort//R0关闭R3端口

//R0处理函数
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));//验证地址为R3地址
        //获取InputBuffer
        //Do something
        ProbeForWrite(OutputBuffer, OutputBufferLength, sizeof(ULONG));//验证地址为R3地址
        //将返回的值拷贝到Outputbuffer
    }
    __except(EXCEPTION_EXECUTE_HANDLER)
    {
        return STATUS_NOT_IMPLEMENTED;
    }
    return STATUS_SUCCESS;
}

R3发送

//获取R0端口,参1为端口名称,参6接收端口
FilterConnectCommunicationPort( ScannerPortName,0,NULL,0,NULL,&Port );

FilterSendMessage(
Port,//R0端口
&request,//R3发给R0数据
sizeof(REQUEST),//R3发给R0数据长度
&reply,//R0返回给R3的结果
sizeof(REPLY),//R0返回给R3的结果的长度
&dwRtn//实际传输的字节数
);

R0主动

//发送消息给R3
timeout.QuadPart = (LONGLONG)40 * -10000000i64; // 40 秒
Status = FltSendMessage( g_pFilter,//MiniFilter全局句柄
            &g_pClientPort,//R3端口
            &request,//发送的数据
            sizeof(SCANNER_NOTIFICATION),//数据的大小
            &reply,//R3函数FilterReplyMessage返回的结果
            &replySize,//返回值的长度
            &timeout );//等待时间

FltCloseCommunicationPort( g_pServerPort ); //R0关闭端口

R3接收

completion = CreateIoCompletionPort( port,NULL,0,1);//创建完成端口

//函数是异步的,需要完成端口判断端口是否完成【数据是否收到】
FilterGetMessage( 
Port,//拿数据的端口
&message->MessageHeader,//message为R0传上来的数据结构
FIELD_OFFSET( SANDBOX_MESSAGE, Ovlp ),
&message->Ovlp );

GetQueuedCompletionStatus(completion, &outSize, &key, &pOvlp, INFINITE );//永久等待

FilterReplyMessage(Port,
(PFILTER_REPLY_HEADER) &replyMessage,
sizeof( replyMessage ) );//R3返回消息到R0的FltSendMassage等待函数中

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值