P01NT
0xa 10/10

حل معمل quizshow.exe (الفصل السابع)

حل معمل quizshow.exe: مصفوفة بنيات وشيفرة ADD-XOR وفك تشفير الإجابات وإعادة بناء بنية quizitem على x64.

· 13 دقائق قراءة · 2635 كلمة
المحتويات

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


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

  • اسم الملف: 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

الشكل 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-XORadd بمفتاح عند الإزاحة 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