الهندسة العكسية لمحاكي NES في لعبة GameCube

الصورة

أثناء البحث عن طرق لتنشيط قوائم المطورين المتبقية في Animal Crossing ، بما في ذلك قائمة اختيار اللعبة لمحاكي NES ، وجدت ميزة مثيرة للاهتمام موجودة في اللعبة الأصلية وكانت نشطة باستمرار ، ولكن لم تستخدمها Nintendo أبدًا.

بالإضافة إلى ألعاب NES / Famicom داخل اللعبة ، يمكنك تنزيل ألعاب NES جديدة من بطاقة ذاكرة.

تمكنت أيضًا من إيجاد طريقة لاستخدام محمل إقلاع ROM هذا لإصلاح الكود والبيانات الخاصة بي في اللعبة ، مما يسمح لك بتنفيذ التعليمات البرمجية من خلال بطاقة ذاكرة.

مقدمة - كائنات وحدة التحكم NES


ألعاب NES العادية ، والتي يمكن الحصول عليها من Animal Crossing ، هي قطع أثاث منفصلة على شكل وحدة تحكم NES مع خرطوشة ملقاة عليها.

بعد تحديد موقع هذا الكائن في منزلك والتفاعل معه ، يمكنك تشغيل هذه اللعبة فقط. تظهر الصورة أدناه Excitebike و Golf.


هناك أيضًا كائن NES Console الشائع الذي لا توجد فيه ألعاب مدمجة. يمكن شراؤها من Redd ، ويتم الحصول عليها في بعض الأحيان من خلال أحداث عشوائية ، على سبيل المثال ، من خلال القراءة على لوحة إعلانات المدينة أن وحدة التحكم مدفونة في نقطة عشوائية في المدينة.


يبدو هذا الكائن مثل وحدة تحكم NES التي لا توجد بها خراطيش.


المشكلة مع هذا الكائن هو أنه كان يعتقد أنه غير قابل للتشغيل. في كل مرة تتفاعل معه ، ترى فقط رسالة تقول أنه ليس لديك برنامج ألعاب.


اتضح أن هذا الكائن يحاول بالفعل مسح بطاقة الذاكرة ضوئيًا لملفات مصممة خصيصًا تحتوي على صور ROM لـ NES! يبدو أن محاكي NES المستخدم لتشغيل الألعاب المضمنة هو محاكي NES القياسي الكامل لـ GameCube ، وهو قادر على إطلاق معظم الألعاب.

قبل شرح هذه الميزات ، سأشرح عملية الهندسة العكسية لها.

بحث محمل ROM على بطاقة الذاكرة


نحن نبحث عن قائمة مطور


في البداية ، كنت أرغب في العثور على رمز ينشط قوائم المطورين المختلفة ، مثل قائمة اختيار الخريطة أو قائمة اختيار اللعبة لمحاكي NES. كان من السهل جدًا العثور على قائمة تحديد خريطة الغابة ، والتي بفضلها يمكنك بسهولة تحميل مواقع مختلفة من اللعبة - لقد بحثت للتو عن خط FOREST MAP SELECT الذي يظهر في أعلى الشاشة (يمكن رؤيته في مقاطع فيديو ولقطات شاشة مختلفة على الإنترنت )

في "FOREST MAP SELECT" توجد مراجع select_print_wait للبيانات إلى دالة select_print_wait ، والتي تؤدي إلى مجموعة من الوظائف الأخرى التي لها أيضًا البادئة select_* ، بما في ذلك دالة select_init . تبين أنها وظائف تتحكم في قائمة اختيار الخريطة.

تؤدي الدالة select_init وظيفة أخرى مثيرة للاهتمام تسمى game_get_next_game_dlftbl . تربط هذه الوظيفة جميع القوائم و "المشاهد" الأخرى التي يمكنك تشغيلها: شاشة تحمل شعار Nintendo ، والشاشة الرئيسية ، وقائمة اختيار البطاقة ، وقائمة محاكي NES (Famicom) ، وما إلى ذلك. يبدأ في بداية إجراء اللعبة الرئيسي ، ويجد وظيفة تهيئة المشهد التي يجب تشغيلها ، ويجد إدخالها في بنية بيانات الجدول التي تسمى game_dlftbls . يحتوي هذا الجدول على روابط لوظائف المعالجة لمشاهد مختلفة ، بالإضافة إلى بعض البيانات الأخرى.


أظهرت دراسة متأنية للكتلة الأولى من الوظيفة أنها تحمّل الوظيفة "اللعبة التالية init" ، ثم تبدأ في مقارنتها بسلسلة من وظائف init المعروفة:

  • first_game_init
  • select_init
  • play_init
  • second_game_init
  • trademark_init
  • player_select_init
  • save_menu_init
  • famicom_emu_init
  • prenmi_init


أحد المؤشرات الدالة التي يبحث عنها هو famicom_emu_init ، وهو المسؤول عن تشغيل محاكي NES / Famicom. game_get_next_game_init نتيجة game_get_next_game_init famicom_emu_init أو select_init في مصحح أخطاء Dolphin ، تمكنت من عرض قوائم خاصة. الخطوة التالية هي تحديد كيفية تعيين هذه المؤشرات بالطريقة العادية أثناء تنفيذ البرنامج. الشيء الوحيد الذي تقوم game_get_next_game_init وظيفة game_get_next_game_init هو تحميل القيمة عند الإزاحة 0xC للوسيطة الأولى إلى game_get_next_game_dlftbl .

كان تتبع هذه القيم التي تم تعيينها في هياكل البيانات المختلفة أمرًا مملًا بعض الشيء ، لذلك سأذهب مباشرة إلى القلب. أهم شيء وجدته:

  • عندما تبدأ اللعبة بالطريقة المعتادة ، فإنها تنفذ التسلسل التالي من الإجراءات:
    • first_game_init
    • second_game_init
    • trademark_init
    • play_init
  • player_select_init بتعيين الحرف الأول التالي إلى select_init . يجب أن تسمح لك هذه الشاشة بتحديد لاعب فورًا بعد اختيار البطاقة ، ولكن يبدو أنها لا تعمل بشكل صحيح.

