在前面的SwapBuffer驱动进阶(一)中我们添加了一个Create的回调,那么如何让后面的PreRead识别出相同的文件,采用的方法是添加文件流的上下文。
首先我们设计一个数据结构,也就是文件流的上下文,主要是需要保存什么文件信息:
//文件的上下文信息
typedef struct _FILE_STREAM_CONTEXT
{
UCHAR ucIsEncryptFile; ----这个文件是不是加密文件,只要在一开始识别一次就可以了,文件流生命周期是文件打开后,只要文件没有关闭,那么这个文件流就一直存在,读写操作的时候,这个文件流的上下文都可以看到。
UINT32 uiFileSize;
UNICODE_STRING usFileName; ----在read的Pre和Post等中,是很可能看不到真正的文件名的,只有在Create的时候才会出现C;\1.txt这样的,所以必须在这里就记录下来,然后文件流存在的期间,就可以通过这个上下文查看自己的真实的文件名,用于策略控制
UNICODE_STRING usVolumeName; ----所在的卷的名称,这个也是上面的文件名,是不带卷路径的,和这个一起拼接完整路径
UCHAR ucIsNeedFilter; ----在Create的时候,判断是否需要过滤之类的标记,一般的策略都在Create的时候就确定了。
PERESOURCE pResource; ----资源锁
} FILE_STREAM_CONTEXT, *PFILE_STREAM_CONTEXT;
1. 需要先在CreateFile中,先尝试获取下当前的文件流上下文,是否已经有存在了:
(注意: 这里一定要在PostCreate中,不能在PreCreate中,因为在PreCreate()中调用FltGetStreamContext()会报告NOT_SUPPORT错误,而在PostCreate()中调用FltGetStreamContext报告Not_Found,这个才是正常的)
还有一个前提条件,每次Create以后,对处理的文件,都要CcFlushCache刷缓存,这样才能到文件里读,而不是缓存中读取,这个方案其实就是刷新缓存的方案。
https://msdn.microsoft.com/en-us/library/windows/hardware/ff543144(v=vs.85).aspx
NTSTATUS FltGetStreamContext(
_In_ PFLT_INSTANCE Instance,
_In_ PFILE_OBJECT FileObject,
_Out_ PFLT_CONTEXT *Context
);
STATUS_NOT_FOUND ---返回该值,说明还没有创建文件流上下文,那么就要创建:
status = FltAllocateContext(pfltGlobalFilterHandle, ---这个驱动创建时候的过滤句柄,全局的
FLT_STREAM_CONTEXT,
FILE_STREAM_CONTEXT_SIZE,
NonPagedPool,
(PFLT_CONTEXT *)&pscFileStreamContext); -----获取的上下文内存
(FltReleaseContext(pscFileStreamContext);
)
继续初始化:
RtlZeroMemory(pscFileStreamContext, FILE_STREAM_CONTEXT_SIZE); ---初始化
给里面的资源锁,分配一个内存
pscFileStreamContext->prResource = (PERESOURCE)ExAllocatePoolWithTag(
NonPagedPool,
sizeof(ERESOURCE),
MEM_TAG_FILE_TABLE);
// 初始化 prResource
ExInitializeResourceLite(pscFileStreamContext->prResource);
//设置上下文
status = FltSetStreamContext(
pfiInstance,
pfoFileObject,
FLT_SET_CONTEXT_KEEP_IF_EXISTS,
pscFileStreamContext,
(PFLT_CONTEXT *)&pscOldStreamContext); ---看看是不是有老的,
//如果有老的,要释放内存
if (pscOldStreamContext != NULL) {
FltReleaseContext(pscOldStreamContext);
}
//通知同步一下
FILE_STREAM_CONTEXT_LOCK_ON(pscFileStreamContext);
这样就表示设置成功了。
pscFileStreamContext ----这个用户就可以直接进行操作了
问题:
期间遇到过一个错误: 就是在调用 FltAllocateContext()分配内存的时候,出现了STATUS_FLT_CONTEXT_ALLOCATION_NOT_FOUND
这个错误是之前需要注册一个上下文的清理函数,代码如下:
#define MEM_CALLBACK_TAG 'calb'
#define MEM_TAG_FILE_TABLE
'cftm'
VOID
FsfilterMinifilter_CleanupContextNotify(
__in PFLT_CONTEXT pcContext,
__in FLT_CONTEXT_TYPE pctContextType
)
{
PVOLUME_CONTEXT pvcVolumeContext = NULL;
PFILE_STREAM_CONTEXT pscFileStreamContext = NULL;
PAGED_CO
DE();
Flt_FsRtlEnterFileSystem();
switch (pctContextType) {
case
FLT_VOLUME_CONTEXT:
{
pvcVolumeContext = (PVOLUME_CONTEXT)pcContext;
if (pvcVolumeContext->Name.Buffer != NULL) {
ExFreePool(pvcVolumeContext->Name.Buffer);
pvcVolumeContext->Name.Buffer = NULL;
}
}
break;
case
FLT_STREAM_CONTEXT:
{
pscFileStreamContext = (PFILE_STREAM_CONTEXT)pcContext;
//
// 释放上下文
//
FctFreeCustFileStreamContext(pscFileStreamContext);
}
break;
}
Flt_FsRtlExitFileSystem();
}
CONST FLT_CONTEXT_REGISTRATION
ContextNotifications[] = {
{
FLT_VOLUME_CONTEXT, 0, FsfilterMinifilter_CleanupContextNotify, sizeof(VOLUME_CONTEXT), MEM_CALLBACK_TAG },
{
FLT_STREAM_CONTEXT, 0, FsfilterMinifilter_CleanupContextNotify, FILE_STREAM_CONTEXT_SIZE, MEM_TAG_FILE_TABLE },
{
FLT_CONTEXT_END }
};
//文件的上下文信息
typedef struct _FILE_STREAM_CONTEXT
{
UCHAR ucIsEncryptFile; ----这个文件是不是加密文件,只要在一开始识别一次就可以了,文件流生命周期是文件打开后,只要文件没有关闭,那么这个文件流就一直存在,读写操作的时候,这个文件流的上下文都可以看到。
UINT32 uiFileSize;
UNICODE_STRING usFileName; ----在read的Pre和Post等中,是很可能看不到真正的文件名的,只有在Create的时候才会出现C;\1.txt这样的,所以必须在这里就记录下来,然后文件流存在的期间,就可以通过这个上下文查看自己的真实的文件名,用于策略控制
UNICODE_STRING usVolumeName; ----所在的卷的名称,这个也是上面的文件名,是不带卷路径的,和这个一起拼接完整路径
UCHAR ucIsNeedFilter; ----在Create的时候,判断是否需要过滤之类的标记,一般的策略都在Create的时候就确定了。
PERESOURCE pResource; ----资源锁
} FILE_STREAM_CONTEXT, *PFILE_STREAM_CONTEXT;
NTSTATUS FltGetStreamContext( _In_ PFLT_INSTANCE Instance, _In_ PFILE_OBJECT FileObject, _Out_ PFLT_CONTEXT *Context );
status = FltAllocateContext(pfltGlobalFilterHandle, ---这个驱动创建时候的过滤句柄,全局的
FLT_STREAM_CONTEXT,
FILE_STREAM_CONTEXT_SIZE,
NonPagedPool,
(PFLT_CONTEXT *)&pscFileStreamContext); -----获取的上下文内存
(FltReleaseContext(pscFileStreamContext);
)
继续初始化:
RtlZeroMemory(pscFileStreamContext, FILE_STREAM_CONTEXT_SIZE); ---初始化
pscFileStreamContext->prResource = (PERESOURCE)ExAllocatePoolWithTag(
NonPagedPool,
sizeof(ERESOURCE),
MEM_TAG_FILE_TABLE);
// 初始化 prResource
ExInitializeResourceLite(pscFileStreamContext->prResource);
status = FltSetStreamContext(
pfiInstance,
pfoFileObject,
FLT_SET_CONTEXT_KEEP_IF_EXISTS,
pscFileStreamContext,
(PFLT_CONTEXT *)&pscOldStreamContext); ---看看是不是有老的,
if (pscOldStreamContext != NULL) {
FltReleaseContext(pscOldStreamContext);
}
这个错误是之前需要注册一个上下文的清理函数,代码如下:
#define MEM_CALLBACK_TAG 'calb'
#define MEM_TAG_FILE_TABLE
'cftm'
VOID
FsfilterMinifilter_CleanupContextNotify(
__in PFLT_CONTEXT pcContext,
__in FLT_CONTEXT_TYPE pctContextType
)
{
PVOLUME_CONTEXT pvcVolumeContext = NULL;
PFILE_STREAM_CONTEXT pscFileStreamContext = NULL;
PAGED_CO
DE();
Flt_FsRtlEnterFileSystem();
switch (pctContextType) {
case
FLT_VOLUME_CONTEXT:
{
pvcVolumeContext = (PVOLUME_CONTEXT)pcContext;
if (pvcVolumeContext->Name.Buffer != NULL) {
ExFreePool(pvcVolumeContext->Name.Buffer);
pvcVolumeContext->Name.Buffer = NULL;
}
}
break;
case
FLT_STREAM_CONTEXT:
{
pscFileStreamContext = (PFILE_STREAM_CONTEXT)pcContext;
//
// 释放上下文
//
FctFreeCustFileStreamContext(pscFileStreamContext);
}
break;
}
Flt_FsRtlExitFileSystem();
}
CONST FLT_CONTEXT_REGISTRATION
ContextNotifications[] = {
{
FLT_VOLUME_CONTEXT, 0, FsfilterMinifilter_CleanupContextNotify, sizeof(VOLUME_CONTEXT), MEM_CALLBACK_TAG },
{
FLT_STREAM_CONTEXT, 0, FsfilterMinifilter_CleanupContextNotify, FILE_STREAM_CONTEXT_SIZE, MEM_TAG_FILE_TABLE },
{
FLT_CONTEXT_END }
};