P01NT
0x9 09/10

حل معمل guesser.exe (الفصل السادس)

حل معمل guesser.exe: سلسلة مشفرة بـ XOR واستعادة مفتاح التسجيل وتجاوزه بتعديل السجلات أو ترقيع القفزات.

· 9 دقائق قراءة · 1772 كلمة
المحتويات

بسم الله الرحمن الرحيم


معلومات العينة

  • اسم الملف: guesser.exe
  • MD5: d60f46728627e44ab8742e17e7cd4562

الإجابات المختصرة (Short Answers)

1. ما القيمة التي يجب أن ترجعها استدعاء الدالة sub_401000 عند 0x4010F9 حتى يقبل البرنامج مفتاح التسجيل؟

يجب أن ترجع الدالة sub_401000 صفرًا حتى يقبل البرنامج مفتاح التسجيل.

2. ما وسيط الدالة الممرر إلى sub_401000 عند 0x4010F9؟

وسيط الدالة هو سلسلة مفتاح التسجيل التي أدخلها المستخدم في سطر الأوامر.

3. حلّل الدالة sub_401000. ما العملية التي تطبقها على وسيط الدالة؟

تطبق الدالة sub_401000 عملية XOR على سلسلة الوسيط المُدخل.

4. استعد مفتاح التسجيل.

مفتاح التسجيل هو: QWDS-LK2O-76NP-12XX

5. صِف طريقة باستخدام تعديل السجلات لتجاوز نافذة التسجيل دون مفتاح التسجيل.

توجد عدة طرق صالحة. إحداها هي وضع نقطة توقف عند العنوان 0x40111E — وهي تعليمة القفز المشروط التي يقرر عندها البرنامج قبول مفتاح التسجيل أو رفضه — ثم تشغيل البرنامج تحت التنقيح حتى تلك النقطة، وضبط علم الصفر (ZF).

6. صِف طريقة باستخدام ترقيع القفزات لتجاوز نافذة التسجيل دون مفتاح التسجيل.

توجد عدة طرق صالحة. إحداها هي تعديل تعليمة JNZ عند 0x40111E إلى JZ. إذا طُبّق هذا الترقيع على البرنامج، فسيقبل البرنامج جميع مفاتيح التسجيل ما عدا مفتاح التسجيل الصحيح.


التحليل التفصيلي (Detailed Analysis)

السؤال 1: ما القيمة التي يجب أن ترجعها الدالة sub_401000 حتى يقبل البرنامج مفتاح التسجيل؟

تُستدعى الدالة sub_401000 عند 0x4010F9 بوسيط واحد. بعد الاستدعاء، تُخزَّن القيمة المرجعة في المتغير المحلي var_1c، كما هو موضح في القائمة 1.

4010F5 lea     eax, [ebp+Str]
4010F8 push    eax              ; Str1
4010F9 call    sub_401000
4010FE add     esp, 4
401101 mov     [ebp+var_1C], eax
401104 jmp     short loc_40111A

القائمة 1: الدالة sub_401000 تأخذ وسيطاً واحداً وتُخزَّن قيمتها المرجعة في المتغير المحلي var_1c

يتبع الاستدعاءُ تفريعٌ مشروط عند 0x40111A، موضح في القائمة 2. إذا كانت القيمة المرجعة في var_1C غير صفرية، يقفز التفريع المشروط إلى loc_40112F، حيث يطبع البرنامج رسالة برفض مفتاح التسجيل ثم يخرج. وإذا كانت القيمة المرجعة في var_1c صفرًا، يطبع البرنامج رسالة بقبول مفتاح التسجيل ويواصل التنفيذ إلى لعبة تخمين الأرقام.

40111A loc_40111A:
40111A cmp     [ebp+var_1C], 0
40111E jnz     short loc_40112F
401120 push    offset aKeyAcceptedEnj ; "\n\n\t[*] KEY ACCEPTED! Enjoy the Game."...
401125 call    _printf
40112A add     esp, 4
40112D jmp     short loc_401143
40112F
40112F loc_40112F:
40112F push    offset aBadKeyFailsauc ; "\n\t[-] Bad Key!!! Failsauce!\n\n"
401134 call    _printf
401139 add     esp, 4
40113C push    1                ; Code
40113E call    _exit

القائمة 2: استخدام القيمة المرجعة من sub_401000 المخزنة في var_1c لتحديد قبول المفتاح أو رفضه

السؤال 2: ما وسيط الدالة الممرر إلى sub_401000 عند 0x4010F9؟