لقد وجدت أيضًا وظيفة بدون اسم تحدد وظيفة init للمحاكي ، ولكن لم أجد أي شيء يضبط وظيفة init على قيمة init للاعب أو اختيار الخريطة.

عند هذه النقطة ، أدركت أن لدي مشكلة غبية أخرى في الطريقة التي حملت بها أسماء الوظائف في المؤسسة الدولية للتنمية: نظرًا للتعبير العادي المستخدم لقص الأسطر في ملف رمز التصحيح ، فاتني جميع أسماء الوظائف التي تبدأ بحرف كبير . تبدو الوظيفة التي famicom_emu_init مثل التحولات بين المشاهد ، وبالطبع ، كانت تسمى Game_play_fbdemo_wipe_proc .

تعالج Game_play_fbdemo_wipe_proc التحولات بين المشاهد ، مثل Game_play_fbdemo_wipe_proc الشاشة وانقطاع التيار الكهربائي.

في ظل ظروف معينة ، تم تنفيذ انتقال الشاشة من اللعب المعتاد إلى عرض المحاكي. كان هو الذي حدد وظيفة init المحاكي.

التعامل مع كائنات وحدة التحكم


في الواقع ، تجعل معالجات كائنات الأثاث لوحدات التحكم في NES مفتاح معالج انتقال الشاشة يتحول إلى المحاكي. عندما يتفاعل لاعب مع أحد وحدات التحكم ، يتم aMR_FamicomEmuCommonMove .

عند استدعاء الوظيفة ، تحتوي r6 على قيمة الفهرس المطابقة للأرقام في أسماء ملفات لعبة NES في famicom.arc :

  • 01_nes_cluclu3.bin.szs
  • 02_usa_balloon.nes.szs
  • 03_nes_donkey1_3.bin.szs
  • 04_usa_jr_math.nes.szs
  • 05_pinball_1.nes.szs
  • 06_nes_tennis3.bin.szs
  • 07_usa_golf.nes.szs
  • 08_punch_wh.nes.szs
  • 09_usa_baseball_1.nes.szs
  • 10_cluclu_1.qd.szs
  • 11_usa_donkey3.nes.szs
  • 12_donkeyjr_1.nes.szs
  • 13_soccer.nes.szs
  • 14_exbike.nes.szs
  • 15_usa_wario.nes.szs
  • 16_usa_icecl.nes.szs
  • 17_nes_mario1_2.bin.szs
  • 18_smario_0.nes.szs
  • 19_usa_zelda1_1.nes.szs

( .arc هو تنسيق أرشيف ملف خاص.)

عندما لا r6 مساوية للصفر ، يتم تمريرها في مكالمة aMR_RequestStartEmu . في هذه الحالة ، يتم تشغيل الانتقال إلى المحاكي.


ومع ذلك ، إذا كان r6 صفرًا ، فسيتم aMR_RequestStartEmu_MemoryC الدالة aMR_RequestStartEmu_MemoryC . عند تعيين القيمة في المصحح إلى 0 ، تلقيت الرسالة "ليس لدي أي برنامج". لم أتذكر على الفور أنني كنت بحاجة إلى التحقق من كائن وحدة التحكم في NES للتأكد من أنه يعيد تعيين قيمة r6 ، ولكن اتضح أن فهرس الصفر يستخدم لكائن وحدة التحكم بدون خرطوشة.

على الرغم من أن aMR_RequestStartEmu يخزن ببساطة قيمة الفهرس في نوع من بنية البيانات ، فإن aMR_RequestStartEmu_MemoryC يقوم بعمليات أكثر تعقيدًا ...


هذه الكتلة الثالثة من التعليمات البرمجية تستدعي aMR_GetCardFamicomCount من نتيجة غير صفرية ، وإلا فإنها تتخطى معظم الأشياء المثيرة للاهتمام على الجانب الأيسر من الرسم البياني للوظيفة.

يستدعي famicom_get_disksystem_titles ، الذي يستدعي بعد ذلك memcard_game_list ، وهنا يصبح كل شيء مثيرًا للاهتمام.

memcard_game_list بتثبيت بطاقة الذاكرة وتبدأ في الدوران في دورة كتابة الملف ، مع التحقق من كل من القيم. من خلال تتبع الوظيفة في مصحح الأخطاء ، تمكنت من فهم أنها كانت تقارن القيم مع كل ملف من الملفات الموجودة على بطاقة الذاكرة.


تحدد الوظيفة ما إذا كان سيتم تنزيل الملف أم لا ، بناءً على نتائج فحص أسطر متعددة. أولاً ، يتحقق من وجود الخطين "GAFE" و "01" ، وهما معرّفان اللعبة والشركة. 01 تعني نينتندو ، GAFE تعني Animal Crossing. أعتقد أنها تقف على GameCube Animal Forest English.

ثم تتحقق من السطور "DobutsunomoriP_F_" و "حفظ". في هذه الحالة ، يجب أن يتطابق السطر الأول ، وليس الثاني. اتضح أن "DobutsunomoriP_F_SAVE" هو اسم الملف الذي يخزن بيانات الألعاب المدمجة لـ NES. لذلك ، سيتم تحميل جميع الملفات باستثناء هذا الملف بالبادئة "DobutsunomoriP_F_".

باستخدام مصحح Dolphin لتخطي مقارنات السلسلة مع "SAVE" وجعل خدعة اللعبة لتعتقد أن ملف "SAVE" الخاص بي يمكن تنزيله بأمان ، حصلت على هذه القائمة بعد استخدام وحدة تحكم NES:


أجبت بـ "نعم" وحاولت تحميل ملف الحفظ كلعبة ، وبعد ذلك رأيت شاشة تحطم اللعبة المدمجة:


