MiniFilter文件系统学习

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架构


结构

在DriverEntry中只需要注册Fliter和Start
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 
{
	FIL
  • 4
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值