fancylf
驱动牛犊
驱动牛犊
  • 注册日期2007-07-29
  • 最后登录2016-06-21
  • 粉丝1
  • 关注0
  • 积分61分
  • 威望501点
  • 贡献值0点
  • 好评度23点
  • 原创分0分
  • 专家分0分
  • 社区居民
阅读:4773回复:9

一个关于透明加解密(加密)的总结,非常不错

楼主#
更多 发布于:2008-12-01 22:02
最近一直看透明加密,头都大了啊,突然在冥冥之中看到这篇文章,犹如一阵清风拂面啊,
不敢独享,希望初识文件透明加解密的同仁少走弯路,也希望后面大家有好文章,直接往驱网上挂

(这是做开发时的一些经验和研究文献中总结出来的,希望对一些朋友有用)
fleshwound@smatrix.org
1.windows文件系统概述 
硬盘文档加密系统驱动原理一般有两种:通过HOOK一些底层的系统内核调用对文件目录的访问权限进行控制,在文件系统输入输出驱动上层直接建立一个驱动对文件目录的数据进行加密。由于windows文件系统不是物理设备,其堆栈结构比较特殊,因此文件系统过滤驱动也与一般过滤驱动有一些不同(这一点背景可以参考下楚狂人[1]的文档和IFS的提供的若干文档,除了正常的文件接口,还有一种称为FASTIO),文件系统是针对每一个磁盘卷而不是针对整个磁盘来构造设备堆栈。由于windows文件系统驱动会生成控制设备对象CDO和卷设备对象VDO两种设备对象,所以文件系统过滤驱动就有两种对应的过滤设备对象(FiDO)。另外,文件系统过滤驱动也有它自己的控制设备对象CDO,并不附着到任何设备对象上,只是起到一些控制功能,需要处理的IRP(I/O请求包)也会有所不同。因此,一个文件系统过滤驱动相应地包括了三种设备对象。不同版本的windows操作系统,同一设备的设备堆栈对同一IRP的处理在细节上有很多不同,这点我们需要根据不同的系统进行处理。下面以windows XP为例对硬盘文档加密系统进行一些探讨。
2.如何实现数据加解密
数据加解密的实现在针对卷设备对象的过滤设备对象中来处理。有一些实际的问题需要考虑,列举如下:
(1)到底对什么文件进行加密?
并不是对所有读写请求中的数据进行加密,例如不应该对系统文件加密,也不能对根目录区进行加密,否则会出现一系列问题。因此需要在加密之前对操作的文件对象进行判断,选择性的进行加密。这可以通过文件全路径来进行区分。而需要加解密的文件全路径存放在一个特殊的文件里,通过后面在文件系统过滤驱动里实现的文件访问控制来使该文件不能被除了该文件系统过滤驱动以外的任何程序访问到。这个特殊文件的内容在过滤驱动加载时即会加载一份到权限表缓冲里。
(2)如何获取数据内容?
要对数据进行加密,就需要先获得数据的位置和内容,我们不可能直接对磁盘扇区直接操作,因此需要通过window底下的自定义文件系统驱动获得。XP中Cache管理部分导致在文件系统或者文件系统过滤驱动程序中,读和写操作分为几种不同的类型:Cached I/O、Non-cached user I/O、Paging I/O。我们可以直接对缓存中的数据进行加密吗?比如数据更新的发生,文件读写的发生,都会直接造成数据的改变,因此对缓存中的数据加密会带来不少人品问题。所以我们要选用Non-cached user I/O和Paging I/O两种方式下,因为无论如何最后数据都肯定通过这两种方式写到硬盘上。这样的结果就是:硬盘上的文件数据始终是密文,而缓存中的数据始终是明文。当用一个应用程序把明文读出来后,明文就一直存在缓冲里,其他程序读时也能看到明文,能把明文拷贝出去,会成为一个安全问题。解决该问题的方法之一是在关闭文件的时候对缓存进行清空处理。这两种方式下的数据内容可以在IRP中的MdlAddress结构或者UserBuffer结构中取得。此外,为了解决这个问题,也可以通过自旋锁的一些设置来解决。
(3)加密算法及其密钥处理
对于每一个文件的读写,文件系统会产生很多个IRP_MJ_READ请求或者IRP_MJ_WRITE请求,数据也被分成了很多块。同时由于windows读文件不一定是顺序的, 加密的时候通过IRP中的Write.Length获取明文长度,如果不够分组整数倍用全0填充。不过值得注意的是如果在某个IRP中处理的是文件的最后一块分页,写成功的长度IoStatus.Information通常会小于IRP请求的长度Write.Length,这时候再用Write.Length来作为参考进行分组加密的话就会出问题,需要进行特殊处理。在IRP_MJ_READ请求中解密的时候则不存在这个问题可以每次都按照返回的长度即IoStatus.Information来进行解密。基于以上要点规则,在文件系统过滤驱动的IRP_MJ_WRITE分派函数进行加密的流程如下:
1)判断IRP的读写方式,只处理以下几种IRP_NOCACHE、IRP_PAGING_IO和IRP_SYNCHRONOUS_PAGING_IO;
2)读取文件路径和类型决定是否需要进行加密,不需要则直接把IRP往下层传,否则执行加密;
3)得到Windows传下来的数据缓冲地址,在内核中分配连续的页作为一个新的缓冲区保存数据;
4)用AES对新的缓冲区中的数据加密;
5)设置完成例程,并将IRP原来的参数作为完成函数下文环境变量;
6)调用IoCallDriver把密文向下层驱动传,写入硬盘;
7)在完成例程中设置IRP的MdlAddress及UserBuffer,成功。
解密过程与此相同,现在还有一个问题,如何来保存AES算法的密钥?在底层微软是没有提供加密算法的密钥容器的。这里有一种方法,可以使用动态密钥,而且可以将此密钥放在硬盘上面。使用一个RSA公钥对动态AES密钥进行加密,再使用时候利用私钥进行解密,获得对称密钥。而这个可以使用微软的CryptoAPI和一个USBKEY,私钥就在USBKEY上,我们只要一个USBKEY就够了,也就是银行里面使用的办法,当然我们还可以使用数字签名的方法。有朋友会问,那为什么不直接使用RSA或者ECC加密数据呢?那样会造成在飞机跑道上开拖拉机的后果,速度非常慢。
4 Hook一些IRP,实现对文件的进一步控制。
实现这些目的的关键在于Hook文件系统驱动的一些函数,从IRP中解析出各种目录操作,然后根据操作系统的不同设置返回值将其拦截即可。并可同时进行日志记录。创建文件和打开一个文件都于IRP_MJ_Create,所以这里需要区别一下新建文件和文件。当创建一个文件的时候,系统会首先发送一个标FILE_OPEN的请求,并且判断底层文件系统的返回值,返回成功,则表明文件存在并且已经成功打开,否则如返回结果是NO SUCH FILE,则紧接着创建一个E_OPEN_IF请求,得以将文件创建,所以如果在MJ_Create的Options域当中发现了FILE_CREATE,E_OPEN_IF或者FILE_OVERWRITE_IF的三个标志之一,则是在创建否则就是打开。
(2)如果获取的IRP为IRP_MJ_SET_INFORMATION,获取与该IRP关联的IO_STACK_LOCATION结构指针IrpSp(该结构中包含了该IRP的类型代码和参数信息以及完成函数的地址),然后取出irpSp->Parameters.SetFile.FileInformationClass,若与FileDispositionInformation相等则为删除操作,若与FileRenameInformation相等则是文件重命名操作。
(3)防止拷到U盘之类的方法是查询该新增卷的设备类型, FILE_REMOVABLE_MEDIA则为移动存储产生。得到类型以后保存到该卷的过滤设备对象中。当该过滤设备对象得到一个写请求的IRP时即可判断是否在将文件拷贝U盘,接下来就是禁止或者允许。
5 参考文献和推荐读物
http://www.smatrix.org/bbs/read.php?tid=4266&fpage=3(该文介绍了一个简单的过程)
http://www.whitecell.org/forums/viewthread.php?tid=238 (该文介绍了实时获取USB的方法,获取USBKEY方法与此类似)
http://www.whitecell.org/forums/viewthread.php?tid=426 (楚狂人文档和sinister的关于Windows 文件过滤驱动经验总结)
http://www.smatrix.org/soft (我们的一个产品的说明书,不过比此文中叙述的原理要复杂的多)


