Исследование системы лицензирования клиентов в ядре Windows, предоставляемой классом SystemPolicyInformation
в системном вызове NtQuerySystemInformation
.
Существует две основные службы пользовательского режима, которые напрямую взаимодействуют с клиентским лицензированием: clipc.dll
(клиент платформы клиентского лицензирования) и clipsvc.dll
(служба клиентских лицензий). Образом ядра, обрабатывающим запросы клиентских лицензий, является clipsp.sys
(политика системы клиентских лицензий). Поскольку основное внимание уделяется внутреннему устройству процедур лицензирования в ядре, о службах пользовательского режима упоминаться не будет.
Клиент запускает службу лицензирования через диспетчер служб и взаимодействует со службой посредством удаленных вызовов процедур (RPC). Служба регистрирует несколько обработчиков, которые используются в API лицензирования программного обеспечения.
Обработчики, которые должны взаимодействовать с информацией о лицензировании ядра, будут вызывать NtQuerySystemInformation
с классом информации SystemPolicyInformation
. Структура класса SystemPolicyInformation
для передачи данных была задокументирована Джеффом Чаппеллом. Структура показана ниже:
typedef struct _SYSTEM_POLICY_INFORMATION
{
PVOID InputData;
PVOID OutputData;
ULONG InputSize;
ULONG OutputSize;
ULONG Version;
NTSTATUS Status;
} SYSTEM_POLICY_INFORMATION, * PSYSTEM_POLICY_INFORMATION;
Справочная страница в MSDN для информационного класса предлагает еще более неполную структуру и предлагает использовать API SL более высокого уровня. Таким образом, внутренние структуры, используемые ядром, недокументированы. Каждая внутренняя структура, описанная в моем исследовании, была реконструирована и названа в соответствии с предполагаемым использованием и может неточно отражать фактическое внутреннее использование.
Краткий обратный инжиниринг обработчиков лицензий 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 для расшифровки и выполнения подписанных полезных данных в куче с использованием шифра Фейстеля, предоставляя специальный класс системной информации. : SystemControlFlowTransition
.
Внутренняя структура, анализируемая ядром, состоит из трех блоков данных, содержащих зашифрованные данные, аргументы расшифровки для шифра WarBird и 64-битный ключ расшифровки. Расшифрованные входные данные содержат тип информации о политике, количество аргументов, а также аргументы шифрования и ключ для шифрования данных. Контрольная сумма XOR для расшифрованных данных добавляется к зашифрованным данным и проверяется после расшифровки. Блок шифрования дешифрования отформатирован так, что аргументы для подпрограмм шифрования расположены вверху, а аргументы — внизу. Ядро будет передавать параметры в обратном порядке в течение 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
, а вместо этого передает таблицу в ClipSpInitialize
расположенный в clipsp.sys
.
Образ политики системы лицензирования клиента ( 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}
.
Дальнейшее обратное проектирование отдельных обратных вызовов требует обратного проектирования структуры _EXP_LICENSE_STATE
в _ESERVERSILO_GLOBALS
.