关键字:透明、文件过滤驱动、加密标识,缓存
文件过滤驱动最重要的两点是搞定加密标识和缓存管理
透明指的是用户在操作的时候,虽然后台在自动的进行加解密,但是用户根本就不知道加密的存在,就像中间隔了一层透明的玻璃一样。
透明的好处在于不改变用户的操作,一切都和加密之前一样,甚至在有些企业安装加密后都无需通知所有的员工,就像加密并不存在一样,只是加密文件到了企业安全环境的外部才会发现文件无法打开。
透明的程度也是加密软件一个很重要的方面,例如:正在编辑一个Word文件时,能否拷贝或者使用其他程序来读取这个文件,如果不能那么这里就不够透明,在一些PDM的文档管理软件中就是文件在一个应用程序打开状态时另一个应用程序进行检入。透明的程度越高,用户使用时就越是和未加密时一样,透明程序越低用户就会发现有越多的操作受到限制,和加密前有较大的差异。
加密类型 |
加密原理 |
举例 |
静态加密 |
把整个文件加密掉、或者把整个文件加密掉。 |
|
动态加密 |
文件整体是加密状态,需要解密时读取多少就解密多少,写入多少就加密多少,而不需要把整个文件都解密掉。 |
|
主动加密
|
由使用者自行要求文件变成加密状态称之为主动加密 |
主动加密的方式很多,例如用WinRar把文件压缩并定义一个口令,或者给Word、Excel文件一个打开或者修改的口令,这样其他人使用时必须由正确的口令才能打开,这是使用者主动进行的加密,如果使用者不进行加密则文件不会在加密状态。只针对某些文件夹加密的方式也可以理解为主动加密方式。主动加密主要应用于个人的文件保护。 |
被动加密 |
使用者并不知道或者并不想文件变成加密状态,系统自动将文件加密,称之为被动加密。 |
被动加密主要用于企业内部防止文件泄密,即使使用者不想文件被加密,系统还是会根据定义加密文件,这样文件被拷贝等各种方法脱离企业环境后,由于文件在加密状态而无法打开。被动加密具有另一个特性就是透明操作,即使用者并不需要进行加密和解密操作,一切都是后台自动完成,使用者可以不知道加密的存在。被动加密与作者的意愿无关,相关文件都会被加密,磁盘加密和文件透明加解密属于此类。 |
文件过滤驱动是微软公开支持进行文件加密的技术手段,在驱动的层次定义中,微软定义了杀毒、压缩、加密等各种功能层次,加密的定义如下:
Load Order Group:FSFilter Encryption ,Altitude Range:140000-149999
由于文件过滤驱动处于驱动层面(不像API Hook处于应用层),可以控制系统的更多细节,和应用程序的关联度很小,可以兼容所有的应用程序,使用文件过滤驱动更利于进行动态加解密的处理。
API Hook 主要是拦截打开和关闭的动作,在这2个动作里做文件的静态加解密处理;而文件过滤驱动主要拦截的读和写的动作,在这2个IRP请求中进行动态的加解密处理,处理的内容很精确(读多少解密多少、写多少加密多少),由于动态加解密实际上处理的大多数都是缓存,没有磁盘IO操作速度更快。当然文件过滤驱动中进行动态加解密的技术难度也要大得多。
由于原有的Legacy Filter架构在很多杀毒等软件中使用,微软于近几年推出了MiniFilter架构,旨在减少各驱动间的冲突,提高工作效率,是微软大力推广的技术架构,可以在后续的各种新操作系统中使用而不用担心。MiniFilter具有兼容、高效、标准、稳定等各种优点。
加密标记是指如何判断一个文件是否加密的关键,也是在做动态透明加解密开发中最为重要的一环,加密标记的正确与否决定了文件是否会丢失、损坏,是必须要保证不能出任何问题的技术关键点。加密标记一般可存放在文件外部和文件内部,存放于文件内部时,又常分为存放在文件头上、文件尾上、压缩在文件内等三类技术方法,相关介绍如下:
序号 |
标记存储位置 |
类型 |
文件变长 |
方法 |
1 |
数据库中 |
文件外部 |
否 |
文件的加密状态记录在数据库中,判断文件加密状态时读取数据库,缺点巨大,在驱动中是很难访问数据库的,且若数据库丢失则所有文件都可能无法打开了。 |
2 |
文件目录中 |
文件外部 |
否 |
文件的加密状态记录在其所在的目录中,文件所属目录变化时程序需要处理各种细节,且存在记录丢失整个目录文件无法打开的可能性。 |
3 |
文件名上 |
文件内部 |
否 |
将文件的名字加上一个后缀来识别是否加密过且使用技术手段对用户隐藏掉这个后缀,通用性不好且容易被人为不小心去掉加密标记。 |
4 |
文件属性里 |
文件内部 |
否 |
将文件的时间或者其他属性改变一下,例如将时间加上1000年且向用户隐藏掉这个变化,通用性太差且加密标记极易丢失。 |
5 |
压缩在文件里 |
文件内部 |
否 |
最好的思路可惜无法兼容所有的文件,一方面如果文件太小,可能很难进行压缩,对于一些压缩软件产生的压缩文件无法继续压缩等,通用性很差。 |
6 |
文件头上 |
文件内部 |
是 |
在文件头部附加一段内容来记录文件的加密标记,和文件尾的对比见图1 |
7 |
文件尾上 |
文件内部 |
是 |
在文件尾部附加一段内容来记录文件的加密标记,和文件头的对比见图1 |
实际上,考虑到整体系统的稳定性和可管理性,文件的加密标记存放在文件内部,更能保证标记的可靠性、恒久性和通用性,而使用重定向原理时由于是静态加解密,加密标记存放位置对此类产品的影响不大。我们这里把使用动态加解密原理时的文件头和文件尾技术进行对比:
假设一个文件的开头位置是P1,结束位置是P2,加密标记的长度是L,带文件头和带文件尾有不同的状态,然后需要发出一个写操作从Pw1位置写到Pw2位置,此事写操作会超过原有的结束位置P2,如图1
图1 动态加解密时文件头和文件尾对比
文件头标记的处理方法:仅改变写入位置参数将Pw1= Pw1+L然后加密Pw2-Pw1这段内容并传递下去。
文件尾标记的处理方法也有两种:一种方法是将L段加密标记复制到内存中,然后加密Pw2-Pw1这段内容并原位置传递下去,再到Pw2的位置把L段加密标记接上去;另一种方法是先到Pw2的位置复制L段加密标记并接上去,然后加密Pw2-Pw1这段内容并原位置传递下去。
从以上分析可以看出,在文件尾加标记会导致加长写文件时软件要多执行一个动作,就是移动尾部加密标记的动作,如果完成第一个动作后由于掉电或其他原因未能完成第二个动作,那么文件就会被损坏。不论是加长写文件还是缩短写文件都需要把加密标记不断移动,可能在一个文档的一个保存操作中这种动作会执行几十上百次。
当然计算机掉电本来就有可能造成文件的损坏,但使用文件头标记不会造成多余的动作,也就意味着使用文件头存放加密标记在文件损坏的概率与文件未加密时是相等的,没有扩大文件损坏的概率。
文件的加密逻辑有2种方法
1、加密指定后缀:
通过指定后缀加密是指保密程序对生成的某一类文档进行加密,而对其他后缀仍然采用明文。其优点是只加密制定类型的文档,而不影响其它文件,在加密范围上相对较小,系统的稳定性容易提高;缺点是可能产生“另存为其他类型的文档”的泄密漏洞,因为此时存储的结果并不是被指定加密的文件类型。即使把程序可能产生的后缀格式都指定也没有用,因为有很多办法另存出来 ,例如在Word中可以点击另存为然后在文件名栏输入2个双引号("),然后在这2个双引号之间输入任意后缀(例如test.123),那么保存后的文件就是明文的,拷贝出来文件名改回doc后缀就可能导致文档泄密。
而且有些程序的文件格式没有定式,甚至可以保存为任意后缀,例如记事本,这样加密指定的文件后缀就带来了安全漏洞。
2、加密不区分后缀:
加密不区分后缀是指保密程序对生成的所有文件都进行加密。这种加密方式由于对指定进程产生的任何文件都加密从而堵住“另存为其他类型的文档”的漏洞,可保证不论用户如何将文件另存都不会出现明文文件。其工作原理为指定进程只要发生写操作就加密被写的文件,任何中间文件和临时文件都会被加密。不区分文件后缀有更高的安全性,技术难度也大得多。
加密不区分后缀的透明加密产品开发的层面有两个:应用层和驱动层,应用层基本上采用重定向原理+加密指定后缀;驱动层可能为2×2的组合即4种模式,如下表所示:
|
加密指定后缀 |
加密不区分后缀 |
重定向原理 |
重定向原理+加密指定后缀(1) |
重定向原理+加密不区分后缀(2) |
动态加解密原理 |
动态加解密原理+加密指定后缀(3) |
动态加解密原理+加密不区分后缀(4) |
上表中模式2技术实现上很困难,所以实际上驱动层加密一般采用模式(1)-模式(3)-模式(4)。
还可能将加解密操作在应用层和驱动层中混合完成,在驱动层拦截文件打开然后传递给应用层加解密,这种方法和纯应用层开发是一样只能采用模式(1)。
需要判断的的IRP标识介绍:
1、IRP_MN_MDL
IoStatus.Information field字段表明有写入MDL的字节数
主要是调用者需要fsd够着写入文件的MDL,fsd来分配和构造MDL
2、IRP_MN_MDL_COMPLETE
表示调用者已经处理完fsd创建的mdl,由mdl来清除和释放mdl
3、IRP_MN_DPC
表示调用者的上下文环境是DPC,fsd必须返回STATUS_PENDING,并且在工作线程中完成此历程
FILE_CONTEXT
RC4流加密
密钥为静态密钥
读过滤条件
A、过滤标志g_bStartFilter是否启动
B、判断pFileObj->FsContext是否存在
C、IrpSp->MinorFunction判断是否为 IRP_MN_COMPLETE_MDL
D、是否为目录操作
获取读文件的读取的长度,起始位置,读取内容:
文件的长度:
Length= IrpSp->Parameters.Read.Length;
文件的偏移量:
Offser.QuadPart= IrpSp->Parameters.Read.ByteOffset.QuadPart
文件内容:
Irp-> MdlAddress不为空,则使用Irp-> MdlAddress,缓冲区位置为MmGetSystemAddressForMdlSafe(Irp-> MdlAddres),否则直接使用Irp-> UserBuffer
#define CdMapUserBuffer(IC, UB) { \
*(UB) = (PVOID) ( ((IC)->MdlAddress == NULL) ? \
(IC)->UserBuffer : \
(MmGetSystemAddressForMdlSafe( (IC)->MdlAddress,HighPagePriority))); \
}
写过滤条件
E、过滤标志g_bStartFilter是否启动
F、判断pFileObj->FsContext是否存在
G、IrpSp->MinorFunction判断是否为IRP_MN_MDL
H、是否为目录操作
文件的长度:
Length= IrpSp->Parameters.Write.Length
文件的偏移量:
LARGE_INTEGERlnOffset=IrpSp->Parameters.Write.ByteOffset;
文件内容:
Irp-> MdlAddress不为空,则使用Irp-> MdlAddress,缓冲区位置为MmGetSystemAddressForMdlSafe(Irp-> MdlAddres),否则直接使用Irp-> UserBuffer
#define CdMapUserBuffer(IC, UB) { \
*(UB) = (PVOID) ( ((IC)->MdlAddress == NULL) ? \
(IC)->UserBuffer : \
(MmGetSystemAddressForMdlSafe((IC)->MdlAddress, HighPagePriority)));
缓存管理
至硬盘,也没有清空内存中的资源。此时非法进程再打开文档,在
IRP_MJ_CREATE例程会刷新缓存操作,所以非法进程打开为乱码
没有清空内存中的资源。此时非法进程再打开文档,如果是word这种占着句
柄没有关闭的文档,判断为非法进程,禁止打开;如果是notedpad采用内存
映射文件的方式的,句柄关闭,则非法进程打开正在被合法进程打开的文档,在IRP_MJ_CREATE例程会刷新缓存操作,所以非法进程打开为乱码
建的文件才刷新缓存
http://www.microsoft.com/downloads/details.aspx?displaylang=zh-cn&FamilyID=B54730CF-8850-4531-B52B-BF28B324C662
http://www.microsoft.com/taiwan/whdc/driver/filterdrv/alt-range.mspx
http://www.microsoft.com/whdc/driver/filterdrv/default.mspx