原文出处:https://www.xfocus.net/bbs/index.php?act=ST&f=3&t=66805

最新喜欢:

arbelarbel
fancylf
驱动牛犊
驱动牛犊
  • 注册日期2007-07-29
  • 最后登录2016-06-21
  • 粉丝1
  • 关注0
  • 积分61分
  • 威望501点
  • 贡献值0点
  • 好评度23点
  • 原创分0分
  • 专家分0分
  • 社区居民
沙发#
发布于:2008-12-01 22:09
清除缓存
文中提到了,首次打开文件的时候,文件会发IRP_MJ_READ到磁盘,然后文件数据会一直保存到缓存中,后面再次打开文件的时候,就直接从缓存中读了,而不再有读请求发出来(我通过调试发现的确是这样),应用程序再读的数据都是明文了,为了解决这个问题需要在关闭文件的时候清除缓存,
不过好像应该是在CLOSEUP中清除缓存
我现在遇到的的问题是,把数据写入到U盘后,可以通过windbg调试看到,已经在磁盘上的文件数据加密了
但是这个时候需要把U盘拔出来后,然后在另一台机器上打开,才看到加密了的数据,而如果不拔出来,我直接把
这个文件发送到别的机器上,仍然是明文,没加密?我严重怀疑,是没清除缓存的问题,不知道如何清除呢?
mz_suya
驱动小牛
驱动小牛
  • 注册日期2008-06-13
  • 最后登录2010-08-01
  • 粉丝0
  • 关注0
  • 积分3分
  • 威望648点
  • 贡献值2点
  • 好评度0点
  • 原创分0分
  • 专家分1分
