面向Windows的文件透明加解密解决方案(4)——客户端服务设计与功能实现

http://blog.csdn.net/wxyyxc1992/article/details/27843461

为了给实现更好的用户体验与轻量级、灵活性地交互设计,在局域网内管理端与客户端之间采用B/S 架构。客户端除了运行于系统底层的文件加解密驱动程序,还有运行于应用层的负责与服务器通信、控制加解密驱动的运行以及实现对恶意用户的部分防护功能。

4.1客户端程序的设计与实现

4.1.1基于HTTP协议与数字证书的用户认证

客户端与服务器的交互实际上是客户端与服务器的双向验证过程,使用HTTPS协议,客户端使用HttpWebRequest向服务器发起一个HTTP请求,使用POST方法将自己的数字证书(这里的数字证书是使用客户端的私钥加密的本机的机器码的MD5值,加密算法采用RSA)传递给服务器,服务器验证客户端的真实性并返回响应的控制指令。客户端与服务器的通信可以分为以下步骤:

(1)客户端向服务器发起HTTP请求,并提交自己的数字证书与Session ID

(2)服务器收到客户端的请求后用客户端的公钥对数字证书进行解密获得客户端的机器码,并在数据库中验证该机器码对应的主机是否为受信主机。如果非受信主机则直接返回ERROR,如果是受信主机则返回一个HTML文件(以字符串形式传回)。文件包含服务器私钥加密的Session ID、服务器授予的客户端的权限(可以使用的机密进程列表)以及其他控制可选项()

(3)客户端服务程序对收到的HTML文件进行解析,使用服务器的公钥对加密的Session ID进行解密以验证服务器的真实性。验证成功之后客户端将服务器的控制命令传递给底层驱动程序。

(4)客户端与服务器会定期进行KeepAlive验证,客户端一旦发现已经脱离了服务器的控制会自动关闭透明加解密驱动。

客户端对于驱动层的控制详见驱动关键技术的交互通信模块。

4.1.2基于Socket通信的服务器控制

客户端第一次向服务器发起的是HTTP请求,这样方便系统管理员进行管理并且保证了通信过程中的安全性。但是B/S架构下客户端与服务器之间采取的是消息响应机制,只有当客户端向服务器发起请求的时候服务器才可以控制客户端。但是在实际应用中往往有系统管理员希望随时掌控客户端的情况,为了解决这一需求,在用户完成认证之后会在本地开启一个Socket 端口,自动侦听来自服务器发出的消息。

Demo示意如下:


在客户端的后台运行的Defense进程中,用户是看不见任何变化的。但是在系统管理端强制关闭透明加解密驱动的时候会给用户相应的提示。

 

4.2进程甄别:防止进程欺骗

在驱动程序的设计中,主要是依靠PEB中获取到的进程名来判断是否为加密进程。但是如果有恶意用户将非法进程的名字改为机密进程名,系统就无法甄别而会当做机密进程来进行处理,最终可能导致机密文件的意外泄漏。由于在底层对于IRP的过滤中对文件路径的判断具有一定的复杂度会导致系统效率的降低,所以对于进程的真实性的验证交由客户端服务完成。客户端服务一旦发现有非法进程冒充机密进程会自动提出警告并且关闭文件透明加解密驱动。

4.2.1 可执行文件的验证

首先针对最简单的进程名冒充,在企业内部限定使用的机密进程可以认为是由系统管理员选定的。那么对于每一个可执行文件(版本号)而言是唯一的,我们可以根据对于可执行文件的完整性验证(HASH验证)来判断可执行文件是否为真实的机密进程。后台得到的数据类似下图:


4.2.2 防止进程注入

    除了上文中提到的伪装进程名这种最简单的方法之外,木马程序往往使用类似于进程注入的方法,将自己作为一个加载模块加载到目标机密进程中从而非法获取数据或者直接将自己的代码写入到目标进程的地址空间中。为了防止恶意用户利用这一手段非法劫持机密进程,我们考虑在对可执行文件的完整性验证之后对于该进程的所有加载模块(DLL文件)也进行安全性与完整性的验证。客户端服务程序获得的进程的加载模块信息如下图所示:


判断的规则分为两个层次:

1)根据加载的路径进行分析,发现非可执行文件路径下或者非系统盘符路径下的加载的DLL文件皆可以认为是可疑的。

(2)加载模块的完整性判断:这是为了防止有恶意用户伪装合法的加载模块恶意注入进程,我们对于加载模块的完整性也进行了HASH验证。一旦发现有非法的加载模块存在会警示用户并且关闭文件驱动。


4.2.3 甄别触发:应用层线程与保护驱动同步

