البحث عن نظام ترخيص العميل في Windows kernel المعروض من فئة SystemPolicyInformation
في استدعاء نظام NtQuerySystemInformation
.
توجد خدمتان أساسيتان لوضع المستخدم تتفاعلان مباشرة مع ترخيص العميل: clipc.dll
(عميل منصة ترخيص العميل) و clipsvc.dll
(خدمة ترخيص العميل). صورة النواة التي تعالج استعلامات ترخيص العميل هي clipsp.sys
(سياسة نظام ترخيص العميل). نظرًا لأن التركيز ينصب على الأجزاء الداخلية لإجراءات الترخيص في النواة، فلن يتم ذكر الكثير عن خدمات وضع المستخدم.
يبدأ العميل خدمة الترخيص من خلال مدير الخدمة ويتواصل مع الخدمة من خلال استدعاءات الإجراءات عن بعد (RPC). تسجل الخدمة العديد من المعالجات المستخدمة في Software Licensing API.
ستقوم المعالجات التي يجب أن تتفاعل مع معلومات ترخيص kernel باستدعاء NtQuerySystemInformation
مع فئة معلومات SystemPolicyInformation
. تم توثيق بنية فئة 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 ذات المستوى الأعلى. وعلى هذا النحو، فإن الهياكل الداخلية التي تستخدمها النواة غير موثقة. تم إجراء هندسة عكسية لكل بنية داخلية موثقة في بحثي وتمت تسميتها وفقًا لاستخدامها المستنتج وقد لا تعكس الاستخدام الداخلي الفعلي بدقة.
تكشف الهندسة العكسية الموجزة لمعالجات ترخيص 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 kernel لفك تشفير الحمولات الموقعة وتنفيذها على الكومة باستخدام تشفير feistel عن طريق الكشف عن فئة معلومات نظام خاصة : 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
الذي سيقوم بتحديث بيانات الترخيص وإعداد Kernel Data Protection. سيقوم الروتين في النهاية باستدعاء ClipInitHandles
، الذي يقوم بتهيئة العناصر العمومية المستخدمة في عمليات الاسترجاعات الخاصة بترخيص العميل بالإضافة إلى g_kernelCallbacks
. لا يقوم kernel فعليًا بإعداد جدول رد اتصال kernel العام في ClipInitHandles
، ولكنه بدلاً من ذلك سيقوم بتمرير الجدول إلى ClipSpInitialize
الموجود في clipsp.sys
.
تعد صورة سياسة نظام ترخيص العميل ( clipsp
) مسؤولة عن التعامل مع الأجزاء الداخلية لوظيفة سياسة النظام في النواة. على هذا النحو، تم تشويشه باستخدام Microsoft WarBird لمنع الهندسة العكسية. تحتوي الصورة على عدة أقسام ذات إنتروبيا عالية ( PAGEwx1
وما إلى ذلك) وأسماء تشير إلى أنه سيتم تفكيكها وتنفيذها أثناء وقت التشغيل.
سوف يقوم Clipsp باستدعاء Warbird Runtime لتفريغ التعليمات البرمجية قبل التنفيذ وإعادة حزمها بعد ذلك. ستقوم الوظائف بتخصيص العديد من قوائم واصفات الذاكرة (MDLs) لإعادة تعيين الصفحات الفعلية إلى عنوان rwx الظاهري في مساحة النظام. لن يكون تفريغ الصورة في وقت التشغيل موثوقًا حيث تتم إعادة تجميع الأقسام بعد التنفيذ وسيتم تفريغ الأجزاء الضرورية للتنفيذ فقط. إحدى الطرق البسيطة لتفريغ المقاطع تلقائيًا هي محاكاة إجراءات فك التشفير باستخدام إطار عمل محاكاة ثنائي مثل Qiling. لقد قمت بكتابة برنامج نصي بسيط لفك الحزم في Python والذي سيحاكي العديد من واجهات برمجة تطبيقات kernel ويتخلص من القسم الذي تم فك حزمه بمجرد تحرير 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
.