Minifilter文件系统过滤框架

来源:互联网 发布:pinyinime 源码 编辑:程序博客网 时间:2024/06/16 13:46

原理

正常的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_STATUSHOOK_PreNtCreateFile (PFLT_CALLBACK_DATA Data,//IRP封装PCFLT_RELATED_OBJECTS FltObjects,//相关对象,例如文件、卷设备、实例和设备对象PVOID *CompletionContext//上下文对象,可用于让pre分配传给post操作 //分配的一个context资源){    //sandbox?,主防?,杀毒引擎?,加解密?    return XXX;}//______________________________post:FLT_POSTOP_CALLBACK_STATUSHOOK_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_SYNCHRONIZEPOST的返回值有:FLT_POSTOP_FINISHED_PROCESSING//无更多处理FLT_POSTOP_MORE_PROCESSING_REQUIRED//需要做更多的处理,例如需要等待线程结束

读写缓冲区

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

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

FLT_IS_IRP_OPERATIONFLT_IS_FASTIO_OPERATIONFLT_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 ContextFltSet/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,//安全描述符,可传入&oafnConnectFromClient, //获得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)//关联到oaFltFreeSecurityDescriptor(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主动

//发送消息给R3timeout.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等待函数中
0 0
原创粉丝点击