Skip to content

双缓冲部分的设计

hkx3upper edited this page May 30, 2022 · 21 revisions

1.为什么要使用双缓冲隔离缓存?

如下图,缓冲管理器管理着一块内存形式的文件,应用程序大部分的读写操作都是直接和这块内存交互的。
下图是一次缓冲读操作,如果这块缓冲没有建立,那么在缓冲管理器建立以后,由System发起一次NonCachedIo、PagingIo的读操作请求,去磁盘中将数据读入缓冲之中。
16
单一缓冲时,授权进程读操作后,缓冲内是明文,此时非授权进程再读缓冲,也会读到明文,这样会导致数据泄露。
所以本课题使用双缓冲,授权进程和非授权进程分别使用明文缓冲和密文缓冲,两个缓冲之间互不干扰,相对独立。

2.什么是双缓冲?

双缓冲即使用原来的缓冲作为明文缓冲,新建的缓冲作为密文缓冲。主要涉及到密文缓冲的创建与销毁,以及密文缓冲与明文缓冲的同步问题。密文缓冲不允许下发非缓冲写请求到文件系统驱动,它的非缓冲读请求不解密,因而应用程序会显示密文。如图
1-2

3.双缓冲方法的实现方式

缓冲是建立在FileObject->SectionObjectPointer结构体指针上的,如下图所示。
2
本课题在minifilter内的私有密文缓冲指针ShadowSectionObjectPointer的基础上建立密文缓冲,替换掉非授权进程原始的SectionObjectPointer指针;而授权进程仍使用文件系统的原始明文缓冲。
至于为什么使用这种实现方式,可以参考开发文档这一节。

4.代码实现细节

(1)密文缓冲的建立和销毁

非授权进程原始的SectionObjectPointer指针的替换在PostCreate中。
FileObject.c->PocInitShadowSectionObjectPointers

ExEnterCriticalRegionAndAcquireResourceExclusive(StreamContext->Resource);

StreamContext->OriginSectionObjectPointers = FileObject->SectionObjectPointer;

ExReleaseResourceAndLeaveCriticalRegion(StreamContext->Resource);

FileObject->SectionObjectPointer = StreamContext->ShadowSectionObjectPointers;

Status = FltReadFileEx(FltObjects->Instance, FileObject, &ByteOffset,   //密文缓冲的创建
	sizeof(Buffer), &Buffer, 0, NULL, NULL, NULL, NULL, NULL);

if (!NT_SUCCESS(Status) && STATUS_END_OF_FILE != Status)
{
	PT_DBG_PRINT(PTDBG_TRACE_ROUTINES, 
            ("PocInitShadowSectionObjectPointers->FltReadFileEx init ciphertext cache failed. Status = 0x%x\n", Status));
	goto EXIT;
}

if (!CcIsFileCached(FileObject))  //判断是否创建成功
{
	PT_DBG_PRINT(PTDBG_TRACE_ROUTINES, ("PocInitShadowSectionObjectPointers->after FltReadFileEx file doesn't have cache.\n"));
	Status = STATUS_UNSUCCESSFUL;
	goto EXIT;
}

Status = STATUS_SUCCESS;

(2)Read中密文缓冲不解密

 if (FltObjects->FileObject->SectionObjectPointer 
    == StreamContext->ShadowSectionObjectPointers)
{
    PT_DBG_PRINT(PTDBG_TRACE_ROUTINES, ("PocPostReadOperation->Don't decrypt ciphertext cache map.\n"));
    Status = FLT_POSTOP_FINISHED_PROCESSING;

    /*
    * 如果想单独验证双缓冲是否建立,这里的读操作也要不执行,直接返回,
    * 如果能看到乱码和空白,说明双缓冲建立成功。
    * Data->IoStatus.Status = STATUS_SUCCESS;
    * Data->IoStatus.Information = Data->Iopb->Parameters.Read.Length;
    * Status = FLT_PREOP_COMPLETE;
    */

    goto EXIT;
}

(3)Write中密文缓冲不允许下发

if (FltObjects->FileObject->SectionObjectPointer == StreamContext->ShadowSectionObjectPointers
    && NonCachedIo)
{
    PT_DBG_PRINT(PTDBG_TRACE_ROUTINES, 
        ("PocPreWriteOperation->Block StartingVbo = %d ProcessName = %ws File = %ws.\n",
        Data->Iopb->Parameters.Write.ByteOffset.LowPart, ProcessName, StreamContext->FileName));

    Data->IoStatus.Status = STATUS_SUCCESS;
    Data->IoStatus.Information = Data->Iopb->Parameters.Write.Length;

    Status = FLT_PREOP_COMPLETE;
    goto ERROR;
}

5.从这里开始创建项目

如果读者打算写一个双缓冲透明加解密驱动,从这里开始就可以创建项目了,首先在PreCreate中过滤一下想要处理的文件类型,比如txt之类的,这里不区分进程为授权还是非授权进程,然后把双缓冲实现一下。
正常情况下,用记事本打开txt文件,看到的只有乱码和空白,没有正常的数据;写入一些数据,关机重启后,不启动驱动打开文件,数据实际上并没有写入磁盘里,说明双缓冲建立成功了。