تأخذ sub_401000 وسيطًا واحدًا، وهو عنوان المتغير المحلي Str. بالبحث عكسيًا، يقودنا أول إشارة إلى Str إلى استدعاء scanf عند 0x4010B9، الموضح في القائمة 3، قرب بداية الدالة main. الوسيط الأول %20s، ووفقًا لمواصفات سلسلة التنسيق، يوجّه scanf لقراءة سلسلة نصية من 20 محرفًا كحد أقصى من سطر الأوامر. وتُخزَّن السلسلة في الوسيط الثاني، وهو موقع Str. وتشير المطالبة المطبوعة بـ printf إلى أن السلسلة هي مفتاح تسجيل المستخدم. وبناءً عليه، يمكننا إعادة تسمية Str إلى user_registration_key.

4010A3 push    offset aEnterYourRegis ; "Enter your registration key, format XXX"...
4010A8 call    _printf
4010AD add     esp, 4
4010B0 lea     eax, [ebp+Str]
4010B3 push    eax
4010B4 push    offset Format   ; "%20s"
4010B9 call    _scanf
4010BE add     esp, 8

القائمة 3: قراءة مفتاح التسجيل من المستخدم باستخدام scanf

بعد قراءة user_registration_key بـ scanf وقبل استدعاء sub_401000، توجد إشارة إضافية إلى user_registration_key عند 0x4010E4، موضحة في القائمة 4، كوسيط لدالة strlen. يقارن التفريع المشروط عند 0x4010F0 طول user_registration_key المرجع من strlen مع الثابت 0x13 (19 بالعشري). إذا لم يكن طول السلسلة 0x13، يقفز التفريع المشروط إلى 0x401106 ويطبع البرنامج رسالة برفض مفتاح التسجيل المدخل بناءً على الطول قبل الخروج.

4010E4 lea     edx, [ebp+user_registration_key]
4010E7 push    edx              ; Str
4010E8 call    _strlen
4010ED add     esp, 4
4010F0 cmp     eax, 13h
4010F3 jnz     short loc_401106
4010F5 lea     eax, [ebp+user_registration_key]
4010F8 push    eax              ; Str1
4010F9 call    sub_401000
4010FE add     esp, 4
401101 mov     [ebp+var_1C], eax
401104 jmp     short loc_40111A
401106
401106 loc_401106:
401106 push    offset aBadKeyLengthPl ; "[-] Bad key length, please try again.\n"
40110B call    _printf
401110 add     esp, 4
401113 push    1                ; Code
401115 call    _exit

القائمة 4: التحقق من طول user_registration_key المُدخل

السؤال 3: ما العملية التي تطبقها الدالة sub_401000 على وسيطها؟

الخطوة الأولى لتحليل sub_401000 هي إعادة تسمية وسيطها Str1 إلى user_registration_key ليعكس ما نعرفه من تحليل الدالة المستدعية.

401000 sub_401000 proc near
401000
401000 var_20= dword ptr -20h
401000 var_1C= dword ptr -1Ch
401000 Str2= byte ptr -18h
401000 var_4= dword ptr -4
401000 user_registration_key= dword ptr 8

القائمة 5: ترويسة الدالة sub_401000 تعرض تعريفات إزاحات المتغيرات المحلية والوسيط

401000 push    ebp
401001 mov     ebp, esp
401003 sub     esp, 20h
401006 push    esi
401007 push    edi
401008 mov     [ebp+var_20], 0
40100F mov     [ebp+var_1C], 0
401016 mov     ecx, 5
40101B mov     esi, offset unk_409030
401020 lea     edi, [ebp+Str2]
401023 rep movsd
401025 mov     [ebp+var_4], 0AAh
40102C mov     [ebp+var_20], 0
401033 jmp     short loc_40103E

القائمة 6: تهيئة الدالة يليها عملية سلاسل نصية

كما هو موضح في القائمة 6، تُهيئ الدالة أولًا إطار المكدس وتضبط المتغيرين المحليين var_20 و var_1c على الصفر. ثم تُجري عملية سلاسل نصية عند 0x401023 باستخدام MOVSD مقرونة ببادئة التكرار REP. تستخدم عملية السلسلة ضمنيًا السجلات الثلاثة التالية:

  • ECX: مضبوط على 5، يحدد عدد مرات تكرار تعليمة السلسلة.
  • ESI: مضبوط على 0x409030، عنوان المصدر.
  • EDI: مضبوط على عنوان المتغير المحلي Str2، عنوان الوجهة.