عظيم! الآن أعلم أنها تحاول بالفعل تنزيل الألعاب من بطاقة ذاكرة ، ويمكنني البدء في تحليل تنسيق الملفات المحفوظة لمعرفة ما إذا كان يمكن تنزيل ROM حقيقي.

أول شيء حاولت القيام به هو محاولة العثور على مكان قراءة اسم اللعبة من ملف بطاقة الذاكرة. عند البحث عن سطر "FEFSC" الموجود في الرسالة "هل تريد تشغيل <name>؟" ، وجدت الإزاحة التي تمت قراءتها من الملف: 0x642 . لقد قمت بنسخ ملف الحفظ ، وقمت بتغيير اسم الملف إلى "DobutsunomoriP_F_TEST" ، وغيرت وحدات البايت عند الإزاحة 0x642 إلى "اختبار" واستوردت الحفظ الذي تم تغييره ، وبعد ذلك ظهر الاسم الذي احتاجه في القائمة.

بعد إضافة عدد قليل من الملفات بهذا التنسيق ، ظهرت بضعة خيارات أخرى في القائمة:


تنزيل ROM


إذا aMR_GetCardFamicomCount إرجاع aMR_GetCardFamicomCount غير صفري ، فسيتم تخصيص الذاكرة على الكومة ، ويتم استدعاء famicom_get_disksystem_titles مباشرة famicom_get_disksystem_titles ، وبعد ذلك يتم تحديد مجموعة من الإزاحات العشوائية في بنية البيانات. وبدلاً من فك رموز مكان قراءة هذه القيم ، بدأت في دراسة قائمة وظائف famicom .

اتضح أنني بحاجة إلى famicom_rom_load . يتحكم في تحميل ROM ، إما من بطاقة ذاكرة ، أو من الموارد الداخلية للعبة.


الشيء الأكثر أهمية في كتلة "التمهيد من بطاقة الذاكرة" هو أنه يتصل
memcard_game_load . تقوم بتثبيت الملف على بطاقة الذاكرة مرة أخرى ، وتقرأه وتحلل. هذا هو المكان الذي تظهر فيه أهم خيارات تنسيق الملف.

قيمة المجموع الاختباري


أول شيء يحدث بعد تحميل الملف هو حساب المجموع الاختباري. يتم calcSum وظيفة calcSum ، وهي خوارزمية بسيطة جدًا تجمع قيم جميع وحدات البايت في البيانات من بطاقة الذاكرة. يجب أن تكون البتات الثمانية السفلية للنتيجة صفرًا. أي ، لتمرير هذا الاختيار ، تحتاج إلى جمع قيم جميع البايتات في الملف المصدر ، وحساب القيمة التي يجب إضافتها بحيث تصبح البتات الثمانية السفلية صفرًا ، ثم تعيين هذه القيمة إلى بايت المجموع الاختباري في الملف.

إذا فشل التحقق ، فستتلقى رسالة حول استحالة قراءة بطاقة الذاكرة بشكل صحيح ، ولا يحدث شيء. أثناء التصحيح ، كل ما علي فعله هو تخطي هذا الاختيار.

نسخ ROM


بالقرب من نهاية memcard_game_load ، يحدث شيء آخر مثير للاهتمام. هناك العديد من كتل التعليمات البرمجية الأكثر إثارة للاهتمام بينه والمجموع الاختباري ، ولكن لا يؤدي أي منها إلى تفرع يتخطى تنفيذ هذا السلوك.


إذا كانت قيمة عدد صحيح 16 بت معينة قراءة من بطاقة الذاكرة لا تساوي صفر ، ثم يتم استدعاء دالة تتحقق من رأس الضغط في المخزن المؤقت. يتحقق من تنسيقات ضغط Nintendo المملوكة من خلال النظر في بداية المخزن المؤقت Yay0 أو Yaz0. إذا تم العثور على أحد هذه الخطوط ، يتم استدعاء وظيفة فك الحزمة. خلاف ذلك ، يتم تنفيذ وظيفة نسخ بسيطة. في أي حال ، بعد ذلك ، nesinfo_data_size متغير يسمى nesinfo_data_size .

تلميح آخر للسياق هنا هو أن ملفات ROM لألعاب NES المضمنة تستخدم ضغط Yaz0 ، وهذا الخط موجود في رؤوس ملفاتهم.

بعد ملاحظة القيمة التي تم التحقق منها للصفر ، وتمرير المخزن المؤقت إلى وظائف فحص الضغط ، اكتشفت بسرعة من أين تتم قراءة اللعبة في الملف الموجود على بطاقة الذاكرة. يتم تنفيذ التدقيق الصفري لجزء من المخزن المؤقت 32 بايت المنسوخ من الإزاحة 0x640 في الملف ، وهو على الأرجح رأس ROM. تقوم هذه الوظيفة أيضًا بفحص الأجزاء الأخرى من الملف ، وفيها يوجد اسم اللعبة (بدءًا من البايت الثالث للرأس).

في مسار تنفيذ التعليمات البرمجية الذي وجدته ، يوجد المخزن المؤقت ROM مباشرة بعد هذا المخزن المؤقت لرأس 32 بايت.


هذه المعلومات كافية لمحاولة إنشاء ملف ROM يعمل. لقد أخذت للتو أحد ملفات حفظ DobutsunomoriP_F_TEST الأخرى وقمت بتحريره في محرر سداسي لاستبدال اسم الملف بـ DobutsunomoriP_F_TEST ومسح جميع المناطق التي أردت لصق البيانات فيها.

لإجراء اختبار ، استخدمت ROM لعبة Pinball ، الموجودة بالفعل في اللعبة ، وأدرجت محتوياتها بعد رأس 32 بايت. بدلاً من حساب قيمة المجموع الاختباري ، قمت بتعيين نقاط التوقف بحيث أقوم ببساطة بتخطي calcSum أيضًا نتائج الاختبارات الأخرى التي قد تؤدي إلى فرع يتخطى عملية التمهيد ROM.

