بسم الله الرحمن الرحيم
معلومات العينة
- اسم الملف: quizshow.exe
- MD5:
fed60db5c7afbb8b0f016e0b2de06b93
الإجابات المختصرة (Short Answers)
1. تمرر الدالة main موقع ذاكرة يحتوي على بيانات إلى sub_140001360 عند العنوان 0x1400011C5. ما نوع البيانات الموجودة في موقع الذاكرة هذا؟
تمرر الدالة main العنوان 0x140003260 الذي يبدو أنه يحتوي على بيانات متعلقة بأسئلة المسابقة.
2. ادخل إلى الدالة sub_140001360. ما الدالة التي يبدو أنها تحدد ما إذا كانت إجابة سؤال المسابقة صحيحة؟
تفترض الدالة sub_140001360 بناءً على القيمة المرجعة من الدالة sub_140001060 لتحديد ما إذا كانت إجابة السؤال صحيحة.
3. كيف تسترد الدالة sub_140001360 سؤال المسابقة لطباعته إلى سطر الأوامر؟
تتعامل الدالة sub_140001360 مع وسيط الدالة بوصفه مصفوفة من البنيات التي تحتوي على سؤال المسابقة عند الإزاحة 2 داخل البنية.
4. كيف تحدد الدالة sub_140001360 الترتيب الذي تُعرض به أسئلة المسابقة؟
مصفوفة على المكدس تمثل ترتيب أسئلة المسابقة تُهيأ بالأعداد الصحيحة من 0 إلى 5 وتُخلط عشوائياً (Shuffle) في بداية الدالة.
5. كيف يقيّم البرنامج ما إذا كانت إجابة سؤال المسابقة صحيحة؟
تُحوَّل إجابة المتسابق باستخدام شيفرة ADD-XOR وتُقارن مع سلسلة الإجابة المشفرة.
6. أعد بناء البنية التي يستخدمها البرنامج لتمثيل زوج سؤال وإجابة المسابقة.
struct quizitem {
+00 char xorkey;
+01 char addkey;
+02 char question[128]
+82 char answer[64]
}
التحليل التفصيلي (Detailed Analysis)
السؤال 1: ما نوع البيانات في موقع الذاكرة الممرر إلى sub_140001360؟
قبل استدعاء الدالة sub_140001360، تُحمّل main العنوان 0x140003260 في سجل RCX، الذي يحمل وسيط الدالة الأول وفق اصطلاح الاستدعاء __fastcall ذي 64-بت. انتقل إلى 0x140003260 لتجد أن هذا الموقع هو بداية منطقة في مقطع .rdata تحتوي على بيانات أسئلة المسابقة.
.rdata:140003260 unk_140003260 db 1Ah
.rdata:140003261 db 10h
.rdata:140003262 db 57h ; W
.rdata:140003263 db 68h ; h
.rdata:140003264 db 61h ; a
.rdata:140003265 db 74h ; t
.rdata:140003266 db 27h ; '
.rdata:140003267 db 73h ; s
.rdata:140003268 db 20h
.rdata:140003269 db 74h ; t
.rdata:14000326A db 68h ; h
.rdata:14000326B db 65h ; e
.rdata:14000326C db 20h
.rdata:14000326D db 6Fh ; o
.rdata:14000326E db 72h ; r
.rdata:14000326F db 69h ; i
.rdata:140003270 db 67h ; g
.rdata:140003271 db 69h ; i
.rdata:140003272 db 6Eh ; n
.rdata:140003273 db 61h ; a
.rdata:140003274 db 6Ch ; l
القائمة 1: بيانات المسابقة بدءاً من العنوان 0x140003260
السؤال 2: ما الدالة التي تحدد صحة الإجابة؟
يقودنا مخطط تدفق التحكم للدالة sub_140001360 والسلاسل النصية الممررة إلى printf إلى التفريع المشروط عند 0x1400015AF، الموضح في القائمة 2. يقرر هذا التفريع ما إذا كان البرنامج يطبع You've got it! أو Try again للإشارة إلى صحة إجابة السؤال. يُقيّم التفريع القيمة المرجعة من sub_140001060: إذا رجعت 0، فالإجابة صحيحة.
14000157E def_14000141F: ; jumptable 14000141F default case
14000157E mov rcx, [rsp+0A8h+var_80]
140001583 call _printf
140001588 lea rcx, aYourAnswer ; "\nYour Answer: "
14000158F call _printf
140001594 lea rcx, [rsp+0A8h+userAnswer] ; userAnswer
140001599 call getUserInput ; يقرأ سطر إدخال المستخدم من سطر الأوامر
14000159E lea rdx, [rsp+0A8h+userAnswer]
1400015A3 mov rcx, [rsp+0A8h+var_78]
1400015A8 call sub_140001060
1400015AD test eax, eax
1400015AF jnz short loc_1400015C9
1400015B1 lea rcx, aYouVeGotIt ; "You've got it!\n\n"
1400015B8 call _printf
1400015BD mov eax, [rsp+0A8h+var_88]
1400015C1 inc eax
1400015C3 mov [rsp+0A8h+var_88], eax
1400015C7 jmp short loc_1400015D5
1400015C9 ; ----------------------------------------------------
1400015C9
1400015C9 loc_1400015C9:
1400015C9 lea rcx, aTryAgain ; "Try again\n"
1400015D0 call _printf
القائمة 2: المنطق المشروط المتفرع بناءً على إجابة المستخدم
يبدو أن الدالة sub_140001060 تأخذ وسيطين. الوسيط الثاني (RDX) هو سلسلة إجابة المستخدم من سطر الأوامر. والوسيط الأول متغير محلي لم نحلله بعد.
إضافة إلى ذلك، عند العنوان 0x14000157E، يمكننا استنتاج أن وسيط المتغير المحلي الممرر إلى printf هو سؤال المسابقة، لأن ما يليه مباشرةً هو مطالبة المستخدم بإدخال إجابته. لا نعرف بعد كيف يُضبط هذا المتغير المحلي على سؤال السلسلة الحالية، لكن في الوقت الحالي يمكننا تسمية هذا المتغير المحلي current_quiz_question.
السؤال 3: كيف تسترد الدالة سؤال المسابقة؟
بالنظر إلى مواضع الإشارة إلى المتغير المحلي current_quiz_question في الدالة، نرى أنه يُهيأ إلى 0 في بداية الدالة، عند العنوان 0x1400013A5، ويُضبط عند كل حالة من حالات جدول القفز، مثل الحالة الموضحة في القائمة 3.
140001421 loc_140001421: ; jumptable 14000141F case 0
140001421 mov eax, 0C2h
140001426 imul rax, 0
14000142A mov rcx, [rsp+0A8h+quiz_data]
140001432 lea rax, [rcx+rax+2]
140001437 mov [rsp+0A8h+current_quiz_question], rax
14000143C mov eax, 0C2h
140001441 imul rax, 0
140001445 mov rcx, [rsp+0A8h+quiz_data]
14000144D add rcx, rax
140001450 mov rax, rcx
140001453 mov [rsp+0A8h+var_78], rax
140001458 jmp def_14000141F ; jumptable 14000141F default case
القائمة 3: استرداد سؤال المسابقة الحالي
في كل حالة من حالات switch، يُضبط current_quiz_question على القWORD الموجود عند [RCX+RAX+2]. على سبيل المثال، لنفكك هذه العملية الحسابية عند العنوان 0x140001432:
RCXهو العنوان0x140003260، الممرر كوسيط (quiz_data) إلى الدالة.RAXهو ناتج ضرب 0 في0xC2، أي 0.
العنوان الناتج هو 0x140003260 + 0x0 + 0x2 = 0x140003262. انتقل إلى هذا العنوان لتجد سلسلة سؤال مسابقة منتهية بـ NULL. وتفعل كل حالة من عبارة switch شيء مشابهًا بفهرس مختلف. على سبيل المثال، في حالة جدول القفز 1:
RCXهو0x140003260.RAXهو ناتج ضرب 1 في0xC2، أي0xC2.
هذا يعني أن current_quiz_question يُضبط على 0x140003260 + 0xC2 + 0x2 = 0x14000332. انتقل إلى هذا العنوان لتجد سؤال مسابقة آخر. نمط الوصول إلى الذاكرة هذا يشير إلى أن وسيط الدالة quiz_data (0x140003260) هو مصفوفة يكون حجم عنصرها 0xC2. والإزاحة 2 تشير إلى أن كل عنصر بنية تحتوي على سلسلة نصية عند الإزاحة 2.
إضافة إلى ذلك، لاحظ أن الدالة تُجري عملية حسابية مماثلة للذاكرة مباشرةً بعد ضبط current_quiz_question ولكن دون إزاحة 2. ثم يُخزَّن العنوان المحسوب في المتغير المحلي var_78. ويُمرر هذا المتغير المحلي إلى الاستدعاء عند 0x1400015A8 للدالة sub_140001060 التي حققناها سابقًا. بناءً على عمليات الوصول إلى الذاكرة، نشك في أن هذا الوسيط مؤشر إلى بنية حجمها 0xC2.
السؤال 4: كيف تحدد الدالة ترتيب عرض الأسئلة؟
كشف السؤال السابق أن كل حالة من حالات جدول القفز تمثل سؤال مسابقة يُطرح على المستخدم. بالعمل عكسيًا من حالات جدول القفز، نرى أنه، كما هو موضح في القائمة 4، فهرس جدول القفز هو المتغير المحلي var_70، وهو بدوره قيمة من مصفوفة مخصصة على المكدس يفهرسها المتغير المحلي var_88.
1400013F1 movsxd rax, [rsp+0A8h+var_88]
1400013F6 mov eax, [rsp+rax*4+0A8h+array]
1400013FA mov [rsp+0A8h+var_70], eax
1400013FE cmp [rsp+0A8h+var_70], 5 ; switch 6 cases
140001403 ja def_14000141F ; jumptable 14000141F default case
140001409 movsxd rax, [rsp+0A8h+var_70]
14000140E lea rcx, cs:140000000h
140001415 mov eax, ds:(jpt_14000141F - 140000000h)[rcx+rax*4]
14000141C add rax, rcx
14000141F jmp rax ; switch jump
القائمة 4: اختيار حالة switch، كل منها يمثل سؤال مسابقة
تُهيأ المصفوفة المحلية في بداية الدالة، كما هو موضح في القائمة 5. بناءً على وسائط دالة shuffle — التي تخلط عشوائيًا مصفوفة إدخال من أعداد صحيحة — يمكننا استنتاج أن هذه المصفوفة المحلية تحتوي على ستة أعداد صحيحة.
14000136D mov [rsp+0A8h+array], 0
140001375 mov [rsp+0A8h+var_64], 1
14000137D mov [rsp+0A8h+var_60], 2
140001385 mov [rsp+0A8h+var_5C], 3
14000138D mov [rsp+0A8h+var_58], 4
140001395 mov [rsp+0A8h+var_54], 5
14000139D mov [rsp+0A8h+var_88], 0
1400013A5 mov [rsp+0A8h+current_quiz_question], 0
1400013AE mov [rsp+0A8h+var_78], 0
1400013B7 lea rax, [rsp+0A8h+userAnswer]
1400013BC mov rdi, rax
1400013BF xor eax, eax
1400013C1 mov ecx, 40h ; '@'
1400013C6 rep stosb
1400013C8 xor ecx, ecx ; Time
1400013CA call time
1400013CF mov ecx, eax ; Seed
1400013D1 call cs:srand
1400013D7 mov edx, 6 ; n
1400013DC lea rcx, [rsp+0A8h+array] ; array
1400013E1 call shuffle ; يخلط عشوائيًا في مكانها مصفوفة إدخال تحتوي على n عددًا صحيحًا
القائمة 5: تهيئة مصفوفة محلية وخلطها
لضبط تعريفات المكدس بناءً على هذه المعلومات:
- انقر نقرًا مزدوجًا على تسمية المتغير المحلي
arrayللانتقال إلى عرض المكدس (Stack View) للدالة. - انقر بزر الفأرة الأيمن على تسمية
arrayفي عرض المكدس الخاص بـsub_140001360. - انقر
Array…. - اضبط Array Size على 6 عناصر، واضغط OK، ثم اضغط Yes على سؤال “Directly convert to array?”.
كما هو موضح في القائمة 6، توضح هذه الخطوات أن العناصر الأولية لهذه المصفوفة هي الأعداد الصحيحة من 0 إلى 5.
14000136D mov [rsp+0A8h+array], 0
140001375 mov [rsp+0A8h+array+4], 1
14000137D mov [rsp+0A8h+array+8], 2
140001385 mov [rsp+0A8h+array+0Ch], 3
14000138D mov [rsp+0A8h+array+10h], 4
140001395 mov [rsp+0A8h+array+14h], 5
14000139D mov [rsp+0A8h+var_88], 0
1400013A5 mov [rsp+0A8h+current_quiz_question], 0
1400013AE mov [rsp+0A8h+var_78], 0
1400013B7 lea rax, [rsp+0A8h+userAnswer]
1400013BC mov rdi, rax
1400013BF xor eax, eax
1400013C1 mov ecx, 40h ; '@'
1400013C6 rep stosb
1400013C8 xor ecx, ecx ; Time
1400013CA call time
1400013CF mov ecx, eax ; Seed
1400013D1 call cs:srand
1400013D7 mov edx, 6 ; n
1400013DC lea rcx, [rsp+0A8h+array] ; array
1400013E1 call shuffle ; يخلط عشوائيًا في مكانها مصفوفة إدخال تحتوي على n عددًا صحيحًا
القائمة 6: المصفوفة المعرفة على المكدس تحمل ترتيب أسئلة المسابقة
تمثل هذه المصفوفة ترتيب أسئلة المسابقة. ويُعشوئى هذا الترتيب في بداية البرنامج بدالة shuffle. ويُصل إلى كل سؤال مسابقة عبر الحالة المقابلة في عبارة switch. والمتغير المحلي var_88 هو الفهرس الذي يمثل سؤال المسابقة الحالي في الترتيب المعشوى. لاحظ كيف يُزاد هذا الفهرس عند العنوان 0x1400015BD بعد إجابة المستخدم الصحيحة عن سؤال ما.
السؤال 5: كيف يقيّم البرنامج صحة الإجابة؟
نعود إلى الدالة sub_140001060، التي حددناها سابقًا بوصفها الدالة التي تُقيّم إجابة المتسابق. وسيطا هذه الدالة هما:
- مؤشر إلى بنية تحتوي على سؤال المسابقة عند الإزاحة 2.
- إجابة المتسابق على سؤال المسابقة.
بما أننا نعرف بالفعل أن الدالة ترجع 0 للإشارة إلى صحة الإجابة، يمكننا العمل عكسيًا من نهاية الدالة حيث تضبط القيمة المرجعة.
140001109 loc_140001109:
140001109 mov rax, [rsp+48h+arg_structPointer]
14000110E add rax, 82h
140001114 mov rdx, rax ; Str2
140001117 mov rcx, [rsp+48h+arg_userAnswer] ; Str1
14000111C call cs:_mbscmp
140001122 add rsp, 48h
140001126 retn
140001126 sub_140001060 endp
القائمة 7: تقييم سلسلة إجابة المستخدم باستخدام mbscmp
القيمة المرجعة للدالة هي القيمة المرجعة من mbscmp، وهي دالة مقارنة سلاسل ترجع 0 فقط إذا كانت سلسلتی الإدخال متطابقتين. الوسيط الأول هو إجابة المتسابق. والوسيط الثاني سلسلة تقع عند الإزاحة 0x82 داخل البنية المشار إليها بواسطة الوسيط الأول للدالة. تهيمن بنية حلقة على بقية الدالة وتشير إلى كلا وسيطي الدالة، لذا لفهم مقارنة السلاسل، علينا التحقيق فيما يحدث داخل الحلقة.
14000106E mov [rsp+48h+var_28], 0
140001076 jmp short loc_140001082
140001082
140001082 loc_140001082:
140001082 movsxd rax, [rsp+48h+var_28]
140001087 mov rcx, [rsp+48h+arg_userAnswer]
14000108C mov [rsp+48h+var_18], rcx
140001091 mov [rsp+48h+var_20], 0FFFFFFFFFFFFFFFFh
14000109A
14000109A loc_14000109A:
14000109A inc [rsp+48h+var_20]
14000109F mov rcx, [rsp+48h+var_18]
1400010A4 mov rdx, [rsp+48h+var_20]
1400010A9 cmp byte ptr [rcx+rdx], 0
1400010AD jnz short loc_14000109A
1400010AF mov rcx, [rsp+48h+var_20]
1400010B4 cmp rax, rcx
1400010B7 jnb short loc_140001109
القائمة 8: شرط الحلقة يقارن فهرس الحلقة مع طول سلسلة المستخدم
يقارن شرط الحلقة عند 0x1400010B4 في القائمة 8 بين var_28 و var_20:
- بناءً على التهيئة إلى 0 في الأعلى وعملية الزيادة عند العنوان
0x140001078، يبدو أنvar_28هو عدّاد الحلقة. - المتغير
var_20هو طول سلسلة الإجابة الوسيطة. الحلقة الصغيرة البادئة عند العنوان0x14000109Aتحسب الطول بالبحث عن بايت الإنهاء NULL.
بناءً على شرط الحلقة، يمكننا استنتاج أن الحلقة تتكرر عبر محتويات سلسلة الإجابة الوسيطة.
1400010B9 movsxd rax, [rsp+48h+loop_index]
1400010BE mov rcx, [rsp+48h+arg_structPointer]
1400010C3 movsx ecx, byte ptr [rcx+1]
1400010C7 mov rdx, [rsp+48h+arg_userAnswer]
1400010CC movsx eax, byte ptr [rdx+rax]
1400010D0 add eax, ecx
1400010D2 movsxd rcx, [rsp+48h+loop_index]
1400010D7 mov rdx, [rsp+48h+arg_userAnswer]
1400010DC mov [rdx+rcx], al
1400010DF movsxd rax, [rsp+48h+loop_index]
1400010E4 mov rcx, [rsp+48h+arg_structPointer]
1400010E9 movsx ecx, byte ptr [rcx]
1400010EC mov rdx, [rsp+48h+arg_userAnswer]
1400010F1 movsx eax, byte ptr [rdx+rax]
1400010F5 xor eax, ecx
1400010F7 movsxd rcx, [rsp+48h+loop_index]
1400010FC mov rdx, [rsp+48h+arg_userAnswer]
140001101 mov [rdx+rcx], al
140001104 jmp loc_140001078
القائمة 9: جسم الحلقة يفك تشفير سلسلة الإجابة بعمليتي ADD و XOR
يتكون جسم الحلقة أساسًا من عمليتين: ADD عند العنوان 0x1400010D0 و XOR عند العنوان 0x1400010F5:
- تضيف عملية
ADDالبايتَ عند الإزاحة 1 داخل بنية الوسيط إلى البايت عند فهرس الحلقة الحالي في سلسلة إجابة المتسابق. - تنفّذ عملية
XORالبايتَ عند الإزاحة 0 من بنية الوسيط مع البايت المعدَّل عند فهرس الحلقة الحالي في سلسلة إجابة المتسابق.
خلاصة القول، تحوّل الحلقةُ سلسلةَ إجابة الوسيط بشيفرة ADD-XOR حيث تُسترد مفاتيحا ADD و XOR من مؤشر بنية الوسيط. تقارن الدالةُ سلسلةَ الإجابة الثابتة المشفرة مع سلسلة الإجابة المشفرة المخزنة في بنية الوسيط عند الإزاحة 0x82. لفك تشفير إجابة المسابقة، يمكننا الانتقال إلى البنية عند الإزاحة 0x82 وتطبيق عملية XOR يليها SUB.
.rdata:140003260 unk_140003260 db 1Ah
.rdata:140003261 db 10h
.rdata:140003262 db 57h ; W
.rdata:140003263 db 68h ; h
.rdata:140003264 db 61h ; a
.rdata:140003265 db 74h ; t
.rdata:140003266 db 27h ; '
.rdata:140003267 db 73h ; s
.rdata:140003268 db 20h
.rdata:140003269 db 74h ; t
.rdata:14000326A db 68h ; h
.rdata:14000326B db 65h ; e
.rdata:14000326C db 20h
.rdata:14000326D db 6Fh ; o
.rdata:14000326E db 72h ; r
.rdata:14000326F db 69h ; i
.rdata:140003270 db 67h ; g
.rdata:140003271 db 69h ; i
.rdata:140003272 db 6Eh ; n
.rdata:140003273 db 61h ; a
.rdata:140003274 db 6Ch ; l
القائمة 10: البنية عند 0x140003260 تحتوي على مفتاح XOR بقيمة 0x1A ومفتاح ADD بقيمة 0x10
السؤال 6: إعادة بناء بنية quizitem
من السؤال السابق، رأينا أن الدالة sub_140001060 تشير إلى ثلاثة عناصر في البنية:
- مفتاح XOR عند الإزاحة 0.
- مفتاح ADD عند الإزاحة 1.
- الإجابة المشفرة عند الإزاحة
0x82.
وقبل ذلك في الدالة sub_140001360، عند كل حالة من حالات switch، رأينا إشارة إلى سؤال المسابقة عند الإزاحة 2. بافتراض عدم وجود عناصر إضافية في البنية لا يشير إليها البرنامج، يمكننا إعادة بناء البنية كما يلي:
struct quizitem {
+00 char xorkey;
+01 char addkey;
+02 char question[128];
+82 char answer[64];
}
في نافذة Local Types في IDA، تأكد من تعريف كل حقل من حقول البنية كسلاسل نصية بالنقر بزر الفأرة الأيمن واختيار String (A). يجب أن تبدو البنية كما يلي بمجرد تعريفها بشكل صحيح:
00000000 struct quizitem // sizeof=0xC2
00000000 {
00000000 char xorkey[1] __strlit(C,"UTF-8");
00000001 char addkey[1] __strlit(C,"UTF-8");
00000002 char question[128] __strlit(C,"UTF-8");
00000082 char answer[64] __strlit(C,"UTF-8");
000000C2 };
بمجرد إعادة إنشاء هذه البنية في تبويب البنيات في IDA، يمكننا تطبيق البنية على كل موقع مُشير. النتائج معروضة في القائمتين 11 و 12.
140001421 loc_140001421: ; jumptable 14000141F case 0
140001421 mov eax, 0C2h
140001426 imul rax, 0
14000142A mov rcx, [rsp+0A8h+quiz_data]
140001432 lea rax, [rcx+rax+quizitem.question]
140001437 mov [rsp+0A8h+current_quiz_question], rax
14000143C mov eax, 0C2h
140001441 imul rax, 0
140001445 mov rcx, [rsp+0A8h+quiz_data]
14000144D add rcx, rax
140001450 mov rax, rcx
140001453 mov [rsp+0A8h+var_78], rax
140001458 jmp def_14000141F ; jumptable 14000141F default case
القائمة 11: تطبيق بنية quizitem على إزاحة في حالة من جدول القفز
1400010B9 movsxd rax, [rsp+48h+loop_index]
1400010BE mov rcx, [rsp+48h+arg_structPointer]
1400010C3 movsx ecx, byte ptr [rcx+quizitem.addkey]
1400010C7 mov rdx, [rsp+48h+arg_userAnswer]
1400010CC movsx eax, byte ptr [rdx+rax]
1400010D0 add eax, ecx
1400010D2 movsxd rcx, [rsp+48h+loop_index]
1400010D7 mov rdx, [rsp+48h+arg_userAnswer]
1400010DC mov [rdx+rcx], al
1400010DF movsxd rax, [rsp+48h+loop_index]
1400010E4 mov rcx, [rsp+48h+arg_structPointer]
1400010E9 movsx ecx, byte ptr [rcx+quizitem.xorkey]
1400010EC mov rdx, [rsp+48h+arg_userAnswer]
1400010F1 movsx eax, byte ptr [rdx+rax]
1400010F5 xor eax, ecx
1400010F7 movsxd rcx, [rsp+48h+loop_index]
1400010FC mov rdx, [rsp+48h+arg_userAnswer]
140001101 mov [rdx+rcx], al
140001104 jmp loc_140001078
; ---------------------------------------------------------------
140001109 loc_140001109:
140001109 mov rax, [rsp+48h+arg_structPointer]
14000110E add rax, quizitem.answer
140001114 mov rdx, rax ; Str2
140001117 mov rcx, [rsp+48h+arg_userAnswer] ; Str1
14000111C call cs:_mbscmp
140001122 add rsp, 48h
140001126 retn
140001126 sub_140001060 endp
القائمة 12: تطبيق بنية quizitem على الإزاحات في الدالة sub_140001060
يمكننا إضافة إلى ذلك تعريف مصفوفة من البنيات عند العنوان 0x140003260 لتحليل بيانات المسابقة إلى بنيات عن طريق:
- ضع المؤشر على العنوان
0x140003260ثمEdit ➔ Functions ➔ Set Type(Y). - أدخل
quizitem[6]كتعريف للنوع.

