技术分析
⑴.主要派遣例程
IRP_MJ_CREATE例程
对应的函数: CFilterEngine::DispatchCreate(DEVICE_OBJECT *device, IRP *irp)
如果是自己的设备访问则直接放过
if(device ==CFilterControl::s_control)
{
irp->IoStatus.Status = STATUS_SUCCESS;
irp->IoStatus.Information =FILE_OPENED;
IoCompleteRequest(irp,IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
再次判断一些系统进程请求如:svchost.exe则放过不进行加解密操作
if(SkipCreate(device,irp))
{
status = irp->IoStatus.Status;
IoCompleteRequest(irp,IO_DISK_INCREMENT);
return status;
}
1. PreCreate阶段:初次判断当前状态,并标志上下文flag,以便在PostCreate阶段进行相应操作
extension->Volume.PreCreate(irp,track),在pre-Create函数中首先判断经过candidate.NormalizeCreate(irp),此函数判断当前irp是否来自网络重定向器,如果来自网络重定向器的请求则返回失败禁止访问,这样可以防止访问网络共享。若果不是则返回到成功到pre-Create函数,接下来判断SL_OPEN_TARGET_DIRECTORY标志,如果是应用的访问必须是带有SL_OPEN_TARGET_DIRECTORY标志,这个标志表示当前访问的文件或者文件夹的父路径已经被打开,内核层的访问不必有这个标志。就接下来就是判断是否是系统相关的目录比如系统盘:/windows ,temp,Document and Setting,IE等等,如果是这些目录则不进行加解密,实现函数是IsSpecific(track,TRACK_SYSTEM | TRACK_IE_CACHE),如果不是这些目录则开始检查当前文件夹下的配置文件,如果当前访问的文件或者文件夹的当前文件夹不存在配置文件,则检查父目录是否存在配置文件,如果不存在则不对当前访问的对象加解密,如果父目录存在配置文件则在当前上下文中设置TRACK_AUTO_CONFIG标志,在postcreate中生成一个配置文件。
2.PostCreate阶段
根据PreCreate阶段分析的结果过设置的标志位,如果标志位是TRACK_AUTO_CONFIG标志,则根据父目录的配置信息,在当前文件夹生成一个配置文件,成功后上下文标志为待加密状态,即TRACK_YES标志,以便在IRP_MJ_WRITE派遣例程中判断标志是否加密。如果此文件夹已经存在配置文件,即之前已经访问过此加密文件夹,则直接把加密链表的加密信息复制到当前上下文加密中。
IRP_MJ_WRITE例程
1. extension->Volume.CheckFileCooked(file, &link)
首先检查加密文件对象cooked中是否存在当前文件夹对象,如果不存在直接调用下一层驱动,存在则进行加密操作。
2.extension->Volume.m_context->Tracker().Check(file) &FILFILE_TRACKER_BYPASS
检查当前文件类型是否属于不被加密操作文件类型,不被加密的文件类型,则WriteBypass(extension,irp, &link),直接写文件原文,不加密。
3. WriteAlreadyEncrypted(extension,irp);
检查当前文件是否已经被加密
4.EstimateCaching(extension,irp, file, &link)
如果文件没有被加密,检查IRP是否需要缓存.
5.写文件
根据文件对象是否需要缓存的标志,来采用不同的方式来写
IRP_MJ_READ例程
1. extension->Volume.CheckFileCooked(file, &link)
首先检查文件对象cooked中是否存在当前文件夹对象,如果不存在直接调用下一层驱动,存在则在后续操作中进行解密操作。
2.extension->Volume.m_context->Tracker().Check(file) &FILFILE_TRACKER_BYPASS
检查当前文件类型是否属于不被加密操作文件类型,不被加密的文件类型,则ReadBypass(extension,irp, &link);直接调用下一层驱动。
3.检查是否读取到文件尾
4.如果没有读取到文件尾部,检查文件是否需要缓存,如果需要则解密读取并缓存
不需要则直接解密文件数据
IRP_MJ_DEVICE_CONTROL例程
应用层代理和驱动层交互例程CFilterEngine::DispatchDeviceControl
1. IOCTL_FILFILE_GET_STATE
获得驱动的状态,驱动的状态有下面几种
enumDriverState {
Unknown, // 错误未知状态
NotInstalled, // 没有被安装
Active, //安装了被激活状态
Passive, // 安装了没有被激活
};
2. IOCTL_FILFILE_SET_STATE
设置驱动的状态,设置Active激活态和Passive不激活态。
Active激活态:对加密文件夹实施透明加解密
Passive不激活态:不对加密文件夹实施透明加解密
2. IOCTL_FILFILE_GET_HEADER
获得加密文件头信息
3. IOCTL_FILFILE_SET_HEADER
设置加密文件头
4. IOCTL_FILFILE_ENTITY
加密文件夹入口点信息管理链表的信息的添加与删除
5. IOCTL_FILFILE_ENUM_ENTITIES
从加密文件夹信息管理链表中获得入口点数量及信息。
6. IOCTL_FILFILE_ENCRYPTION
对指定文件进行加密
7. IOCTL_FILFILE_CALLBACK_CONNECTION
应用层代理和驱动链接传递一些全局事件,这些事情作用就是用于请求密码的交互,即在
IOCTL_FILFILE_CALLBACK_REQUEST和IOCTL_FILFILE_CALLBACK_RESPONSE交互
8. IOCTL_FILFILE_ADD_CREDIBLE_PROCESS
添加可信进程,可信进程以及其子进程有权访问加密文件夹
9. IOCTL_FILFILE_CALLBACK_REQUEST
驱动通过激活IOCTL_FILFILE_CALLBACK_CONNECTION传递进来的事件,是应用层发送
此控制码来请求待解密的密码数据
10.IOCTL_FILFILE_CALLBACK_RESPONSE
应用层代码通过IOCTL_FILFILE_CALLBACK_REQUEST请求的密码数据解密所得密码通过此控制码返回密码给驱动
11.IOCTL_FILFILE_OPEN_FILE
调用IoCreateFileSpecifyDeviceObjectHint函数打开文件,此函数打开文件可以直接从下层设备打开而不进入文件系统栈,避免文件系统重入问题
⑵.其他主要功能函数
1. NTSTATUSCFilterCallback::Connect(HANDLErandom, HANDLE key,
HANDLE notify)
HANDLErandom, HANDLE key, HANDLE notify,从应用层代理传进驱动的三个事件,分别是请求random随机数事件,请求加密解密Key事件,通知回调事件。
2. 应用层与驱动进行Key请求交互的函数
请求key函数
NTSTATUSCFilterCallback::FireKey(ULONGflags, FILFILE_TRACK_CONTEXT *track)
NTSTATUSCFilterCallback::RequestKey(ULONGflags, FILFILE_CONTROL_OUT *request,ULONG *requestSize)
NTSTATUSCFilterCallback::Request(ULONGflags, FILFILE_CONTROL_OUT *request,ULONG *requestSize)
返回key函数
NTSTATUSCFilterCallback::Response(ULONGcookie, UCHAR *response,ULONG responseSize)
3. 注册回调IoRegisterFsRegistrationChange
当一个文件系统激活或者解注册过滤驱动会得到一个通知,注册的回调函数FileSystemRegister,在此函数中实现了对FILE_DEVICE_DISK_FILE_SYSTEM磁盘文件系统的挂载,并生成了设备驱动的扩展设备设备,扩展设备结构为
structFILFILE_COMMON_EXTENSION
{
USHORT Type;
USHORT Size;
DEVICE_OBJECT* Device;
};
structFILFILE_VOLUME_EXTENSION
{
FILFILE_COMMON_EXTENSION Common;//自己设备
LIST_ENTRY Link;
DEVICE_OBJECT* Real;//真实设备
DEVICE_OBJECT* Lower;
ULONG LowerType;
UNICODE_STRING LowerName;
CFilterVolume Volume;
bool System;
};
4. NTSTATUSCFilterCipherManager::RecognizeHeader(
FILE_OBJECT *file,
CFilterHeader *header,
ULONGflags)
识别文件头的功能函数。在检查文件夹存在一个pgpFs.INI配置文件后,首先就要检查加密文件头是否是本设备对象所识别的加密文件头。
文件头的结构如下:
structFILFILE_HEADER_BLOCK
{
ULONG Magic; // 文件头标志: FilF;
ULONG Version; // 文件头版本
ULONG Cipher; //加密核心算法选择分为AES128 AES256 AES192
ULONG BlockSize; //当前文件头Block大小
ULONG PayloadSize; //文件头bloak后的payload大小
ULONG PayloadCrc; //Payload的crc32校验和
ULONG Deepness; // AutoConfig files only: Deepness of correspondig Entity [~0u:=INFINITE, 0:=1, ..., N:=N+1]
ULONG Reserved; // not used yet
LARGE_INTEGER Nonce; // Nonce, unique for each file, combined with file Offset forms an IV
UCHAR FileKey[32]; // 驱动请求解密key的密码
};
加密文件开始即以上的FILFILE_HEADER_BLOCK结构,在这段结构之后即payload块,两者加在一起是4K的大小。
验证文件头块的过程中首先验证文件头的Magic标志,如果标志位不是FilF,则是无效文件头,再次验证payload的crc32校验和,计算payload校验和和FILFILE_HEADER_BLOCK. PayloadCrc不相等则是无效头文件,最后验证Cipher的高位和低位,高位必须是0xF,低位必须是{1,2,3}中的一个值,否则就是无效文件头,有效的文件头才可以实施透明加解密。
5. NTSTATUSCFilterBase::CreateFile(DEVICE_OBJECT* device,
UNICODE_STRING* path,
ULONG access,
ULONG share,
ULONG options, ULONG attribs,
FILE_OBJECT** file,
HANDLE* fileHandle)
设备驱动打开文件,函数中调用了IoCreateFileSpecifyDeviceObjectHint函数,此函数打开文件可以直接从下层设备打开而不进入文件系统栈,解决文件系统重入问题
6.NTSTATUSCFilterBase::ReadWrite(DEVICE_OBJECT *device,
FILE_OBJECT *file,
FILFILE_READ_WRITEconst* readWrite)
FILFILE_READ_WRITE结构如下
structFILFILE_READ_WRITE
{
UCHAR* Buffer;
MDL* Mdl;
LARGE_INTEGEROffset;
ULONG Length;
ULONG Flags;
UCHAR Major;
BOOLEAN Wait;
};
利用IRP同步转发的技术实现文件的同步读写,等待device设备返回结果,使用IoAllocateIrp分配IRP,根据FILFILE_READ_WRITE传入的参数设置IRP相关参数,最后通过IoCallDriver调用转发IRP到下一层设备中读取文件,跳过当前设备栈,设置等待事件,达到同步的目的,通过IoSetCompletionRoutine设置回调通知事件态,重新获得IRP的控制权,而获得读取或者写入文件数据。
7.CFilterVolume::IsSpecific(FILFILE_TRACK_CONTEXT *track,
ULONG flags)
检查一些特别目录的函数,如"//Documents and Settings//","//Local Settings//Temporary Internet Files//Content.IE5//",”//Wndows”等系统特有的目录则不进行加密,