لاحق تعليمة MOVS هو D، لذا تنسخ كل دورة أربعة بايتات. في المجموع، تنسخ عملية السلسلة عشرين بايتًا من 0x409030 إلى موقع Str2. السلسلة المصدرية عند 0x409030، الموضحة في القائمة 7، تبدو عشوائية وغير مقروءة للإنسان، مما يشير إلى أنها قد تكون مشفرة. في الوقت الحالي، نعيد تسمية المتغير Str2 إلى global_string ليعكس مصدره.

.data:00409030 unk_409030     db 0FBh
.data:00409031                db 0FDh
.data:00409032                db 0EEh
.data:00409033                db 0F9h
.data:00409034                db 87h
.data:00409035                db 0E6h

القائمة 7: السلسلة عند 0x409030 تتكون من بايتات تبدو عشوائية

الشكل 1: الحلقة تطبق عملية XOR على user_registration_key

الشكل 1: الحلقة تطبق عملية XOR على user_registration_key

بعد عملية السلسلة، تُهيئ الدالة المتغير المحلي var_4 إلى 0xAA و var_20 إلى 0 ثم تدخل الحلقة بدءًا من 0x40103E:

  • الحلقة، الموضحة في الشكل 1، تُقيّم أولًا شرط الحلقة بحساب طول user_registration_key ومقارنته مع المتغير المحلي var_20.
  • تعليمة القفز المشروط هي JNB (اقفز إذا لم يكن أقل). يمكن التعبير عن الشرط بـ “اقفز إذا كان var_20 !< len(user_registration_key)” وهو يعادل “اقفز إذا كان var_20 >= len(user_registration_key)”. من هذا الشرط، وتهيئة الحلقة عند 0x40102C، وتحديث الحلقة عند 0x401035، يمكننا استنتاج أن var_20 هو عدّاد الحلقة المُزاد في كل دورة، وأن الحلقة تخرج عندما يصل هذا العدّاد إلى طول user_registration_key.

الشكل 2: تصوّر مكونات الحلقة كمخطط

الشكل 2: تصوّر مكونات الحلقة كمخطط

جسم الحلقة، البادئ عند 0x40104F، يطبق عملية XOR بمفتاح 0xAA على كل بايت في user_registration_key عن طريق:

  1. الفهرسة في سلسلة user_registration_key بعدّاد الحلقة var_20: ينقل عنوان السلسلة إلى EDX، ويضيف عدّاد الحلقة لحساب عنوان البايت N، ثم يحمّل ذلك البايت في EAX.
  2. تنفيذ XOR للبايت في EAX مع المفتاح في var_4.
  3. كتابة المحرف المعدَّل عائدًا إلى user_registration_key بحساب عنوان البايت مجددًا وكتابة البايت المُعمَّى بـ XOR من EAX إلى ذلك العنوان.

السؤال 4: استعد مفتاح التسجيل

بعد تطبيق عملية XOR بالمفتاح 0xAA على user_registration_key والخروج من الحلقة، تستدعي الدالة الدالة mbscmp بوسيطين، كما هو موضح في القائمة 8: user_registration_key المعدَّلة وglobal_string المنسوخة سابقًا في الدالة.

401065 loc_401065:
401065 lea     edx, [ebp+global_string]
401068 push    edx              ; Str2
401069 mov     eax, [ebp+user_registration_key]
40106C push    eax              ; Str1
40106D call    __mbscmp
401072 add     esp, 8
401075 mov     [ebp+var_20], eax
401078 mov     eax, [ebp+var_20]
40107B pop     edi
40107C pop     esi
40107D mov     esp, ebp
40107F pop     ebp
401080 retn

القائمة 8: إرجاع نتيجة المقارنة بين user_registration_key و global_string

الدالة mbscmp هي دالة مقارنة سلاسل نصية تشبه strcmp. أما التمييز بأن الأولى تدعم تسلسلات المحارف متعددة البايتات فغير ذي صلة هنا، لأن المقارنة لا تتضمن تسلسلات محارف متعددة البايتات.

ترجع mbscmp صفرًا إذا كانت سلسلتی الإدخال متطابقتين وقيمة غير صفرية في غير ذلك. بعد استدعاء mbscmp، ترجع الدالة القيمة المرجعة من mbscmp. ووفقًا للتحليل السابق، هذا يعني أن mbscmp يجب أن ترجع صفرًا حتى يقبل البرنامج مفتاح التسجيل، مما يعني أن السلسلتين user_registration_key و global_string يجب أن تكونا متطابقتين.

بما أن الدالة تطبق عملية XOR على user_registration_key المقدمة من المستخدم قبل المقارنة، يمكننا تنفيذ العملية العكسية بتطبيق عملية XOR على global_string لاستعادة ما يجب أن يكون عليه مفتاح التسجيل قبل الـ XOR.