أخيرًا ، قمت باستيراد الملف الجديد من خلال مدير بطاقة ذاكرة Dolphin ، وأعدت تشغيل اللعبة ، وحاولت تشغيل وحدة التحكم.



عملت! كانت هناك بعض الأخطاء الرسومية الصغيرة المتعلقة بمعلمات Dolphin ، والتي أثرت على وضع الرسومات المستخدم من قبل محاكي NES ، ولكن بشكل عام كانت اللعبة تؤدي بشكل جيد. (في الإصدارات الأحدث من Dolphin ، يجب أن تعمل بشكل افتراضي.)

للتأكد من بدء الألعاب الأخرى أيضًا ، حاولت كتابة العديد من ROMs الأخرى التي لم تكن في اللعبة. بدأت Battletoads ، لكنها توقفت عن العمل بعد نص شاشة البداية (بعد إعدادات أخرى تمكنت من جعلها قابلة للتشغيل). من ناحية أخرى ، عمل Mega Man بشكل مثالي:


من أجل معرفة كيفية إنشاء ملفات ROM جديدة يمكن تحميلها دون تدخل مصححات الأخطاء ، كان علي أن أبدأ في كتابة التعليمات البرمجية وفهم تحليل تنسيق الملف بشكل أفضل.

تنسيق ملف ROM الخارجي


أهم جزء من تحليل الملفات يحدث في memcard_game_load . هناك ستة أقسام رئيسية من كتل تحليل الكود في هذه الوظيفة:

  • المجموع الاختباري
  • حفظ اسم الملف
  • رأس ملف ROM
  • تم نسخ مخزن مؤقت غير معروف بدون أي معالجة
  • تعليق نصي ورمز وتحميل لافتة (لإنشاء ملف حفظ جديد)
  • محمل إقلاع ROM


المجموع الاختباري


يجب أن تكون البتات الثمانية السفلية من مجموع كافة قيم البايت في ملف الحفظ صفرًا. إليك كود Python البسيط الذي يولد بايت المجموع الاختباري اللازم:

 checksum = 0 for byte_val in new_data_tmp: checksum += byte_val checksum = checksum % (2**32) # keep it 32 bit checkbyte = 256 - (checksum % 256) new_data_tmp[-1] = checkbyte 

ربما يكون هناك مكان خاص لتخزين بايت المجموع الاختباري ، ولكن إضافته إلى المساحة الفارغة في نهاية ملف الحفظ يعمل بشكل جيد.

اسم الملف


مرة أخرى ، يجب أن يبدأ اسم ملف الحفظ بـ "DobutsunomoriP_F_" وينتهي بشيء لا يحتوي على "حفظ". يتم نسخ اسم الملف هذا عدة مرات ، وفي حالة واحدة يتم استبدال الحرف "F" بالحرف "S". سيكون هذا هو اسم الملفات المحفوظة للعبة NES ("DobutsunomoriP_S_NAME").

رأس ROM


يتم تحميل نسخة مباشرة من رأس 32 بايت في الذاكرة. يتم استخدام بعض القيم في هذا الرأس لتحديد كيفية التعامل مع الأقسام اللاحقة. في الأساس ، هذه بعض قيم حجم 16 بت وبتات المعلمات المعبأة.

إذا قمت بتتبع المؤشر الذي تم نسخه بواسطة الرأس إلى بداية الوظيفة ووجدت موضع MemcardGameHeader_t* توقيع الوظيفة أدناه أنه يحتوي بالفعل على نوع MemcardGameHeader_t* .

 memcard_game_load(unsigned char *, int, unsigned char **, char *, char *, MemcardGameHeader_t *, unsigned char *, unsigned long, unsigned char *, unsigned long) 

مخزن مؤقت غير معروف


للتحقق من قيمة حجم 16 بت من الرأس. إذا لم يكن يساوي صفرًا ، فسيتم نسخ عدد وحدات البايت المقابلة مباشرةً من مخزن الملفات المؤقت إلى كتلة جديدة من الذاكرة المخصصة. يؤدي ذلك إلى نقل مؤشر البيانات في المخزن المؤقت للملف بحيث يمكن مواصلة النسخ من القسم التالي.

لافتة ، أيقونة وتعليق


يتم فحص قيمة حجم أخرى في الرأس ، وإذا لم تكن تساوي الصفر ، يتم استدعاء وظيفة فحص ضغط الملف. إذا لزم الأمر ، سيتم تشغيل خوارزمية التفريغ ، وبعد ذلك يتم SetupExternCommentImage .

تقوم هذه الوظيفة بثلاثة أشياء: "التعليق" وصورة البانر والرمز. لكل منهم ، يوجد رمز في رأس ROM يوضح كيفية التعامل معهم. هناك الخيارات التالية:

  1. استخدم القيمة الافتراضية
  2. نسخ من قسم بانر / أيقونة / تعليق في ملف ROM
  3. نسخ من المخزن المؤقت البديل

تتسبب القيم الافتراضية للرمز في تحميل الرمز أو البانر من المورد الموجود على القرص ، ويتم تعيين اسم "حفظ الملف" و "NES Cassette Save Data" باسم الملف المحفوظ والتعليق (الوصف النصي للملف). إليك ما يبدو عليه:


تنسخ القيمة الثانية للرمز ببساطة اسم اللعبة من ملف ROM (بديل لـ "Animal Crossing") ، ثم تحاول العثور على السلسلة "] ROM" في تعليق الملف واستبدالها بـ "] SAVE". على ما يبدو ، كان من المفترض أن تكون الملفات التي أرادت Nintendo إصدارها بتنسيق أسماء "Game Name [NES] ROM" أو شيء مماثل.

بالنسبة إلى الرمز والشعار ، يحاول الرمز تحديد تنسيق الصورة ، والحصول على قيمة حجم ثابتة تتوافق مع هذا التنسيق ، ثم نسخ الصورة.

في قيمة الرمز الأخيرة ، يتم نسخ اسم الملف والوصف بدون تغييرات من المخزن المؤقت ، ويتم أيضًا تحميل الرمز والشعار من المخزن المؤقت البديل.

