标签:
Minfilter与legacy filter区别//在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 //重命名获得
结构
FltRegisterFilter( DriverObject, &fileMonitorRegistration, &g_pFilter ); FltStartFiltering( g_pFilter );
这是一个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 };
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 } };
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; } }
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; }
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 //创建的权限
PVOID pQueryBuffer = Data->Iopb->Parameters.DirectoryControl.QueryDirectory.DirectoryBuffer; ULONG uQueryBufferSize = Data->Iopb->Parameters.DirectoryControl.QueryDirectory.Length
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);
// 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 //重命名获得
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);
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 } };
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 ); //下面就是判断是否创建成功,成功后就开始开启过滤
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 ); }
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;
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;
//发送消息给R3 timeout.QuadPart = (LONGLONG)40 * -10000000i64; // 内核等待 40 seconds Status = FltSendMessage( g_pFilter, &g_pClientPort,//给R3发消息 &request, sizeof(SCANNER_NOTIFICATION), &reply, &replySize, &timeout );
什么是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;
//处理写关闭 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; }
为什么需要这个函数呢?
如果不是写,我们不能直接缓存区数据,那么我们需要读到这个文件的内容发给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; }
所以,我们需要会他设置一个上下文,上下文中有一个标志,改标志的作用是告诉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 ); }
//处理打开时 有写权限但 打开成功时是安全的,等它关闭的时候的我们来扫描它 //触发这个回调的条件是文件引用技术为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; }
通信方式(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__
/*++ 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__ */
/*++ 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; }
/*++ 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__
/*++ 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; }
这个通信一般是加规则
R3主动通信使用 FilterSendMessage
//主动发请求给R0 FilterSendMessage( Port, request, sizeof(REQUEST), reply ? reply : NULL, reply ? sizeof(REPLY) : 0, &dwRtn);
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; }
*/ #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; }
《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的巨大漏洞
在沙盒中的程序如果使用剪切功能 是对外部文件的真正操作
标签:
原文地址:http://blog.csdn.net/zhuhuibeishadiao/article/details/51229122