الشكل 1: تطبيق بنية quizitem على مصفوفة بنيات بيانات المسابقة عند 0x140003260
خلاصة الحل
| المفهوم (x86-64) | التطبيق في quizshow.exe |
|---|---|
اصطلاح __fastcall | الوسيط الأول (مصفوفة البيانات) في RCX |
| المصفوفات والبنيات | مصفوفة من 6 بنيات، حجم البنية 0xC2؛ السؤال عند [base + index*0xC2 + 2] |
| جداول القفز | 6 حالات (switch 6 cases) عبر jpt_14000141F |
| الحلقات | عدّاد var_28 + حساب الطول بحلقة صغيرة (بحث عن NULL) |
| شيفرة ADD-XOR | add بمفتاح عند الإزاحة 1 ثم xor بمفتاح عند الإزاحة 0 |
rep stosb | تصفير مخزن إجابة المستخدم (64 بايتاً) |
mbscmp | ترجع 0 عند التطابق — الإجابة المشفرة تُقارن بالإدخال المعمَّى |
| إعادة بناء البنية | quizitem: xorkey(+0) / addkey(+1) / question128 / answer64 |
| فك التشفير | عكس الشيفرة: XOR يليه SUB بالمفتاحين نفسهما |
الدروس المستفادة:
- نمط
[base + index*0xC2 + offset]يكشف مصفوفة بنيات — احسب حجم العنصر من مضاعف الفهرس. - عمليات
ADDوXORمتتالية على نفس البيانات = شيفرة تشفير بسيطة؛ اعكسها بالترتيب المعاكس (XORثمSUB). - البايتان الأولان من البنية (المفاتيح) غالباً ما يكونان قيمتين ثابتتين صغيرتين (
0x1Aو0x10) — انتبه لهما عند فحص.rdata. - تطبيق البنية المُعاد بناؤها في IDA (
Set Type Yمعquizitem[6]) يحوّل البيانات الخام إلى حقول مقروءة فوراً.
المصدر الأصلي: Malware Analysis Crash Course — FLARE Learning Hub