ROM


إذا نظرت بعناية إلى لقطة شاشة memcard_game_load نسخ ROM memcard_game_load ، يمكنك أن ترى أن قيمة 16 بت المحددة للتحقق من المساواة إلى الصفر يتم إزالتها إلى اليسار بمقدار 4 بت (مضروبة في 16) ، ثم يتم استخدامها كحجم وظيفة memcpy إذا لم يتم الكشف عن الضغط. هذه قيمة حجم أخرى موجودة في الرأس.

إذا كان الحجم لا يساوي الصفر ، يتم التحقق من ضغط بيانات ROM ثم نسخها.

مخزن مؤقت غير معروف والبحث عن الأخطاء


على الرغم من أن تنزيل ROMs الجديدة أمر غريب للغاية ، إلا أن الشيء الأكثر إثارة للاهتمام بالنسبة إلى محمل ROM هذا هو أنه في الواقع هذا هو الجزء الوحيد من اللعبة الذي يتلقى مدخلات المستخدم ذات الحجم المتغير وينسخها إلى مواقع ذاكرة مختلفة. تقريبا كل شيء آخر يستخدم المخازن المؤقتة ذات الحجم الثابت. قد تبدو أشياء مثل الأسماء ونصوص الحروف مختلفة في الطول ، ولكن في الأساس يتم ملء المساحة الفارغة بمسافات. يتم استخدام السلاسل المنتهية بصفر بشكل غير متكرر ، وتجنب أخطاء تلف الذاكرة الشائعة ، مثل استخدام strcpy مع مخزن مؤقت صغير جدًا لنسخ السلاسل فيه.

كنت مهتمًا جدًا بإمكانية العثور على استغلال للعبة استنادًا إلى حفظ الملفات ، وبدا أن هذا كان الخيار الأفضل.

تستخدم معظم عمليات ملفات ROM الموضحة أعلاه نسخًا ذات حجم ثابت ، باستثناء مخزن مؤقت وبيانات ROM غير معروفة. لسوء الحظ ، فإن الرمز الذي يعالج هذا المخزن المؤقت يخصص بالضبط نفس المساحة اللازمة لنسخه ، لذلك لا يوجد تجاوز ، ولم يكن إعداد أحجام ملفات ROM كبيرة جدًا مفيدًا للغاية.

ولكن ما زلت أرغب في معرفة ما يحدث لهذا المخزن المؤقت ، الذي يتم نسخه دون أي معالجة.

معالجات بطاقات معلومات NES


عدت إلى famicom_rom_load . بعد تحميل ROM من بطاقة ذاكرة أو قرص ، تسمى العديد من الوظائف:

  • nesinfo_tag_process1
  • nesinfo_tag_process2
  • nesinfo_tag_process3

بعد تتبع المكان الذي يتم فيه نسخ المخزن المؤقت غير المعروف ، تأكدت من أن هذه المهمة يتم تنفيذها بواسطة هذه الوظائف. يبدأون باستدعاء nesinfo_next_tag ، الذي ينفذ خوارزمية بسيطة:

  • للتحقق مما إذا كان المؤشر المحدد nesinfo_tags_end المؤشر في nesinfo_tags_end . إذا كانت أقل من nesinfo_tags_end أو nesinfo_tags_end صفرًا ، فإنها تتحقق من وجود السلسلة "END" في رأس المؤشر.

    • إذا تم الوصول إلى "END" ، أو ارتفع المؤشر إلى nesinfo_tags_end أو أعلى ، nesinfo_tags_end الدالة قيمة خالية.
    • وإلا ، تتم إضافة البايت عند الإزاحة 0x3 للمؤشر إلى 4 وإلى المؤشر الحالي ، وبعد ذلك يتم إرجاع القيمة.

يخبرنا هذا أن هناك نوعًا من تنسيق التسمية من اسم مكون من ثلاثة أحرف وقيمة حجم البيانات والبيانات نفسها. والنتيجة هي مؤشر للتسمية التالية ، لأنه تم تخطي التسمية الحالية (يتخطى cur_ptr + 4 الاسم المكون من ثلاثة أحرف والبايت الواحد ، ويتخطى size_byte البيانات).

إذا لم تكن النتيجة صفرًا ، فإن وظيفة معالجة الملصق تقوم بسلسلة من مقارنات السلسلة لمعرفة الملصق الذي يجب معالجته. بعض أسماء الملصقات المحددة في nesinfo_tag_process1 : VEQ و VNE و GID و GNO و BBR و QDS.


إذا تم العثور على تطابق تسمية ، يتم تنفيذ بعض التعليمات البرمجية للمعالج. لا تقوم بعض المعالجات بأي شيء سوى عرض تسمية في رسالة التصحيح. البعض الآخر معالجات أكثر تعقيدا. بعد معالجة الملصق ، تحاول الوظيفة الحصول على الملصق التالي ومتابعة المعالجة.

لحسن الحظ ، هناك العديد من رسائل التصحيح التفصيلية التي يتم عرضها عند الكشف عن العلامات.جميعها باللغة اليابانية ، لذا يجب أولاً فك تشفيرها من Shift-JIS وترجمتها. على سبيل المثال ، قد تظهر رسالة لـ QDS "تحميل منطقة حفظ القرص" أو "نظرًا لأن هذه هي المرة الأولى ، أنشئ منطقة حفظ قرص". قراءة رسائل BBR "تحميل نسخة احتياطية من البطارية" أو "نظرًا لأن هذه هي البداية ، نقوم بإجراء عملية تنظيف".

يقوم كل من هذين الرمزين أيضًا بتحميل بعض القيم من قسم البيانات في تسمياتها واستخدامها لحساب الإزاحة في بيانات ROM ، وبعد ذلك يقومون بإجراء عمليات النسخ. من الواضح أنهم مسؤولون عن تحديد الأجزاء في ذاكرة ROM المرتبطة بالحفاظ على الحالة.

