研究從NtQuerySystemInformation
系統呼叫中的SystemPolicyInformation
類別公開的 Windows 核心中的客戶端許可系統。
有兩個主要的使用者模式服務直接與客戶端授權互動: clipc.dll
(客戶端授權平台用戶端)和clipsvc.dll
(客戶端授權服務)。處理客戶端許可證查詢的核心映像是clipsp.sys
(客戶端許可證系統策略)。由於重點是核心中許可例程的內部結構,因此不會過多提及使用者模式服務。
用戶端透過服務管理員啟動許可證服務,並透過遠端程序呼叫(RPC)與服務進行通訊。該服務註冊了幾個在軟體授權 API 中使用的處理程序。
必須與核心許可資訊互動的處理程序將使用SystemPolicyInformation
資訊類別呼叫NtQuerySystemInformation
。用於資料傳輸的SystemPolicyInformation
類別結構已由 Geoff Chappell 記錄。結構如下圖所示:
typedef struct _SYSTEM_POLICY_INFORMATION
{
PVOID InputData;
PVOID OutputData;
ULONG InputSize;
ULONG OutputSize;
ULONG Version;
NTSTATUS Status;
} SYSTEM_POLICY_INFORMATION, * PSYSTEM_POLICY_INFORMATION;
MSDN 中資訊類別的參考頁面提供了更不完整的結構,並建議使用更高層級的 SL API。因此,核心使用的內部結構沒有文檔記錄。我的研究中記錄的每個內部結構都經過逆向工程,並根據其推斷的用途進行命名,可能無法準確反映實際的內部用途。
ClipSVC 許可證處理程序的簡短逆向工程表明,輸入和輸出結構是使用 Microsoft 內部混淆器 WarBird 實現的密碼進行加密和解密的。
// setup decryption cipher and key
pDecryptionCipher = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 0xA0 );
if ( !pDecryptionCipher )
goto LABEL_2;
decryptionCipher = pDecryptionCipher;
*pDecryptionCipher = `WarbirdUmGetDecryptionCipher ' ::`2 ' ::DecryptionCipher[ 0 ];
pDecryptionCipher[ 1 ] = `WarbirdUmGetDecryptionCipher ' ::`2 ' ::DecryptionCipher[ 1 ];
pDecryptionCipher[ 2 ] = `WarbirdUmGetDecryptionCipher ' ::`2 ' ::DecryptionCipher[ 2 ];
pDecryptionCipher[ 3 ] = `WarbirdUmGetDecryptionCipher ' ::`2 ' ::DecryptionCipher[ 3 ];
pDecryptionCipher[ 4 ] = `WarbirdUmGetDecryptionCipher ' ::`2 ' ::DecryptionCipher[ 4 ];
pDecryptionCipher[ 5 ] = `WarbirdUmGetDecryptionCipher ' ::`2 ' ::DecryptionCipher[ 5 ];
pDecryptionCipher[ 6 ] = `WarbirdUmGetDecryptionCipher ' ::`2 ' ::DecryptionCipher[ 6 ];
pDecryptionCipher[ 7 ] = `WarbirdUmGetDecryptionCipher ' ::`2 ' ::DecryptionCipher[ 7 ];
pDecryptionCipher[ 8 ] = `WarbirdUmGetDecryptionCipher ' ::`2 ' ::DecryptionCipher[ 8 ];
pDecryptionCipher[ 9 ] = `WarbirdUmGetDecryptionCipher ' ::`2 ' ::DecryptionCipher[ 9 ];
pDecryptionKey = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 8 );
if ( !pDecryptionKey )
{
LABEL_2:
ReturnStatus = STATUS_NO_MEMORY;
goto END;
}
decryptionKey = pDecryptionKey;
*pDecryptionKey = `WarbirdUmGetDecryptionKey ' ::`2 ' ::nDecryptionKey;
Microsoft WarBird 在前幾年進行了研究,並公開了各種不同的混淆過程,包括虛擬機器混淆、程式碼打包,甚至整合了Windows 核心的功能,透過公開特殊的系統資訊類,使用Feistel 密碼在堆上解密和執行簽章的有效負載: SystemControlFlowTransition
。
核心解析的內部結構由三個資料塊組成,其中包含加密資料、WarBird 密碼的解密參數和 64 位元解密金鑰。解密的輸入資料包含策略資訊類型、參數計數以及加密資料的密碼參數和金鑰。解密資料的異或校驗和被附加到加密資料上,並在解密後進行驗證。解密密碼塊的格式為位於頂部的密碼子程式的參數和位於底部的參數。核心將以相反的順序傳遞參數,進行 16 次迭代。密鑰和密碼參數根據策略類別進行硬編碼。
// +-------------------+ +-------------------+
// | Size | | | Block B
// +-------------------+ +-------------------+ ------------ 0x0
// | | | | ^
// | | | 0xC998E51B | |
// | Function Args | | 0xA3A9632E | |
// | 8 bytes | | | | 128 bytes
// | | | | |
// | | | 0x00000000 | |
// | | | | |
// +-------------------+ +-------------------+ ---------- 0x7E
// | | | |
// | Fn Ptr Index | | 0x050B1902 | ^ 32 bytes
// | 2 bytes | | 0x1F1F1F1F | | 0x9E
// +-------------------+ +-------------------+ | ---------- 0xA0
typedef struct _WB_CIPHER
{
UCHAR FnArgs[ 128 ];
UCHAR FnIndex[ 32 ];
} WB_CIPHER, * WB_CIPHER;
typedef struct PWB_KEY
{
ULONGLONG Key;
} WB_KEY, * PWB_KEY;
typedef struct _SP_ENCRYPTED_HEADER
{
UCHAR EncryptedBody[ SP_DATA_SIZE ];
ULONGLONG XorChkKey;
} SP_ENCRYPTED_HEADER;
typedef struct _SP_ENCRYPTED_DATA
{
ULONG EncryptedDataSize;
SP_ENCRYPTED_HEADER EncryptedHeaderData;
ULONG DecryptArgSize;
WB_CIPHER DecryptArgs;
ULONG KeySize;
WB_KEY DecryptKey;
} SP_ENCRYPTED_DATA, * PSP_ENCRYPTED_DATA;
解密的輸入結構包含與政策資訊類型相關的參數數量和指定資訊類型的標頭以及 WarBird 密碼加密資料所需的參數。
typedef struct _SP_DECRYPTED_HEADER
{
ULONG PolicyTypeSize;
SYSTEM_POLICY_CLASS PolicyType;
ULONG EncyptArgSize;
WB_CIPHER EncryptArgs;
ULONG KeySize;
WB_KEY EncryptKey;
SP_BODY Body;
} SP_DECRYPTED_HEADER;
typedef struct _SP_DECRYPTED_DATA
{
ULONG ParameterCount;
ULONG DecryptedSize;
SP_DECRYPTED_HEADER HeaderData;
ULONG a;
USHORT b;
} SP_DECRYPTED_DATA;
一旦準備好加密和解密資料所需的 WarBird 密碼和金鑰,資料就會被加密,並將執行傳遞到NtQuerySystemInformation
。資訊類切換表最終會將資料分派到SPCall2ServerInternal
,在那裡它將解密和驗證資料並呼叫內部許可證例程之一。顯示了一些反向策略類別:
typedef enum _SYSTEM_POLICY_CLASS
{
QueryPolicy,
UpdatePolicies,
AuthenticateCaller,
WaitForDisplayWindow = 5 ,
FileUsnQuery = 22 ,
FileIntegrityUpdate,
FileIntegrityQuery,
UpdateLicense = 100 ,
RemoveLicense,
NotImplemented,
CreateLicenseEfsHeader,
LicenseEfsHeaderContainsFek,
GetLicenseChallange,
GetBaseContentKeyFromLicense,
GetBaseContentKeyFromKeyID,
IsAppLicensed = 109 ,
DumpLicenseGroup,
Clear,
ClepSign,
ClepKdf,
UpdateOsLicenseBlob = 204 ,
CheckLicense,
GetCurrentHardwareID,
CreateLicenseKeyIDEfsHeader,
GetAppPolicyValue,
QueryCachedOptionalInfo,
AcRequest,
AcHmac,
UpdateImdsResponse
} SYSTEM_POLICY_CLASS;
一些許可例程將進一步分派到位於全域表nt!g_kernelCallbacks
中的函數。此全域函數表包含clipsp.sys
內部的函數指針,用於處理客戶端許可證系統策略。在許可證資料初始化期間,核心將首先在全域伺服器孤島( PspHostSiloGlobals->ExpLicenseState
)中設定許可證狀態,並從ProductOptions
下的註冊表載入許可證值。然後它將呼叫ExInitLicenseData
,它將更新許可證資料並設定核心資料保護。該例程最終將呼叫ClipInitHandles
,它初始化用於客戶端許可回呼的全域變數以及g_kernelCallbacks
。核心其實並沒有在ClipInitHandles
中設定全域核心回呼表,而是將該表傳遞給位於clipsp.sys
中的ClipSpInitialize
。
客戶端許可系統策略映像 ( clipsp
) 負責處理核心中系統策略功能的內部結構。因此,它與 Microsoft WarBird 混淆以防止逆向工程。該圖像包含幾個具有高熵的部分( PAGEwx1
等)以及指示它將在運行時解壓縮和執行的名稱。
Clipsp 將呼叫 Warbird 執行時間在執行前解壓縮程式碼並在執行後重新打包。這些函數將分配多個記憶體描述符清單 (MDL),以將實體頁重新對應到系統空間中的 rwx 虛擬位址。在運行時轉儲映像並不可靠,因為這些部分在執行後會重新打包,並且只有執行所需的部分才會被解包。自動解包這些部分的一個簡單方法是使用二進位模擬框架(例如 Qiling)來模擬解密例程。我用 Python 編寫了一個簡單的解包器腳本,它將模擬各種核心 API,並在釋放 MDL 後轉儲解包部分。
將打包的部分替換為未打包的程式碼後,可以進行進一步的分析。 ClipSpInitialize
將呼叫SpInitialize
來填入g_kernelCallbacks
、設定登錄項目並初始化 CNG 提供者和加密金鑰。
SpInitializeDeviceExtension
子程式將首先驗證對位於\Registry\Machine\System\CurrentControlSet\Control\{7746D80F-97E0-4E26-9543-26B41FC22F79}
為數位權利保留的特殊登錄機碼項目的存取權限。對特定註冊表項的存取僅用於許可證使用,嘗試從非特權程序存取它將導致ACCESS_DENIED
。此外,它將訪問同一密鑰下的多個子密鑰,包括{A25AE4F2-1B96-4CED-8007-AA30E9B1A218}
、 {D73E01AC-F5A0-4D80-928B-33C1920C38BA}
、 {59AEE675-B203-4D61-9A1F-04518A20F359}
, {FB9F5B62-B48B-45F5-8586-E514958C92E2}
和{221601AB-48C7-4970-B0EC-96E66F578407}
。
對各個回調的進一步逆向工程需要對_ESERVERSILO_GLOBALS
中的_EXP_LICENSE_STATE
結構進行逆向工程。