原文地址: https://blog.csdn.net/zhuhuibeishadiao/article/details/51229122
Minfilter与legacy filter区别
比sfilter加载顺序更易控制. altitude被绑定到合适的位置。
Minfilter在注册表服务项中有一项Altitude值
此值越高位置越靠前 (待考证
每一个minifilter驱动必须有一个叫做altitude的唯一标识符.一个minifilter驱动的altitude定义了它加载时在I/O栈中相对其他minifilter驱动的位置。值越小,栈中位置就越低
FSFilter Anti-Virus 320000-329999 此组包括在文件I/O期间探测并杀毒的过滤驱动.
FSFilter Encryption 140000-149999此组包括在文件I/O期间加密和解密数据的过滤驱动.
图片2
可卸载能力.
Callback模型仅需处理必要操作的能力.
不再需要给一个IRP配置一个完成例程,Minfilter每个过滤功能有2个回调函数,一个是“事前”回调(PreCallBack),一个是“事后”回调(PosCallBack)
相当于PosCallBack就是sfilter中的IRP完成例程
要调用PosCallBack只需要PreCallBack 返回 FLT_PREOP_SUCCESS_WITH_CALLBACK
而返回FLT_PREOP_SUCCESS_NO_CALLBACK则告诉系统,处理好这件事后不用调用PosCallBack了
一个相当于sfilter中的Cpy,一个是skip
阻止下发返回FLT_PREOP_COMPLETE
兼容性更好
名字处理更容易
FltGetFileNameInformation
只需要传入回调函数CALL_DATA data 和一个PFLT_FILE_NAME_INFORMATION指针就可以获得相关文件的信息 然后再调用
FltParseFileNameInformation就可以获得路径了
例子:(注意 获取路径需要在Pos中获取(即创建成功后才能获取到真实的数据))
-
-
-
- PFLT_FILE_NAME_INFORMATION pNameInfo = NULL;
-
-
- ntStatus = FltGetFileNameInformation(Data,
- FLT_FILE_NAME_NORMALIZED|
- FLT_FILE_NAME_QUERY_DEFAULT,
- &pNameInfo);
- FltParseFileNameInformation(pNameInfo);
-
-
- pNameInfo->Name.Buffer
- pNameInfo->Volume
-
-
- FltReleaseFileNameInformation(pNameInfo);
//重命名的获得:
- PFILE_RENAME_INFORMATION
- pFileRenameInfomation = (PFILE_RENAME_INFORMATION)Data->Iopb->Parameters.SetFileInformation.InfoBuffer;
-
-
- FltGetDestinationFileNameInformation
安装方式(.inf/动态加载)
通信方式(port)
同样遵循IRQL,锁等内核开发通用机制
FltCreateFile
Minfilter架构
结构
在DriverEntry中只需要注册Fliter和Start
- FltRegisterFilter( DriverObject,
- &fileMonitorRegistration,
- &g_pFilter );
- FltStartFiltering( g_pFilter );
fileMonitorRegistration是唯一我们需要做的
这是一个FLT_REGISTRATION 结构
- const FLT_REGISTRATION fileMonitorRegistration =
- {
- sizeof( FLT_REGISTRATION ),
- FLT_REGISTRATION_VERSION,
- 0,
- ContextRegistration,
- fileMonitorCallbacks,
- fileMonUnload,
- fileMonInstanceSetup,
- NULL,
- fileMonInstanceTeardownStart,
- NULL,
- NULL,
- NULL,
- NULL
- };
fileMonitorCallbacks 例子:
可以只需要一个回调如IRP_MJ_CLEANUP
- 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
- }
- };
//一个回调的例子:
- FLT_PREOP_CALLBACK_STATUS
- HOOK_PreNtCreateFile (
- PFLT_CALLBACK_DATA Data,
- PCFLT_RELATED_OBJECTS FltObjects,
- PVOID *CompletionContext
-
- )
- {
-
-
-
-
- return XXX;
- }
- FLT_POSTOP_CALLBACK_STATUS
- HOOK_PostNtCreateFile (
- PFLT_CALLBACK_DATA Data,
- PCFLT_RELATED_OBJECTS FltObjects,
- PVOID CompletionContext,
-
-
- FLT_POST_OPERATION_FLAGS Flags
- )
- {
- return XXX;
- }
上下位数组 例子:
- PFLT_FILTER g_pFilter = NULL;
- const FLT_CONTEXT_REGISTRATION
- ContextRegistration[] =
- {
- {
- FLT_INSTANCE_CONTEXT,
- 0,
- CtxContextCleanup,
- CTX_INSTANCE_CONTEXT_SIZE,
- CTX_INSTANCE_CONTEXT_TAG
- },
- {
- FLT_FILE_CONTEXT,
- 0,
- CtxContextCleanup,
- CTX_FILE_CONTEXT_SIZE,
- CTX_FILE_CONTEXT_TAG
- },
- {
- FLT_STREAM_CONTEXT,
- 0,
- CtxContextCleanup,
- CTX_STREAM_CONTEXT_SIZE,
- CTX_STREAM_CONTEXT_TAG
- },
- {
- FLT_STREAMHANDLE_CONTEXT,
- 0,
- CtxContextCleanup,
- CTX_STREAMHANDLE_CONTEXT_SIZE,
- CTX_STREAMHANDLE_CONTEXT_TAG
- },
- { FLT_CONTEXT_END }
- };
Minifilter的启动
- NTSTATUS initFileMonitor (PDRIVER_OBJECT DriverObject )
- {
- return FltRegisterFilter( DriverObject,
- &fileMonitorRegistration,
- &g_pFilter );
- }
-
-
-
-
- NTSTATUS startFileMonitor( )
- {
- if(g_pFilter)
- return FltStartFiltering( g_pFilter );
- return STATUS_INSUFFICIENT_RESOURCES;
- }
-
-
- VOID stopFileMonitor( )
- {
- if(g_pFilter)
- {
- FltUnregisterFilter( g_pFilter );
- g_pFilter = NULL;
- }
- }
.inf文件安装minifilter
这个就是抄啊改的 没什么介绍的,只需要注意里面的ClassGUID和Class必须对上
这是查询网址
http://msdn.microsoft.com/en-us/library/windows/hardware/ff540394(v=vs.85).aspx
如果需要用自己的加载器加载Minfilter 只需要在注册表服务对应的RegPath下创建REG_SZ类型的Instances子健
在这里键下创建键值项,项值为Altitude
- SYSTEM\\CurrentControlSet\\Services\\DriverName\\Instances子健下的键值项
- 例子:
-
-
-
- strcpy(szTempStr,"SYSTEM\\CurrentControlSet\\Services\\");
- strcat(szTempStr,lpszDriverName);
- strcat(szTempStr,"\\Instances");
- if(RegCreateKeyEx(HKEY_LOCAL_MACHINE,szTempStr,0,"",REG_OPTION_NON_VOLATILE,KEY_ALL_ACCESS,NULL,&hKey,(LPDWORD)&dwData)!=ERROR_SUCCESS)
- {
- return FALSE;
- }
-
- strcpy(szTempStr,lpszDriverName);
- strcat(szTempStr," Instance");
- if(RegSetValueEx(hKey,"DefaultInstance",0,REG_SZ,(CONST BYTE*)szTempStr,(DWORD)strlen(szTempStr))!=ERROR_SUCCESS)
- {
- return FALSE;
- }
- RegFlushKey(hKey);
- RegCloseKey(hKey);
-
-
-
-
-
-
- strcpy(szTempStr,"SYSTEM\\CurrentControlSet\\Services\\");
- strcat(szTempStr,lpszDriverName);
- strcat(szTempStr,"\\Instances\\");
- strcat(szTempStr,lpszDriverName);
- strcat(szTempStr," Instance");
- if(RegCreateKeyEx(HKEY_LOCAL_MACHINE,szTempStr,0,"",REG_OPTION_NON_VOLATILE,KEY_ALL_ACCESS,NULL,&hKey,(LPDWORD)&dwData)!=ERROR_SUCCESS)
- {
- return FALSE;
- }
-
- strcpy(szTempStr,lpszAltitude);
- if(RegSetValueEx(hKey,"Altitude",0,REG_SZ,(CONST BYTE*)szTempStr,(DWORD)strlen(szTempStr))!=ERROR_SUCCESS)
- {
- return FALSE;
- }
-
- dwData=0x0;
- if(RegSetValueEx(hKey,"Flags",0,REG_DWORD,(CONST BYTE*)&dwData,sizeof(DWORD))!=ERROR_SUCCESS)
- {
- return FALSE;
- }
- RegFlushKey(hKey);
- RegCloseKey(hKey);
-
-
- return TRUE;
下面说一说回调
- FLT_PREOP_CALLBACK_STATUS
- HOOK_PreNtCreateFile (
- PFLT_CALLBACK_DATA Data,
- PCFLT_RELATED_OBJECTS FltObjects,
- PVOID *CompletionContext
-
- )
- {
-
-
-
-
- return XXX;
- }
- FLT_POSTOP_CALLBACK_STATUS
- HOOK_PostNtCreateFile (
- PFLT_CALLBACK_DATA Data,
- PCFLT_RELATED_OBJECTS FltObjects,
- PVOID CompletionContext,
-
-
- FLT_POST_OPERATION_FLAGS Flags
- )
- {
- return XXX;
- }
PRE-OP的返回值:
FLT_PREOP_SUCCESS_WITH_CALLBACK,//常用
FLT_PREOP_SUCCESS_NO_CALLBACK,//常用
FLT_PREOP_PENDING,//挂起IRP 不常用
FLT_PREOP_DISALLOW_FASTIO,//关闭FASTIO
FLT_PREOP_COMPLETE,//阻止
FLT_PREOP_SYNCHRONIZE//不常用
POST-OP的返回值:
FLT_POSTOP_FINISHED_PROCESSING,//常用
FLT_POSTOP_MORE_PROCESSING_REQUIRED
我们可以判断这个Data是什么请求
判断Data是什么操作的宏
- FLT_IS_IRP_OPERATION
- FLT_IS_FASTIO_OPERATION
- FLT_IS_FS_FILTER_OPERATION
- if(FLT_IS_FASTIO_OPERATION(Data))
- {
- ntStatus = STATUS_FLT_DISALLOW_FAST_IO;
- Data->IoStatus.Status = ntStatus;
- Data->IoStatus.Information = 0;
- return FLT_PREOP_DISALLOW_FASTIO;
-
-
- }
参数数据的获取:
- PFLT_CALLBACK_DATA Data;
- PEPROCESS processObject =
- Data->Thread ? IoThreadToProcess(Data->Thread) : PsGetCurrentProcess();
- HandleToUlong(PsGetProcessId(processObject));
-
-
- Data->IoStatus.Status = ntStatus;
- Data->IoStatus.Information = 0;
-
-
- FltObjects->Volume,
- FltObjects->Instance,
- FltObjects->FileObject,
- FltObjects->FileObject->DeviceObject
-
-
- Data->Iopb->Parameters.Create.SecurityContext->DesiredAccess
比如这次是查询目录 (怎么判断是什么操作?每个对应的回调就告诉你了这是什么操作,不可以在Create的回调中收到写操作把)
- PVOID pQueryBuffer =
- Data->Iopb->Parameters.DirectoryControl.QueryDirectory.DirectoryBuffer;
- ULONG uQueryBufferSize =
- Data->Iopb->Parameters.DirectoryControl.QueryDirectory.Length
//读, 读有可能是使用MDL可能使用其它buff 判断的方法是看这个有没有值,没有值则是另一种
- PMDL pReadMdl = Data->Iopb->Parameters.Read. MdlAddress;
- PVOID pReadBuffer = Data->Iopb->Parameters.Read. ReadBuffer;
- ULONG uReadLength = Data->Iopb->Parameters.Read.Length;
写同上面
路径的获取:
-
-
-
- PFLT_FILE_NAME_INFORMATION pNameInfo = NULL;
-
-
- ntStatus = FltGetFileNameInformation(Data,
- FLT_FILE_NAME_NORMALIZED|
- FLT_FILE_NAME_QUERY_DEFAULT,
- &pNameInfo);
- FltParseFileNameInformation(pNameInfo);
-
-
- pNameInfo->Name.Buffer
- pNameInfo->Volume
-
-
- FltReleaseFileNameInformation(pNameInfo);
pNameInfo里面还有很多东西
WDK里面的例子:
//重命名的获得:
- PFILE_RENAME_INFORMATION
- pFileRenameInfomation = (PFILE_RENAME_INFORMATION)Data->Iopb->Parameters.SetFileInformation.InfoBuffer;
-
-
- FltGetDestinationFileNameInformation
其实NtCreateSection对应着一个IRP
IRP_MJ_ACQUIRE_FOR_SECTION_SYNCHRONIZATION
Data->Iopb->Parameters.AcquireForSectionSynchronization.PageProtection == PAGE_EXECUTE (等于这个就是创建进程)
在x64可以用这个监控进程 不过不需要
文件操作
在过滤驱动中,我们不能使用默认的文件操作,这会引起重入
Minfilter给我提供了专门的函数
FltCreateFile
FltReadFile
FltWriteFile
FltClose
FltQueryXxx
FltSetXxx
FltGetXxx
FltPerformXxx
其中的一个例子:
- ntStatus = FltCreateFile(pFilter,
- pDstInstance,
- &hDstFile,
- GENERIC_WRITE | SYNCHRONIZE,
- &objDstAttrib,
- &ioStatus,
- 0,
- FILE_ATTRIBUTE_NORMAL,
- FILE_SHARE_READ |
- FILE_SHARE_WRITE |
- FILE_SHARE_DELETE,
- FILE_CREATE,
- CreateOptions,
- NULL,0,0);
Minfilter上下文:
Context上下文:其实就是附着在某个对象上的一段数据,这段数据由自己定义;
FltAllocateContext
FltReleaseContext
Stream Context - 流上下文,也就是大家常用的FCB(File Control Block)的上下文,文件和FCB是一对一的关系;
FltGetStreamContext
FltSetStreamContext
Stream Handle Context - 流句柄上下文,也就是大家常见的FO(File Object)的上下文,一个文件可以对应多个FO,属一对多关系;
FltGetStreamHandleContext
FltSetStreamHandleContext
Instance Context - 实例上下文,也就是过滤驱动在文件系统的设备堆栈上创建的一个过滤器实例;
FltGetInstanceContext
FltSetInstanceContext
Volume Context - 卷上下文,卷就是大家通常看到的C,D,E盘以及网络重定向器,一般情况下一个卷对应一个过滤器实例对象,在实际应用上经常用Instance Context来代替Volume Context。
FltGetVolumeContext
FltSetVolumeContext
文件上下文(vista之后)
FltGetFileContext
FltSetFileContext
- PFLT_FILTER g_pFilter = NULL;
- const FLT_CONTEXT_REGISTRATION
- ContextRegistration[] =
- {
- {
- FLT_INSTANCE_CONTEXT,
- 0,
- CtxContextCleanup,
- CTX_INSTANCE_CONTEXT_SIZE,
- CTX_INSTANCE_CONTEXT_TAG
- },
- {
- FLT_FILE_CONTEXT,
- 0,
- CtxContextCleanup,
- CTX_FILE_CONTEXT_SIZE,
- CTX_FILE_CONTEXT_TAG
- },
- {
- FLT_STREAM_CONTEXT,
- 0,
- CtxContextCleanup,
- CTX_STREAM_CONTEXT_SIZE,
- CTX_STREAM_CONTEXT_TAG
- },
- {
- FLT_STREAMHANDLE_CONTEXT,
- 0,
- CtxContextCleanup,
- CTX_STREAMHANDLE_CONTEXT_SIZE,
- CTX_STREAMHANDLE_CONTEXT_TAG
- },
- { FLT_CONTEXT_END }
- };
Context使用例子
- 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 STATUS_SUCCESS;
- }
- RtlZeroMemory(pContext, sizeof(INSTANCE_CONTEXT));
- }
- pContext ->m_DeviceType = VolumeDeviceType;
- pContext->m_FSType = VolumeFilesystemType;
- 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;
Minifilter R3与R0通信
不用像NT框架里面的通信方式了,Minifilter为我们提供了专门的函数进行通信
在通信时,我们使用Port进行通信
在R0,我们创建一个Port,R3在通信前会得到Port的句柄,我们就可以通过这个port进行通信了
R0创建端口代码:
- 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,
- ScannerPortDisconnect,
- NULL,
- 1 );
-
-
-
-
-
- FltFreeSecurityDescriptor( sd );
-
先说说HIPS的实现,
r3,首先得到R0通信端口的句柄
使用FilterConnectCommunicationPort 只需要注意第一个参数和最后一个参数即可 其它都为0或NULL
然后绑定这个端口(我理解为绑定)
使用CreateIoCompletionPort 只需要注意第一个参数最后一个参数即可,最后一个参数:为这个工作线程设置几个线程
这样有助于高效率 一般小于64 ,也可以设置请求次数 这样回复也会高效率一些(具体看后面的代码)
我们首先使用FiltetGetMessage异步获取一下消息,如果有信息,则下面的GetQueuedCompletionStatus就会恢复(如果没有消息,GetQueuedCompletionStatus就会暂停下来,并让出CPU,等待有信息)
有了信息后我们就可以进行扫描 过滤,搞定之后就调用FilterReplyMessage返回给内核
然后继续调用FilterGetMessage-等待--处理--返回给内核
代码:
- FilterConnectCommunicationPort( ScannerPortName,
- 0,
- NULL,
- 0,
- NULL,
- &port );
-
-
-
- completion = CreateIoCompletionPort( port,NULL,0,1);
- 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 );
- }
注意:这里的FILTER_REPLY_HEADER结构,里面有一项是固定的,有一项是可以自定义的,包括增加自己的结构
同样的Message对应的结构里面有一项也是可以自定义的 ,这要跟内核对应起来就可以了,内核里面只有自定义的那一项
比如:R0数据结构 这个就是自定义的
- typedef struct _SCANNER_NOTIFICATION
- {
- ULONG BytesToScan;
- ULONG Reserved;
- UCHAR Contents[SCANNER_READ_BUFFER_SIZE];
-
- } SCANNER_NOTIFICATION, *PSCANNER_NOTIFICATION;
-
-
- typedef struct _SCANNER_REPLY
- {
- BOOLEAN SafeToOpen;
-
-
- } SCANNER_REPLY, *PSCANNER_REPLY;
R3的结构
- typedef struct _SCANNER_MESSAGE
- {
- FILTER_MESSAGE_HEADER MessageHeader;
- SCANNER_NOTIFICATION Notification;
- OVERLAPPED Ovlp;
- } SCANNER_MESSAGE, *PSCANNER_MESSAGE;
-
-
- typedef struct _SCANNER_REPLY_MESSAGE
- {
- FILTER_REPLY_HEADER ReplyHeader;
- SCANNER_REPLY Reply;
- } SCANNER_REPLY_MESSAGE,
- *PSCANNER_REPLY_MESSAGE;
那R0怎么发送消息给R3呢
使用FltSendMessage
实例代码:
-
- timeout.QuadPart = (LONGLONG)40 * -10000000i64;
- Status = FltSendMessage( g_pFilter,
- &g_pClientPort,
- &request,
- sizeof(SCANNER_NOTIFICATION),
- &reply,
- &replySize,
- &timeout );
主动通信就讲完了,不过需要注意的时,应用程序可能已经退出了,带sys还在,那要怎么办呢,
R3的程序退出时,R0中那个断开连接的回调就会触发,我们需要在那个回调中设置用户通信端口为NULL
其它过滤函数中需要判断这个是不是NULL 不是NULL就通信,是NULL就放行
什么是R3通信端口?
其实这个是一个R3端口的句柄,当用户使用FilterConnectCommunicationPort建立连接时,
内核中那个连接函数就会被调用,在那里面就有用户的通信端口(句柄)(不记得了?往上拉看看吧,我们还在那里面设置本进程的ID呢)
说完了主动通信,我们来说说缓冲区的使用
比如写操作,写的数据可能在MDLbuff中,也可能在USERbuff中,那要怎么操作呢,我记得上面提到过
判断MDLbuff是不是为空,不为空则数据就在这个里面,否则就是userbuff中
注意这里需要使用异常处理结构块,主流程使用tyr and finally 内存操作使用try and except( EXCEPTION_EXECUTE_HANDLER )
这里还介绍了上下文的使用 只不过这里是很简单的处理了下
首先在CreatePre事前回调中,我们设置上下文,然后再后面操作中,我们获取这个上下文,如果获取不到就不是我们要操作的文件
这里其实没啥用,只是演示如果使用上下文,当然这里的上下文结构里面可以自定义,我这里是设置了需要不需要重新扫描
- typedef struct _SCANNER_STREAM_HANDLE_CONTEXT {
-
-
- BOOLEAN RescanRequired;
-
- } SCANNER_STREAM_HANDLE_CONTEXT, *PSCANNER_STREAM_HANDLE_CONTEXT;
代码例子:内核处理写操作 需要注意的事,有一个IRP是FLT管理器发的,有点特殊,需要放过,见代码尾巴
-
- FLT_PREOP_CALLBACK_STATUS
- ScannerPreWrite (
- __inout PFLT_CALLBACK_DATA Data,
- __in PCFLT_RELATED_OBJECTS FltObjects,
- __deref_out_opt PVOID *CompletionContext
- )
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {
- FLT_PREOP_CALLBACK_STATUS returnStatus = FLT_PREOP_SUCCESS_NO_CALLBACK;
- NTSTATUS status;
- PSCANNER_NOTIFICATION notification = NULL;
- PSCANNER_STREAM_HANDLE_CONTEXT context = NULL;
- ULONG replyLength;
- BOOLEAN safe = TRUE;
- PUCHAR buffer;
-
-
- UNREFERENCED_PARAMETER( CompletionContext );
-
-
-
-
-
-
- if (ScannerData.ClientPort == NULL) {
-
-
- return FLT_PREOP_SUCCESS_NO_CALLBACK;
- }
-
- status = FltGetStreamHandleContext( FltObjects->Instance,
- FltObjects->FileObject,
- &context );
-
-
- if (!NT_SUCCESS( status )) {
-
-
-
-
-
-
-
- return FLT_PREOP_SUCCESS_NO_CALLBACK;
-
-
- }
-
-
-
-
-
-
-
-
- try {
-
-
-
-
-
-
- if (Data->Iopb->Parameters.Write.Length != 0) {
-
-
-
-
-
-
-
- if (Data->Iopb->Parameters.Write.MdlAddress != NULL) {
-
-
- buffer = MmGetSystemAddressForMdlSafe( Data->Iopb->Parameters.Write.MdlAddress,
- NormalPagePriority );
-
-
-
-
-
-
-
- if (buffer == NULL) {
-
-
- Data->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
- Data->IoStatus.Information = 0;
- returnStatus = FLT_PREOP_COMPLETE;
- leave;
- }
-
-
- } else {
-
-
-
-
-
-
- buffer = Data->Iopb->Parameters.Write.WriteBuffer;
- }
-
-
-
-
-
-
-
-
- notification = ExAllocatePoolWithTag( NonPagedPool,
- sizeof( SCANNER_NOTIFICATION ),
- 'nacS' );
- if (notification == NULL) {
-
-
- Data->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
- Data->IoStatus.Information = 0;
- returnStatus = FLT_PREOP_COMPLETE;
- leave;
- }
-
- notification->BytesToScan = min( Data->Iopb->Parameters.Write.Length, SCANNER_READ_BUFFER_SIZE );
-
-
-
-
-
-
- try {
-
-
- RtlCopyMemory( ¬ification->Contents,
- buffer,
- notification->BytesToScan );
-
-
- } except( EXCEPTION_EXECUTE_HANDLER ) {
-
-
-
-
-
-
- Data->IoStatus.Status = GetExceptionCode() ;
- Data->IoStatus.Information = 0;
- returnStatus = FLT_PREOP_COMPLETE;
- leave;
- }
-
-
-
-
-
-
-
-
- replyLength = sizeof( SCANNER_REPLY );
-
-
- status = FltSendMessage( ScannerData.Filter,
- &ScannerData.ClientPort,
- notification,
- sizeof( SCANNER_NOTIFICATION ),
- notification,
- &replyLength,
- NULL );
-
- if (STATUS_SUCCESS == status) {
-
- safe = ((PSCANNER_REPLY) notification)->SafeToOpen;
-
-
- } else {
-
-
-
-
-
-
-
- DbgPrint( "!!! scanner.sys --- couldn't send message to user-mode to scan file, status 0x%X\n", status );
- }
- }
-
- if (!safe) {
-
-
-
-
-
-
-
-
-
-
-
- DbgPrint( "!!! scanner.sys -- foul language detected in write !!!\n" );
-
-
-
- if (!FlagOn( Data->Iopb->IrpFlags, IRP_PAGING_IO )) {
-
-
- DbgPrint( "!!! scanner.sys -- blocking the write !!!\n" );
-
-
- Data->IoStatus.Status = STATUS_ACCESS_DENIED;
- Data->IoStatus.Information = 0;
- returnStatus = FLT_PREOP_COMPLETE;
- }
- }
-
-
- } finally {
-
- if (notification != NULL) {
-
-
- ExFreePoolWithTag( notification, 'nacS' );
- }
-
-
- if (context) {
-
-
- FltReleaseContext( context );
- }
- }
-
-
- return returnStatus;
- }
MF实现的一个封转的处理函数,这个函数可以将文件的内容发给R3,让R3处理并返回一个结果
为什么需要这个函数呢?
如果不是写,我们不能直接缓存区数据,那么我们需要读到这个文件的内容发给R3,这个函数就是这个功能
代码:其中包括了Minifilter读文件操作
其实注意的是申请的大小,我们是不知道这个文件到底有多大的,但我们确定的是这个文件一般比这个卷的大小小,所以我们暂时先申请卷的大小
然后下面读的时候会返回文件的大小,到时候就可以知道有多大了
- NTSTATUS
- ScannerpScanFileInUserMode (
- __in PFLT_INSTANCE Instance,
- __in PFILE_OBJECT FileObject,
- __out PBOOLEAN SafeToOpen
- )
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {
- NTSTATUS status = STATUS_SUCCESS;
- PVOID buffer = NULL;
- ULONG bytesRead;
- PSCANNER_NOTIFICATION notification = NULL;
- FLT_VOLUME_PROPERTIES volumeProps;
- LARGE_INTEGER offset;
- ULONG replyLength, length;
- PFLT_VOLUME volume = NULL;
-
-
- *SafeToOpen = TRUE;
-
-
-
-
-
-
-
- if (ScannerData.ClientPort == NULL) {
-
-
- return STATUS_SUCCESS;
- }
-
-
- try {
-
-
-
-
-
-
-
- status = FltGetVolumeFromInstance( Instance, &volume );
-
-
- if (!NT_SUCCESS( status )) {
-
-
- leave;
- }
-
-
-
-
-
-
-
-
-
- status = FltGetVolumeProperties( volume,
- &volumeProps,
- sizeof( volumeProps ),
- &length );
-
-
-
-
-
-
- if (NT_ERROR( status )) {
-
-
- leave;
- }
-
-
- length = max( SCANNER_READ_BUFFER_SIZE, volumeProps.SectorSize );
-
-
-
-
-
-
-
- buffer = FltAllocatePoolAlignedWithTag( Instance,
- NonPagedPool,
- length,
- 'nacS' );
-
-
- if (NULL == buffer) {
-
-
- status = STATUS_INSUFFICIENT_RESOURCES;
- leave;
- }
-
-
- notification = ExAllocatePoolWithTag( NonPagedPool,
- sizeof( SCANNER_NOTIFICATION ),
- 'nacS' );
-
-
- if(NULL == notification) {
-
-
- status = STATUS_INSUFFICIENT_RESOURCES;
- leave;
- }
-
-
-
-
-
-
-
- offset.QuadPart = bytesRead = 0;
- status = FltReadFile( Instance,
- FileObject,
- &offset,
- length,
- buffer,
- FLTFL_IO_OPERATION_NON_CACHED |
- FLTFL_IO_OPERATION_DO_NOT_UPDATE_BYTE_OFFSET,
- &bytesRead,
- NULL,
- NULL );
-
-
- if (NT_SUCCESS( status ) && (0 != bytesRead)) {
-
-
- notification->BytesToScan = (ULONG) bytesRead;
-
-
-
-
-
-
-
- RtlCopyMemory( ¬ification->Contents,
- buffer,
- min( notification->BytesToScan, SCANNER_READ_BUFFER_SIZE ) );
-
-
- replyLength = sizeof( SCANNER_REPLY );
-
-
- status = FltSendMessage( ScannerData.Filter,
- &ScannerData.ClientPort,
- notification,
- sizeof(SCANNER_NOTIFICATION),
- notification,
- &replyLength,
- NULL );
-
-
- if (STATUS_SUCCESS == status) {
-
-
- *SafeToOpen = ((PSCANNER_REPLY) notification)->SafeToOpen;
-
-
- } else {
-
-
-
-
-
-
-
- DbgPrint( "!!! scanner.sys --- couldn't send message to user-mode to scan file, status 0x%X\n", status );
- }
- }
-
-
- } finally {
-
-
- if (NULL != buffer) {
-
-
- FltFreePoolAlignedWithTag( Instance, buffer, 'nacS' );
- }
-
-
- if (NULL != notification) {
-
-
- ExFreePoolWithTag( notification, 'nacS' );
- }
-
-
- if (NULL != volume) {
-
-
- FltObjectDereference( volume );
- }
- }
-
-
- return status;
- }
思考:传BUFF给R3干什么?
解答:你猜
说一说一种比较特殊的情况
一个文件已写权限打开(创建)了,刚开始我们扫描过它没有问题
问:一个人现在是好人,那么他一辈子都是好人吗?
所以,我们需要会他设置一个上下文,上下文中有一个标志,改标志的作用是告诉Close时在扫描一次
其实这就是介绍上下文使用啦
核心代码:在创建事后回调中(别以为事后回调没有控制权了,它还是可以返回取消前面的操作哦 见最后面的代码)
-
- if (FltObjects->FileObject->WriteAccess) {
-
-
-
-
-
-
-
-
-
- status = FltAllocateContext( ScannerData.Filter,
- FLT_STREAMHANDLE_CONTEXT,
- sizeof(SCANNER_STREAM_HANDLE_CONTEXT),
- PagedPool,
- &scannerContext );
-
-
- if (NT_SUCCESS(status)) {
-
-
-
-
-
-
-
- scannerContext->RescanRequired = TRUE;
-
-
- (VOID) FltSetStreamHandleContext( FltObjects->Instance,
- FltObjects->FileObject,
- FLT_SET_CONTEXT_REPLACE_IF_EXISTS,
- scannerContext,
- NULL );
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- FltReleaseContext( scannerContext );
- }
然后再close的时候我们处理
这里说一说cleanup和closeup有什么区别,前者是使用zwclose或者closehandle 后者使用Obdef (忘记了对象计数减1的那个)
-
-
- FLT_PREOP_CALLBACK_STATUS
- ScannerPreCleanup (
- __inout PFLT_CALLBACK_DATA Data,
- __in PCFLT_RELATED_OBJECTS FltObjects,
- __deref_out_opt PVOID *CompletionContext
- )
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {
- NTSTATUS status;
- PSCANNER_STREAM_HANDLE_CONTEXT context;
- BOOLEAN safe;
-
-
- UNREFERENCED_PARAMETER( Data );
- UNREFERENCED_PARAMETER( CompletionContext );
-
-
- status = FltGetStreamHandleContext( FltObjects->Instance,
- FltObjects->FileObject,
- &context );
-
-
- if (NT_SUCCESS( status )) {
-
-
- if (context->RescanRequired) {
-
-
- (VOID) ScannerpScanFileInUserMode( FltObjects->Instance,
- FltObjects->FileObject,
- &safe );
-
-
- if (!safe) {
-
-
- DbgPrint( "!!! scanner.sys -- foul language detected in precleanup !!!\n" );
- }
- }
-
-
- FltReleaseContext( context );
- }
-
-
-
-
- return FLT_PREOP_SUCCESS_NO_CALLBACK;
- }
例子:这个例子拦截后缀为txt的文件,如果txt中的内容有foul就被认定为病毒
这个例子演示了
通信方式(HIPS)
上下文的使用
文件名的获得
缓冲的使用
共有头文件
scannuk.h
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- #ifndef __SCANUK_H__
- #define __SCANUK_H__
-
-
-
-
-
-
-
- const PWSTR ScannerPortName = L"\\ScannerPort";
-
-
-
-
- #define SCANNER_READ_BUFFER_SIZE 1024
-
-
- typedef struct _SCANNER_NOTIFICATION {
-
-
- ULONG BytesToScan;
- ULONG Reserved;
- UCHAR Contents[SCANNER_READ_BUFFER_SIZE];
-
- } SCANNER_NOTIFICATION, *PSCANNER_NOTIFICATION;
-
-
- typedef struct _SCANNER_REPLY {
-
-
- BOOLEAN SafeToOpen;
-
- } SCANNER_REPLY, *PSCANNER_REPLY;
-
-
- #endif // __SCANUK_H__
内核.h文件
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- #ifndef __SCANNER_H__
- #define __SCANNER_H__
-
-
-
-
-
-
-
-
-
-
-
-
-
- typedef struct _SCANNER_DATA {
-
-
-
-
-
-
-
- PDRIVER_OBJECT DriverObject;
-
-
-
-
-
-
-
-
- PFLT_FILTER Filter;
-
-
-
-
-
-
-
- PFLT_PORT ServerPort;
-
-
-
-
-
-
-
- PEPROCESS UserProcess;
-
-
-
-
-
-
-
- PFLT_PORT ClientPort;
-
-
- } SCANNER_DATA, *PSCANNER_DATA;
-
-
- extern SCANNER_DATA ScannerData;
-
-
- typedef struct _SCANNER_STREAM_HANDLE_CONTEXT {
-
-
- BOOLEAN RescanRequired;
-
- } SCANNER_STREAM_HANDLE_CONTEXT, *PSCANNER_STREAM_HANDLE_CONTEXT;
-
-
- #pragma warning(push)
- #pragma warning(disable:4200) // disable warnings for structures with zero length arrays.
-
-
- typedef struct _SCANNER_CREATE_PARAMS {
-
-
- WCHAR String[0];
-
-
- } SCANNER_CREATE_PARAMS, *PSCANNER_CREATE_PARAMS;
-
-
- #pragma warning(pop)
-
-
-
-
-
-
-
-
-
-
-
-
- DRIVER_INITIALIZE DriverEntry;
- NTSTATUS
- DriverEntry (
- __in PDRIVER_OBJECT DriverObject,
- __in PUNICODE_STRING RegistryPath
- );
-
-
- NTSTATUS
- ScannerUnload (
- __in FLT_FILTER_UNLOAD_FLAGS Flags
- );
-
-
- NTSTATUS
- ScannerQueryTeardown (
- __in PCFLT_RELATED_OBJECTS FltObjects,
- __in FLT_INSTANCE_QUERY_TEARDOWN_FLAGS Flags
- );
-
-
- FLT_PREOP_CALLBACK_STATUS
- ScannerPreCreate (
- __inout PFLT_CALLBACK_DATA Data,
- __in PCFLT_RELATED_OBJECTS FltObjects,
- __deref_out_opt PVOID *CompletionContext
- );
-
-
- FLT_POSTOP_CALLBACK_STATUS
- ScannerPostCreate (
- __inout PFLT_CALLBACK_DATA Data,
- __in PCFLT_RELATED_OBJECTS FltObjects,
- __in_opt PVOID CompletionContext,
- __in FLT_POST_OPERATION_FLAGS Flags
- );
-
-
- FLT_PREOP_CALLBACK_STATUS
- ScannerPreCleanup (
- __inout PFLT_CALLBACK_DATA Data,
- __in PCFLT_RELATED_OBJECTS FltObjects,
- __deref_out_opt PVOID *CompletionContext
- );
-
-
- FLT_PREOP_CALLBACK_STATUS
- ScannerPreWrite (
- __inout PFLT_CALLBACK_DATA Data,
- __in PCFLT_RELATED_OBJECTS FltObjects,
- __deref_out_opt PVOID *CompletionContext
- );
-
-
- NTSTATUS
- ScannerInstanceSetup (
- __in PCFLT_RELATED_OBJECTS FltObjects,
- __in FLT_INSTANCE_SETUP_FLAGS Flags,
- __in DEVICE_TYPE VolumeDeviceType,
- __in FLT_FILESYSTEM_TYPE VolumeFilesystemType
- );
-
-
-
-
- #endif /* __SCANNER_H__ */
内核.c文件
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- #include
- #include
- #include
- #include "scanuk.h"
- #include "scanner.h"
-
-
- #pragma prefast(disable:__WARNING_ENCODE_MEMBER_FUNCTION_POINTER, "Not valid for kernel mode drivers")
-
-
-
-
-
-
-
-
- SCANNER_DATA ScannerData;
-
-
-
-
-
-
-
- const UNICODE_STRING ScannerExtensionsToScan[] =
- { RTL_CONSTANT_STRING( L"doc"),
- RTL_CONSTANT_STRING( L"txt"),
- RTL_CONSTANT_STRING( L"bat"),
- RTL_CONSTANT_STRING( L"cmd"),
- RTL_CONSTANT_STRING( L"inf"),
-
- {0, 0, NULL}
- };
-
-
-
-
-
-
-
-
-
- NTSTATUS
- ScannerPortConnect (
- __in PFLT_PORT ClientPort,
- __in_opt PVOID ServerPortCookie,
- __in_bcount_opt(SizeOfContext) PVOID ConnectionContext,
- __in ULONG SizeOfContext,
- __deref_out_opt PVOID *ConnectionCookie
- );
-
-
- VOID
- ScannerPortDisconnect (
- __in_opt PVOID ConnectionCookie
- );
-
-
- NTSTATUS
- ScannerpScanFileInUserMode (
- __in PFLT_INSTANCE Instance,
- __in PFILE_OBJECT FileObject,
- __out PBOOLEAN SafeToOpen
- );
-
-
- BOOLEAN
- ScannerpCheckExtension (
- __in PUNICODE_STRING Extension
- );
-
-
-
-
-
-
-
- #ifdef ALLOC_PRAGMA
- #pragma alloc_text(INIT, DriverEntry)
- #pragma alloc_text(PAGE, ScannerInstanceSetup)
- #pragma alloc_text(PAGE, ScannerPreCreate)
- #pragma alloc_text(PAGE, ScannerPortConnect)
- #pragma alloc_text(PAGE, ScannerPortDisconnect)
- #endif
-
-
-
-
-
-
-
-
-
-
-
- const FLT_OPERATION_REGISTRATION Callbacks[] = {
-
-
- { IRP_MJ_CREATE,
- 0,
- ScannerPreCreate,
- ScannerPostCreate},
-
-
- { IRP_MJ_CLEANUP,
- 0,
- ScannerPreCleanup,
- NULL},
-
-
- { IRP_MJ_WRITE,
- 0,
- ScannerPreWrite,
- NULL},
-
-
- { IRP_MJ_OPERATION_END}
- };
-
-
-
-
- const FLT_CONTEXT_REGISTRATION ContextRegistration[] = {
-
-
- { FLT_STREAMHANDLE_CONTEXT,
- 0,
- NULL,
- sizeof(SCANNER_STREAM_HANDLE_CONTEXT),
- 'chBS' },
-
-
- { FLT_CONTEXT_END }
- };
-
-
- const FLT_REGISTRATION FilterRegistration = {
-
-
- sizeof( FLT_REGISTRATION ),
- FLT_REGISTRATION_VERSION,
- 0,
- ContextRegistration,
- Callbacks,
- ScannerUnload,
- ScannerInstanceSetup,
- ScannerQueryTeardown,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL
- };
-
-
-
-
-
-
-
-
-
- NTSTATUS
- DriverEntry (
- __in PDRIVER_OBJECT DriverObject,
- __in PUNICODE_STRING RegistryPath
- )
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {
- OBJECT_ATTRIBUTES oa;
- UNICODE_STRING uniString;
- PSECURITY_DESCRIPTOR sd;
- NTSTATUS status;
-
-
- UNREFERENCED_PARAMETER( RegistryPath );
-
-
-
-
-
-
-
- status = FltRegisterFilter( DriverObject,
- &FilterRegistration,
- &ScannerData.Filter );
-
-
-
-
- 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,
- ScannerPortDisconnect,
- NULL,
- 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;
- }
-
-
-
-
- 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();
-
-
- UNREFERENCED_PARAMETER( ServerPortCookie );
- UNREFERENCED_PARAMETER( ConnectionContext );
- UNREFERENCED_PARAMETER( SizeOfContext);
- UNREFERENCED_PARAMETER( ConnectionCookie );
-
-
- ASSERT( ScannerData.ClientPort == NULL );
- ASSERT( ScannerData.UserProcess == NULL );
-
-
-
-
-
-
- ScannerData.UserProcess = PsGetCurrentProcess();
- ScannerData.ClientPort = ClientPort;
-
-
- DbgPrint( "!!! scanner.sys --- connected, port=0x%p\n", ClientPort );
-
-
- return STATUS_SUCCESS;
- }
-
-
-
- VOID
- ScannerPortDisconnect(
- __in_opt PVOID ConnectionCookie
- )
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {
- UNREFERENCED_PARAMETER( ConnectionCookie );
-
-
- PAGED_CODE();
-
-
- DbgPrint( "!!! scanner.sys --- disconnected, port=0x%p\n", ScannerData.ClientPort );
-
-
-
-
-
-
-
-
-
- FltCloseClientPort( ScannerData.Filter, &ScannerData.ClientPort );
-
-
-
-
-
-
-
-
- ScannerData.UserProcess = NULL;
- }
-
-
-
- NTSTATUS
- ScannerUnload (
- __in FLT_FILTER_UNLOAD_FLAGS Flags
- )
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {
- UNREFERENCED_PARAMETER( Flags );
-
-
-
-
-
-
-
- FltCloseCommunicationPort( ScannerData.ServerPort );
-
-
-
-
-
-
-
- FltUnregisterFilter( ScannerData.Filter );
-
-
- return STATUS_SUCCESS;
- }
-
-
- NTSTATUS
- ScannerInstanceSetup (
- __in PCFLT_RELATED_OBJECTS FltObjects,
- __in FLT_INSTANCE_SETUP_FLAGS Flags,
- __in DEVICE_TYPE VolumeDeviceType,
- __in FLT_FILESYSTEM_TYPE VolumeFilesystemType
- )
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-