هناك أيضًا علامة "HSC" مع رسالة تصحيح تفيد أنها تعالج سجلات النقاط. تحصل على تعويض في ROM من بيانات العلامة الخاصة بها ، بالإضافة إلى قيمة سجل الدرجات الأصلية. يمكن استخدام هذه العلامات للإشارة إلى مكان في ذاكرة لعبة NES لتخزين النتائج العالية ، ربما لحفظها واستعادتها في المستقبل.

تقوم هذه العلامات بإنشاء نظام تحميل بيانات تعريف ROM معقد إلى حد ما. علاوة على ذلك ، يؤدي العديد منها إلى مكالمات memcpyبناءً على القيم المرسلة في بيانات الملصق.

صيد الحشرات


معظم العلامات التي تؤدي إلى التلاعب بالذاكرة ليست مفيدة جدًا للثغرات ، لأن جميع قيمها القصوى للإزاحة والحجم محددة كأرقام صحيحة ذات 16 بت. هذا يكفي للعمل مع مساحة عنوان NES 16 بت ، ولكن ليس كافيًا لكتابة قيم هدف مفيدة ، مثل مؤشرات الوظائف أو إرجاع العناوين على المكدس في مساحة عنوان GameCube 32 بت.

ومع ذلك ، هناك عدة حالات memcpyيمكن أن تتجاوز فيها قيم إزاحة الحجم المرسلة 0xFFFF.

QDS

يقوم QDS بتحميل إزاحة 24 بت من بيانات العلامات الخاصة بها ، بالإضافة إلى قيمة حجم 16 بت.

الشيء الجيد هنا هو أن الإزاحة تستخدم لحساب عنوان الوجهة لعملية النسخ. العنوان الأساسي للإزاحة هو بداية البيانات التي تم تنزيلها ، ومصدر النسخ موجود في ملف ROM لبطاقة الذاكرة ، ويتم تعيين الحجم من خلال قيمة حجم 16 بت من التسمية.

تحتوي القيمة 24 بت على قيمة قصوى 0xFFFFFF، وهي أكثر بكثير مما هو ضروري للكتابة خارج بيانات ROM المحملة. ومع ذلك ، هناك بعض المشكلات ...

الأول هو أنه على الرغم من أن القيمة القصوى للحجم متساوية 0xFFFF، إلا أنها تستخدم في البداية لإعادة تعيين قسم الذاكرة. إذا كانت قيمة الحجم عالية جدًا (ليست أكبر بكثير 0x1000) ، فسيؤدي ذلك إلى إعادة تعيين علامة "QDS" في رمز اللعبة.

وهنا تكمن المشكلة ، لأنها nesinfo_tag_process1تسمى في الواقع مرتين. للمرة الأولى ، تتلقى بعض المعلومات حول المساحة التي تحتاجها للتحضير للبيانات المخزنة. لا تتم معالجة علامات QDS و BBR بالكامل عند التشغيل الأول. بعد التشغيل الأول ، يتم تحضير مكان لحفظ البيانات ، ويتم استدعاء الوظيفة مرة أخرى. هذه المرة ، تتم معالجة علامات QDS و BBR بالكامل ، ولكن إذا تم مسح سلاسل اسم العلامة من الذاكرة ، فمن المستحيل مطابقة العلامات مرة أخرى!

يمكن تجنب ذلك عن طريق تعيين قيمة حجم أصغر. مشكلة أخرى هي أن قيمة الإزاحة يمكن أن تتحرك إلى الأمام فقط في الذاكرة ، وبيانات ROM NES موجودة في كومة الذاكرة المؤقتة قريبة جدًا من نهاية الذاكرة المتوفرة.

بعدها لا يوجد سوى عدد قليل من أكوام ، وليس واحد منهم لديه أي شيء مفيد بشكل خاص ، مثل مؤشرات وظيفة واضحة.

في الحالة العادية ، يمكنك استخدام هذا لاستغلال تجاوز سعة كومة الذاكرة المؤقتة ، ولكن في التنفيذ mallocالمستخدم لهذا الكومة ، تمت إضافة عدد قليل جدًا من وحدات البايت لفحوصات الصحة في الكتل malloc. يمكننا الكتابة فوق قيم المؤشر في كتل كومة الذاكرة المؤقتة اللاحقة. بدون الفحوصات الصحية ، يمكن استخدام هذا للكتابة إلى منطقة ذاكرة عشوائية عند استدعائها freeلكتلة كومة معنية.

ومع ذلك ، mallocيتحقق التطبيق المستخدم هنا من وجود نمط بايت محدد ( 0x7373) في بداية الكتل التالية والسابقة التي سيتم معالجتها عند استدعاءfree. إذا لم تجد هذه البايتات ، عندها تتصل OSPanicوتتجمد اللعبة.


غير قادر على التأثير على وجود هذه البايت في بعض المواقع المستهدفة ، لا يمكن الكتابة هنا. بمعنى آخر ، من المستحيل تسجيل شيء ما في مكان تعسفي دون القدرة على تسجيل شيء بالقرب من هذا المكان. ربما بعض الطريق للتأكد من أن القيمة 0x73730000المخزنة في كومة الحق قبل عنوان المرسل و المكان الذي أشار إليه القيمة التي نريد أن الكتابة إلى العنوان المقصود (كما سيتم فحص ذلك، كما لو كان مؤشر كتلة كومة)، ولكن هذا من الصعب تحقيق ذلك واستخدامه في استغلال.

nesinfo_update_highscore

وظيفة أخرى تتعلق بعلامات QDS و BBR و HSC هي هذه nesinfo_update_highscore. تُستخدم أحجام علامات QDS و BBR و OFS (الإزاحة) لحساب الإزاحة التي يتم تسجيلها ، وتتضمن علامة HSC التسجيل في ذلك الموقع. يتم تنفيذ هذه الوظيفة لكل إطار معالج بواسطة محاكي NES.