4.2.14.2.2中基本介绍了对于进程甄别的方法,这里将会介绍一下怎样触发对于进程的甄别。最简单的思路是在进程中设置一个定时器或者开启一个独立的线程,每隔固定的时间开始对系统当中的所有进程的安全性进行分析。但是这样如果间隔时间设置过长会导致恶意用户钻时间漏洞,如果间隔时间过短会导致系统开销过多,占用了过多的系统资源而导致系统性能受到影响。另一个思路是HOOK WindowsCreateProcess函数,当有进程(譬如explorer进程)调用这个函数创建新的进程的时候开始对进程的真实性与安全性进行鉴别,但是Ring 3层的HOOK极容易被恶意用户破坏并且对系统的性能的影响也较大。用户创建进程的操作本质上也是对于PE文件的操作,所以对于触发选择这一模块可以交由保护驱动来完成。如果有进程发起对机密进程的可执行文件的访问则自动唤醒甄别函数,可以用事件(Event)的方法来实现Ring 3层与Ring 0层的同步。

 

4.3复制监控:剪贴板实时监控与简易反截屏

剪贴板是在Windows系统中单独预留出来的一块内存,它用来暂时存放在Windwos应用程序间要交换的数据,这些数据可以是文本、图像、声音或应用程序等,简单地说,只要能够在硬盘上存储的数据,就能存放在剪贴板里。剪贴板并不是一个独立的应用程序,而是Windows中的一类API函数(应用程序编程接口函数),各种应用程序通过调用这类函数来管理应用程序间进行的数据交换。Windows应用程序中的剪切、复制、粘贴命令是剪贴板应用的典型操作,它的流程就是当用剪切或复制命令对数据进行操作后,这些数据就被暂时存放在剪贴板当中,使用粘贴命令就会把这些数据从剪贴板中拷贝到目标应用程序中,剪切和复制命令的不同之处就在于执行的结果,剪切会删除原来的数据,而复制操作后仍保留原来的数据。在剪贴板中在同一时间只能存放此前最后一次剪切或复制的数据,再进行剪切或复制操作时,新的数据就会覆盖掉原有的数据.

根据剪贴板的以上特性可以知道,恶意用户可以在通过正常途径打开机密文件之后,在机密文件中复制相关内容(或者直接截屏),此时保存在剪贴板中的内容是明文,恶意用户将此类明文复制到非机密文件中即可以通过其他途径将文件带出。根据剪贴板的基本原理,可以提出如下两种基本的解决方案:

(1)Hook技术

应用程序对于剪贴板的操作或者截屏全部是通过Windows 提供的API函数调用实现的,根据这个原理,我们可以针对在操作剪贴板或者截屏调用的相关函数进行HOOK,对如WM_PASTEWM_GETMESSAGE之类的消息或者针对以下几个函数:

GetDC

GetWindowDC

CreateCompatibleDC

ReleaseDC

DeleteDC

BitBlt

StretchBlt

WindowFromPoint

ChildWindowFromPoint

进行过滤,基本上可以实现对于剪贴板、截屏的完全控制。但是这样的工程量很大而且会对用户的正常操作造成较大的影响,所以我们可以选用Windows为我们提供的剪贴板操作的接口。

2)操作剪贴板监听链

系统提供的解决方案即是在客户端服务运行期间即时监控剪贴板的内容,一旦发现有非法内容的出现随时擦除剪贴板。过滤的基本规则是:

(1)不允许剪贴板中出现Image格式的文件,一旦出现立刻擦除剪贴板。

(2)只允许非机密进程打开的非机密文件向机密进程打开的机密文件中拷贝数据,否则清空剪贴板。

Windows系统为所有打开的窗体程序封装有一个剪贴板监听链,程序可以通过SetClipboardViewer()这个API将自己的窗口句柄插入到剪贴板监听链中。换言之,Defense进程中实际上创建了Form但是不会在桌面显示。

当剪贴板发生变化时,Windows系统会从打开剪贴板的窗体所处在监听链中的位置处发出WM_DRAWCLIPBOARD或者WM_CHANGECBCHAIN消息依次通知各个窗体。对于这两个消息的处理放置在了Form的消息回调函数WndProc中:

           case WM_CHANGECBCHAIN:

           if (m.WParam == nextClipboardViewer)

             nextClipboardViewer = m.LParam;

           else

             SendMessage(nextClipboardViewer, m.Msg, m.WParam, m.LParam);

             break;

WM_CHANGECBCHAIN消息是在有窗体加入或者退出剪贴板监听链的时候系统发出的,处理函数在接受到这个消息之后要进行判断。如果即将移出剪贴板监听链的窗体正好是当前窗体的下一个,那么需要将nextClipboardViewer 指针绑定到移除之后存在的那一个窗体上。如果不对这个消息进行处理会导致整个剪贴板监听链的崩溃。

而我们对于剪贴板的控制主要放在WM_DRAWCLIPBOARD消息的处理函数中,如果发现打开剪贴板的例程属于机密进程则调用C# 提供的ClipBoard类清空剪贴板。

效果截图:

(1)剪贴板控制:

 

左侧的是一个对于剪贴板内容的监控程序,从上图可以看出,正常的譬如在浏览器之中的复制是可以放入剪贴板中的。

 

而如果我们从譬如TXT文档中复制内容的时候,在剪贴板上存在的是乱码。

(2)PrtSc截图:

 

左侧是未开启Defense程序时候,PrtSc可以将屏幕内容截屏放置到画图中,右侧

