MiniFilter文件系统学习
原文地址: https://blog.****.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中获取(即创建成功后才能获取到真实的数据))
- //在postcreate里获得
- 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架构
结构
- FltRegisterFilter( DriverObject,
- &fileMonitorRegistration,
- &g_pFilter );
- FltStartFiltering( g_pFilter );
fileMonitorRegistration是唯一我们需要做的
这是一个FLT_REGISTRATION 结构
- const FLT_REGISTRATION fileMonitorRegistration =
- {
- sizeof( FLT_REGISTRATION ), // Size
- FLT_REGISTRATION_VERSION, // Version
- 0, // Flags
- ContextRegistration, // ContextRegistration //上下文数组
- fileMonitorCallbacks, // Operation callbacks//最重要的
- fileMonUnload, // FilterUnload
- fileMonInstanceSetup, // InstanceSetup
- NULL, // InstanceQueryTeardown
- fileMonInstanceTeardownStart, // InstanceTeardownStart
- NULL, // InstanceTeardownComplete
- NULL, // GenerateFileName
- NULL, // GenerateDestinationFileName
- NULL // NormalizeNameComponent
- };
fileMonitorCallbacks 例子:
可以只需要一个回调如IRP_MJ_CLEANUP
- const FLT_OPERATION_REGISTRATION
- fileMonitorCallbacks[] =
- {
- {
- IRP_MJ_CREATE,
- FLTFL_OPERATION_REGISTRATION_SKIP_PAGING_IO,//这个是可以忽略的IRP
- 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
- //分配的一个context资源
- )
- {
- //sandbox?
- //主防??
- //杀毒引擎??
- //加解密??
- return XXX;
- }
- 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;
- }
上下位数组 例子:
- PFLT_FILTER g_pFilter = NULL;
- const FLT_CONTEXT_REGISTRATION
- ContextRegistration[] =
- {//在释放context之前调用,可以在此释放context里的内存等
- {
- 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子健下的键值项
- 例子:
- //-------------------------------------------------------------------------------------------------------
- // 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;
- }
- // 注册表驱动程序的DefaultInstance 值
- 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);
- //-------------------------------------------------------------------------------------------------------
- // SYSTEM\\CurrentControlSet\\Services\\DriverName\\Instances\\DriverName Instance子健下的键值项
- //-------------------------------------------------------------------------------------------------------
- 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;
- }
- // 注册表驱动程序的Altitude 值
- strcpy(szTempStr,lpszAltitude);
- if(RegSetValueEx(hKey,"Altitude",0,REG_SZ,(CONST BYTE*)szTempStr,(DWORD)strlen(szTempStr))!=ERROR_SUCCESS)
- {
- return FALSE;
- }
- // 注册表驱动程序的Flags 值
- 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
- //分配的一个context资源
- )
- {
- //sandbox?
- //主防??
- //杀毒引擎??
- //加解密??
- return XXX;
- }
- 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;
- }
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();//获取EPROCESS
- HandleToUlong(PsGetProcessId(processObject));//获取PID
- Data->IoStatus.Status = ntStatus;//返回给R3的
- 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;
写同上面
- //在postcreate里获得
- 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里面的例子:
- // look again at the first example string from above:
- //
- // \Device\HarddiskVolume1\Documents and Settings\MyUser\My Documents\Test Results.txt:stream1
- //
- // Extension = "txt"
- // Stream = ":stream1"
- // FinalComponent = "Test Results.txt:stream1"
- // ParentDir = "\Documents and Settings\MyUser\My Documents\"
//重命名的获得:
- 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[] =
- {//在释放context之前调用,可以在此释放context里的内存等
- {
- 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 );
- //
- // We secure the port so only ADMINs & SYSTEM can acecss it.
- //
- //设置通信端口权限 ,只有管理员和系统进程才能操作
- 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的进程ID或EPROCESS以便放过本进程 还有记录R3的通信端口,给后面主动通信的时候用
- ScannerPortDisconnect,//当R3离线时回调 主要是关闭R3端口和设置R3的进程信息为NULL
- NULL,//处理R3主动函数 比如R3下新的规则,
- 1 );//最后一个常为1
- //
- // Free the security descriptor in all cases. It is not needed once
- // the call to FltCreateCommunicationPort() is made.
- //
- //设置好后需要释放权限的设置
- FltFreeSecurityDescriptor( sd );
- //下面就是判断是否创建成功,成功后就开始开启过滤
先说说HIPS的实现,
r3,首先得到R0通信端口的句柄
使用FilterConnectCommunicationPort 只需要注意第一个参数和最后一个参数即可 其它都为0或NULL
然后绑定这个端口(我理解为绑定)
使用CreateIoCompletionPort 只需要注意第一个参数最后一个参数即可,最后一个参数:为这个工作线程设置几个线程
这样有助于高效率 一般小于64 ,也可以设置请求次数 这样回复也会高效率一些(具体看后面的代码)
我们首先使用FiltetGetMessage异步获取一下消息,如果有信息,则下面的GetQueuedCompletionStatus就会恢复(如果没有消息,GetQueuedCompletionStatus就会暂停下来,并让出CPU,等待有信息)
有了信息后我们就可以进行扫描 过滤,搞定之后就调用FilterReplyMessage返回给内核
然后继续调用FilterGetMessage-等待--处理--返回给内核
代码:
- FilterConnectCommunicationPort( ScannerPortName,//与R0的名字一致
- 0,
- NULL,
- 0,
- NULL,
- &port );//R0端口
- //处理从R0来的请求,即R0调用FltSendMessage的请求
- 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
实例代码:
- //发送消息给R3
- timeout.QuadPart = (LONGLONG)40 * -10000000i64; // 内核等待 40 seconds
- Status = FltSendMessage( g_pFilter,
- &g_pClientPort,//给R3发消息
- &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
- )
- /*++
- Routine Description:
- Pre write callback. We want to scan what's being written now.
- Arguments:
- Data - The structure which describes the operation parameters.
- FltObject - The structure which describes the objects affected by this
- operation.
- CompletionContext - Output parameter which can be used to pass a context
- from this pre-write callback to the post-write callback.
- Return Value:
- Always FLT_PREOP_SUCCESS_NO_CALLBACK.
- --*/
- {
- 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 not client port just ignore this write.
- //
- //如果R3进程退出了
- if (ScannerData.ClientPort == NULL) {
- return FLT_PREOP_SUCCESS_NO_CALLBACK;
- }
- //获取上下文
- status = FltGetStreamHandleContext( FltObjects->Instance,
- FltObjects->FileObject,
- &context );
- //不是我们要处理的文件,(可以在创建事前回调中设置上下文,比如判断这个文件是不是记事本,如果是,我们就给它设置一个上下文,然后到后我们就可以知道这个文件是不是我们设置过的记事本)
- //这也可以判断,不过一般不这么使用,一般是在上下文中插入自己想要信息,然后到这里我们到这个上下文中去取
- if (!NT_SUCCESS( status )) {
- //
- // We are not interested in this file
- //
- return FLT_PREOP_SUCCESS_NO_CALLBACK;
- }
- //
- // Use try-finally to cleanup
- //
- //必须使用异常处理结构
- try {
- //
- // Pass the contents of the buffer to user mode.
- //
- //如果写的长度为0 就放行
- if (Data->Iopb->Parameters.Write.Length != 0) {
- //
- // Get the users buffer address. If there is a MDL defined, use
- // it. If not use the given buffer address.
- //
- //开始获取数据缓存区 有2个缓存区,需要判断在数据在哪个buff中 判断的方法前面说过了
- if (Data->Iopb->Parameters.Write.MdlAddress != NULL) {
- buffer = MmGetSystemAddressForMdlSafe( Data->Iopb->Parameters.Write.MdlAddress,
- NormalPagePriority );
- //
- // If we have a MDL but could not get and address, we ran out
- // of memory, report the correct error
- //
- //如果获取失败了 就返回资源不足 并不下发了
- if (buffer == NULL) {
- Data->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
- Data->IoStatus.Information = 0;
- returnStatus = FLT_PREOP_COMPLETE;
- leave;
- }
- } else {
- //
- // Use the users buffer
- //
- //不是MDL就是USERbuff
- buffer = Data->Iopb->Parameters.Write.WriteBuffer;
- }
- //
- // In a production-level filter, we would actually let user mode scan the file directly.
- // Allocating & freeing huge amounts of non-paged pool like this is not very good for system perf.
- // This is just a sample!
- //
- //为发送给R3数据申请内存
- 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;
- }
- //取最小啦 这里设置SCANNER_READ_BUFFER_SIZE为1024
- notification->BytesToScan = min( Data->Iopb->Parameters.Write.Length, SCANNER_READ_BUFFER_SIZE );
- //
- // The buffer can be a raw user buffer. Protect access to it
- //
- //内存操作 必须使用结构化异常处理
- try {
- RtlCopyMemory( ¬ification->Contents,
- buffer,
- notification->BytesToScan );
- } except( EXCEPTION_EXECUTE_HANDLER ) {
- //
- // Error accessing buffer. Complete i/o with failure
- //
- //出错就返回错误代码并阻止下发啦
- Data->IoStatus.Status = GetExceptionCode() ;
- Data->IoStatus.Information = 0;
- returnStatus = FLT_PREOP_COMPLETE;
- leave;
- }
- //
- // Send message to user mode to indicate it should scan the buffer.
- // We don't have to synchronize between the send and close of the handle
- // as FltSendMessage takes care of that.
- //
- //发送数据给R3 处理
- replyLength = sizeof( SCANNER_REPLY );
- status = FltSendMessage( ScannerData.Filter,
- &ScannerData.ClientPort,
- notification,
- sizeof( SCANNER_NOTIFICATION ),
- notification,
- &replyLength,
- NULL );//永远等待,一般40秒吧
- //为什么共用一块内存呢 你猜
- if (STATUS_SUCCESS == status) {
- //用户返回的处理结果
- safe = ((PSCANNER_REPLY) notification)->SafeToOpen;
- } else {
- //
- // Couldn't send message. This sample will let the i/o through.
- //
- DbgPrint( "!!! scanner.sys --- couldn't send message to user-mode to scan file, status 0x%X\n", status );
- }
- }
- //这个是不安全的 你猜怎么办?
- if (!safe) {
- //
- // Block this write if not paging i/o (as a result of course, this scanner will not prevent memory mapped writes of contaminated
- // strings to the file, but only regular writes). The effect of getting ERROR_ACCESS_DENIED for many apps to delete the file they
- // are trying to write usually.
- // To handle memory mapped writes - we should be scanning at close time (which is when we can really establish that the file object
- // is not going to be used for any more writes)
- //
- DbgPrint( "!!! scanner.sys -- foul language detected in write !!!\n" );
- //如果不是Flt管理发送的,这个IRP很特殊 ,必须放行,如果不是这个IRP就阻止了,因为它是不安全的
- 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
- )
- /*++
- Routine Description:
- This routine is called to send a request up to user mode to scan a given
- file and tell our caller whether it's safe to open this file.
- Note that if the scan fails, we set SafeToOpen to TRUE. The scan may fail
- because the service hasn't started, or perhaps because this create/cleanup
- is for a directory, and there's no data to read & scan.
- If we failed creates when the service isn't running, there'd be a
- bootstrapping problem -- how would we ever load the .exe for the service?
- Arguments:
- Instance - Handle to the filter instance for the scanner on this volume.
- FileObject - File to be scanned.
- SafeToOpen - Set to FALSE if the file is scanned successfully and it contains
- foul language.
- Return Value:
- The status of the operation, hopefully STATUS_SUCCESS. The common failure
- status will probably be STATUS_INSUFFICIENT_RESOURCES.
- --*/
- {
- 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 not client port just return.
- //
- if (ScannerData.ClientPort == NULL) {
- return STATUS_SUCCESS;
- }
- try {
- //
- // Obtain the volume object .
- //
- status = FltGetVolumeFromInstance( Instance, &volume );
- if (!NT_SUCCESS( status )) {
- leave;
- }
- //
- // Determine sector size. Noncached I/O can only be done at sector size offsets, and in lengths which are
- // multiples of sector size. A more efficient way is to make this call once and remember the sector size in the
- // instance setup routine and setup an instance context where we can cache it.
- //
- status = FltGetVolumeProperties( volume,
- &volumeProps,
- sizeof( volumeProps ),
- &length );
- //
- // STATUS_BUFFER_OVERFLOW can be returned - however we only need the properties, not the names
- // hence we only check for error status.
- //
- if (NT_ERROR( status )) {
- leave;
- }
- length = max( SCANNER_READ_BUFFER_SIZE, volumeProps.SectorSize );
- //
- // Use non-buffered i/o, so allocate aligned pool
- //
- 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;
- }
- //
- // Read the beginning of the file and pass the contents to user mode.
- //
- 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;
- //
- // Copy only as much as the buffer can hold
- //
- 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 {
- //
- // Couldn't send message
- //
- 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) {
- //
- //
- // The create has requested write access, mark to rescan the file.
- // Allocate the context.
- //
- status = FltAllocateContext( ScannerData.Filter,
- FLT_STREAMHANDLE_CONTEXT,
- sizeof(SCANNER_STREAM_HANDLE_CONTEXT),
- PagedPool,
- &scannerContext );
- if (NT_SUCCESS(status)) {
- //
- // Set the handle context.
- //
- scannerContext->RescanRequired = TRUE;
- (VOID) FltSetStreamHandleContext( FltObjects->Instance,
- FltObjects->FileObject,
- FLT_SET_CONTEXT_REPLACE_IF_EXISTS,
- scannerContext,
- NULL );
- //
- // Normally we would check the results of FltSetStreamHandleContext
- // for a variety of error cases. However, The only error status
- // that could be returned, in this case, would tell us that
- // contexts are not supported. Even if we got this error,
- // we just want to release the context now and that will free
- // this memory if it was not successfully set.
- //
- //
- // Release our reference on the context (the set adds a reference)
- //
- FltReleaseContext( scannerContext );
- }
然后再close的时候我们处理
这里说一说cleanup和closeup有什么区别,前者是使用zwclose或者closehandle 后者使用Obdef (忘记了对象计数减1的那个)
- //处理打开时 有写权限但 打开成功时是安全的,等它关闭的时候的我们来扫描它
- //触发这个回调的条件是文件引用技术为0,这个包括内核+R3的计数,一般是在上层使用了ZwClose或者CloseHandle时调用
- FLT_PREOP_CALLBACK_STATUS
- ScannerPreCleanup (
- __inout PFLT_CALLBACK_DATA Data,
- __in PCFLT_RELATED_OBJECTS FltObjects,
- __deref_out_opt PVOID *CompletionContext
- )
- /*++
- Routine Description:
- Pre cleanup callback. If this file was opened for write access, we want
- to rescan it now.
- Arguments:
- Data - The structure which describes the operation parameters.
- FltObject - The structure which describes the objects affected by this
- operation.
- CompletionContext - Output parameter which can be used to pass a context
- from this pre-cleanup callback to the post-cleanup callback.
- Return Value:
- Always FLT_PREOP_SUCCESS_NO_CALLBACK.
- --*/
- {
- 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
- /*++
- Copyright (c) 1999-2002 Microsoft Corporation
- Module Name:
- scanuk.h
- Abstract:
- Header file which contains the structures, type definitions,
- constants, global variables and function prototypes that are
- shared between kernel and user mode.
- Environment:
- Kernel & user mode
- --*/
- #ifndef __SCANUK_H__
- #define __SCANUK_H__
- //
- // Name of port used to communicate
- //
- const PWSTR ScannerPortName = L"\\ScannerPort";
- #define SCANNER_READ_BUFFER_SIZE 1024
- typedef struct _SCANNER_NOTIFICATION {
- ULONG BytesToScan;
- ULONG Reserved; // for quad-word alignement of the Contents structure
- UCHAR Contents[SCANNER_READ_BUFFER_SIZE];
- } SCANNER_NOTIFICATION, *PSCANNER_NOTIFICATION;
- typedef struct _SCANNER_REPLY {
- BOOLEAN SafeToOpen;
- } SCANNER_REPLY, *PSCANNER_REPLY;
- #endif // __SCANUK_H__
内核.h文件
- /*++
- Copyright (c) 1999-2002 Microsoft Corporation
- Module Name:
- scrubber.h
- Abstract:
- Header file which contains the structures, type definitions,
- constants, global variables and function prototypes that are
- only visible within the kernel.
- Environment:
- Kernel mode
- --*/
- #ifndef __SCANNER_H__
- #define __SCANNER_H__
- ///////////////////////////////////////////////////////////////////////////
- //
- // Global variables
- //
- ///////////////////////////////////////////////////////////////////////////
- typedef struct _SCANNER_DATA {
- //
- // The object that identifies this driver.
- //
- PDRIVER_OBJECT DriverObject;
- //
- // The filter handle that results from a call to
- // FltRegisterFilter.
- //
- PFLT_FILTER Filter;
- //
- // Listens for incoming connections
- //
- PFLT_PORT ServerPort;
- //
- // User process that connected to the port
- //
- PEPROCESS UserProcess;
- //
- // Client port for a connection to user-mode
- //
- 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)
- ///////////////////////////////////////////////////////////////////////////
- //
- // Prototypes for the startup and unload routines used for
- // this Filter.
- //
- // Implementation in scanner.c
- //
- ///////////////////////////////////////////////////////////////////////////
- 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文件
- /*++
- Copyright (c) 1999-2002 Microsoft Corporation
- Module Name:
- scanner.c
- Abstract:
- This is the main module of the scanner filter.
- This filter scans the data in a file before allowing an open to proceed. This is similar
- to what virus checkers do.
- Environment:
- Kernel mode
- --*/
- #include <fltKernel.h>
- #include <dontuse.h>
- #include <suppress.h>
- #include "scanuk.h"
- #include "scanner.h"
- #pragma prefast(disable:__WARNING_ENCODE_MEMBER_FUNCTION_POINTER, "Not valid for kernel mode drivers")
- //
- // Structure that contains all the global data structures
- // used throughout the scanner.
- //
- SCANNER_DATA ScannerData;
- //
- // This is a static list of file name extensions files we are interested in scanning
- //
- 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"),
- /*RTL_CONSTANT_STRING( L"ini"), Removed, to much usage*/
- {0, 0, NULL}
- };
- //
- // Function prototypes
- //
- 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
- );
- //
- // Assign text sections for each routine.
- //
- #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
- //
- // Constant FLT_REGISTRATION structure for our filter. This
- // initializes the callback routines our filter wants to register
- // for. This is only used to register with the filter manager
- //
- 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 ), // Size
- FLT_REGISTRATION_VERSION, // Version
- 0, // Flags
- ContextRegistration, // Context Registration.
- Callbacks, // Operation callbacks
- ScannerUnload, // FilterUnload
- ScannerInstanceSetup, // InstanceSetup
- ScannerQueryTeardown, // InstanceQueryTeardown
- NULL, // InstanceTeardownStart
- NULL, // InstanceTeardownComplete
- NULL, // GenerateFileName
- NULL, // GenerateDestinationFileName
- NULL // NormalizeNameComponent
- };
- ////////////////////////////////////////////////////////////////////////////
- //
- // Filter initialization and unload routines.
- //
- ////////////////////////////////////////////////////////////////////////////
- NTSTATUS
- DriverEntry (
- __in PDRIVER_OBJECT DriverObject,
- __in PUNICODE_STRING RegistryPath
- )
- /*++
- Routine Description:
- This is the initialization routine for the Filter driver. This
- registers the Filter with the filter manager and initializes all
- its global data structures.
- Arguments:
- DriverObject - Pointer to driver object created by the system to
- represent this driver.
- RegistryPath - Unicode string identifying where the parameters for this
- driver are located in the registry.
- Return Value:
- Returns STATUS_SUCCESS.
- --*/
- {
- OBJECT_ATTRIBUTES oa;
- UNICODE_STRING uniString;
- PSECURITY_DESCRIPTOR sd;
- NTSTATUS status;
- UNREFERENCED_PARAMETER( RegistryPath );
- //
- // Register with filter manager.
- //
- status = FltRegisterFilter( DriverObject,
- &FilterRegistration,
- &ScannerData.Filter );
- if (!NT_SUCCESS( status )) {
- return status;
- }
- //
- // Create a communication port.
- //
- RtlInitUnicodeString( &uniString, ScannerPortName );
- //
- // We secure the port so only ADMINs & SYSTEM can acecss it.
- //
- //设置通信端口权限 ,只有管理员和系统进程才能操作
- 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的进程ID或EPROCESS以便放过本进程 还有记录R3的通信端口,给后面主动通信的时候用
- ScannerPortDisconnect,//当R3离线时回调 主要是关闭R3端口和设置R3的进程信息为NULL
- NULL,//处理R3主动函数 比如R3下新的规则,
- 1 );//最后一个常为1
- //
- // Free the security descriptor in all cases. It is not needed once
- // the call to FltCreateCommunicationPort() is made.
- //
- //设置好后需要释放权限的设置
- FltFreeSecurityDescriptor( sd );
- if (NT_SUCCESS( status )) {
- //
- // Start filtering I/O.
- //
- //开始过滤
- 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
- )
- /*++
- Routine Description
- This is called when user-mode connects to the server port - to establish a
- connection
- Arguments
- ClientPort - This is the client connection port that will be used to
- send messages from the filter
- ServerPortCookie - The context associated with this port when the
- minifilter created this port.
- ConnectionContext - Context from entity connecting to this port (most likely
- your user mode service)
- SizeofContext - Size of ConnectionContext in bytes
- ConnectionCookie - Context to be passed to the port disconnect routine.
- Return Value
- STATUS_SUCCESS - to accept the connection
- --*/
- {
- PAGED_CODE();
- UNREFERENCED_PARAMETER( ServerPortCookie );
- UNREFERENCED_PARAMETER( ConnectionContext );
- UNREFERENCED_PARAMETER( SizeOfContext);
- UNREFERENCED_PARAMETER( ConnectionCookie );
- ASSERT( ScannerData.ClientPort == NULL );
- ASSERT( ScannerData.UserProcess == NULL );
- //
- // Set the user process and port.
- //
- //设置本身进程 和 R3的的通信端口 给后面判断和通信时使用
- ScannerData.UserProcess = PsGetCurrentProcess();
- ScannerData.ClientPort = ClientPort;
- DbgPrint( "!!! scanner.sys --- connected, port=0x%p\n", ClientPort );
- return STATUS_SUCCESS;
- }
- VOID
- ScannerPortDisconnect(
- __in_opt PVOID ConnectionCookie
- )
- /*++
- Routine Description
- This is called when the connection is torn-down. We use it to close our
- handle to the connection
- Arguments
- ConnectionCookie - Context from the port connect routine
- Return value
- None
- --*/
- {
- UNREFERENCED_PARAMETER( ConnectionCookie );
- PAGED_CODE();
- DbgPrint( "!!! scanner.sys --- disconnected, port=0x%p\n", ScannerData.ClientPort );
- //
- // Close our handle to the connection: note, since we limited max connections to 1,
- // another connect will not be allowed until we return from the disconnect routine.
- //
- //关闭R3通信端口
- FltCloseClientPort( ScannerData.Filter, &ScannerData.ClientPort );
- //
- // Reset the user-process field.
- //
- //设置R3进程为0
- ScannerData.UserProcess = NULL;
- }
- NTSTATUS
- ScannerUnload (
- __in FLT_FILTER_UNLOAD_FLAGS Flags
- )
- /*++
- Routine Description:
- This is the unload routine for the Filter driver. This unregisters the
- Filter with the filter manager and frees any allocated global data
- structures.
- Arguments:
- None.
- Return Value:
- Returns the final status of the deallocation routines.
- --*/
- {
- UNREFERENCED_PARAMETER( Flags );
- //
- // Close the server port.
- //
- FltCloseCommunicationPort( ScannerData.ServerPort );
- //
- // Unregister the filter
- //
- 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
- )
- /*++
- Routine Description:
- This routine is called by the filter manager when a new instance is created.
- We specified in the registry that we only want for manual attachments,
- so that is all we should receive here.
- Arguments:
- FltObjects - Describes the instance and volume which we are being asked to
- setup.
- Flags - Flags describing the type of attachment this is.
- VolumeDeviceType - The DEVICE_TYPE for the volume to which this instance
- will attach.
- VolumeFileSystemType - The file system formatted on this volume.
- Return Value:
- FLT_NOTIFY_STATUS_ATTACH - we wish to attach to the volume
- FLT_NOTIFY_STATUS_DO_NOT_ATTACH - no, thank you
- --*/
- {
- UNREFERENCED_PARAMETER( FltObjects );
- UNREFERENCED_PARAMETER( Flags );
- UNREFERENCED_PARAMETER( VolumeFilesystemType );
- PAGED_CODE();
- ASSERT( FltObjects->Filter == ScannerData.Filter );
- //
- // Don't attach to network volumes.
- //
- if (VolumeDeviceType == FILE_DEVICE_NETWORK_FILE_SYSTEM) {
- return STATUS_FLT_DO_NOT_ATTACH;
- }
- return STATUS_SUCCESS;
- }
- NTSTATUS
- ScannerQueryTeardown (
- __in PCFLT_RELATED_OBJECTS FltObjects,
- __in FLT_INSTANCE_QUERY_TEARDOWN_FLAGS Flags
- )
- /*++
- Routine Description:
- This is the instance detach routine for the filter. This
- routine is called by filter manager when a user initiates a manual instance
- detach. This is a 'query' routine: if the filter does not want to support
- manual detach, it can return a failure status
- Arguments:
- FltObjects - Describes the instance and volume for which we are receiving
- this query teardown request.
- Flags - Unused
- Return Value:
- STATUS_SUCCESS - we allow instance detach to happen
- --*/
- {
- UNREFERENCED_PARAMETER( FltObjects );
- UNREFERENCED_PARAMETER( Flags );
- return STATUS_SUCCESS;
- }
- FLT_PREOP_CALLBACK_STATUS
- ScannerPreCreate (
- __inout PFLT_CALLBACK_DATA Data,
- __in PCFLT_RELATED_OBJECTS FltObjects,
- __deref_out_opt PVOID *CompletionContext
- )
- /*++
- Routine Description:
- Pre create callback. We need to remember whether this file has been
- opened for write access. If it has, we'll want to rescan it in cleanup.
- This scheme results in extra scans in at least two cases:
- -- if the create fails (perhaps for access denied)
- -- the file is opened for write access but never actually written to
- The assumption is that writes are more common than creates, and checking
- or setting the context in the write path would be less efficient than
- taking a good guess before the create.
- Arguments:
- Data - The structure which describes the operation parameters.
- FltObject - The structure which describes the objects affected by this
- operation.
- CompletionContext - Output parameter which can be used to pass a context
- from this pre-create callback to the post-create callback.
- Return Value:
- FLT_PREOP_SUCCESS_WITH_CALLBACK - If this is not our user-mode process.
- FLT_PREOP_SUCCESS_NO_CALLBACK - All other threads.
- --*/
- {
- UNREFERENCED_PARAMETER( FltObjects );
- UNREFERENCED_PARAMETER( CompletionContext );
- PAGED_CODE();
- //
- // See if this create is being done by our user process.
- //
- if (IoThreadToProcess( Data->Thread ) == ScannerData.UserProcess) {
- DbgPrint( "!!! scanner.sys -- allowing create for trusted process \n" );
- return FLT_PREOP_SUCCESS_NO_CALLBACK;
- }
- return FLT_PREOP_SUCCESS_WITH_CALLBACK;
- }
- BOOLEAN
- ScannerpCheckExtension (
- __in PUNICODE_STRING Extension
- )
- /*++
- Routine Description:
- Checks if this file name extension is something we are interested in
- Arguments
- Extension - Pointer to the file name extension
- Return Value
- TRUE - Yes we are interested
- FALSE - No
- --*/
- {
- const UNICODE_STRING *ext;
- if (Extension->Length == 0) {
- return FALSE;
- }
- //
- // Check if it matches any one of our static extension list
- //
- ext = ScannerExtensionsToScan;
- while (ext->Buffer != NULL) {
- if (RtlCompareUnicodeString( Extension, ext, TRUE ) == 0) {
- //
- // A match. We are interested in this file
- //
- return TRUE;
- }
- ext++;
- }
- return FALSE;
- }
- //处理打开,创建时(一般这个时候提示的话就是已经被感染了)
- 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
- )
- /*++
- Routine Description:
- Post create callback. We can't scan the file until after the create has
- gone to the filesystem, since otherwise the filesystem wouldn't be ready
- to read the file for us.
- Arguments:
- Data - The structure which describes the operation parameters.
- FltObject - The structure which describes the objects affected by this
- operation.
- CompletionContext - The operation context passed fron the pre-create
- callback.
- Flags - Flags to say why we are getting this post-operation callback.
- Return Value:
- FLT_POSTOP_FINISHED_PROCESSING - ok to open the file or we wish to deny
- access to this file, hence undo the open
- --*/
- {
- PSCANNER_STREAM_HANDLE_CONTEXT scannerContext;
- FLT_POSTOP_CALLBACK_STATUS returnStatus = FLT_POSTOP_FINISHED_PROCESSING;
- PFLT_FILE_NAME_INFORMATION nameInfo;
- NTSTATUS status;
- BOOLEAN safeToOpen, scanFile;
- UNREFERENCED_PARAMETER( CompletionContext );
- UNREFERENCED_PARAMETER( Flags );
- //
- // If this create was failing anyway, don't bother scanning now.
- //
- if (!NT_SUCCESS( Data->IoStatus.Status ) ||
- (STATUS_REPARSE == Data->IoStatus.Status)) {
- return FLT_POSTOP_FINISHED_PROCESSING;
- }
- //
- // Check if we are interested in this file.
- //
- status = FltGetFileNameInformation( Data,
- FLT_FILE_NAME_NORMALIZED |
- FLT_FILE_NAME_QUERY_DEFAULT,
- &nameInfo );
- if (!NT_SUCCESS( status )) {
- return FLT_POSTOP_FINISHED_PROCESSING;
- }
- FltParseFileNameInformation( nameInfo );
- //
- // Check if the extension matches the list of extensions we are interested in
- //
- scanFile = ScannerpCheckExtension( &nameInfo->Extension );
- //
- // Release file name info, we're done with it
- //
- FltReleaseFileNameInformation( nameInfo );
- if (!scanFile) {
- //
- // Not an extension we are interested in
- //
- return FLT_POSTOP_FINISHED_PROCESSING;
- }
- (VOID) ScannerpScanFileInUserMode( FltObjects->Instance,
- FltObjects->FileObject,
- &safeToOpen );
- if (!safeToOpen) {
- //
- // Ask the filter manager to undo the create.
- //
- DbgPrint( "!!! scanner.sys -- foul language detected in postcreate !!!\n" );
- DbgPrint( "!!! scanner.sys -- undoing create \n" );
- FltCancelFileOpen( FltObjects->Instance, FltObjects->FileObject );
- Data->IoStatus.Status = STATUS_ACCESS_DENIED;
- Data->IoStatus.Information = 0;
- returnStatus = FLT_POSTOP_FINISHED_PROCESSING;
- } else if (FltObjects->FileObject->WriteAccess) {
- //
- //
- // The create has requested write access, mark to rescan the file.
- // Allocate the context.
- //
- status = FltAllocateContext( ScannerData.Filter,
- FLT_STREAMHANDLE_CONTEXT,
- sizeof(SCANNER_STREAM_HANDLE_CONTEXT),
- PagedPool,
- &scannerContext );
- if (NT_SUCCESS(status)) {
- //
- // Set the handle context.
- //
- scannerContext->RescanRequired = TRUE;
- (VOID) FltSetStreamHandleContext( FltObjects->Instance,
- FltObjects->FileObject,
- FLT_SET_CONTEXT_REPLACE_IF_EXISTS,
- scannerContext,
- NULL );
- //
- // Normally we would check the results of FltSetStreamHandleContext
- // for a variety of error cases. However, The only error status
- // that could be returned, in this case, would tell us that
- // contexts are not supported. Even if we got this error,
- // we just want to release the context now and that will free
- // this memory if it was not successfully set.
- //
- //
- // Release our reference on the context (the set adds a reference)
- //
- FltReleaseContext( scannerContext );
- }
- }
- return returnStatus;
- }
- //处理打开时 有写权限但 打开成功时是安全的,等它关闭的时候的我们来扫描它
- //触发这个回调的条件是文件引用技术为0,这个包括内核+R3的计数,一般是在上层使用了ZwClose或者CloseHandle时调用
- FLT_PREOP_CALLBACK_STATUS
- ScannerPreCleanup (
- __inout PFLT_CALLBACK_DATA Data,
- __in PCFLT_RELATED_OBJECTS FltObjects,
- __deref_out_opt PVOID *CompletionContext
- )
- /*++
- Routine Description:
- Pre cleanup callback. If this file was opened for write access, we want
- to rescan it now.
- Arguments:
- Data - The structure which describes the operation parameters.
- FltObject - The structure which describes the objects affected by this
- operation.
- CompletionContext - Output parameter which can be used to pass a context
- from this pre-cleanup callback to the post-cleanup callback.
- Return Value:
- Always FLT_PREOP_SUCCESS_NO_CALLBACK.
- --*/
- {
- 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;
- }
- //处理写关闭
- FLT_PREOP_CALLBACK_STATUS
- ScannerPreWrite (
- __inout PFLT_CALLBACK_DATA Data,
- __in PCFLT_RELATED_OBJECTS FltObjects,
- __deref_out_opt PVOID *CompletionContext
- )
- /*++
- Routine Description:
- Pre write callback. We want to scan what's being written now.
- Arguments:
- Data - The structure which describes the operation parameters.
- FltObject - The structure which describes the objects affected by this
- operation.
- CompletionContext - Output parameter which can be used to pass a context
- from this pre-write callback to the post-write callback.
- Return Value:
- Always FLT_PREOP_SUCCESS_NO_CALLBACK.
- --*/
- {
- 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 not client port just ignore this write.
- //
- //如果R3进程退出了
- if (ScannerData.ClientPort == NULL) {
- return FLT_PREOP_SUCCESS_NO_CALLBACK;
- }
- //获取上下文
- status = FltGetStreamHandleContext( FltObjects->Instance,
- FltObjects->FileObject,
- &context );
- //不是我们要处理的文件,(可以在创建事前回调中设置上下文,比如判断这个文件是不是记事本,如果是,我们就给它设置一个上下文,然后到后我们就可以知道这个文件是不是我们设置过的记事本)
- //这也可以判断,不过一般不这么使用,一般是在上下文中插入自己想要信息,然后到这里我们到这个上下文中去取
- if (!NT_SUCCESS( status )) {
- //
- // We are not interested in this file
- //
- return FLT_PREOP_SUCCESS_NO_CALLBACK;
- }
- //
- // Use try-finally to cleanup
- //
- //必须使用异常处理结构
- try {
- //
- // Pass the contents of the buffer to user mode.
- //
- //如果写的长度为0 就放行
- if (Data->Iopb->Parameters.Write.Length != 0) {
- //
- // Get the users buffer address. If there is a MDL defined, use
- // it. If not use the given buffer address.
- //
- //开始获取数据缓存区 有2个缓存区,需要判断在数据在哪个buff中 判断的方法前面说过了
- if (Data->Iopb->Parameters.Write.MdlAddress != NULL) {
- buffer = MmGetSystemAddressForMdlSafe( Data->Iopb->Parameters.Write.MdlAddress,
- NormalPagePriority );
- //
- // If we have a MDL but could not get and address, we ran out
- // of memory, report the correct error
- //
- //如果获取失败了 就返回资源不足 并不下发了
- if (buffer == NULL) {
- Data->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
- Data->IoStatus.Information = 0;
- returnStatus = FLT_PREOP_COMPLETE;
- leave;
- }
- } else {
- //
- // Use the users buffer
- //
- //不是MDL就是USERbuff
- buffer = Data->Iopb->Parameters.Write.WriteBuffer;
- }
- //
- // In a production-level filter, we would actually let user mode scan the file directly.
- // Allocating & freeing huge amounts of non-paged pool like this is not very good for system perf.
- // This is just a sample!
- //
- //为发送给R3数据申请内存
- 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;
- }
- //取最小啦 这里设置SCANNER_READ_BUFFER_SIZE为1024
- notification->BytesToScan = min( Data->Iopb->Parameters.Write.Length, SCANNER_READ_BUFFER_SIZE );
- //
- // The buffer can be a raw user buffer. Protect access to it
- //
- //内存操作 必须使用结构化异常处理
- try {
- RtlCopyMemory( ¬ification->Contents,
- buffer,
- notification->BytesToScan );
- } except( EXCEPTION_EXECUTE_HANDLER ) {
- //
- // Error accessing buffer. Complete i/o with failure
- //
- //出错就返回错误代码并阻止下发啦
- Data->IoStatus.Status = GetExceptionCode() ;
- Data->IoStatus.Information = 0;
- returnStatus = FLT_PREOP_COMPLETE;
- leave;
- }
- //
- // Send message to user mode to indicate it should scan the buffer.
- // We don't have to synchronize between the send and close of the handle
- // as FltSendMessage takes care of that.
- //
- //发送数据给R3 处理
- replyLength = sizeof( SCANNER_REPLY );
- status = FltSendMessage( ScannerData.Filter,
- &ScannerData.ClientPort,
- notification,
- sizeof( SCANNER_NOTIFICATION ),
- notification,
- &replyLength,
- NULL );//永远等待,一般40秒吧
- //为什么共用一块内存呢 你猜
- if (STATUS_SUCCESS == status) {
- //用户返回的处理结果
- safe = ((PSCANNER_REPLY) notification)->SafeToOpen;
- } else {
- //
- // Couldn't send message. This sample will let the i/o through.
- //
- DbgPrint( "!!! scanner.sys --- couldn't send message to user-mode to scan file, status 0x%X\n", status );
- }
- }
- //这个是不安全的 你猜怎么办?
- if (!safe) {
- //
- // Block this write if not paging i/o (as a result of course, this scanner will not prevent memory mapped writes of contaminated
- // strings to the file, but only regular writes). The effect of getting ERROR_ACCESS_DENIED for many apps to delete the file they
- // are trying to write usually.
- // To handle memory mapped writes - we should be scanning at close time (which is when we can really establish that the file object
- // is not going to be used for any more writes)
- //
- DbgPrint( "!!! scanner.sys -- foul language detected in write !!!\n" );
- //如果不是Flt管理发送的,这个IRP很特殊 ,必须放行,如果不是这个IRP就阻止了,因为它是不安全的
- 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;
- }
- //////////////////////////////////////////////////////////////////////////
- // Local support routines.
- //
- /////////////////////////////////////////////////////////////////////////
- NTSTATUS
- ScannerpScanFileInUserMode (
- __in PFLT_INSTANCE Instance,
- __in PFILE_OBJECT FileObject,
- __out PBOOLEAN SafeToOpen
- )
- /*++
- Routine Description:
- This routine is called to send a request up to user mode to scan a given
- file and tell our caller whether it's safe to open this file.
- Note that if the scan fails, we set SafeToOpen to TRUE. The scan may fail
- because the service hasn't started, or perhaps because this create/cleanup
- is for a directory, and there's no data to read & scan.
- If we failed creates when the service isn't running, there'd be a
- bootstrapping problem -- how would we ever load the .exe for the service?
- Arguments:
- Instance - Handle to the filter instance for the scanner on this volume.
- FileObject - File to be scanned.
- SafeToOpen - Set to FALSE if the file is scanned successfully and it contains
- foul language.
- Return Value:
- The status of the operation, hopefully STATUS_SUCCESS. The common failure
- status will probably be STATUS_INSUFFICIENT_RESOURCES.
- --*/
- {
- 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 not client port just return.
- //
- if (ScannerData.ClientPort == NULL) {
- return STATUS_SUCCESS;
- }
- try {
- //
- // Obtain the volume object .
- //
- status = FltGetVolumeFromInstance( Instance, &volume );
- if (!NT_SUCCESS( status )) {
- leave;
- }
- //
- // Determine sector size. Noncached I/O can only be done at sector size offsets, and in lengths which are
- // multiples of sector size. A more efficient way is to make this call once and remember the sector size in the
- // instance setup routine and setup an instance context where we can cache it.
- //
- status = FltGetVolumeProperties( volume,
- &volumeProps,
- sizeof( volumeProps ),
- &length );
- //
- // STATUS_BUFFER_OVERFLOW can be returned - however we only need the properties, not the names
- // hence we only check for error status.
- //
- if (NT_ERROR( status )) {
- leave;
- }
- length = max( SCANNER_READ_BUFFER_SIZE, volumeProps.SectorSize );
- //
- // Use non-buffered i/o, so allocate aligned pool
- //
- 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;
- }
- //
- // Read the beginning of the file and pass the contents to user mode.
- //
- 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;
- //
- // Copy only as much as the buffer can hold
- //
- 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 {
- //
- // Couldn't send message
- //
- 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;
- }
r3.h文件
- /*++
- Copyright (c) 1999-2002 Microsoft Corporation
- Module Name:
- scanuser.h
- Abstract:
- Header file which contains the structures, type definitions,
- constants, global variables and function prototypes for the
- user mode part of the scanner.
- Environment:
- Kernel & user mode
- --*/
- #ifndef __SCANUSER_H__
- #define __SCANUSER_H__
- #pragma pack(1)
- typedef struct _SCANNER_MESSAGE {
- //
- // Required structure header.
- //
- FILTER_MESSAGE_HEADER MessageHeader;
- //
- // Private scanner-specific fields begin here.
- //
- SCANNER_NOTIFICATION Notification;
- //
- // Overlapped structure: this is not really part of the message
- // However we embed it instead of using a separately allocated overlap structure
- //
- OVERLAPPED Ovlp;
- } SCANNER_MESSAGE, *PSCANNER_MESSAGE;
- typedef struct _SCANNER_REPLY_MESSAGE {
- //
- // Required structure header.
- //
- FILTER_REPLY_HEADER ReplyHeader;
- //
- // Private scanner-specific fields begin here.
- //
- SCANNER_REPLY Reply;
- } SCANNER_REPLY_MESSAGE, *PSCANNER_REPLY_MESSAGE;
- #endif // __SCANUSER_H__
r3.c文件
- /*++
- Copyright (c) 1999-2002 Microsoft Corporation
- Module Name:
- scanUser.c
- Abstract:
- This file contains the implementation for the main function of the
- user application piece of scanner. This function is responsible for
- actually scanning file contents.
- Environment:
- User mode
- --*/
- #include <windows.h>
- #include <stdlib.h>
- #include <stdio.h>
- #include <winioctl.h>
- #include <string.h>
- #include <crtdbg.h>
- #include <assert.h>
- #include <fltuser.h>
- #include "../MiniFliter_Scaner/scanuk.h"
- #include "scanuser.h"
- #include <dontuse.h>
- #pragma comment(lib,"fltlib.lib")
- #define SCANNER_DEFAULT_REQUEST_COUNT 5
- #define SCANNER_DEFAULT_THREAD_COUNT 2
- #define SCANNER_MAX_THREAD_COUNT 64
- UCHAR FoulString[] = "foul";
- //
- // Context passed to worker threads
- //
- typedef struct _SCANNER_THREAD_CONTEXT {
- HANDLE Port;
- HANDLE Completion;
- } SCANNER_THREAD_CONTEXT, *PSCANNER_THREAD_CONTEXT;
- VOID
- Usage (
- VOID
- )
- /*++
- Routine Description
- Prints usage
- Arguments
- None
- Return Value
- None
- --*/
- {
- printf( "Connects to the scanner filter and scans buffers \n" );
- printf( "Usage: scanuser [requests per thread] [number of threads(1-64)]\n" );
- }
- BOOL
- ScanBuffer (
- __in_bcount(BufferSize) PUCHAR Buffer,
- __in ULONG BufferSize
- )
- /*++
- Routine Description
- Scans the supplied buffer for an instance of FoulString.
- Note: Pattern matching algorithm used here is just for illustration purposes,
- there are many better algorithms available for real world filters
- Arguments
- Buffer - Pointer to buffer
- BufferSize - Size of passed in buffer
- Return Value
- TRUE - Found an occurrence of the appropriate FoulString
- FALSE - Buffer is ok
- --*/
- {
- PUCHAR p;
- ULONG searchStringLength = sizeof(FoulString) - sizeof(UCHAR);
- for (p = Buffer;
- p <= (Buffer + BufferSize - searchStringLength);
- p++) {
- if (RtlEqualMemory( p, FoulString, searchStringLength )) {
- printf( "Found a string\n" );
- //
- // Once we find our search string, we're not interested in seeing
- // whether it appears again.
- //
- return TRUE;
- }
- }
- return FALSE;
- }
- DWORD
- ScannerWorker(
- __in PSCANNER_THREAD_CONTEXT Context
- )
- /*++
- Routine Description
- This is a worker thread that
- Arguments
- Context - This thread context has a pointer to the port handle we use to send/receive messages,
- and a completion port handle that was already associated with the comm. port by the caller
- Return Value
- HRESULT indicating the status of thread exit.
- --*/
- {
- PSCANNER_NOTIFICATION notification;
- SCANNER_REPLY_MESSAGE replyMessage;
- PSCANNER_MESSAGE message;
- LPOVERLAPPED pOvlp;
- BOOL result;
- DWORD outSize;
- HRESULT hr;
- ULONG_PTR key;
- #pragma warning(push)
- #pragma warning(disable:4127) // conditional expression is constant
- while (TRUE) {
- #pragma warning(pop)
- //
- // Poll for messages from the filter component to scan.
- //
- result = GetQueuedCompletionStatus( Context->Completion, &outSize, &key, &pOvlp, INFINITE );
- //
- // Obtain the message: note that the message we sent down via FltGetMessage() may NOT be
- // the one dequeued off the completion queue: this is solely because there are multiple
- // threads per single port handle. Any of the FilterGetMessage() issued messages can be
- // completed in random order - and we will just dequeue a random one.
- //
- message = CONTAINING_RECORD( pOvlp, SCANNER_MESSAGE, Ovlp );
- if (!result) {
- //
- // An error occured.
- //
- hr = HRESULT_FROM_WIN32( GetLastError() );
- break;
- }
- printf( "Received message, size %d\n", pOvlp->InternalHigh );
- notification = &message->Notification;
- assert(notification->BytesToScan <= SCANNER_READ_BUFFER_SIZE);
- __analysis_assume(notification->BytesToScan <= SCANNER_READ_BUFFER_SIZE);
- result = ScanBuffer( notification->Contents, notification->BytesToScan );
- replyMessage.ReplyHeader.Status = 0;
- replyMessage.ReplyHeader.MessageId = message->MessageHeader.MessageId;
- //
- // Need to invert the boolean -- result is true if found
- // foul language, in which case SafeToOpen should be set to false.
- //
- replyMessage.Reply.SafeToOpen = !result;
- printf( "Replying message, SafeToOpen: %d\n", replyMessage.Reply.SafeToOpen );
- hr = FilterReplyMessage( Context->Port,
- (PFILTER_REPLY_HEADER) &replyMessage,
- sizeof( replyMessage ) );
- if (SUCCEEDED( hr )) {
- printf( "Replied message\n" );
- } else {
- printf( "Scanner: Error replying message. Error = 0x%X\n", hr );
- break;
- }
- memset( &message->Ovlp, 0, sizeof( OVERLAPPED ) );
- hr = FilterGetMessage( Context->Port,
- &message->MessageHeader,
- FIELD_OFFSET( SCANNER_MESSAGE, Ovlp ),
- &message->Ovlp );
- if (hr != HRESULT_FROM_WIN32( ERROR_IO_PENDING )) {
- break;
- }
- }
- if (!SUCCEEDED( hr )) {
- if (hr == HRESULT_FROM_WIN32( ERROR_INVALID_HANDLE )) {
- //
- // Scanner port disconncted.
- //
- printf( "Scanner: Port is disconnected, probably due to scanner filter unloading.\n" );
- } else {
- printf( "Scanner: Unknown error occured. Error = 0x%X\n", hr );
- }
- }
- free( message );
- return hr;
- }
- int _cdecl
- main (
- __in int argc,
- __in_ecount(argc) char *argv[]
- )
- {
- DWORD requestCount = SCANNER_DEFAULT_REQUEST_COUNT;
- DWORD threadCount = SCANNER_DEFAULT_THREAD_COUNT;
- HANDLE threads[SCANNER_MAX_THREAD_COUNT];
- SCANNER_THREAD_CONTEXT context;
- HANDLE port, completion;
- PSCANNER_MESSAGE msg;
- DWORD threadId;
- HRESULT hr;
- DWORD i, j;
- //
- // Check how many threads and per thread requests are desired.
- //
- if (argc > 1) {
- requestCount = atoi( argv[1] );
- if (requestCount <= 0) {
- Usage();
- return 1;
- }
- if (argc > 2) {
- threadCount = atoi( argv[2] );
- }
- if (threadCount <= 0 || threadCount > 64) {
- Usage();
- return 1;
- }
- }
- //
- // Open a commuication channel to the filter
- //
- printf( "Scanner: Connecting to the filter ...\n" );
- hr = FilterConnectCommunicationPort( ScannerPortName,
- 0,
- NULL,
- 0,
- NULL,
- &port );
- if (IS_ERROR( hr )) {
- printf( "ERROR: Connecting to filter port: 0x%08x\n", hr );
- return 2;
- }
- //
- // Create a completion port to associate with this handle.
- //
- completion = CreateIoCompletionPort( port,
- NULL,
- 0,
- threadCount );
- if (completion == NULL) {
- printf( "ERROR: Creating completion port: %d\n", GetLastError() );
- CloseHandle( port );
- return 3;
- }
- printf( "Scanner: Port = 0x%p Completion = 0x%p\n", port, completion );
- context.Port = port;
- context.Completion = completion;
- //
- // Create specified number of threads.
- //
- for (i = 0; i < threadCount; i++) {
- threads[i] = CreateThread( NULL,
- 0,
- (LPTHREAD_START_ROUTINE)ScannerWorker,
- &context,
- 0,
- &threadId );
- if (threads[i] == NULL) {
- //
- // Couldn't create thread.
- //
- hr = GetLastError();
- printf( "ERROR: Couldn't create thread: %d\n", hr );
- goto main_cleanup;
- }
- for (j = 0; j < requestCount; j++) {
- //
- // Allocate the message.
- //
- #pragma prefast(suppress:__WARNING_MEMORY_LEAK, "msg will not be leaked because it is freed in ScannerWorker")
- msg = malloc( sizeof( SCANNER_MESSAGE ) );
- if (msg == NULL) {
- hr = ERROR_NOT_ENOUGH_MEMORY;
- goto main_cleanup;
- }
- memset( &msg->Ovlp, 0, sizeof( OVERLAPPED ) );
- //
- // Request messages from the filter driver.
- //
- hr = FilterGetMessage( port,
- &msg->MessageHeader,
- FIELD_OFFSET( SCANNER_MESSAGE, Ovlp ),
- &msg->Ovlp );
- if (hr != HRESULT_FROM_WIN32( ERROR_IO_PENDING )) {
- free( msg );
- goto main_cleanup;
- }
- }
- }
- hr = S_OK;
- WaitForMultipleObjectsEx( i, threads, TRUE, INFINITE, FALSE );
- main_cleanup:
- printf( "Scanner: All done. Result = 0x%08x\n", hr );
- CloseHandle( port );
- CloseHandle( completion );
- return hr;
- }
编译平台:VS2012 + WDK8.1
测试平台:Winxp Win7 x86 Win7x64
上面是R0主动通信 下面说说R3主动通信
这个通信一般是加规则
R3主动通信使用 FilterSendMessage
- //主动发请求给R0
- FilterSendMessage(
- Port,
- request,
- sizeof(REQUEST),
- reply ? reply : NULL,
- reply ? sizeof(REPLY) : 0,
- &dwRtn);
R0处理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));
- //GET InputBuffer
- //Do something
- ProbeForWrite(OutputBuffer, OutputBufferLength, sizeof(ULONG));
- //Copy Result to Outputbuffer
- }
- __except(EXCEPTION_EXECUTE_HANDLER)
- {
- return STATUS_NOT_IMPLEMENTED;
- }
- return STATUS_SUCCESS;
- }
例子:来自http://bbs.pediy.com/showthread.php?t=186931
标 题: 【原创】minifilter通讯之简单示例之一
作 者: correy
时 间: 2014-04-24,14:10:34
链 接: http://bbs.pediy.com/showthread.php?t=186931
made by correy
made at 2013.11.13
email:kouleguan at hotmail dot com
homepage:http://correy.webs.com
效果如下(包括驱动打印的消息):
用户发来的信息是:test
[1124] FilterSendMessage ok!
[1124] 从内核发来的信息是:
[1124] to user client
[1124]
- */
- #include <windows.h>
- //这两个文件在VS中没有,在WDK中有.
- //如果要用VS编译要拷贝相应的文件到相应的目录或者改变目录的设置等.
- #include <fltuser.h>
- #pragma comment(lib, "fltLib.lib")
- int _tmain(int argc, _TCHAR* argv[])
- {
- MessageBox(0,L"请附加调试器",L"调试专用",0);//如果是远程调试的话,这行特别有用.
- HANDLE port = INVALID_HANDLE_VALUE;
- HRESULT hResult = S_OK;
- hResult = FilterConnectCommunicationPort( L"\\CommunicationPort", 0, NULL, 0, NULL, &port );
- if (IS_ERROR( hResult )) {
- OutputDebugString(L"FilterConnectCommunicationPort fail!\n");
- return hResult;
- }
- wchar_t InBuffer[] = L"test";
- wchar_t OutBuffer[MAX_PATH] = {0};
- DWORD bytesReturned = 0;
- hResult = FilterSendMessage(port, InBuffer, lstrlen(InBuffer), OutBuffer, sizeof(OutBuffer), &bytesReturned);
- if (IS_ERROR( hResult )) {
- OutputDebugString(L"FilterSendMessage fail!\n");
- CloseHandle( port );
- return hResult;
- } else {
- OutputDebugString(L"FilterSendMessage ok!\n");
- }
- OutputDebugString(L"从内核发来的信息是:");
- OutputDebugString(OutBuffer);
- OutputDebugString(L"\n");
- CloseHandle( port );
- return 0;
- }
驱动的代码如下:
- /*
- 内核中没有:FltGetMessage,FltReplyMessage函数.
- 个人认为:MessageNotifyCallback有FltGetMessage,FltReplyMessage,FltSendMessage这三个函数的功能.
- 所以在MessageNotifyCallback里面调用这些函数是不对的,得到一些意想不到的效果.建议不要这样做.
- FltGetMessage除了在MessageNotifyCallback里面,大多的地方都可以调用,但是用户层最好开启线程处理函数.
- FltGetMessage函数调用示例代码如下:
- //{
- // wchar_t SenderBuffer[] = L"SenderBuffer";
- // wchar_t ReplyBuffer[] = L"ReplyBuffer";
- // ULONG replyLength = sizeof(ReplyBuffer);
- //
- // status = FltSendMessage( gFilterHandle, &g_ClientPort, SenderBuffer, sizeof(SenderBuffer), ReplyBuffer, &replyLength, NULL);
- // if (STATUS_SUCCESS == status) {
- // DbgPrint( "send message to user-mode\n");
- // } else {
- // DbgPrint( "couldn't send message to user-mode to scan file, status 0x%X\n", status );
- // }
- //}
- */
- #include <fltKernel.h>
- PFLT_FILTER gFilterHandle;
- PFLT_PORT g_ServerPort;
- PFLT_PORT g_ClientPort;
- NTSTATUS MessageNotifyCallback (
- IN PVOID PortCookie,
- IN PVOID InputBuffer OPTIONAL,
- IN ULONG InputBufferLength,
- OUT PVOID OutputBuffer OPTIONAL,
- IN ULONG OutputBufferLength,//用户可以接受的数据的最大长度.
- OUT PULONG ReturnOutputBufferLength)
- /*
- 这里要注意:1.数据地址的对齐.
- 2.文档建议使用:try/except处理.
- 3.如果是64位的驱动要考虑32位的EXE发来的请求.
- */
- {
- NTSTATUS status = 0;
- wchar_t buffer[] = L"to user client";//
- PAGED_CODE();
- UNREFERENCED_PARAMETER(PortCookie);
- //打印用户发来的信息
- KdPrint(("用户发来的信息是:%ls\n",InputBuffer));
- //返回用户一些信息.
- *ReturnOutputBufferLength = sizeof(buffer);
- RtlCopyMemory(OutputBuffer,buffer,* ReturnOutputBufferLength);
- /*
- minispy在这里用FilterSendMessage获取信息的,对就是FilterSendMessage.
- 这里某个类型里面获取信息,这些信息是在各种操作时(IRP的MJ_)加入链表的.
- 注意链表的操作一定要同步,支持多线程.
- 然后用户的一个线程在不停的获取这些信息.
- */
- return status;
- }
- VOID DisconnectNotifyCallback (_In_opt_ PVOID ConnectionCookie)
- {
- PAGED_CODE();
- UNREFERENCED_PARAMETER(ConnectionCookie);
- FltCloseClientPort(gFilterHandle, &g_ClientPort);//应该加判断,如果ConnectionCookie == 我们的值就执行这行.
- }
- NTSTATUS ConnectNotifyCallback (IN PFLT_PORT ClientPort, IN PVOID ServerPortCookie, IN PVOID ConnectionContext, IN ULONG SizeOfContext, OUT PVOID * ConnectionPortCookie)
- {
- PAGED_CODE();
- UNREFERENCED_PARAMETER( ServerPortCookie );
- UNREFERENCED_PARAMETER( ConnectionContext );
- UNREFERENCED_PARAMETER( SizeOfContext);
- UNREFERENCED_PARAMETER( ConnectionPortCookie);
- //可以加以判断,禁止非法的连接,从而给予保护.
- g_ClientPort = ClientPort;//保存以供以后使用.
- return STATUS_SUCCESS;
- }
- #pragma PAGEDCODE
- NTSTATUS PtInstanceQueryTeardown (__in PCFLT_RELATED_OBJECTS FltObjects,__in FLT_INSTANCE_QUERY_TEARDOWN_FLAGS Flags)
- {
- return STATUS_SUCCESS;
- }
- #pragma PAGEDCODE//#pragma alloc_text(PAGE, PtUnload)
- NTSTATUS PtUnload (__in FLT_FILTER_UNLOAD_FLAGS Flags)
- {
- FltCloseCommunicationPort(g_ServerPort);//没有这一行是停止不了驱动的,查询也是永远等待中.
- FltUnregisterFilter( gFilterHandle );
- return STATUS_SUCCESS;
- }
- CONST FLT_REGISTRATION FilterRegistration = {
- sizeof( FLT_REGISTRATION ), // Size
- FLT_REGISTRATION_VERSION, // Version
- 0, // Flags
- NULL, // Context
- NULL, // Operation callbacks
- PtUnload, // MiniFilterUnload
- NULL, // InstanceSetup
- PtInstanceQueryTeardown, // InstanceQueryTeardown
- NULL, // InstanceTeardownStart
- NULL, // InstanceTeardownComplete
- NULL, // GenerateFileName
- NULL, // GenerateDestinationFileName
- NULL // NormalizeNameComponent
- };
- DRIVER_INITIALIZE DriverEntry;
- #pragma alloc_text(INIT, DriverEntry)//#pragma INITCODE
- NTSTATUS DriverEntry (_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath)
- {
- NTSTATUS status;
- PSECURITY_DESCRIPTOR sd;
- OBJECT_ATTRIBUTES oa;
- UNICODE_STRING uniString;
- UNREFERENCED_PARAMETER(RegistryPath);
- KdBreakPoint();
- __try
- {
- status = FltRegisterFilter(DriverObject, &FilterRegistration, &gFilterHandle);
- if (!NT_SUCCESS(status)) //;
- {
- __leave;
- }
- status = FltBuildDefaultSecurityDescriptor(&sd, FLT_PORT_ALL_ACCESS);
- if (!NT_SUCCESS( status )) {
- __leave;
- }
- RtlInitUnicodeString(&uniString, L"\\CommunicationPort");
- InitializeObjectAttributes( &oa, &uniString, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL, sd);
- status = FltCreateCommunicationPort(gFilterHandle, &g_ServerPort, &oa, NULL, ConnectNotifyCallback, DisconnectNotifyCallback, MessageNotifyCallback, 1);
- FltFreeSecurityDescriptor( sd );
- if (!NT_SUCCESS( status )) {
- __leave;
- }
- status = FltStartFiltering(gFilterHandle);//这个结果在下面判断.
- } __finally {
- if (!NT_SUCCESS( status ) )
- {
- if (NULL != g_ServerPort) {
- FltCloseCommunicationPort(g_ServerPort);
- }
- if (NULL != gFilterHandle) {
- FltUnregisterFilter(gFilterHandle);
- }
- }
- }
- return status;
- }
上面的部分代码最好放到线程中去指向,原因你猜
一些参考文章:
http://www.cnblogs.com/huangyong9527/archive/2012/09/07/2674720.html minifilter 与用户态的通信
http://bbs.pediy.com/showthread.php?t=186931 //minifilter通讯之简单示例之一
http://bbs.pediy.com/showthread.php?t=186932 //minifilter通讯之二:使用FilterGetMessage获取内核拦截的目录的创建
http://blog.****.net/caimouse/article/details/1855142 //Windows API一日一练(60)CreateIoCompletionPort和GetQueuedCompletionStatus函数
《Windows内核安全和驱动开发》Minifliter 例子:简单的MiniFilter框架实现了记事本文件无法使用的代码
- #include "fltKernel.h"
- #include "ntddk.h"
- #pragma prefast(disable:__WARNING_ENCODE_MEMBER_FUNCTION_POINTER, "Not valid for kernel mode drivers")
- NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject,PUNICODE_STRING pReg);
- NTSTATUS NPUnload(FLT_FILTER_UNLOAD_FLAGS Flags);
- FLT_PREOP_CALLBACK_STATUS NPPreCreate(
- __inout PFLT_CALLBACK_DATA Data,
- __in PCFLT_RELATED_OBJECTS FltObjects,
- __deref_out_opt PVOID *CompletionContext);
- FLT_POSTOP_CALLBACK_STATUS NPPostCreate(
- __inout PFLT_CALLBACK_DATA Data,
- __in PCFLT_RELATED_OBJECTS FltObjects,
- __in_opt PVOID CompletionContext,
- __in FLT_POST_OPERATION_FLAGS Flags);
- #ifdef ALLOC_PRAGMA
- #pragma alloc_text(INIT,DriverEntry)
- #pragma alloc_text(PAGE,NPUnload)
- #pragma alloc_text(PAGE,NPPreCreate)
- #endif
- PFLT_FILTER gFilterHandle;
- const FLT_OPERATION_REGISTRATION Callbacks[] =
- {
- {
- IRP_MJ_CREATE,
- 0,
- NPPreCreate,
- NPPostCreate
- },
- {
- IRP_MJ_OPERATION_END
- }
- };
- const FLT_REGISTRATION FltRegistration =
- {
- sizeof(FLT_REGISTRATION),
- FLT_REGISTRATION_VERSION,
- 0,
- NULL,
- Callbacks,
- NPUnload,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL
- };
- NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject,PUNICODE_STRING pReg)
- {
- NTSTATUS st = STATUS_SUCCESS;
- st = FltRegisterFilter(pDriverObject,&FltRegistration,&gFilterHandle);
- if(NT_SUCCESS(st))
- {
- st = FltStartFiltering(gFilterHandle);
- if(!NT_SUCCESS(st))
- {
- FltUnregisterFilter(gFilterHandle);
- }
- }
- DbgPrint("[MiniFilter Entry]\n");
- return st;
- }
- NTSTATUS NPUnload(FLT_FILTER_UNLOAD_FLAGS Flags)
- {
- UNREFERENCED_PARAMETER(Flags);
- PAGED_CODE();
- DbgPrint("[MiniFilter Unload]\n");
- FltUnregisterFilter(gFilterHandle);
- return STATUS_SUCCESS;
- }
- FLT_PREOP_CALLBACK_STATUS NPPreCreate(
- __inout PFLT_CALLBACK_DATA Data,
- __in PCFLT_RELATED_OBJECTS FltObjects,
- __deref_out_opt PVOID *CompletionContext)
- {
- UNREFERENCED_PARAMETER(FltObjects);
- UNREFERENCED_PARAMETER(CompletionContext);
- PAGED_CODE();
- {
- UCHAR MajorFunction = 0;
- PFLT_FILE_NAME_INFORMATION nameInfo;
- MajorFunction = Data->Iopb->MajorFunction;
- if(IRP_MJ_CREATE == MajorFunction &&
- NT_SUCCESS(FltGetFileNameInformation(Data,FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT,&nameInfo)))
- {
- if(NT_SUCCESS(FltParseFileNameInformation(nameInfo)))
- {//查找notepad.exe字符串,并阻止
- if(NULL!=wcsstr(nameInfo->Name.Buffer,L"notepad.exe"))
- {
- Data->IoStatus.Status = STATUS_ACCESS_DENIED;
- Data->IoStatus.Information = 0;
- FltReleaseFileNameInformation(nameInfo);
- return FLT_PREOP_COMPLETE;
- }
- }
- FltReleaseFileNameInformation(nameInfo);
- }
- //DbgPrint("ENTER CREATE_CALLBACK\n");
- }
- return FLT_PREOP_SUCCESS_WITH_CALLBACK;
- }
- FLT_POSTOP_CALLBACK_STATUS NPPostCreate(
- __inout PFLT_CALLBACK_DATA Data,
- __in PCFLT_RELATED_OBJECTS FltObjects,
- __in_opt PVOID CompletionContext,
- __in FLT_POST_OPERATION_FLAGS Flags)
- {
- return FLT_POSTOP_FINISHED_PROCESSING;
- }
其他可以看TA的教程
至于沙盒
要注意的是
In and out
重定向
删除的处理
路径的处理
再次说说sandbox的巨大漏洞
在沙盒中的程序如果使用剪切功能 是对外部文件的真正操作
在index下有一些帮助文档
wdk samples index ->Windows Driver Kit samples
以及
Windows Drvier Kit -> Device and Driver Technologies->Installable File System Drivers->File System Minifilter Drivers
其它的也可以到这里找哦