الحد الأقصى لقيمة الإزاحة لكل تسمية في هذه الحالة ، حتى بالنسبة لـ QDS ، يساوي 0xFFFF. ومع ذلك ، أثناء دورة معالجة الملصق ، تتراكم قيم الأبعاد من تسميات BBR و QDS . وهذا يعني أنه يمكن استخدام علامات متعددة لحساب أي قيمة تعويض تقريبًا. القيد هو عدد التسميات التي يمكن احتواؤها في قسم البيانات في تسميات ROM في ملف على بطاقة ذاكرة ، ولها أيضًا حجم أقصى 0xFFFF.

العنوان الأساسي الذي تتم إضافة الإزاحة إليه هو 0x800C3180مخزن البيانات المخزن المؤقت. هذا العنوان أقل بكثير من بيانات ROM ، مما يمنحنا المزيد من الحرية في اختيار موقع التسجيل. على سبيل المثال ، سيكون من السهل جدًا إعادة كتابة عنوان المرسل في المكدس إلى العنوان 0x812F95DC.

لسوء الحظ ، لم ينجح هذا أيضًا. اتضح أنه nesinfo_tag_process1يتحقق أيضًا من الحجم المتراكم للإزاحات من هذه التسميات ، ويستخدم هذا الحجم لتهيئة المساحة:

 bzero(nintendo_hi_0, ((offset_sum + 0xB) * 4) + 0x40) 


مع قيمة الإزاحة التي كنت أحاول حسابها ، أدى ذلك إلى حقيقة أنه تم مسح 0x48D91EC(76،386،796) بايت من الذاكرة ، وهذا هو السبب في تعطل اللعبة بشكل مذهل.

علامة PAT


لقد بدأت بالفعل أفقد الأمل ، لأن جميع هذه العلامات التي أجرت مكالمات غير محمية memcpyقد فشلت حتى قبل أن أتمكن من استخدامها. لقد قررت فقط توثيق الغرض من كل علامة ، وحصلت تدريجياً على العلامات في nesinfo_tag_process2. لا تبدأ

معظم معالجات التسميات nesinfo_tag_process2أبدًا ، لأنها تعمل فقط عندما يكون المؤشر nesinfo_rom_startغير صفري. لا يوجد شيء في التعليمات البرمجية يعين قيمة غير صفرية لهذا المؤشر. تمت تهيئته بقيمة فارغة ولا يتم استخدامه مرة أخرى مطلقًا. عند تحميل ROM فقط nesinfo_data_start، لذلك يبدو وكأنه رمز ميت.

ومع ذلك ، هناك تسمية واحدة يمكن أن تعمل عندما تكون غير صفر nesinfo_rom_start: PAT. هذا هو أصعب تسمية في دالة nesinfo_tag_process2.


يستخدم أيضًا كمؤشر nesinfo_rom_start، لكنه لا يتحقق من الصفر. تقرأ علامة PAT المخزن المؤقت لبيانات العلامة الخاصة بها ، وتقوم بمعالجة الرموز التي تحسب الإزاحة. تتم إضافة هذه الإزاحات إلى المؤشر nesinfo_rom_startلحساب عنوان الوجهة ، ثم يتم نسخ وحدات البايت من مخزن التصحيح إلى هذا الموقع. يتم هذا النسخ عن طريق تحميل وحفظ وحدات البايت ، وعدم استخدام الإرشادات memcpy، لذلك لم ألاحظها من قبل.

يحتوي كل مخزن مؤقت لبيانات علامة PAT على رمز نوع 8 بت ، وحجم تصحيح 8 بت ، وقيمة إزاحة 16 بت ، متبوعة ببيانات التصحيح.

  • إذا كان الرمز هو 2 ، فسيتم إضافة قيمة الإزاحة إلى مجموع الإزاحات الحالي.
  • إذا كان الرمز 9 ، فسيتم إزاحة الإزاحة لأعلى بمقدار 4 بتات وإضافتها إلى مجموع الإزاحات الحالي.
  • إذا كان الرمز 3 ، فسيتم إعادة تعيين مجموع الإزاحات إلى 0.

الحد الأقصى لحجم ملصق معلومات NES هو 255 ، أي أن أكبر حجم بات بات هو 251 بايت. ومع ذلك ، يمكن استخدام العديد من علامات PAT ، أي يمكنك تصحيح أكثر من 251 بايت ، بالإضافة إلى تصحيح المسافات غير المتجاورة.

طالما أن لدينا سلسلة من باطن PAT برمز 2 أو رمز 9 ، يستمر إزاحة مؤشر الوجهة في التراكم. عند نسخ بيانات التصحيح ، يتم إعادة تعيينها إلى صفر ، ولكن إذا كنت تستخدم حجم تصحيح صفر ، فيمكن تجنب ذلك. من الواضح أنه يمكن استخدام هذا لحساب بعض الإزاحة التعسفية بمؤشر فارغ nesinfo_rom_startباستخدام العديد من علامات PAT.

ومع ذلك ، هناك نوعان آخران من التحقق من قيم التعليمات البرمجية ...

  • إذا كان الرمز بين 0x80و 0xFF، فسيتم إضافته إلى 0x7F80، ثم إزاحته لأعلى 16 بت. ثم يضاف إلى قيمة الإزاحة 16 بت ويستخدم كعنوان نهاية للتصحيح.

هذا يسمح لنا بتعيين عنوان وجهة للرقعة في النطاق من 0x80000000إلى 0x807FFFFF! هذا هو المكان الذي يوجد فيه الجزء الأكبر من رمز معبر الحيوان في الذاكرة. هذا يعني أنه يمكننا تصحيح رمز عبور الحيوانات نفسه باستخدام ملصقات بيانات التعريف ROM من ملف موجود على بطاقة ذاكرة.

بمساعدة أداة تحميل التصحيح الصغيرة ، يمكنك بسهولة تنزيل تصحيحات أكبر من بطاقة ذاكرة إلى أي عنوان.