(3)QQ截图:

4.4系统休眠处理

4.4.1 Windows 系统休眠机制分析

Windows休眠功能被普遍理解为一种节能机制。这种理解不准确。事实上,休眠Windows操作平台下更像是一种关闭计算机的机制,因为当休眠被激活之后,休眠的最末一个动作是关掉主机上的电源,用户随后也可以将市电开关关掉,这意味着Windows的休眠状态无须电力维持,该休眠状态完全可以脱离市电而独立存在。与睡眠机制不同——“睡眠是将当前活动的用户数据和状态暂时保存到内存中留待用户过一会再回来使用——休眠则是将用户当时的操作——即内存中活动的用户数据和状态——以文件的形式写入到硬盘中加以保存,待下一次开机时再迅速从文件中读取数据,绕过Windows的常规启动机制,直接通过恢复数据的方式进入上一回休眠关机前的桌面状态。可见,睡眠是临时性的,需要电力维持,休眠则是关机机制,操作完成后可以脱离电力。

从用户的角度来说,系统停机时,按下休眠按钮,则会自动生成一个有效的Hibernation FileHibernation File也就是系统的休眠文件Hiberfil.sys,位于系统卷的根目录下。windows在系统休眠时,将物理内存中的数据(包括系统运行时的状态数据)dumpHiberfil.sys,并生成一个有效的文件头。下次系统开机的时候,利用hiberfil.sys文件中的数据恢复系统。
从电源管理器的角度来说,只有当系统电源状态从S0到S4的时候,才会生成一个有效的休眠文件。当系统正常运行时,电源状态为S0;完全关闭时,电源状态S5;休眠状态时,电源状态S4,此时只有电源电路和唤醒电路才有点滴电流;中间的几个状态S1,S2,S3,都是不同程度的睡眠状态。从S4到S0的过程,也就是利用Hiberfil.sys恢复系统的过程。

4.4.2 对于恶意休眠的处理

对于休眠的处理方案有两个思路:

(1)在系统进入休眠时候关闭所有打开的机密文件对象并清除系统缓冲区。这种解决方案有更好地用户体验,可以通过Ring 3层的Rootkit技术或者PCI驱动中对IRP过滤而实现。但是在实际的操作中,Windows的电源管理是一个非常复杂的模块,很多函数、机制都是未公开的,使得实际操作具有很大的难度,我们也选择另一种较暴力的方案。

(2)在系统中抹去休眠这一功能。根据4.4.1的介绍我们可以知道,系统休眠的本质上是Windows将内存内容写入到Hiberfil.sys中。根据这一机制我们可以在机密进程运行期间将这一文件从系统中隐藏,即不允许任何对这文件的访问。

在这里,我们采取的也是基于Minifilter框架开发的文件过滤驱动,在IRP_MJ_CREATE的预回调函数中判断用户发起的请求的文件对象的路径名,如果含有Hiberfil.sys则直接拒绝该IRP请求。该微过滤驱动属于保护类型的驱动,与上文中提到的文件透明加解密驱动区别之处在于二者的GUID值不同,换言之,文件透明加解密驱动位于设备栈的最底层而该保护驱动位于设备栈的顶层。随着系统开发的进一步深入与完善,该保护模块的功能也会进一步完善。实际操作效果如下图所示:



  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我可以为你答关于面向对象实现疑惑加解密的问题。 首先,我们需要确定加解密算法的类型。对于对称加密算法,常用的算法有DES、AES等。这些算法的实现都需要密钥,所以我们需要在类中定义一个私有成员变量来存储密钥。同时,我们还需要定义两个方法,一个用于加密,一个用于密。 以AES算法为例,我们可以定义一个AesEncryptor类,其中包含一个私有成员变量key用于存储密钥,以及两个公有方法encrypt和decrypt。 ```python import hashlib from Crypto.Cipher import AES class AesEncryptor: def __init__(self, key): self.key = hashlib.sha256(key.encode()).digest() self.aes = AES.new(self.key, AES.MODE_ECB) def encrypt(self, plaintext): padding_size = 16 - len(plaintext) % 16 plaintext += bytes([padding_size] * padding_size) ciphertext = self.aes.encrypt(plaintext) return ciphertext def decrypt(self, ciphertext): plaintext = self.aes.decrypt(ciphertext) padding_size = plaintext[-1] plaintext = plaintext[:-padding_size] return plaintext ``` 在这个例子中,我们使用了hashlib模块来对密钥进行哈希,以确保密钥的安全性。同时,我们使用了pycryptodome模块中的AES类来实现加解密。encrypt方法用于加密明文,decrypt方法用于密密文。 使用时,我们可以创建一个AesEncryptor对象,然后调用其encrypt和decrypt方法来加解密数据。 ```python encryptor = AesEncryptor('my secret key') ciphertext = encryptor.encrypt(b'hello world') print(ciphertext) plaintext = encryptor.decrypt(ciphertext) print(plaintext) ``` 希望这个例子能够帮助你更好地理如何使用面向对象实现疑惑加解密

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值