技術分析
⑴.主要派遣例程
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”等系統特有的目錄則不進行加密,