كتحقق سريع ، قمت بإنشاء تصحيح يتضمن "وضع zuru 2" (وضع مطور اللعبة ، الموصوف في مقالتي السابقة) عندما يقوم المستخدم بتحميل ROM من خريطة اللعبة. اتضح أن مجموعة الغش من المفاتيح لا تنشط سوى وضع "zuru mode 1" ، الذي لا يمكنه الوصول إلى الوظائف التي يمتلكها الوضع 2. مع هذا التصحيح ، بفضل بطاقة الذاكرة ، يمكننا الحصول على وصول كامل إلى وضع المطور على الأجهزة الحقيقية.


ستتم معالجة علامات التصحيح عند تمهيد ROM.


بعد تحميل ROM ، تحتاج إلى الخروج من محاكي NES لرؤية النتيجة.


يعمل!

تنسيق تسمية معلومات التصحيح


تبدو علامات المعلومات في ملف الحفظ التي تقوم بتشغيل هذا التصحيح كما يلي:

000000 5a 5a 5a 00 50 41 54 08 a0 04 6f 9c 00 00 00 7d >ZZZ.PAT...o....}<
000010 45 4e 44 00 >END.<


  • ZZZ \x00: علامة البداية التي تم تجاهلها. 0x00هو حجم مخزن البيانات: صفر.
  • PAT \x08 \xA0 \x04 \x6F\x9C \x00\x00\x00\x7D: التصحيح 0x80206F9Cفي 0x0000007D.
    • 0x08 هو حجم المخزن المؤقت للملصق.
    • 0xA0عندما تضاف إلى 0x7F80التحول 0x8020، وهذا هو، والعلوي 16 بت من عنوان الوجهة.
    • 0x04هو حجم بيانات التصحيح ( 0x0000007D).
    • 0x6F9C هي الجزء السفلي 16 بت من عنوان الوجهة.
    • 0x0000007D هي بيانات التصحيح.
  • END \x00 : علامة علامة النهاية.

إذا كنت ترغب في التجربة بمفردك بإنشاء ملفات تصحيح أو حفظ ملفات ROM ، فعندئذٍ على https://github.com/jamchamb/ac-nesrom-save-generator قمت بنشر كود بسيط جدًا لإنشاء الملفات. يمكن إنشاء تصحيح مثل ذلك الموضح أعلاه بالأمر التالي:

$ ./patcher.py Patcher /dev/null zuru_mode_2.gci -p 80206F9c 0000007D

تنفيذ القانون التعسفي


بفضل هذه العلامة ، يمكنك تحقيق تنفيذ التعليمات البرمجية التعسفية في Animal Crossing.

ولكن هنا تأتي العقبة الأخيرة: استخدام التصحيحات للبيانات يعمل بشكل جيد ، ولكن تنشأ مشاكل عند تصحيح تعليمات التعليمات البرمجية.

عندما يتم تسجيل التصحيحات ، تستمر اللعبة في اتباع التعليمات القديمة التي كانت في مكانها. يبدو أن هذه مشكلة تخزين مؤقت ، وهي في الواقع كذلك. تحتوي وحدة المعالجة المركزية GameCube على ذاكرة تخزين مؤقت للتعليمات ، كما هو موضح في المواصفات .

لفهم كيفية مسح ذاكرة التخزين المؤقت ، بدأت في دراسة الوظائف المتعلقة بذاكرة التخزين المؤقت من وثائق GameCube SDK ، واكتشفت ICInvalidateRange. تعمل هذه الوظيفة على إبطال كتل التعليمات المخزنة مؤقتًا في عنوان الذاكرة المحدد ، مما يسمح بتنفيذ ذاكرة التعليمات المعدلة برمز محدث.

ومع ذلك ، بدون القدرة على تشغيل الكود الأصلي ، ما زلنا لا نستطيع الاتصال ICInvalidateRange. لتنفيذ الشفرة بنجاح ، نحتاج إلى خدعة أخرى.

عند دراسة التنفيذ mallocلإمكانية استخدام برمجية إكسبلويت مع تجاوز سعة الكومة ، علمت أنه mallocيمكن تعطيل وظائف التنفيذ ديناميكيًا باستخدام بنية بيانات تسمى my_malloc. my_mallocيحمّل المؤشر إلى التنفيذ الحالي mallocأو freeمن مكان ثابت في الذاكرة ، ثم يستدعي هذه الوظيفة ، ويمرر كل الوسيطات التي تم تمريرها إلى my_malloc.

يستخدم محاكي NES بنشاطmy_mallocلتخصيص وتحرير ذاكرة لبيانات NES ذات الصلة ROM ، لذلك كنت متأكدًا من أنه سيتم إطلاقها عدة مرات في نفس وقت علامات PAT تقريبًا.

نظرًا لأنه يقوم my_mallocبتحميل مؤشر من الذاكرة ويقوم بالانتقال إليه ، يمكنني تغيير عملية تنفيذ البرنامج ببساطة عن طريق استبدال المؤشر بحيث يشير إلى الوظيفة الحالية mallocأو free. لن يمنع التخزين المؤقت للأداة حدوث ذلك ، لأنه لا يلزم تغيير التعليمات my_malloc.

قام مطور مشروع مروحة Dōbutsu no Mori e + ، المسمى Cuyler ، بكتابة مثل هذا المحمل في مجمع PowerPC وأظهر استخدامه لحقن كود جديد في هذا الفيديو: https://www.youtube.com/watch؟v=BdxN7gP6WIc. (كان Dōbutsu no Mori e + آخر تكرار لـ Animal Crossing على GameCube ، والذي كان يحتوي على معظم التحديثات. تم إصداره في اليابان فقط.) يقوم التصحيح بتنزيل بعض الرموز التي تسمح للاعب بإنشاء أي كائنات عن طريق إدخال معرفه بالحرف والضغط على الزر Z.


بفضل هذا ، يمكنك تنزيل التعديلات والغش والبيرة في نسخة عادية من Animal
Crossing على GameCube حقيقي.

Source: https://habr.com/ru/post/ar417117/


All Articles