بسم الله الرحمن الرحيم
معلومات العينة
- اسم الملف: 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
بعد عملية السلسلة، تُهيئ الدالة المتغير المحلي 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: تصوّر مكونات الحلقة كمخطط
جسم الحلقة، البادئ عند 0x40104F، يطبق عملية XOR بمفتاح 0xAA على كل بايت في user_registration_key عن طريق:
- الفهرسة في سلسلة
user_registration_keyبعدّاد الحلقةvar_20: ينقل عنوان السلسلة إلىEDX، ويضيف عدّاد الحلقة لحساب عنوان البايت N، ثم يحمّل ذلك البايت فيEAX. - تنفيذ XOR للبايت في
EAXمع المفتاح فيvar_4. - كتابة المحرف المعدَّل عائدًا إلى
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: ضع نقطة توقف عند التفريع المشروط الحاسم وعكس اتجاه القفزة بتعديل علم الصفر
السؤال 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 |
|---|---|
| تعليمات السلاسل + REP | rep 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