خلاصة القول، يمكننا استعادة مفتاح التسجيل بتطبيق XOR بالمفتاح 0xAA على السلسلة العشرينية البايتات عند 0x409030:

FB FD EE F9 87 E6 E1 98 E5 87 9D 9C E4 FA 87 9B 98 F2 F2

يؤدي تطبيق عملية XOR باستخدام أداة مثل CyberChef إلى إنتاج مفتاح التسجيل:

QWDS-LK2O-76NP-12XX

والذي يمكننا التحقق منه بإدخاله في البرنامج من سطر الأوامر.

السؤال 5: طريقة تجاوز نافذة التسجيل باستخدام تعديل السجلات

توجد عدة طرق صالحة. إحداها وضع نقطة توقف عند 0x40111E، حيث يقرر البرنامج قبول مفتاح التسجيل أو رفضه. شغّل البرنامج تحت التنقيح في IDA ودع البرنامج ينفّذ. عند المطالبة بمفتاح التسجيل، أدخل أي سلسلة نصية بالطول المطلوب (0x13). عندما يصل البرنامج إلى نقطة التوقف، كما هو موضح في الشكل 3، غيّر اتجاه القفزة بعكس علم الصفر. عند استئناف البرنامج، لن يأخذ القفزة وسيكمل إلى لعبة تخمين الأرقام.

الشكل 3: ضع نقطة توقف عند التفريع المشروط الحاسم وعكس اتجاه القفزة بتعديل علم الصفر

الشكل 3: ضع نقطة توقف عند التفريع المشروط الحاسم وعكس اتجاه القفزة بتعديل علم الصفر

السؤال 6: طريقة تجاوز نافذة التسجيل باستخدام ترقيع القفزات

توجد عدة طرق صالحة. إحداها تعديل تعليمة JNZ عند 0x40111E إلى JZ كما هو موضح في القائمة 10. إذا طُبّق هذا الترقيع على البرنامج، فسيقبل البرنامج جميع مفاتيح التسجيل ما عدا مفتاح التسجيل الصحيح.

40111A loc_40111A:
40111A cmp     [ebp+var_1C], 0
40111E jz      short loc_40112F
401120 push    offset aKeyAcceptedEnj ; "\n\n\t[*] KEY ACCEPTED! Enjoy the Game."...
401125 call    _printf
40112A add     esp, 4
40112D jmp     short loc_401143
40112F
40112F loc_40112F:
40112F push    offset aBadKeyFailsauc ; "\n\t[-] Bad Key!!! Failsauce!\n\n"
401134 call    _printf
401139 add     esp, 4
40113C push    1                ; Code
40113E call    _exit

القائمة 10: تعديل تعليمة JNZ عند 0x40111E إلى تعليمة JZ


خلاصة الحل

المفهومالتطبيق في guesser.exe
تعليمات السلاسل + REPrep movsd ينسخ 20 بايتاً من السلسلة المشفرة عند 0x409030
الحلقاتحلقة بعدّاد var_20 تطبق XOR بمفتاح 0xAA على كل بايت من الإدخال
المصفوفاتفهرسة user_registration_key بحساب العنوان: EDX + var_20
التحقق من الطولstrlen + مقارنة مع 0x13 (19 محرفاً) قبل التحقق من المفتاح
المقارنةmbscmp ترجع 0 عند التطابق — لذلك يجب أن ترجع sub_401000 صفراً
استعادة المفتاحXOR عكسي بمفتاح 0xAA على السلسلة المشفرة ← QWDS-LK2O-76NP-12XX
تعديل السجلاتنقطة توقف عند 0x40111E + ضبط علم الصفر ZF
ترقيع القفزاتتحويل JNZ إلى JZ عند 0x40111E (يقبل كل المفاتيح عدا الصحيح!)

الدروس المستفادة:

  • السلاسل “العشوائية” في مقطع .data غالباً مشفرة — ابحث عن عملية XOR في الشيفرة التي تصل إليها.
  • XOR عملية متماثلة: تطبيقها بنفس المفتاح مرتين يعيد الأصل — لذلك يمكن استخراج المفتاح الصحيح بتعمية السلسلة المشفرة نفسها.
  • مقارنة mbscmp/strcmp ترجع 0 عند التطابق — انتبه لاتجاه الشرط عند تحليل القيمة المرجعة.
  • التحقق من الطول قبل التحقق من المحتوى يكشف تنسيق المفتاح المتوقع (19 محرفاً).

المصدر الأصلي: Malware Analysis Crash Course — FLARE Learning Hub