Forschung zum Client-Lizenzierungssystem im Windows-Kernel, verfügbar gemacht von der SystemPolicyInformation
Klasse im NtQuerySystemInformation
-Systemaufruf.
Es gibt zwei primäre Benutzermodusdienste, die direkt mit der Clientlizenzierung interagieren: clipc.dll
(Client Licensing Platform Client) und clipsvc.dll
(Client License Service). Das Kernel-Image, das Client-Lizenzabfragen verarbeitet, ist clipsp.sys
(Client License System Policy). Da der Schwerpunkt auf den Interna der Lizenzierungsroutinen im Kernel liegt, wird nicht viel über die Usermode-Dienste erwähnt.
Der Client startet den Lizenzdienst über den Dienstmanager und kommuniziert mit dem Dienst über Remote Procedure Calls (RPC). Der Dienst registriert mehrere Handler, die in der Softwarelizenzierungs-API verwendet werden.
Handler, die eine Schnittstelle zu Kernel-Lizenzinformationen benötigen, rufen NtQuerySystemInformation
mit der Informationsklasse SystemPolicyInformation
auf. Die SystemPolicyInformation
Klassenstruktur für die Datenübertragung wurde von Geoff Chappell dokumentiert. Die Struktur ist unten dargestellt:
typedef struct _SYSTEM_POLICY_INFORMATION
{
PVOID InputData;
PVOID OutputData;
ULONG InputSize;
ULONG OutputSize;
ULONG Version;
NTSTATUS Status;
} SYSTEM_POLICY_INFORMATION, * PSYSTEM_POLICY_INFORMATION;
Die Referenzseite in MSDN für die Informationsklasse bietet eine noch unvollständigere Struktur und schlägt die Verwendung der übergeordneten SL-API vor. Daher sind die vom Kernel verwendeten internen Strukturen nicht dokumentiert. Jede in meiner Forschung dokumentierte interne Struktur wurde rückentwickelt und entsprechend ihrer abgeleiteten Verwendung benannt und spiegelt möglicherweise nicht genau die tatsächliche interne Verwendung wider.
Ein kurzes Reverse Engineering der ClipSVC-Lizenzhandler zeigt, dass Eingabe- und Ausgabestrukturen mithilfe einer Chiffre verschlüsselt und entschlüsselt werden, die von Microsofts internem Verschleierer WarBird implementiert wird.
// 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 wurde in früheren Jahren untersucht und deckt verschiedene Verschleierungsvorgänge auf, darunter die Verschleierung virtueller Maschinen, das Packen von Code und sogar in den Windows-Kernel integrierte Funktionen zum Entschlüsseln und Ausführen signierter Nutzlasten auf dem Heap mithilfe einer Feistel-Chiffre, indem eine spezielle Systeminformationsklasse offengelegt wird : SystemControlFlowTransition
.
Die vom Kernel analysierte interne Struktur besteht aus drei Datenblöcken, die die verschlüsselten Daten, die Entschlüsselungsargumente für die WarBird-Verschlüsselung und einen 64-Bit-Entschlüsselungsschlüssel enthalten. Die entschlüsselten Eingabedaten enthalten den Richtlinieninformationstyp, die Argumentanzahl sowie die Verschlüsselungsargumente und den Schlüssel zum Verschlüsseln der Daten. Eine XOR-Prüfsumme für die entschlüsselten Daten wird an die verschlüsselten Daten angehängt und nach der Entschlüsselung überprüft. Der Entschlüsselungs-Verschlüsselungsblock ist mit Argumenten für Verschlüsselungsunterroutinen oben und unten mit Argumenten formatiert. Der Kernel übergibt die Parameter 16 Iterationen lang in umgekehrter Reihenfolge. Die Schlüssel und Verschlüsselungsargumente sind je nach Richtlinienklasse fest codiert.
// +-------------------+ +-------------------+
// | 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;
Die entschlüsselte Eingabestruktur enthält die Anzahl der Parameter relativ zum Richtlinieninformationstyp und einen Header, der den Informationstyp zusammen mit Argumenten angibt, die für die WarBird-Verschlüsselung zum Verschlüsseln der Daten erforderlich sind.
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;
Sobald die WarBird-Verschlüsselung und die zum Ver- und Entschlüsseln der Daten erforderlichen Schlüssel vorbereitet sind, werden die Daten verschlüsselt und die Ausführung an NtQuerySystemInformation
übergeben. Die Informationsklassen-Umschalttabelle sendet die Daten schließlich an SPCall2ServerInternal
, wo sie die Daten entschlüsselt und überprüft und eine der internen Lizenzroutinen aufruft. Es werden einige der umgekehrten Versicherungsklassen angezeigt:
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;
Einige der Lizenzierungsroutinen werden außerdem an eine Funktion weitergeleitet, die sich in einer globalen Tabelle befindet, nt!g_kernelCallbacks
. Diese globale Funktionstabelle enthält Funktionszeiger innerhalb von clipsp.sys
, das die Client-Lizenzsystemrichtlinien verwaltet. Während der Initialisierung der Lizenzdaten richtet der Kernel zunächst den Lizenzstatus in einem globalen Serversilo ( PspHostSiloGlobals->ExpLicenseState
) ein und lädt Lizenzwerte aus der Registrierung unter ProductOptions
. Anschließend wird ExInitLicenseData
aufgerufen, das die Lizenzdaten aktualisiert und den Kernel Data Protection einrichtet. Die Routine ruft schließlich ClipInitHandles
auf, das zusammen mit g_kernelCallbacks
Globals initialisiert, die für Client-Lizenzierungsrückrufe verwendet werden. Der Kernel richtet die globale Kernel-Callback-Tabelle nicht wirklich in ClipInitHandles
ein, sondern übergibt die Tabelle stattdessen an ClipSpInitialize
in clipsp.sys
.
Das Client-Lizenzierungssystemrichtlinien-Image ( clipsp
) ist für die Handhabung der Interna der Systemrichtlinienfunktionen im Kernel verantwortlich. Daher wird es mit Microsoft WarBird verschleiert, um Reverse Engineering zu verhindern. Das Bild enthält mehrere Abschnitte mit hoher Entropie ( PAGEwx1
usw.) und Namen, die darauf hinweisen, dass es zur Laufzeit entpackt und ausgeführt wird.
Clipsp fordert die Warbird-Laufzeit auf, den Code vor der Ausführung zu entpacken und anschließend neu zu packen. Die Funktionen weisen mehrere Speicherdeskriptorlisten (MDLs) zu, um die physischen Seiten einer virtuellen RWX-Adresse im Systemraum neu zuzuordnen. Das Speichern des Bildes zur Laufzeit ist nicht zuverlässig, da die Abschnitte nach der Ausführung neu gepackt werden und nur die für die Ausführung erforderlichen Abschnitte entpackt werden. Eine einfache Methode zum automatischen Entpacken der Abschnitte besteht darin, die Entschlüsselungsroutinen mit einem binären Emulationsframework wie Qiling zu emulieren. Ich habe ein einfaches Entpacker-Skript in Python geschrieben, das verschiedene Kernel-APIs emuliert und den entpackten Abschnitt ausgibt, sobald die MDL freigegeben ist.
Eine weitere Analyse kann durchgeführt werden, nachdem die gepackten Abschnitte durch den entpackten Code ersetzt wurden. ClipSpInitialize
ruft SpInitialize
auf, um g_kernelCallbacks
zu füllen, Registrierungsschlüssel einzurichten und CNG-Anbieter und kryptografische Schlüssel zu initialisieren.
Die SpInitializeDeviceExtension
Subroutine überprüft zunächst die Zugriffsrechte auf einen speziellen Registrierungsschlüssel unter \Registry\Machine\System\CurrentControlSet\Control\{7746D80F-97E0-4E26-9543-26B41FC22F79}
der für digitale Berechtigungen reserviert ist. Der Zugriff auf den spezifischen Registrierungsschlüssel ist nur für die Lizenznutzung vorgesehen und Versuche, über einen nicht privilegierten Prozess darauf zuzugreifen, führen zu ACCESS_DENIED
. Darüber hinaus wird auf mehrere Unterschlüssel unter demselben Schlüssel zugegriffen, darunter {A25AE4F2-1B96-4CED-8007-AA30E9B1A218}
, {D73E01AC-F5A0-4D80-928B-33C1920C38BA}
. {59AEE675-B203-4D61-9A1F-04518A20F359}
, {FB9F5B62-B48B-45F5-8586-E514958C92E2}
und {221601AB-48C7-4970-B0EC-96E66F578407}
.
Weiteres Reverse Engineering der einzelnen Rückrufe erfordert Reverse Engineering der Struktur _EXP_LICENSE_STATE
in _ESERVERSILO_GLOBALS
.