Penelitian tentang sistem lisensi klien di kernel Windows yang diekspos dari kelas SystemPolicyInformation
dalam panggilan sistem NtQuerySystemInformation
.
Ada dua layanan mode pengguna utama yang berinteraksi langsung dengan pelisensian klien: clipc.dll
(Klien Platform Lisensi Klien) dan clipsvc.dll
(Layanan Lisensi Klien). Gambar kernel yang menangani pertanyaan lisensi klien adalah clipsp.sys
(Kebijakan Sistem Lisensi Klien). Karena fokusnya adalah pada internal rutinitas perizinan di kernel, tidak banyak yang akan disebutkan tentang layanan mode pengguna.
Klien memulai layanan lisensi melalui manajer layanan dan berkomunikasi dengan layanan melalui panggilan prosedur jarak jauh (RPC). Layanan ini mendaftarkan beberapa penangan yang digunakan dalam API Lisensi Perangkat Lunak.
Penangan yang harus berinteraksi dengan informasi lisensi kernel akan memanggil NtQuerySystemInformation
dengan kelas informasi SystemPolicyInformation
. Struktur kelas SystemPolicyInformation
untuk transfer data telah didokumentasikan oleh Geoff Chappell. Strukturnya ditunjukkan di bawah ini:
typedef struct _SYSTEM_POLICY_INFORMATION
{
PVOID InputData;
PVOID OutputData;
ULONG InputSize;
ULONG OutputSize;
ULONG Version;
NTSTATUS Status;
} SYSTEM_POLICY_INFORMATION, * PSYSTEM_POLICY_INFORMATION;
Halaman referensi di MSDN untuk kelas informasi menawarkan struktur yang lebih tidak lengkap dan menyarankan penggunaan SL API tingkat yang lebih tinggi. Dengan demikian, struktur internal yang digunakan oleh kernel tidak terdokumentasi. Setiap struktur internal yang didokumentasikan dalam penelitian saya telah direkayasa balik dan diberi nama sesuai dengan kesimpulan penggunaan dan mungkin tidak secara akurat mencerminkan penggunaan internal sebenarnya.
Rekayasa balik singkat dari pengendali lisensi ClipSVC mengungkapkan bahwa struktur masukan dan keluaran dienkripsi dan didekripsi menggunakan sandi yang diterapkan oleh obfuscator internal 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 telah diteliti pada tahun-tahun sebelumnya dan memperlihatkan berbagai cara penyamaran yang berbeda termasuk penyamaran mesin virtual, pengepakan kode, dan bahkan fungsionalitas yang terintegrasi dengan kernel windows untuk mendekripsi dan mengeksekusi muatan yang ditandatangani di heap menggunakan sandi feistel dengan memaparkan kelas informasi sistem khusus : SystemControlFlowTransition
.
Struktur internal yang diuraikan oleh kernel terdiri dari tiga blok data yang berisi data terenkripsi, argumen dekripsi untuk cipher WarBird, dan kunci dekripsi 64bit. Data masukan yang didekripsi berisi jenis informasi kebijakan, jumlah argumen, dan argumen sandi serta kunci untuk mengenkripsi data. Checksum XOR untuk data yang didekripsi ditambahkan ke data terenkripsi dan diverifikasi setelah dekripsi. Blok sandi dekripsi diformat dengan argumen untuk subrutin sandi yang ditempatkan di atas dan argumen di bawah. Kernel akan meneruskan parameter dalam urutan terbalik selama 16 iterasi. Argumen kunci dan sandi di-hardcode tergantung pada kelas kebijakan.
// +-------------------+ +-------------------+
// | 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;
Struktur input yang didekripsi berisi jumlah parameter yang berhubungan dengan tipe informasi kebijakan dan header yang menentukan tipe informasi beserta argumen yang diperlukan cipher WarBird untuk mengenkripsi data.
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;
Setelah sandi dan kunci WarBird yang diperlukan untuk mengenkripsi dan mendekripsi data disiapkan, data dienkripsi dan eksekusi diteruskan ke NtQuerySystemInformation
. Tabel saklar kelas informasi pada akhirnya akan mengirimkan data ke SPCall2ServerInternal
, di mana ia akan mendekripsi dan memverifikasi data dan memanggil salah satu rutinitas lisensi internal. Beberapa kelas kebijakan terbalik ditampilkan:
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;
Beberapa rutinitas perizinan selanjutnya akan dikirim ke fungsi yang terletak di tabel global, nt!g_kernelCallbacks
. Tabel fungsi global ini berisi penunjuk fungsi di dalam clipsp.sys
, yang menangani kebijakan sistem lisensi klien. Selama inisialisasi data lisensi, kernel pertama-tama akan mengatur status lisensi di silo server global ( PspHostSiloGlobals->ExpLicenseState
) dan akan memuat nilai lisensi dari registri di bawah ProductOptions
. Kemudian akan memanggil ExInitLicenseData
yang akan memperbarui data lisensi dan mengatur Perlindungan Data Kernel. Rutinitas pada akhirnya akan memanggil ClipInitHandles
, yang menginisialisasi global yang digunakan untuk callback lisensi klien bersama dengan g_kernelCallbacks
. Kernel sebenarnya tidak mengatur tabel callback kernel global di ClipInitHandles
, namun akan meneruskan tabel tersebut ke ClipSpInitialize
yang terletak di clipsp.sys
.
Citra kebijakan sistem lisensi klien ( clipsp
) bertanggung jawab untuk menangani fungsi internal kebijakan sistem di kernel. Oleh karena itu, hal ini dikaburkan dengan Microsoft WarBird untuk mencegah rekayasa balik. Gambar berisi beberapa bagian dengan entropi tinggi ( PAGEwx1
dll.) dan nama yang menunjukkan bahwa gambar tersebut akan dibongkar dan dieksekusi selama runtime.
Clipsp akan memanggil Warbird Runtime untuk membongkar kode sebelum dieksekusi dan dikemas ulang setelahnya. Fungsi tersebut akan mengalokasikan beberapa daftar deskriptor memori (MDL) untuk memetakan ulang halaman fisik ke alamat virtual rwx di ruang sistem. Membuang gambar saat runtime tidak akan dapat diandalkan karena bagian-bagian tersebut dikemas ulang setelah eksekusi dan hanya bagian-bagian yang diperlukan untuk eksekusi yang akan dibongkar. Metode sederhana untuk membongkar bagian secara otomatis adalah dengan meniru rutinitas dekripsi dengan kerangka kerja emulasi biner seperti Qiling. Saya telah menulis skrip unpacker sederhana dengan Python yang akan meniru berbagai API kernel dan membuang bagian yang belum dibongkar setelah MDL dibebaskan.
Analisis lebih lanjut dapat dilakukan setelah mengganti bagian yang dikemas dengan kode yang belum dibongkar. ClipSpInitialize
akan memanggil SpInitialize
untuk mengisi g_kernelCallbacks
, mengatur kunci registri dan menginisialisasi Penyedia CNG dan kunci kriptografi.
Subrutin SpInitializeDeviceExtension
pertama-tama akan memverifikasi hak akses ke kunci registri khusus yang terletak di \Registry\Machine\System\CurrentControlSet\Control\{7746D80F-97E0-4E26-9543-26B41FC22F79}
yang dicadangkan untuk hak digital. Akses ke kunci registri tertentu ditujukan hanya untuk penggunaan lisensi dan upaya mengaksesnya dari proses yang tidak memiliki hak istimewa akan menghasilkan ACCESS_DENIED
. Selanjutnya, ia akan mengakses beberapa subkunci di bawah kunci yang sama termasuk {A25AE4F2-1B96-4CED-8007-AA30E9B1A218}
, {D73E01AC-F5A0-4D80-928B-33C1920C38BA}
, {59AEE675-B203-4D61-9A1F-04518A20F359}
, {FB9F5B62-B48B-45F5-8586-E514958C92E2}
dan {221601AB-48C7-4970-B0EC-96E66F578407}
.
Rekayasa balik lebih lanjut dari masing-masing callback memerlukan rekayasa balik struktur _EXP_LICENSE_STATE
di _ESERVERSILO_GLOBALS
.