板凳#
发布于:2008-12-02 10:32
这个文件发送到别的机器上,仍然是明文,没加密?我严重怀疑,是没清除缓存的问题,不知道如何清除呢?

这个跟你的缓存没有关系 不要啥事情都怪罪缓存   你直接发 难道就不需要读磁盘吗?所以 如果你限制你的网络程序所代表的进程可以仅获得原始文件(密文)的话 那么就可以实现这个功能了


不要啥事情都怪罪缓存 许多事情即便没有缓存 你这样做也是现在的状况 。
wanted999
驱动牛犊
驱动牛犊
  • 注册日期2006-03-28
  • 最后登录2012-08-13
  • 粉丝0
  • 关注0
  • 积分519分
  • 威望429点
  • 贡献值0点
  • 好评度48点
  • 原创分0分
  • 专家分0分
地板#
发布于:2008-12-07 04:30
冰山一角.
gooddaddy
驱动牛犊
驱动牛犊
  • 注册日期2008-09-16
  • 最后登录2009-12-29
  • 粉丝0
  • 关注0
  • 积分1分
  • 威望11点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
地下室#
发布于:2009-05-22 16:40
namdaa
驱动牛犊
驱动牛犊
  • 注册日期2006-10-24
  • 最后登录2009-06-09
  • 粉丝0
  • 关注0
  • 积分6分
  • 威望61点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
5楼#
发布于:2009-05-22 20:16
回 2楼(mz_suya) 的帖子
所以 如果你限制你的网络程序所代表的进程可以仅获得原始文件(密文)的话 那么就可以实现这个功能了


那怎么判断当前读文件的进程是不是网络程序呢?
Chinaluo_007
驱动牛犊
驱动牛犊
  • 注册日期2009-04-22
  • 最后登录2010-04-13
  • 粉丝1
  • 关注1
  • 积分40分
  • 威望361点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
6楼#
发布于:2009-05-25 23:03
是啊,冰山一角,远远不止这么简单。
想实现还有很多事情要做。
misssir
驱动牛犊
驱动牛犊
  • 注册日期2007-10-25
  • 最后登录2016-01-09
  • 粉丝0
  • 关注0
  • 积分143分
  • 威望183点
  • 贡献值0点
  • 好评度12点
  • 原创分0分
  • 专家分0分
7楼#
发布于:2009-05-28 18:52
写的非常好
还是原始社会好 / QQ: 13633292
Chinaluo_007
驱动牛犊
驱动牛犊
  • 注册日期2009-04-22
  • 最后登录2010-04-13
  • 粉丝1
  • 关注1
  • 积分40分
  • 威望361点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
8楼#
发布于:2009-05-31 16:17
这时候再用Write.Length来作为参考进行分组加密的话就会出问题,需要进行特殊处理。
请问这个特殊处理应该如何进行了?
也就是在写入的时候,如果不足一个分页大小,如何获取实际需要操作的文件长度.
bjjxh
驱动牛犊
驱动牛犊
  • 注册日期2009-04-21
  • 最后登录2010-09-20
  • 粉丝0
  • 关注0
  • 积分23分
  • 威望231点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分1分
9楼#
发布于:2009-06-05 13:54
如果不足一个分页大小,如何获取实际需要操作的文件长度.
在完成例程中可以得到 或者 利用FCB
游客

返回顶部