易 丹
(武汉交通职业学院,湖北 武汉 430065)
随着计算机与网络应用的普及,人们之间的信息交互越来越方便,但由此产生的信息安全问题也日益突出。为了保证信息的安全,我们需要对存储在计算机系统上的文件信息进行加密。目前常见的文件加密方式有四种:用户层文件加密、系统调用层加密(APIHOOK)、文件系统过滤驱动加密和硬件加密。本文综合比较四种加密方式的原理及效率(见表1),发现文件系统过滤驱动加密达到了在性能、成本和安全性上的平衡。接下来,本文重点阐述基于Windows文件系统过滤驱动的文件透明加密技术。
表1 四种文件加密方式比较
文件系统过滤驱动工作在Windows内核模式下。因此,我们需要对Windows操作系统进行比较深入的学习和了解。
如图1所示,在Windows系统中,应用软件是“看不见”操作系统内核的。应用软件需要通过API函数调用获取操作系统的支持,才能访问内核级的代码。
Windows内核模块由以下几部分组成[1]:(1)硬件抽象层(HAL)。HAL为Windows其它组件提供硬件平台低层接口。它隐藏了与硬件相关的细节,比如,中断控制器,I/O接口等与机器相关的功能。Windows内部组件可以调用HAL的例程来实现可移植性。(2)设备驱动层。设备驱动程序是可以动态加载卸载的内核模块,它和I/O管理协同工作,在用户和真实的物理设备之间建立连接,保证用户请求能够由相应的硬件设备顺利完成。(3)Windows kernel。Windows内核为执行体提供了操作系统的工作机制和底层原语。提供了执行体组件需要使用的(Ntoskrnl.exe中的)一组函数,例如,中断和异常分发,线程调度和同步等。使得执行体可以实现更高层次的功能。(4)Windows执行体。Windows执行体包含了操作系统可以提供的服务操作,包括系统服务函数(system service),调用设备驱动器的函数,以及内存管理、对象管理等系统组件的管理。
图1 windows系统结构
Windows的文件操作主要涉及到I/O管理和内存管理,这两个内核组件提供了由过滤驱动实现文件加解密的理论基础。[2]
2.2.1 I/O管理
WindowsI/O系统的作用是为上层用户程序提供一个有关设备的抽象,用特定的数据结构实现与底层物理或者虚拟设备的数据传递,将用户的请求分发给相应的设备驱动程序处理。此设备抽象具有以下特性:①统一的,跨设备的安全性和命名机制;②高性能的,异步的,基于数据包的I/O;③提供支持用高级语言编写驱动的服务;④动态的加载和卸载驱动程序,允许加入新的驱动程序;⑤支持即插即用和电源管理;⑥支持多个安装的文件系统,包括FAT、CD-ROM、UDF、NTFS等。
I/O管理器作为I/O系统的核心,监视着windowsI/O子系统,它能够把来至用户的请求构建成一个IRP(即I/O请求包),把该IRP传递给目标驱动程序;驱动程序根据IRP中的相应参数将这些请求转变成与硬件相关的操作。文件系统过滤驱动就位于I/O管理器和文件系统驱动之间。
2.2.2 内存管理
支持多进程的现代操作系统一般都会采用基于页面映射的“虚拟内存机制”,内存管理器提供了一组系统服务来完成并发进程下的各种任务,如下所示:分配和释放虚拟内存;在进程之间共享内存;将文件映射到内存;将虚拟页面刷新到磁盘上;获得一定范围内虚拟页面的信息;改变虚拟页面的保护属性;将虚拟页面锁在内存中。
内存管理服务允许其调用者提供一个进程句柄,来指明哪个特定进程的虚拟内存需要进行操作。这些服务中的大多数是通过Windows API暴露给用户的,Windows API有三组函数可用来管理应用程序中的内存:页面粒度的虚拟内存函数、内存映射文件函数、堆函数。
另外,为了缓解文件读写过程中反复读写磁盘造成的高负荷,引入了缓存管理,缓存管理能够实现“预读”和“延迟写”的功能。文件系统在接到读写文件的请求时,会通过缓存管理器将文件读写到缓存缓冲区中,当一段时间内出现多次对该文件的读写操作时,就避免了多次读写磁盘的麻烦。
由以上可以看出,来自于用户的文件请求,经过I/O系统处理后,最终由文件系统在内存管理器的配合下直接完成或者下发给底层驱动完成。
文件系统驱动是一个虚拟设备驱动,它与内存管理协同工作实现用户在计算机系统中的信息管理与操作。
利用文件系统过滤驱动实现文件透明加解密就是在I/O管理器和文件系统驱动之间加一层过滤驱动。本来发给文件系统的来至于用户的所有请求,都会先被过滤驱动截取,只要在过滤驱动中替换或者扩展原始请求的功能,就能够实现文件的加解密,并且这一过程是在内核中自动完成的,不会影响用户的任何体验,所以称作透明加密。图2展示了过滤驱动在设备栈中的位置。
图2 过滤驱动在设备堆栈中的位置
Windows驱动程序工作在内核模式下,属于直接和硬件打交道的模块,主要由驱动程序入口函数、派遣例程、驱动程序卸载函数三部分组成。[3]
驱动程序入口函数DriverEntry(IN DriverObject,IN RegistryPath),驱动程序派遣函数DispatchRutine(IN DeviceObject,IN irp),驱动卸载函数XXXUnload(IN DriverObject)。
其中,参数DriverObject是每一个驱动程序的唯一驱动对象,它是在驱动加载的时候,被内核中的对象管理程序所创建的;参数RegistryPath是一个Unicode(宽字节)字符串,指向此驱动负责的注册表;参数DeviceObject是设备对象,每个驱动程序会有一个或多个设备对象,设备对象是由程序员自己创建的,其中保存真实物理设备的特征和设备的状态信息;参数irp就是I/O管理器送过来的请求包,驱动程序会根据包的内容确定调用哪一个派遣例程,常见的有读(read),写(write),打开(open),关闭(close)等。
当驱动程序被卸载的时候,由I/O管理器负责调用驱动卸载函数,遍历系统中所有的此类设备对象,然后删除这些设备对象。
(1)配置过滤驱动
将创建的过滤驱动的设备对象挂接到文件系统驱动所在的驱动设备栈。通过函数IoAttach-DeviceToDeviceStack(IN SourceDevice,IN Target-Device)来实现。接下来对过滤驱动进行配置,拦截相应的文件操作请求,代码如下:
以上代码设置了需要过滤的IRP,如果是与加解密无关的操作,则直接跳过过滤驱动,正常下发到底层驱动。
预处理:
后处理:
其中,写时加密是在写到磁盘前进行的,即在预处理中实现;读时解密是在从磁盘读到内存后进行的,即在后处理中实现。
(2)加解密实现过程
加解密时需要知道将要被处理的数据在内存中的具体位置,我们可以通过了解IO的读写方式来获取这一信息。I/O操作一般有三种方式:缓冲方式、直接方式及其他方式。文件读写一般采用后两种方式[4]。
直接方式使用IRP->MdlAddress来传递缓冲区。因为来自于用户的请求,其指针指向的缓冲区都在用户空间中,工作于内核模式下的驱动程序虽然可以正常使用该指针,但是,如果在完成的时侯,需要其他进程的线程协同处理,那么就形成了进程切换,这些缓冲区的指针就失效了。引入MDL的作用是把由用户空间映射的一段物理地址再用内核(系统)空间映射一次,即同一段物理地址既属于用户空间又属于内核空间,因为所有的进程共享内核空间,所以进程切换时,指向内核的同一指针的内容是相同的。
其他方式使用IRP->UserBuffer来传递缓冲区。直接把用户空间的指针传递到内核,不做任何其他处理。前提是整个请求只在当前线程的上下文中处理,没有进行切换。
在WindowsNT及XP系统中,应用程序的读写文件请求一般遵循如图3的流程,应该根据此流程编写加解密代码。
图3 应用程序的文件读写请求过程
加密过程:即函数EncryptWritePre(irp,Irpsp,context)的处理:
基于文件系统过滤驱动开发的文件透明加密系统与windows内核紧密关联,开发难度大,容易出错。但是正因为它是在Windows内核实现的,所以,安全机制完善,不容易被攻击和破解,并且得益于驱动程序的动态加载卸载,开发灵活性高,利用过滤驱动进行加解密在安全领域具有很高的实用价值。
[1]毛德操.Windows内核情景分析[M].北京:电子工业出版社,2009:1099-1175.
[2]Mark E.Russinovich.Microsoft Windows Internals[M].潘爱明,译.北京:电子工业出版社,2010:537-555.
[3]张帆,史彩成.Windows驱动开发技术详解[M].北京:电子工业出版社,2008:87-117.
[4]谭文,杨潇.Windows内核安全编程[M].北京:电子工业出版社,2009:236-262.