مقدمة
يمكن وصف الهندسة العكسية لملف بيانات غير مألوف كعملية فهم تدريجية. يشبه في نواح كثيرة منهجًا علميًا ، يتم تطبيقه فقط على الأشياء المجردة التي أنشأها الإنسان ، وليس على العالم الطبيعي. نبدأ بجمع البيانات ، ثم نستخدم هذه المعلومات لطرح فرضية واحدة أو أكثر. نحن نختبر الفرضيات ونطبق نتائج هذه الاختبارات لتوضيحها. إذا لزم الأمر ، كرر العملية.
تطوير المهارات الهندسية العكسية هو في الأساس مسألة ممارسة. من خلال اكتساب الخبرة ، فإنك تبني فهمًا بديهيًا لما تحتاج إلى استكشافه أولاً وقبل كل شيء ، والأنماط التي تحتاج إلى البحث عنها ، والأدوات الأكثر ملاءمة للاستخدام.
في هذه المقالة ، سأتحدث بالتفصيل عن عملية ملفات بيانات الهندسة العكسية من لعبة كمبيوتر قديمة لشرح كيفية القيام بذلك.
خلفية صغيرة
بدأ كل شيء عندما حاولت إعادة إنشاء
تحدي Chip's على نظام Linux.
تم إصدار
تحدي Chip's Challenge في الأصل عام 1989 لوحدة التحكم المحمولة Atari Lynx المنسية الآن. في ذلك الوقت ، كانت Atari Lynx سيارة مثيرة للإعجاب ، ولكنها ظهرت في نفس الوقت مثل Nintendo Game Boy ، التي استحوذت على السوق في النهاية.
Chip's Challenge هي لعبة ألغاز ذات منظر علوي وخريطة تجانبية. كما هو الحال مع معظم هذه الألعاب ، فإن الهدف من كل مستوى هو الوصول إلى الخروج. في معظم المستويات ، يكون الإخراج محميًا بواسطة موصل للرقاقة ، والذي لا يمكن تجاوزه إلا من خلال جمع عدد معين من شرائح الكمبيوتر.
فيديو:
أتاري الوشق في العمل ،
مستوى تجول واحد .
تبدأ لعبة جديدة من المستوى الأول تحت اسم "الدرس 1". بالإضافة إلى رقائق وفتحة لشريحة ، تظهر مفاتيح وأبواب عليها. على مستويات أخرى ، تنشأ عقبات مثل الفخاخ والقنابل والمياه والكائنات التي (في معظم الأحيان) تتحرك على طول طرق يمكن التنبؤ بها. تتيح لك مجموعة واسعة من الكائنات والأجهزة إنشاء العديد من الألغاز وحدود الوقت. لإكمال اللعبة ، يجب أن تمر بأكثر من 140 مستوى.
على الرغم من فشل Lynx في النهاية ، أثبت
تشيب تشالينج شعبية كبيرة وتم نقله إلى العديد من المنصات الأخرى ، والذي ظهر أخيرًا على Microsoft Windows ، حيث أصبح واسع الانتشار. حول اللعبة ، تم تشكيل قاعدة صغيرة ولكنها مخصصة من المعجبين ، وبمرور الوقت ، تم كتابة محرر مستوى يسمح للاعبين بإنشاء مستويات لا حصر لها.
وهنا تبدأ قصتي. قررت أن أرغب في إنشاء نسخة من محرك اللعبة المفتوح المصدر الأساسي حتى أتمكن من لعب
تحدي Chip's على نظامي Linux و Windows ، وجعل تشغيل جميع المستويات التي أنشأها المشجعون أسهل.
لقد كان وجود محرر المستوى بمثابة معجزة بالنسبة لي ، لأنني استطعت استكشاف الميزات الخفية لمنطق اللعبة ، وخلق مستوياتي الخاصة وإجراء الاختبارات. لسوء الحظ ، لم يكن هناك محرر مستوى للعبة Lynx الأصلية ؛ فقد ظهر فقط في المنفذ الأكثر شهرة ضمن Windows.

لم يتم إنشاء منفذ Windows بواسطة المطورين الأصليين ، لذا ظهرت العديد من التغييرات في منطق اللعبة (ولم تكن جميعها مقصودة). عندما بدأت في كتابة محركي ، أردت إعادة إنشاء منطق اللعبة الأصلية على Lynx والإصدار الأكثر شهرة لنظام التشغيل Windows. لكن الافتقار إلى محرر مستوي على Lynx قد حد بشكل خطير من قدرتي على دراسة اللعبة الأصلية بالتفصيل. يتمتع منفذ Windows بميزة: تم تخزين المستويات في ملف بيانات منفصل ، مما سهل من اكتشافه وهندسته العكسية. تم توزيع لعبة Lynx على خراطيش ROM التي تحتوي على صور العفاريت والمؤثرات الصوتية ورمز الجهاز ، بالإضافة إلى بيانات المستوى التي تم تنفيذها معًا. لا توجد أي تلميحات حول مكان وجود البيانات في تفريغ ذاكرة الوصول العشوائي (RAM) الذي يبلغ حجمه 128 كيلو بايت ، أو كيف تبدو ، وبدون هذه المعرفة ، لم أستطع إنشاء محرر مستوى لإصدار Lynx.
مرة واحدة ، في عملية بحثية على مهل ، صادفت نسخة من منفذ
Chip's Challenge تحت MS-DOS. كما هو الحال مع معظم المنافذ المبكرة للعبة ، كان منطقها أقرب إلى الأصل من إصدار Windows. عندما نظرت إلى بيانات البرنامج لمعرفة كيف يتم تخزينها ، فوجئت عندما وجدت أن بيانات المستوى قد تم تخصيصها في دليل منفصل ، وتم تخزين كل مستوى في ملفه الخاص. بعد أن تم فصل بيانات المستوى بشكل ملائم جدًا ، اقترحت أنه لن يكون من الصعب للغاية إجراء هندسة عكسية لملفات بيانات المستوى. ويتيح لك ذلك كتابة محرر مستوى لإصدار اللعبة ضمن MS-DOS. قررت أن هذه كانت فرصة مثيرة للاهتمام.
ولكن بعد ذلك حذرني عضو آخر
في مجتمع
تحدي تشيب من حقيقة مثيرة للاهتمام. تحولت محتويات ملفات المستوى لـ MS-DOS إلى تفريغ بايت ROM من Lynx. هذا يعني أنه إذا تمكنت من فك تشفير ملفات MS-DOS ، فيمكنني حينئذٍ استخدام هذه المعرفة لقراءة وتغيير المستويات داخل تفريغ ROM Lynx. ثم يمكنك إنشاء محرر مستوى مباشرة للعبة الأصلية على Lynx.
فجأة ، كانت أهم أولوياتي هي ملفات المستوى الهندسي العكسي لـ MS-DOS.
ملفات البيانات
هنا رابط إلى دليل
tarball يحتوي على جميع ملفات البيانات. أعطيها في حالة رغبتك في تكرار كل الخطوات الموضحة في هذه المقالة ، أو محاولة فك تشفير ملفات البيانات بنفسك.
هل هو قانوني؟ سؤال جيد نظرًا لأن هذه الملفات ليست سوى جزء صغير من برنامج MS-DOS ، وهي بحد ذاتها عديمة الفائدة ، وبما أنني نشرها لأغراض تعليمية فقط ، أعتقد أن هذا يندرج تحت متطلبات الاستخدام العادل. آمل أن تتفق معي جميع الأطراف المعنية. (إذا تلقيت مع ذلك رسالة تهديد من المحامين ، فيمكنني تغيير المقالة لتقديم ملفات البيانات بطريقة مضحكة ، ثم إعلان أنها محاكاة ساخرة.)
المتطلبات الأساسية
سأفترض أنك تعرف حساب التفاضل والتكامل السداسي عشر ، حتى لو كنت لا تعرف فك رموز القيم السداسية العشرية ، وأنك على دراية بقذيفة يونكس. يتم تشغيل جلسة shell الموضحة في هذه المقالة على نظام Linux قياسي ، ولكن الأوامر المستخدمة تقريبًا هي أدوات مساعدة Unix شائعة ، ويتم توزيعها على نطاق واسع على أنظمة أخرى تشبه Unix.
النظرة الأولى
فيما يلي قائمة بالدليل الذي يحتوي على ملفات البيانات من المنفذ تحت MS-DOS:
مستويات $ ls
all_full.pak cake_wal.pak eeny_min.pak iceberg.pak lesson_5.pak mulligan.pak playtime.pak southpol.pak بالكامل_.pak
alphabet.pak castle_m.pak elementa.pak ice_cube.pak lesson_6.pak nice_day.pak potpourr.pak special.pak traffic_.pak
amsterda.pak catacomb.pak fireflie.pak icedeath.pak lesson_7.pak nightmar.pak problems.pak spirals.pak trinity.pak
apartmen.pak cellbloc.pak firetrap.pak icehouse.pak lesson_8.pak now_you_.pak refracti.pak spooks.pak trust_me.pak
arcticfl.pak chchchip.pak floorgas.pak invincib.pak lobster_.pak nuts_and.pak revers_.pak steam.pak steam.pak undergro.pak
balls_o_.pak chiller.pak القسري i.pak lock_blo.pak on_the_r.pak rink.pak stripes.pak up_the_b.pak
beware_o.pak chipmine.pak force_fi.pak i_slide.pak loop_aro.pak oorto_ge.pak roadsign.pak suicide.pak vanishin.pak
blink.pak citybloc.pak force_sq.pak jailer.pak memory.pak open_que.pak sampler.pak telebloc.pak crime.pak
blobdanc.pak colony.pak fortune_.pak jumping_.pak metastab.pak oversea_.pak scavenge.pak telenet.pak vortex.pak
blobnet.pak corridor.pak four_ple.pak kablam.pak mind_blo.pak pain.pak scoundre.pak t_fair.pak wars.pak
block_fa.pak cypher.pak four_squ.pak knot.pak mishmesh.pak paranoia.pak see_s.pak the_last.pak writers_.pak
block_ii.pak deceptio.pak glut.pak ladder.pak miss_dir.pak part_ci.pak short_ci.pak the_mars.pak yorkhous.pak
block_n_.pak deepfree.pak goldkey.pak lemmings.pak mixed_nu.pak pentagra.pak shrinkin.pak the_pris.pak
block_ou.pak digdirt.pak go_with_.pak lesson_1.pak mix_up.pak perfect_.pak skelzie.pak three_do.pak
block.pak digger.pak grail.pak lesson_2.pak monster_.pak pier_sev.pak slide_st.pak time_lap.pak
bounce_c.pak doublema.pak hidden_d.pak lesson_3.pak morton.pak ping_pon.pak slo_mo.pak torturec.pak
brushfir.pak draw_an.pak hunt.pak lesson_4.pak mugger_s.pak playhous.pak socialis.pak tossed_s.pak
كما ترون ، كل الملفات تنتهي بـ
.pak
.
.pak
هو الإذن القياسي لملف بيانات التطبيق ، وهذا ، لسوء الحظ ، لا يقدم لنا أي معلومات حول بنيته الداخلية. أسماء الملفات هي الأحرف الثمانية الأولى من اسم المستوى ، مع بعض الاستثناءات. (على سبيل المثال ، في أسماء الملفات ذات المستوى "BLOCK BUSTER" و "BLOCK BUSTER II" ، يتم حذف كلمة "buster" بحيث لا تتطابق.)
مستويات $ ls | مرحاض
17 148 1974
يوجد 148 ملف بيانات في الدليل ، وللعبة بالفعل 148 مستوى ، لذلك كل شيء هو نفسه هنا.
الآن لنفحص ماهية هذه الملفات.
xxd
أداة مساعدة قياسية لإلقاء البيانات السداسية عشرية (hexdump). دعونا نرى ما يبدو داخل الدرس 1.
مستويات $ xxd / lesson_1.pak
00000000: 1100 cb00 0200 0004 0202 0504 0407 0505 ................
00000010: 0807 0709 0001 0a01 010b 0808 0d0a 0a11 ................
00000020: 0023 1509 0718 0200 2209 0d26 0911 270b. # ...... ".. & .. '.
00000030: 0b28 0705 291e 0127 2705 020d 0122 0704. (..) ..''.... "..
00000040: 0902 090a 0215 0426 0925 0111 1502 221d ....... &.٪ .... ".
00000050: 0124 011d 0d01 0709 0020 001b 0400 1a00. $ ....... ......
00000060: 2015 2609 1f00 3300 2911 1522 2302 110d. & ... 3.) .. "# ...
00000070: 0107 2609 1f18 2911 1509 181a 0223 021b .. & ...) ...... # ..
00000080: 0215 2201 1c01 1c0d 0a07 0409 0201 0201 .. ".............
00000090: 2826 0123 1505 0902 0121 1505 220a 2727 (&. # .....! .. ". ''
000000a0: 0b05 0400 060b 0828 0418 780b 0828 0418 ....... (.. x .. (..
000000b0: 700b 0828 0418 6400 1710 1e1e 1a19 0103 p .. (.. d .........
000000c0: 000e 1a17 1710 0e1f 010e 1314 1b29 1f1a .............) ..
000000d0: 0012 101f 011b 0c1e 1f01 1f13 1001 0e13 ................
000000e0: 141b 001e 1a0e 1610 1f2d 0020 1e10 0116 .........-. ....
000000f0: 1024 291f 1a01 1a1b 1019 000f 1a1a 1d1e. $) .............
00000100: 2d02
ما هي أداة hexdump؟ تفريغ ست عشري هو طريقة قياسية لعرض البايتات بالضبط من ملف ثنائي. لا يمكن ربط معظم قيم البايت بأحرف ASCII قابلة للطباعة ، أو يكون لها مظهر غير مفهوم (مثل حرف علامة تبويب). في ملف سداسي عشري ، يتم إخراج وحدات البايت الفردية كقيم رقمية. يتم عرض القيم في ست عشري ، وبالتالي الاسم. في المثال أعلاه ، يتم عرض 16 بايت على سطر واحد من الإخراج. يُظهر العمود الموجود في أقصى اليسار موضع السطر في الملف ، وكذلك في ست عشري ، وبالتالي يزيد الرقم في كل سطر بمقدار 16. يتم عرض البايتات في ثمانية أعمدة ، ويتم عرض وحدتي بايت في كل عمود. يوضح الشكل السداسي على اليمين كيف ستبدو وحدات البايت عند عرضها بواسطة أحرف ، ويتم استبدال جميع قيم ASCII غير القابلة للطباعة بالنقاط فقط. هذا يجعل من السهل العثور على سلاسل يمكن تضمينها في ملف ثنائي.
من الواضح أن الهندسة العكسية لهذه الملفات لن تغلي فقط لتصفح المحتويات واستكشاف ما هو مرئي هناك. حتى الآن ، لا يوجد شيء يخبرنا عن الوظائف التي تؤديها البيانات.
ماذا نتوقع أن نرى؟
دعنا نتراجع ونوضح الموقف: ما هي البيانات المحددة التي نتوقع العثور عليها في ملفات البيانات هذه؟
الأكثر وضوحا هو "خريطة" معينة للمستوى: بيانات تشير إلى مواقع الجدران والأبواب ، وكذلك كل شيء آخر ، مما يجعل المستوى فريدًا.
(لحسن الحظ بالنسبة لنا ، قام مشجعو اللعبة بعمل شاق وقاموا بجمع خرائط كاملة لجميع المستويات الـ 148 ، حتى نتمكن من استخدامها لمعرفة ما يجب أن يكون على كل خريطة.)
بالإضافة إلى الخريطة ، يجب أن يكون لكل مستوى عدة سمات أخرى. على سبيل المثال ، قد تلاحظ أن كل مستوى له اسم ، على سبيل المثال ، "الدرس 1" ، و "تطابق كامل" ، و "رسم ومربّع" ، وما إلى ذلك. مستويات مختلفة أيضًا لها حدود زمنية مختلفة ، لذلك يمكننا افتراض أن هذه المعلومات مضمنة أيضًا في البيانات. بالإضافة إلى ذلك ، كل مستوى له عدد رقائق خاص به. (يمكننا أن نفترض أن هذا الرقم يتوافق ببساطة مع عدد الرقائق على المستوى ، ولكن اتضح أنه في بعض المستويات يوجد عدد أكبر من الرقائق المطلوبة لفتح موصل الرقائق. على الأقل بالنسبة لهذه المستويات ، ينبغي الإشارة إلى الحد الأدنى للرقم في شكل واضح.)
جزء آخر من البيانات التي نتوقع العثور عليها في بيانات المستوى هو نص التلميح. في بعض المستويات ، هناك "زر تلميح" - علامة استفهام كبيرة ملقاة على الأرض. عندما يستيقظ رقاقة ، يتم عرض نص تلميح الأدوات. زر التلميح هو في حوالي 20 المستويات.
أخيرًا ، يحتوي كل مستوى على كلمة مرور - سلسلة من أربعة أحرف تتيح للاعب مواصلة اللعبة من هذا المستوى. (كلمة المرور هذه مطلوبة لأن Lynx لا يحتوي على مخزن بيانات. كان من المستحيل حفظ الألعاب على وحدة التحكم ، لذلك يمكنك متابعة لعب اللعبة بعد تشغيل وحدة التحكم باستخدام كلمات المرور.)
حتى هنا لدينا قائمة من البيانات ذات الصلة:
- خريطة المستوى
- اسم المستوى
- مستوى كلمة المرور
- المهلة
- عدد الرقائق
- نص تلميح الأدوات
دعنا نقدر الحجم الكلي للبيانات تقريبًا. أسهل طريقة لتحديد الحد الزمني وعدد الرقائق. يمكن أن تحتوي كلتا المعلمتين على قيم في النطاق من 0 إلى 999 ، لذلك من المحتمل تخزينها كقيم عدد صحيح بحجم إجمالي يبلغ 4 بايت. تتكون كلمة المرور دائمًا من أربعة أحرف ، لذلك من المحتمل تخزينها كأربعة بايتات أخرى ، أي 8 بايت فقط. يتراوح طول أسماء المستويات من أربعة إلى تسعة عشر حرفًا. إذا افترضنا أننا نحتاج إلى بايت آخر لإكمال السطر ، فهذا هو عشرين بايت ، أي الإجمالي الفرعي هو 28 بايت. يزيد طول نص تلميح الأدوات عن 80 بايت ؛ إذا قمنا بتقريب هذه القيمة إلى 90 ، فسنحصل على 118 بايت في المجموع.
ماذا عن مخطط المستوى؟ معظم المستويات لها حجم 32 × 32 البلاط. مستويات أكبر لا وجود لها. تكون بعض المستويات أقل ، ولكن سيكون من المنطقي افتراض أنها مضمنة ببساطة في بطاقة 32 × 32. إذا افترضنا أن بايت واحد مطلوب لبلاط واحد ، فإن هناك حاجة إلى 1024 بايت للدائرة الكاملة. هذا هو ، بشكل عام ، نحصل على تقدير تقريبي 1142 بايت لكل مستوى. بالطبع ، هذا مجرد تقدير أولي تقريبي. من الممكن أن يتم تخزين بعض هذه العناصر بشكل مختلف أو لا يتم تخزينها على الإطلاق داخل ملفات المستوى. أو قد تحتوي على بيانات أخرى لم نلاحظها أو لا نعرف عنها. لكن حتى الآن وضعنا أساسا جيدا.
بعد تحديد ما نتوقع رؤيته في ملفات البيانات ، دعنا نعود إلى دراسة ما تحتويه بالفعل.
ما هو هناك وما هو ليس كذلك
على الرغم من أن ملف البيانات يبدو للوهلة الأولى غير مفهوم تمامًا ، إلا أنه لا يزال بإمكانك ملاحظة بضع نقاط فيه. أولاً ، هذا ما
لا نراه. على سبيل المثال ، لا نرى اسم مستوى النص أو النصائح. يمكنك أن تفهم أن هذه ليست مصادفة ، فقد درست الملفات الأخرى:
مستويات سلاسل $ / * | أقل
: !! ؛ #
&> '' :: 4 #
. ،!
-54 "؛
/ & 67
!) 60
<171
* (0 *
82> '= /
8> <171 &&
9> # 2 ') (
،) 9
0hX
`PX
) "" *
24 ** 5
؛)) <
B777: .. 22C1
E ، F
-GDED
EGFF16G ؛ ؛ H <
IECJ
9K444
= MBBB >> N9 "O" 9P3؟
الأسطر 1-24 / 1544 (المزيد)
لا يوجد شيء مرئي هنا باستثناء الأجزاء العشوائية من القمامة ASCII.
من المفترض ، في مكان ما في هذه الملفات ، توجد أسماء وتلميحات على مستوى ، لكنها إما غير مخزنة في ASCII ، أو خضعت لبعض التحولات (على سبيل المثال ، بسبب الضغط).
تجدر الإشارة إلى ما يلي: الملف بالكاد يصل إلى 256 بايت في الحجم. هذا صغير جدًا ، مع مراعاة أننا في البداية قدرنا حجمه بأكثر من 1140 بايت.
يقوم الخيار
-S
بفرز الملفات بترتيب تنازلي من حيث الحجم.
مستويات $ ls -lS | رئيس
المجموع 592
-rw-r - r-- 1 صندوق الخبز breadbox 680 يونيو 23 2015 mulligan.pak
-rw-r - r-- 1 breadbox breadbox 675 Jun 23 2015 shrinkin.pak
-rw-r - r-- 1 صندوق الخبز breadbox 671 23 يونيو 2015 balls_o_.pak
-rw-r - r-- 1 breadbox breadbox 648 Jun 23 2015 cake_wal.pak
-rw-r - r-- 1 breadbox breadbox 647 Jun 23 2015 citybloc.pak
-rw-r - r-- 1 breadbox breadbox 639 Jun 23 2015 four_ple.pak
-rw-r - r-- 1 breadbox breadbox 636 Jun 23 2015 trust_me.pak
-rw-r - r-- 1 breadbox breadbox 625 Jun 23 2015 block_n_.pak
-rw-r - r-- 1 breadbox breadbox 622 Jun 23 2015 mix_up.pak
يأخذ الملف الأكبر 680 بايت فقط ، وهذا ليس كثيرًا جدًا. وماذا سيكون أصغر؟
يخبر الخيار
-r
ls
بعكس الترتيب.
مستويات $ ls -lSr | رئيس
المجموع 592
-rw-r - r-- 1 breadbox breadbox 206 Jun 23 2015 kablam.pak
-rw-r - r-- 1 breadbox breadbox 214 Jun 23 2015 fortune_.pak
-rw-r - r-- 1 breadbox breadbox 219 Jun 23 2015 digdirt.pak
-rw-r - r-- 1 breadbox breadbox 226 يونيو 23 2015 lesson_2.pak
-rw-r - r-- 1 breadbox breadbox 229 Jun 23 2015 lesson_8.pak
-rw-r - r-- 1 صندوق الخبز breadbox 237 يونيو 23 2015
-rw-r - r-- 1 breadbox breadbox 239 Jun 23 2015 knot.pak
-rw-r - r-- 1 breadbox breadbox 247 يونيو 23 2015 cellbloc.pak
-rw-r - r-- 1 صندوق الخبز breadbox 248 يونيو 23 2015 torturec.pak
أصغر ملف يأخذ 206 بايت فقط ، وهو أكثر من ثلاث مرات أصغر من الأكبر. هذا نطاق واسع إلى حد ما ، مع الأخذ في الاعتبار حقيقة أننا توقعنا أحجام المستوى نفسه تقريبًا.
في تقييمنا المبدئي ، افترضنا أن البطاقة ستحتاج إلى بايت واحد لكل بلاطة ، و 1024 بايت فقط. إذا قمنا بخفض هذا التقدير إلى النصف ، أي أن كل مربع سيتطلب 4 بت فقط (أو اثنين من البايت لكل بايت) ، فإن البطاقة ستظل تحتل 512 بايت. 512 أصغر من 680 ، ولكن لا يزال أكبر من معظم المستويات. وعلى أي حال - ستوفر 4 بتات 16 قيمة مختلفة فقط ، وفي اللعبة هناك كائنات مختلفة أكثر بكثير.
بمعنى أنه من الواضح أن البطاقات لا يتم تخزينها في هذه الملفات بشكل مفتوح. يستخدمون تشفيرًا أكثر تعقيدًا ، ويقدمون وصفًا أكثر كفاءة ، و / أو يتم ضغطهم بطريقة أو بأخرى. على سبيل المثال ، على مستوى "LESSON 1" ، يمكننا أن نرى كيف ستؤدي الإدخالات المفقودة للبلاطات "الفارغة" إلى تقليل الحجم الكلي لبيانات الخريطة إلى حد كبير.
يمكننا إلقاء نظرة على خرائط لأكبر الملفات:
مستوى 57 خريطة: غريب الغريبةمستوى 98 بطاقة: يتقلصثم قارنهم بخرائط لأصغر الملفات:
مستوى 106 بطاقة: KABLAMمستوى 112 بطاقة: FORTUNE المفضلةتدعم هذه المقارنة فكرتنا المتمثلة في أن ملفات البيانات الصغيرة تتوافق مع مستويات أبسط ، أو تحتوي على مزيد من التكرار. على سبيل المثال ، إذا تم ضغط البيانات عن طريق نوع من الترميز بطول المدى ، يمكن أن يفسر ذلك بسهولة الفواصل الزمنية لحجم الملفات المختلفة.
إذا كانت الملفات مشفرة بالفعل ، فسنضطر على الأرجح إلى فك تشفير الضغط قبل أن نبدأ في فك تشفير بيانات البطاقة.
ندرس عدة ملفات في نفس الوقت
سمحت لنا دراستنا القصيرة لملف البيانات الأول بعمل بعض الافتراضات ، لكن لم نجد شيئًا ملموسًا. كخطوة تالية ، سنبدأ في استكشاف أنماط العديد من ملفات البيانات. في الوقت الحالي ، نفترض أن جميع الملفات الـ 148 تستخدم نفس نظام الترتيب لتشفير البيانات ، لذا فإن البحث عن أنماط مكررة في هذه الملفات سيساعدنا على البدء.
لنبدأ من بداية البلاط. غالبًا ما يتم استخدام الجزء العلوي من الملف لتخزين "البيانات الوصفية" التي تخبرنا عن محتويات الملف. من خلال النظر فقط في السطر الأول من التفريغ الست عشري ، يمكننا إجراء مقارنة بسيطة وسريعة لأول 16 بايت والبحث عن أنماط بارزة فيها:
$ ل f في المستويات / * ؛ هل xxd $ f | sed-n 1p؛ عمله | أقل
00000000: 2300 dc01 0300 0004 0101 0a03 030b 2323 # ............. ##
00000000: 2d00 bf01 0300 0015 0101 2203 0329 2222 -... "..)"
00000000: 2b00 a101 0301 0105 0000 0601 0207 0505 + ...............
00000000: 1d00 d300 0200 0003 0101 0402 0205 0102 ................
00000000: 2d00 7a01 0300 0006 1414 0701 0109 0303 -.z .............
00000000: 3100 0802 0200 0003 0101 0502 0206 1313 1 ...............
00000000: 1a00 b700 0200 0003 0100 0502 0206 0101 ................
00000000: 1a00 0601 0300 0005 0001 0601 0107 0303 ................
00000000: 2000 7a01 0200 0003 0202 0401 0105 0028 .z ............ (
00000000: 3a00 a400 0200 0003 2828 0428 0205 0303: ....... ((. (....
00000000: 2600 da00 0300 0004 0507 0901 010a 0303 & ...............
00000000: 2400 f000 0300 0004 0303 0504 0407 0101 $ ...............
00000000: 2a00 ef01 0300 0005 0101 0614 0007 0303 * ...............
00000000: 2c00 8c01 0300 0004 0303 0500 0107 0101، ...............
00000000: 2a00 0001 0300 0004 0303 0501 0107 0404 * ...............
00000000: 1b00 6d01 0200 0003 0101 0502 0206 0003 ..m .............
00000000: 1e00 1701 0200 0003 0202 0401 0105 0013 ................
00000000: 3200 ee01 0f00 0015 0101 270f 0f29 1414 2 ......... '..) ..
00000000: 2a00 5b01 0300 0005 0303 0601 0107 1414 *. [.............
00000000: 2c00 8a01 0200 0003 0202 0401 0105 0303، ...............
00000000: 1d00 9c00 0216 1604 0000 0516 0107 0205 ................
00000000: 2000 e100 0200 0003 0101 0402 0205 0303 ...............
00000000: 2000 2601 0300 0004 0303 0502 0207 0101. & .............
00000000: 1f00 f600 0132 0403 0000 0532 3206 0404 ..... 2 ..... 22 ...
الأسطر 1-24 / 148 (المزيد)
بالنظر إلى هذا التفريغ ، يمكنك أن ترى أنه في كل عمود هناك بعض القيم المتشابهة.
بدءًا من البايت الأول ، سرعان ما ندرك أن قيمته في نطاق محدود جدًا من القيم ، في نطاق ست عشري
40
(أو حوالي
20–60
تقريبًا في العشري). هذه هي ميزة محددة جدا.
الأكثر إثارة للاهتمام هو أن البايت الثاني من كل ملف هو دائما صفر ، مع عدم وجود استثناءات. ربما لا يتم استخدام البايت الثاني أو عنصر نائب. ومع ذلك ، هناك احتمال آخر - تمثل هاتان البايتان الأوليتان معاً قيمة 16 بت مخزنة بترتيب endian الصغير.
ما هو القليل endian؟ عند تخزين قيمة عددية تزيد عن بايت واحد ، يجب أولاً تحديد الترتيب الذي سيتم به تخزين البايتات. إذا قمت أولاً بتخزين بايت يمثل الجزء الأصغر من الرقم ، فسيتم تسمية ذلك بالطلب المباشر ( end-little-endian ) ؛ إذا قمت أولاً بتخزين البايتات تشير إلى معظم الأرقام ، فهذا هو الترتيب العكسي ( end-big ). على سبيل المثال ، نكتب القيم العشرية بترتيب عكسي (end-endian): السطر "42" يعني "اثنان وأربعون" ، وليس "أربعة وعشرون". Little-endian هو أمر طبيعي للعديد من عائلات المعالجات الدقيقة ، لذلك عادة ما يكون أكثر شيوعًا ، باستثناء بروتوكولات الشبكة ، التي تتطلب عادة endian الكبيرة.
إذا واصلنا التحليل ، فسنرى قريبًا أن البايتة الثالثة في الملف ليست مماثلة للاثنتين السابقتين: تختلف قيمته على نطاق واسع. ومع ذلك ، البايت الرابع دائمًا هو
00
أو
01
أو
02
، و
01
هو الأكثر شيوعًا. يشير هذا أيضًا إلى أن هاتين البايتتين تشكلان قيمة 16 بت أخرى ، وهي تقريبًا في حدود القيم العشرية 0-700. يمكن أيضًا تأكيد هذه الفرضية من خلال حقيقة أن قيمة البايتة الثالثة عادة ما تكون منخفضة إذا كانت قيمة البايتة الرابعة هي
02
، وعادة ما تكون كبيرة إذا كانت البايتة الرابعة هي
00
.
بالمناسبة ، تجدر الإشارة إلى أن هذا هو السبب جزئياً في أن تنسيق التفريغ الست عشري يعرض البايتات في أزواج بشكل افتراضي - وهذا يجعل من الأسهل قراءة تسلسل من أعداد صحيحة 16 بت. تم توحيد تنسيق تفريغ سداسي عشرية عند استخدام أجهزة كمبيوتر 16 بت. حاول استبدال
xxd
بـ
xxd -g1
لتعطيل التجميع تمامًا ، وستلاحظ أن التعرف على أزواج البايت في منتصف الخط هو الكثير من العمل. هذا مثال بسيط على الطريقة التي تميل بها الأدوات المستخدمة لدراسة البيانات غير المألوفة إلى إشعار أنواع معينة من الأنماط. من الجيد أن يبرز
xxd
هذا النمط افتراضيًا لأنه شائع جدًا (حتى اليوم ، عند استخدام أجهزة كمبيوتر 64 بت في كل مكان). لكن من المفيد معرفة كيفية تغيير هذه المعلمات إذا لم تساعد.
دعنا نستمر في الاستكشاف البصري ، ونرى ما إذا كان هذا النمط يتم الحفاظ عليه من قيم عدد صحيح 16 بت. عادةً ما تحتوي البايتة الخامسة على قيم منخفضة جدًا: غالبًا ما تتم مصادفة
02
و
03
، ويبدو أن القيمة القصوى هي
05
. غالبًا ما تساوي البايت السادس للملف صفراً - ولكنه يحتوي أحيانًا على قيم أكبر بكثير ، على سبيل المثال
32
أو
2C
. في هذا الزوج ، لم يتم تأكيد افتراضنا حول القيم الموزعة في الفاصل الزمني بشكل خاص.
نحن ندرس بعناية القيم الأولية
يمكننا اختبار
od
باستخدام
od
لإنشاء تفريغ سداسي عشرية. تشبه الأداة المساعدة
od
xxd
، ولكنها توفر مجموعة أكبر من تنسيقات الإخراج. يمكننا استخدامه لتفريغ الإخراج كعدد صحيح عشري 16 بت:
يحدد الخيار
-t
إلى الأداة المساعدة
od
تنسيق الإخراج. في هذه الحالة ، تعني
u
الأرقام العشرية غير الموقعة ، بينما يرمز
2
إلى وحدتي بايت لكل سجل. (يمكنك أيضًا تحديد هذا التنسيق باستخدام الخيار
-d
.)
$ ل f في المستويات / * ؛ هل od-tu2 $ f | sed-n 1p؛ عمله | أقل
0000000 35 476 3 1024 257 778 2819 8995
0000000 45 447 3 5376 257 802 10499 8738
0000000 43 417 259 1281 0 262 1794 1285
0000000 29 211 2 768 257 516 1282 513
0000000 45 378 3 1536 5140 263 2305 771
0000000 49 520 2 768 257 517 1538 4883
0000000 26 183 2 768 1 517 1538 257
0000000 26 262 3 1280 256 262 1793 771
0000000 32 378 2 768 514 260 1281 10240
0000000 58 164 2 768 10280 10244 1282 771
0000000 38 218 3 1024 1797 265 2561 771
0000000 36 240 3 1024 771 1029 1796 257
0000000 42 495 3 1280 257 5126 1792 771
0000000 44 396 3 1024 771 5 1793 257
0000000 42 256 3 1024 771 261 1793 1028
0000000 27 365 2 768 257 517 1538 768
0000000 30 279 2 768 514 260 1281 4864
0000000 50 494 15 5376 257 3879 10511 5140
0000000 42 347 3 1280 771 262 1793 5140
0000000 44 394 2 768 514 260 1281 771
0000000 29 156 5634 1046 0 5637 1793 1282
0000000 32 225 2 768 257 516 1282 771
0000000 32 294 3 1024 771 517 1794 257
0000000 31 246 12801 772 0 12805 1586 1028
الأسطر 1-24 / 148 (المزيد)
يوضح هذا الإخراج أن تخميناتنا بشأن البايتات القليلة الأولى كانت صحيحة. نرى أن القيمة 16 بت الأولى تقع في النطاق العشري من 20 إلى 70 ، بينما تكون القيمة 16 بت الثانية في النطاق العشري من 100 إلى 600. ومع ذلك ، القيم اللاحقة لا تتصرف بشكل جيد. تظهر أنماط معينة فيها (على سبيل المثال ، في الموضع الرابع ، غالبًا ما يكون من المستغرب 1024) ، لكنها لا تحتوي على التكرار المتأصل في القيم الأولى للملف.
لذلك ، لنفترض أولاً أن البايتات الأربع الأولى من الملف خاصة وتتألف من قيمتين 16 بت. نظرًا لأنهم في بداية الملف ، فهم على الأرجح بيانات وصفية ويساعدون في تحديد كيفية قراءة بقية الملف.
في الواقع ، الفاصل الزمني الثاني للقيم (100-600) قريب جدًا من الفاصل الزمني لحجم الملف الذي لاحظناه سابقًا (208-680). ربما هذه ليست صدفة؟ دعنا نطرح فرضية: قيمة 16 بت المخزنة في البايت الثالث والرابع من الملف ترتبط بالحجم الكلي للملف. الآن بعد أن لدينا فرضية ، يمكننا اختبارها. دعونا نرى ما إذا كانت الملفات الكبيرة في هذا المكان لها قيم كبيرة في جميع الأوقات ، والملفات الصغيرة لها قيم صغيرة.
لعرض حجم الملف بالبايت دون أي معلومات أخرى ، يمكنك استخدام
wc
مع الخيار
-c
. وبالمثل ، يمكنك إضافة خيارات إلى
od
تتيح لك فقط عرض القيم التي تهمنا. ثم يمكننا استخدام استبدال الأوامر لكتابة هذه القيم لقذيفة المتغيرات وعرضها معًا:
الخيار
-An
من الأداة المساعدة
od
يعطل العمود الموجود في أقصى اليسار ، والذي يعرض الإزاحة في الملف ، ويخبر
-N4
بالتوقف بعد أول 4 بايت من الملف.
$ ل f في المستويات / * ؛ حجم العمل = $ (wc -c <$ f) ؛ البيانات = $ (od-tuS -An -N4 $ f) ؛ صدى "$ size: $ data" ؛ عمله | أقل
585: 35 476
586: 45 447
550: 43.417
302: 29،211
517: 45 378
671: 49 520
265: 26 183
344: 26،262
478: 32378
342: 58 164
336: 38 218
352: 36240
625: 42 495
532: 44396
386: 42256
450: 27 365
373: 30 279
648: 50 494
477: 42 347
530: 44394
247: 29 156
325: 32225
394: 32294
343: 31246
بالنظر إلى هذا الإخراج ، يمكنك أن ترى أن القيم مرتبطة تقريبًا. عادةً ما يكون للملفات الصغيرة قيم أقل في الموضع الثاني ، والملفات الكبيرة لها قيم أكبر. ومع ذلك ، فإن العلاقة ليست دقيقة ، وتجدر الإشارة إلى أن حجم الملف هو دائما أكبر بكثير من القيمة المخزنة فيه.
بالإضافة إلى ذلك ، تكون القيمة الأولى ذات 16 بت أكبر أيضًا بأحجام الملفات الكبيرة ، لكن المطابقة أيضًا ليست كاملة تمامًا ، ويمكنك بسهولة العثور على أمثلة للملفات متوسطة الحجم ذات القيم الكبيرة نسبيًا في الموضع الأول. ولكن ربما إذا أضفنا هاتين القيمتين معًا ، فسوف يكون مجموعهما مرتبطًا بشكل أفضل بحجم الملف؟يمكننا استخدام read
استخراج رقمين من الإخراج od
إلى متغيرات منفصلة ، ثم استخدام حساب shell للعثور على مجموعهم:أمر commandread
لا يمكن استخدامها على الجانب الأيمن من الشريط العمودي ، لأنه يتم تنفيذ الأوامر المنقولة إلى خط الأنابيب في معالج أوامر تابع (علامة فرعية) ، والتي ، عند خروجها ، تأخذ متغيرات البيئة الخاصة بها إلى مستقبل البت. لذلك ، بدلاً من ذلك ، نحتاج إلى استخدام وظيفة استبدال العملية bash
وتوجيه الإخراج od
إلى ملف مؤقت ، والذي يمكن إعادة توجيهه إلى الأمر read
.$ ل f في المستويات / * ؛ حجم العمل = $ (wc -c <$ f) ؛ قراءة v1 v2 << (od -tuS -An -N4 $ f) ؛ المبلغ = $ (($ v1 + $ v2)) ؛
صدى "$ size: $ v1 + $ v2 = $ sum"؛ عمله | أقل
585: 35 + 476 = 511
586: 45 + 447 = 492
550: 43 + 417 = 460
302: 29 + 211 = 240
517: 45 + 378 = 423
671: 49 + 520 = 569
265: 26 + 183 = 209
344: 26 + 262 = 288
478: 32 + 378 = 410
342: 58 + 164 = 222
336: 38 + 218 = 256
352: 36 + 240 = 276
625: 42 + 495 = 537
532: 44 + 396 = 440
386: 42 + 256 = 298
450: 27 + 365 = 392
373: 30 + 279 = 309
648: 50 + 494 = 544
477: 42 + 347 = 389
530: 44 + 394 = 438
247: 29 + 156 = 185
325: 32 + 225 = 257
394: 32 + 294 = 326
343: 31 + 246 = 277
الأسطر 1-24 / 148 (المزيد)
يرتبط مجموع الرقمين أيضًا تقريبًا بحجم الملف ، لكن لا يزال غير متطابق تمامًا. ما مدى اختلافهم؟ دعنا نظهر اختلافهم:$ ل f في المستويات / * ؛ حجم العمل = $ (wc -c <$ f) ؛ قراءة v1 v2 << (od -tuS -An -N4 $ f) ؛ فرق = $ (($ size - $ v1 - $ v2)) ؛
echo "$ size = $ v1 + $ v2 + $ diff"؛ عمله | أقل
585 = 35 + 476 + 74
586 = 45 + 447 + 94
550 = 43 + 417 + 90
302 = 29 + 211 + 62
517 = 45 + 378 + 94
671 = 49 + 520 + 102
265 = 26 + 183 + 56
344 = 26 + 262 + 56
478 = 32 + 378 + 68
342 = 58 + 164 + 120
336 = 38 + 218 + 80
352 = 36 + 240 + 76
625 = 42 + 495 + 88
532 = 44 + 396 + 92
386 = 42 + 256 + 88
450 = 27 + 365 + 58
373 = 30 + 279 + 64
648 = 50 + 494 + 104
477 = 42 + 347 + 88
530 = 44 + 394 + 92
247 = 29 + 156 + 62
325 = 32 + 225 + 68
394 = 32 + 294 + 68
343 = 31 + 246 + 66
lines 1-24/148 (more)
يتم عرض الفرق ، أو القيمة "المتبقية" ، على الجانب الأيمن للغاية من الإخراج. لا تقع هذه القيمة تمامًا في النموذج الثابت ، ولكن يبدو أنها تظل تقريبًا في نطاق محدود يتراوح بين 40 و 120. ومرة أخرى ، كلما كانت الملفات أكبر ، كانت القيمة المتبقية عادةً أكبر. لكن في بعض الأحيان تحتوي الملفات الصغيرة أيضًا على قيم متبقية كبيرة ، لذلك هذا ليس ثابتًا كما نود.ومع ذلك ، تجدر الإشارة إلى أن قيم المخلفات ليست سالبة أبدًا. لذلك ، فإن فكرة أن قيمتي البيانات الأولية هذه تشير إلى أن الأقسام الفرعية للملف تظل جذابة.(إذا كنت حريصًا بما يكفي ، فقد رأيت بالفعل شيئًا يعطي تلميحًا حول اتصال لم يتم ملاحظته بعد. إذا لم تكن كذلك ، فاستمر في القراءة ، وسيتم الكشف عن السر قريبًا).أكبر مقارنات عبر الملفات
في هذه المرحلة ، سيكون من الجيد أن تكون قادرًا على مقارنة أكثر من 16 بايت في المرة الواحدة. لهذا نحن بحاجة إلى نوع مختلف من التصور. تتمثل إحدى الطرق الجيدة في إنشاء صورة يشير فيها كل بكسل إلى بايت منفصل لأحد الملفات ، ويشير اللون إلى قيمة هذه البايت. يمكن للصورة أن تظهر شريحة من جميع الملفات الـ 148 في وقت واحد إذا تم الإشارة إلى كل ملف بيانات بواسطة صف واحد من وحدات بكسل الصورة. نظرًا لأن جميع الملفات ذات أحجام مختلفة ، فإننا نأخذ أول 200 بايت من كل منها لإنشاء صورة مستطيلة.أسهل طريقة هي إنشاء صورة بتدرج الرمادي ، حيث تتوافق قيمة كل بايت مع مستويات مختلفة من اللون الرمادي. من السهل جدًا إنشاء ملف PGM باستخدام بياناتنا ، لأن رأس PGM يتكون من نص ASCII فقط: $ echo P5 200 148 255 >hdr.pgm
PGM? PGM, «portable graymap» (« ») — , : ASCII, . — PBM («portable bitmap», « »), 8 , PPM («portable pixmap», « »), 3 .
P5
— , PGM. ,
200
148
, , ,
255
, . PGM , . ( , PGM , PGM , - (whitespace).)
head
, 200 :
$ for f in levels/* ; do head -c200 $f ; done >out.pgm
بعد ذلك يمكننا أن نربطهم بعناوين ونخلق صورة معروضة:xview
- هذا برنامج X قديم لعرض الصور في نافذة. يمكنك استبداله بمشاهد الصور المفضل لديك ، على سبيل المثال ، أداة مساعدة display
من ImageMagick ، ولكن ضع في اعتبارك أن هناك العديد من عارض الصور بشكل مفاجئ لا يقبل إعادة توجيه ملف صورة إلى الإدخال القياسي.$ cat hdr.pgm out.pgm | xview / ديف / stdin
إذا كان من الصعب عليك التفكير في التفاصيل في صورة مظلمة ، فيمكنك اختيار نظام ألوان مختلف. استخدم الأداة المساعدة pgmtoppm
من ImageMagick لتحويل البكسل إلى مجموعة مختلفة من الألوان. سيخلق هذا الإصدار صورة "سلبية":$ cat hdr.pgm out.pgm | pgmtoppm أبيض أسود | xview / ديف / stdin
وهذا الإصدار يجعل القيم المنخفضة الأصفر والقيم العالية زرقاء:$ cat hdr.pgm out.pgm | pgmtoppm الأصفر والأزرق | xview / ديف / stdin
تعد رؤية الألوان مسألة ذاتية للغاية ، لذلك يمكنك تجربة واختيار ما هو الأفضل لك. مهما كان الأمر ، فإن الصورة 200 × 148 صغيرة جدًا ، لذا فمن الأفضل زيادة ظهورها عن طريق زيادة حجمها:$ cat hdr.pgm out.pgm | xview -zoom 300 / dev / stdin
الصورة مظلمة ، وهذا يعني أن معظم البايتات تحتوي على قيم صغيرة. يتناقض مع ذلك شريط ملحوظ من معظم وحدات البكسل الساطعة القريبة من الحافة اليسرى. يوجد هذا الشريط في البايت الثالث من الملف ، والذي ، كما قلنا أعلاه ، يختلف في مجموعة كاملة من القيم.وعلى الرغم من عدم وجود العديد من القيم العالية خارج البايتة الثالثة ، عند ظهورها ، غالبًا ما تتكون من مسلسلات ، مما يؤدي إلى إنشاء خطوط مشرقة قصيرة في الصورة. تتم مقاطعة بعض هذه السلسلة بشكل دوري ، مما يخلق تأثير خط متقطع. (ربما ، مع تحديد اللون المناسب ، سيكون من الممكن ملاحظة مثل هذه التسلسلات بألوان أغمق).من خلال دراسة متأنية للصورة ، يمكنك أن تفهم أن الخطوط الرأسية الصغيرة تهيمن بشكل رئيسي في الجزء الأيسر. تخبرنا هذه العصابات ببعض التكرار في معظم الملفات. ولكن ليس في جميع الملفات - من وقت لآخر ، توجد خطوط من البيكسلات يتم فيها مقاطعة النطاقات - ولكن هذا أكثر من كاف لتحديد وجود نمط حقيقي. يختفي هذا النمط على الجانب الأيمن من الصورة ، الخلفية الخلفية المظلمة من الخطوط تفسح المجال لشيء أكثر صاخبة وغير مسمى. (يبدو أن الأشرطة مفقودة أيضًا في الجزء الأيسر للغاية من الصورة ، لكنني أكرر أنه من الممكن ، عند استخدام نظام ألوان مختلف ، أن ترى أنها تبدأ أقرب إلى الحافة اليسرى مما تبدو هنا.)تتكون الخطوط من خطوط رقيقة من وحدات بكسل أكثر إشراقًا قليلاً على خلفية وحدات بكسل أغمق قليلاً. لذلك ، يجب أن يرتبط نمط الرسوم هذا بنمط ملفات البيانات التي تتناثر فيها القيم الأكبر قليلاً بين قيم البايت الأصغر قليلاً. يبدو أن المشارب مستنفدة تقريبًا في منتصف الصورة. نظرًا لأنه يظهر أول 200 بايت من الملفات ، يجب أن تتوقع أن ينتهي نمط البايت بعد حوالي 100 بايت.حقيقة أن هذه الأنماط تتغير في ملفات البيانات المختلفة يجب أن تقودنا إلى السؤال: كيف ستبدو الملفات بعد أول 200 بايت. حسنًا ، يمكننا بسهولة استبدال الأداة المساعدة بأداة head
مساعدة tail
ونرى كيف تبدو آخر 200 بايت:$ ل f في المستويات / * ؛ هل الذيل -200 دولار و ؛ تم> خارج. القط hdr.pgm out.pgm | xview -zoom 300 / dev / stdin
نرى على الفور أن هذه المنطقة من ملفات البيانات مختلفة جدا. هنا ، تعد وحدات البايت أكثر شيوعًا ، خاصةً في نهاية الملف. (ومع ذلك ، كما كان الحال من قبل ، يفضلون التجميع معًا ، مع تغطية الصورة بخطوط أفقية ساطعة.) يبدو أن تواتر قيم البايت العالية يزيد تقريبًا إلى النهاية ، حيث ينكسر بشكل مفاجئ ويستبدل بقيم منخفضة في حوالي 10 إلى 12 بايت. والنمط هنا أيضًا ليس عالميًا ، لكن من المعتاد جدًا ألا تكون مصادفة.ربما في منتصف الملفات قد تكون هناك مجالات أخرى لم نأخذها في الاعتبار بعد. الشيء التالي الذي نريد القيام به هو فحص الملفات بأكملها بهذه الطريقة. ولكن نظرًا لأن جميع الملفات لها أحجام مختلفة ، فلا يمكن وضعها في مجموعة مستطيلة جميلة من البكسل. يمكننا ملء نهاية كل سطر بالبكسل الأسود ، ولكن سيكون من الأفضل إذا قمنا بتغيير حجمها بحيث تكون جميع الخطوط متماثلة العرض والمساحات التناسبية لملفات مختلفة أكثر أو أقل.ويمكننا فعل ذلك في الواقع ببذل المزيد من الجهد. يمكنك استخدام Python ومكتبتها للتعامل مع الصور PIL
("Pillow"):File showbytes.py: import sys from PIL import Image
عندما نسمي هذا البرنامج النصي ، وذلك باستخدام القائمة الكاملة لملفات البيانات كوسائط ، فإنه سينشئ صورة كاملة ويعرضها في نافذة منفصلة: مستويات $ showytes.py بيثون / *
لسوء الحظ ، على الرغم من اكتمال هذه الصورة ، إلا أنها لا تظهر لنا أي شيء جديد. (ولكن في الواقع ، تظهر هذه النسبة أقل ، لأن تغيير الحجم دمر النمط من الخطوط.) ربما ، لدراسة مجموعة البيانات بأكملها ، نحتاج إلى عملية تصوير أفضل.تميز البيانات
مع وضع ذلك في الاعتبار ، دعنا نتوقف للحظة ونكمل تعداد بيانات كامل. نحتاج إلى معرفة ما إذا كانت ملفات البيانات تعطي الأفضلية لقيم بايت معينة. على سبيل المثال ، إذا كانت كل قيمة مكررة في العادة ، فسيكون هذا دليلًا قويًا على أن الملفات مضغوطة بالفعل.لإعادةكتابة القيم تمامًا ، تكفي بضعة أسطر في Python: ملف census.py: import sys data = sys.stdin.read() for c in range(256): print c, data.count(chr(c))
بعد دفع جميع البيانات في متغير واحد ، يمكننا حساب تواتر حدوث كل قيمة بايت.مستويات القط $ / * | الثعبان ./census.py | أقل
0 2458
1 2525
2 1626
3 1768
4 1042
5 1491
6 1081
7 1445
8 958
9 1541
10 1279
11 1224
12845
13 908
14 859
15 1022
16 679
17 1087
18881
19 1116
20 1007
21 1189
22 1029
23733
الأسطر 1-24 / 256 (المزيد)
نرى أنه في أغلب الأحيان توجد قيم البايتتين 0 و 1 ، والقيم التالية في التردد هي 2 و 3 ، وبعد ذلك يستمر العدد في التناقص (على الرغم من وجود ثبات أقل). من أجل رؤية أفضل لهذه البيانات ، يمكننا نقل الإخراج إلى gnuplot
هذا التعداد وتحويله إلى رسم بياني: يطلبخيار -p
الأداة المساعدة gnuplot
عدم إغلاق النافذة مع الرسم البياني بعد الانتهاء من العمل gnuplot
.مستويات القط $ / * | الثعبان ./census.py | gnuplot -p -e 'plot "-" with boxes "
من الملاحظ أن قيم البايت القليلة الأولى أكثر شيوعًا من غيرها. العديد من القيم التالية شائعة جدًا أيضًا ، ثم تبدأ ترددات القيم من حوالي 50 في الانخفاض على طول منحنى الاحتمال السلس. ومع ذلك ، هناك مجموعة فرعية من القيم العالية مفصولة بالتساوي عن بعضها البعض ، والتي يبدو ترددها مستقرًا إلى حد ما. من خلال النظر إلى المخرجات الأصلية ، يمكننا التأكد من أن هذه المجموعة الفرعية تتكون من قيم يمكن تقسيمها على ثمانية.تشير هذه الاختلافات في عدد القيم إلى وجود عدة "فئات" مختلفة من قيم البايتات ، لذلك سيكون من المنطقي رؤية كيفية توزيع هذه الفئات. ستكون المجموعة الأولى من قيم البايت هي أدنى القيم: 0 ، 1 ، 2 ، و 3. ثم يمكن أن تكون المجموعة الثانية قيمًا من 4 إلى 64. والمجموعة الثالثة ستكون قيمًا أعلى من 64 ، قابلة للقسمة على 8. بدون تتبع ، كل شيء آخر ، بما في ذلك غير قابلة للقسمة على 8 قيم أكبر من 64 ستكون المجموعة الرابعة والأخيرة.مع وضع كل هذا في الاعتبار ، يمكننا تغيير آخر سيناريو لتوليد الصور المكتوبة. بدلاً من عرض القيم الفعلية لكل بايت في لون منفصل ، دعونا فقط نعرض المجموعة التي ينتمي إليها كل بايت. يمكنك تعيين لون فريد لكل مجموعة من المجموعات الأربع ، وسيساعدنا ذلك في معرفة ما إذا كانت بعض القيم تظهر فعليًا في أماكن معينة.ملف Showbytes2.py: import sys from PIL import Image
خصصنا أربع مجموعات الأحمر والأخضر والأزرق والأبيض. (مرة أخرى ، يمكنك محاولة اختيار ألوان أخرى لتناسب تفضيلاتك.) مستويات $ showytes2.py بيثون / *
بفضل هذه الصورة ، يمكننا تأكيد صحة فصل ملفات البيانات إلى خمسة أجزاء:- رأس أربعة بايت وجدنا في وقت سابق.
- , , (.. ).
- , (. 64).
- , .
- , .
بفضل هذه الألوان ، من الواضح أنه في الجزء الرابع ، حيث تسود قيم البايت العالية ، كما يمكن رؤيته في الصورة بتدرج الرمادي ، قيم البايت العالية ، مقسومة على 8. وخاصةً ، تسودمن الصورة السابقة . من الصورة السابقة ، نعلم أن الجزء الثاني هو الجزء ذو خطوط يمتد على مساحة حمراء تماما تقريبا. في الواقع ، في إحدى الصور الأولى رأينا أن الجزء مع المشارب ، من اليسار إلى اليمين ، يضيء ببطء في المتوسط.نرى مرة أخرى أن البيكسلات الخضراء من الجزء الرئيسي الثالث تشكل نقوشًا متقطعة من البيكسلات الخضراء والحمراء المتقطعة (إما زرقاء أو بيضاء) من وقت لآخر. ومع ذلك ، فإن هذا النمط ليس منتظمًا بشكل خاص ، وقد يكون وهميًا.بالطبع ، هذا التقسيم للملف إلى خمسة أجزاء هو تعسفي للغاية. قد يتحول الجزء الرابع الذي يحتوي على قيم عالية للبايت إلى ثمانية إلى نهاية الجزء الثالث فقط. أو قد يتضح أنه من الأفضل تقسيم جزء كبير ثالث إلى عدة أجزاء لم نحددها بعد. في هذه المرحلة ، يساعدنا اكتشاف الأجزاء في العثور على أماكن لمزيد من البحث. في الوقت الحالي ، يكفي أن نعرف أن هناك أجزاء يتغير فيها التكوين العام لقيم البايت ، وسوف تساعدنا فكرة تقريبية عن حجمها على مواصلة بحثنا.هيكل البحث
ما الذي يجب أن نبحث عنه بعد ذلك؟ حسنًا ، كما كان من قبل ، أسهل طريقة للبدء هي من أعلى الملف. أو بالأحرى ، بالقرب من القمة. نظرًا لأننا حددنا الجزء الأول بثقة إلى حد كبير على أنه رأس مكون من أربعة بايتات ، فلنلقي نظرة فاحصة على ما يأتي بعد ذلك - المنطقة التي نسميها الجزء الثاني ، أو جزءًا من النطاقات. هذه الأشرطة هي أقوى تلميح لوجود الهيكل ، لذلك من الأفضل البحث عن دليل جديد على وجود النموذج هنا.(في الوقت الحالي ، نفترض أن نمط الشريط يبدأ مباشرة بعد البايتات الأربعة الأولى. بصريًا ، هذا غير واضح ، لكن يبدو محتملاً ، ويجب أن يوضح لنا فحص قيم البايت الحقيقة بسرعة).دعنا نعود إلى تفريغ سداسي عشرية ، مع التركيز هذه المرة على الجزء الثاني. تذكر أننا نتوقع العثور على نمط متكرر من القيم الأعلى قليلاً موزعة بالتساوي بين القيم الأقل قليلاً. يأمرالخيار بتخطي وحدات البايت الأربع الأولى من الملف.-s4
xxd
$ ل f في المستويات / * ؛ تفعل xxd -s4 $ f | sed-n 1p؛ عمله | أقل
00000004: 0200 0003 0202 0401 0105 0303 0700 0108 ................
00000004: 0201 0104 0000 0504 0407 0202 0902 010a ................
00000004: 0300 0004 0303 0504 0407 0505 0801 0109 ................
00000004: 0300 0009 0202 1203 0313 0909 1401 0115 ................
00000004: 0203 0305 0000 0602 0207 0505 0901 010a ................
00000004: 0203 0304 0000 0502 0206 0404 0901 010a ................
00000004: 0300 0005 022a 0602 2907 0303 0902 000a ..... * ..) .......
00000004: 0203 0305 0000 0605 0507 0101 0802 0209 ................
00000004: 0300 0007 0303 0901 010a 0707 0b09 090c ................
00000004: 0300 0004 0101 0903 030e 0404 1609 0920 ...............
00000004: 0200 0003 1313 0402 0205 0013 0701 0109 ................
00000004: 0500 0006 0505 0701 0109 0606 0e07 070f ................
00000004: 0100 0003 0101 0a03 030b 0a0a 0e32 3216 .............22.
00000004: 0300 0004 0705 0903 030a 0606 0b08 080c ................
00000004: 0200 0003 0701 0402 0209 0501 0a08 080b ................
00000004: 0200 0003 0202 0901 010a 0303 0b05 010d ................
00000004: 0200 0003 0202 0403 0305 0101 0904 040a ................
00000004: 0300 0007 0303 0f01 0115 0707 2114 1422 ............!.."
00000004: 0200 0003 0202 0403 0309 0101 0a04 040b ................
00000004: 0231 3103 0202 0500 0006 0303 0701 0109 .11.............
00000004: 0200 0003 0202 0b32 320c 0303 0e08 0811 .......22.......
00000004: 0201 0103 0000 0902 020a 0303 0b09 090c ................
00000004: 0200 0003 0202 0a01 010b 0303 0d0b 0b0f ................
00000004: 0300 0005 0303 0701 0109 0001 0b05 051b ................
lines 27-50/148 (more)
من خلال النظر بعناية في تسلسل البايتات في السلسلة ، يمكننا أن نرى نمطًا تكون فيه البايتات الأولى والرابعة والسابعة والعاشرة أكبر من أقرب جيرانها. هذا النمط غير كامل ، له استثناءات ، لكنه بالتأكيد مستقر بدرجة كافية لإنشاء التكرار المرئي للعصابات التي تظهر على جميع الصور. (ويكفي تأكيد افتراضنا أن نمط الخطوط يبدأ مباشرةً بعد رأس البايتات الأربعة.) يشيرثبات هذا النمط بوضوح إلى أن هذا الجزء من الملف عبارة عن صفيف أو جدول ، ولكل سجل في الصفيف طوله ثلاث بايت.يمكنك تكوين تنسيق تفريغ سداسي عشرية لتسهيل رؤية الإخراج كسلسلة من ثلاثة توائم:يقوم الخيار -g3
بتعيين التجميع بمقدار ثلاث بايت بدلاً من اثنين. خيار-c18
يعين 18 بايت (مضاعفات 3) لكل سطر بدلاً من 16.$ ل f في المستويات / * ؛ do xxd -s4 -g3 -c18 $ f | sed-n 1p؛ عمله | أقل
00000004: 050000 060505 070101 090606 0e0707 0f0001 ..................
00000004: 010000 030101 0a0303 0b0a0a 0e3232 161414 ............. 22 ...
00000004: 030000 040705 090303 0a0606 0b0808 0c0101 ..................
00000004: 020000 030701 040202 090501 0a0808 0b0101 ..................
00000004: 020000 030202 090101 0a0303 0b0501 0d0302 ..................
00000004: 020000 030202 040303 050101 090404 0a0302 ..................
00000004: 030000 070303 0f0101 150707 211414 221313 ............! .. "..
00000004: 020000 030202 040303 090101 0a0404 0b0001 ..................
00000004: 023131 030202 050000 060303 070101 090505 .11 ...............
00000004: 020000 030202 0b3232 0c0303 0e0808 110b0b ....... 22 .........
00000004: 020101 030000 090202 0a0303 0b0909 0c0a0a ..................
00000004: 020000 030202 0a0101 0b0303 0d0b0b 0f2323 ................##
00000004: 030000 050303 070101 090001 0b0505 1b0707 ..................
00000004: 022323 030000 040202 050303 030101 070505 .##...............
00000004: 031414 050000 060303 070505 080101 090707 ..................
00000004: 030000 050202 060303 070505 080101 090606 ..................
00000004: 030202 040000 050303 070404 080005 090101 ..................
00000004: 030202 040000 050303 090404 1d0101 1f0909 ..................
00000004: 020000 050303 060101 070202 0f0300 110505 ..................
00000004: 050000 070101 0c0505 0d0007 110c0c 120707 ..................
00000004: 030202 050000 060303 070505 080101 090606 ..................
00000004: 020000 030101 050202 060505 070100 080303 ..................
00000004: 020000 030202 050303 090101 0a0505 0b0302 ..................
00000004: 022c2c 030000 040202 020303 050101 060202 .,,...............
lines 38-61/148 (more)
في تفريغ منسق بهذه الطريقة ، تبدأ بعض الميزات الأخرى لهذا النمط في الظهور. كما كان من قبل ، تكون البايتة الأولى من كل ثلاثية أكبر عادة من البايتات المحيطة بها. يمكنك أيضًا ملاحظة أن البايتتين الثانية والثالثة من كل ثلاثية يتم تكرارهما في الغالب. عند نزول العمود الأول ، سنرى أن معظم قيم البايتتين الثانية والثالثة متساوية 0000
. لكن القيم غير الصفرية توجد أيضًا غالبًا في أزواج ، على سبيل المثال ، 0101
أو 2323
. هذا النمط غير كامل أيضًا ، لكن يوجد الكثير من الأشياء المشتركة بينه وبين الصدفة. وبالنظر إلى عمود ASCII على اليمين ، سنرى أنه عندما يكون لدينا قيم بايت تتوافق مع حرف ASCII القابل للطباعة ، فغالبًا ما توجد في أزواج.نمط آخر جدير بالذكر لا يمكن ملاحظته على الفور: البايتة الأولى من كل ثلاثية تزيد من اليسار إلى اليمين. على الرغم من أن هذا النمط أقل وضوحًا ، إلا أن ثباته مرتفع ؛ نحتاج إلى إلقاء نظرة على الكثير من الخطوط قبل أن نجد عدم التطابق الأول. وعادة ما تزيد البايتات بقيم صغيرة ، لكنها لا تمثل أي نمط منتظم.عند دراسة الصورة الأصلية ، لاحظنا أن الجزء الذي يحتوي على خطوط ينتهي في كل ملف وليس في نفس المكان. هناك انتقال من إنشاء شريط نقش على اليسار إلى ضوضاء عشوائية على اليمين ، ولكن يحدث هذا الانتقال لكل صف من وحدات البكسل في نقاط مختلفة. هذا يعني أنه يجب أن يكون هناك بعض العلامات حتى يتمكن البرنامج الذي يقرأ ملف البيانات من فهم أين ينتهي الصفيف وتبدأ مجموعة أخرى من البيانات.دعنا نعود إلى تفريغ المستوى الأول فقط لفحص الملف بأكمله: مستويات $ xxd -s4 -g3 -c18 / lesson_1.pak
00000004: 020000 040202 050404 070505 080707 090001 ..................
00000016: 0a0101 0b0808 0d0a0a 110023 150907 180200 ........... # ......
00000028: 22090d 260911 270b0b 280705 291e01 272705 ".. & .. '.. (..) ..' '.
0000003a: 020d01 220704 090209 0a0215 042609 250111 ... "......... &.٪ ..
0000004c: 150222 1d0124 011d0d 010709 002000 1b0400 .. ".. $ ....... ....
0000005e: 1a0020 152609 1f0033 002911 152223 02110d ... & ... 3.) .. "# ...
00000070: 010726 091f18 291115 09181a 022302 1b0215 .. & ...) ...... # ....
00000082: 22011c 011c0d 0a0704 090201 020128 260123 "............. (&. #
00000094: 150509 020121 150522 0a2727 0b0504 00060b .....! .. ".''......
000000a6: 082804 18780b 082804 18700b 082804 186400 .(..x..(..p..(..d.
000000b8: 17101e 1e1a19 010300 0e1a17 17100e 1f010e ..................
000000ca: 13141b 291f1a 001210 1f011b 0c1e1f 011f13 ...)..............
000000dc: 10010e 13141b 001e1a 0e1610 1f2d00 201e10 .............-. ..
000000ee: 011610 24291f 1a011a 1b1019 000f1a 1a1d1e ...$).............
00000100: 2d02
دراسة تسلسل ثلاثة توائم ، يمكننا أن نفترض مبدئيا أن الجزء مع العصابات في هذه البيانات ينتهي بعد 17 ثلاثة توائم ، عن طريق الإزاحة 00000036
. هذا ليس دقيقًا ، لكن البايتة الأولى من كل ثلاثة توائم تزيد من قيمتها باستمرار ، ثم تتناقص بمقدار الثلاثية الثامنة عشرة. دليل آخر: في الثلاثة عشر الثامنة عشر ، للبايت الثاني نفس معنى الأول. لم نلاحظ ذلك بعد ، لكن إذا عدنا وننظر ، سنرى أن البايتة الأولى لا تساوي البايتة الثانية أو الثالثة.إذا كانت نظرية العلامة صحيحة ، فهناك احتمالان. أولاً ، من المحتمل أنه بعد الجزء التعريفي ، هناك بعض قيمة البايت الخاصة (مباشرة بعد الثلاثي السابع عشر). ثانياً ، من المحتمل وجود قيمة مخزنة في مكان ما تساوي حجم الجزء مع خطوط. يمكن أن يكون هذا الحجم مساوياً لـ 17 (أي ، يشير إلى عدد من ثلاثة توائم) ، أو 51 (يشير إلى إجمالي عدد البايتات في جزء ما) ، أو 55 (51 زائد 4 ، أي إزاحة الملف حيث ينتهي هذا الجزء).بالنسبة للخيار الأول ، قد تكون قيمة البايت المزدوجة علامة لنهاية الجزء (بالنظر إلى أن مثل هذا التسلسل لا يحدث أبدًا في الجزء الثاني). ومع ذلك ، فإن الدراسة الدقيقة للعديد من ملفات البيانات الأخرى تتناقض مع هذه الفكرة ، لأن مثل هذا النمط لا يظهر في أي مكان آخر.بالنسبة للخيار الثاني ، سيبحث بوضوح عن مؤشر الحجم في الجزء الأول. هو - أول قيمة 16 بت في رأس الملف أربعة بايت هي 17: $ od -An -tuS -N4 levels / lesson_1.pak
17 203
إذا كانت نظريتنا صحيحة ، فإن هذه القيمة لا تحدد حجم الجزء مع خطوط ، ولكن عدد السجلات ثلاثية البايت. لاختبار هذه الفكرة ، دعنا نعود إلى الحوسبة ، حيث قارنا مجموع قيمتي عدد صحيح 16 بت بحجم الملف. هذه المرة نقوم بضرب الرقم الأول بثلاثة للحصول على الحجم الحقيقي بالبايت:$ ل f في المستويات / * ؛ حجم العمل = $ (wc -c <$ f) ؛ قراءة v1 v2 << (od -tuS -An -N4 $ f) ؛ فرق = $ ((حجم $ - 3 * $ v1 - $ v2)) ؛
صدى "$ size = 3 * $ v1 + $ v2 + $ diff"؛ عمله | أقل
585 = 3 * 35 + 476 + 4
586 = 3 * 45 + 447 + 4
550 = 3 * 43 + 417 + 4
302 = 3 * 29 + 211 + 4
517 = 3 * 45 + 378 + 4
671 = 3 * 49 + 520 + 4
265 = 3 * 26 + 183 + 4
344 = 3 * 26 + 262 + 4
478 = 3 * 32 + 378 + 4
342 = 3 * 58 + 164 + 4
336 = 3 * 38 + 218 + 4
352 = 3 * 36 + 240 + 4
625 = 3 * 42 + 495 + 4
532 = 3 * 44 + 396 + 4
386 = 3 * 42 + 256 + 4
450 = 3 * 27 + 365 + 4
373 = 3 * 30 + 279 + 4
648 = 3 * 50 + 494 + 4
477 = 3 * 42 + 347 + 4
530 = 3 * 44 + 394 + 4
247 = 3 * 29 + 156 + 4
325 = 3 * 32 + 225 + 4
394 = 3 * 32 + 294 + 4
343 = 3 * 31 + 246 + 4
lines 1-24/148 (more)
آها! بعد هذا التغيير ، يكون المبلغ الإجمالي من الرأس دائمًا أقل من حجم ملف البيانات بالكامل أربعة. ونظرًا لأن أربعة هي أيضًا عدد البايتات في الرأس ، فمن الواضح أن هذه ليست مصادفة. يعطينا الرقم الأول عدد إدخالات ثلاث بايت في الجدول ، ويعطينا الرقم الثاني عدد البايتات التي تشكل باقي ملف البيانات.لقد وجدنا صيغة ثابتة ، مما يعني أننا نفهم تمامًا الآن ما تعنيه الأرقام في الجزء الأول.(بالمناسبة ، هذا هو النمط السري للغاية الذي قد يلاحظه القراء المهتمون. توضح الدراسة الدقيقة للمعادلات أنه عندما يكون للملفات نفس الرقم الأول ، فإنهم يحصلون على نفس الفارق المتبقي. يحدث هذا لأن الفرق يكون دائمًا ضعف القيمة الرقم الأول. هذا هو نمط غير واضح ، ولكن يمكن أن يلاحظه أحد المراقبين الناجحين أو الناجحين.)لذلك ، يمكننا القول أن الملف يحتوي على ثلاثة أجزاء رئيسية:- رأس أربعة بايت ؛
- جدول بثلاثة سجلات بايت ؛ و
- ما تبقى من الملف ، والذي من المفترض أن يحتوي على معظم البيانات.
(وبالتالي ، فإن الأجزاء الأخرى التي حددناها سابقًا تقريبًا يجب أن تكون أجزاء فرعية من الجزء الثالث).تفسير البيانات الوصفية
بالنظر إلى هذا المخطط ، سيكون من المنطقي افتراض أن الإدخالات الموجودة في جدول الجزء الثاني هي بعض البيانات الوصفية الضرورية لتفسير بيانات الجزء الثالث.ولكن ما نوع البيانات الوصفية التي يحتوي عليها هذا الجدول؟لقد لاحظنا أعلاه وجود عدة علامات على أن ملف البيانات قد يكون مضغوطًا. (الآن يبدو هذا أكثر منطقية ، لأننا نعلم أن الجزء الثالث من كل ملف ، والذي يُفترض أنه يحتوي على بيانات من كل مستوى ، يبلغ حجمه 100-600 بايت فقط.) إذا كان الأمر كذلك ، فمن الممكن أن يحتوي الجدول الذي يسبق البيانات الرئيسية على بيانات ضغط مضغوطة - قاموس يستخدم أثناء التفريغ. على سبيل المثال ، قبل البيانات المشفرة بواسطة خوارزمية هوفمان ، عادة ما يكون هناك قاموس يقوم بتعيين قيم البايت الأصلية لتسلسلات البتات. على الرغم من أننا لا نتوقع أن يتم تشفير هذه الملفات بواسطة خوارزمية هوفمان (نظرًا لأن البيانات تُظهر أنماطًا واضحة على مستوى البايت ، أي أنها بالكاد تكون بتة دفق البتات) ، فمن الحكمة محاولة ترجمة هذا الجدول على أنه قاموس إلغاء الضغط.(, . , deflate,
gzip
zlib
, . , .)
: . , , , , . , — , , . , , — .
إذا كانت هذه هي الحالة ، فإن إحدى أبسط الطرق لتفسير جزء الشريط هي أن البايتة الأولى هي قيمة البايت التي يجب استبدالها في البيانات المضغوطة ، والبايتة الثانية والثالثة هي القيمة التي يجب استبدالها. ستكون النتيجة التي تم إنشاؤها بواسطة هذا المخطط أكبر بالتأكيد ، على الرغم من أنه ليس من الواضح كم. أيا كان الأمر ، فهذه فرضية منطقية ، ومن السهل التحقق منها. يمكننا كتابة برنامج قصير في Python يقوم بتنفيذ مخطط إزالة الضغط هذا:File decompress.py: import struct import sys
الآن يمكننا التحقق من هذا البرنامج النصي باستخدام ملف بيانات مثال:$ python ./decompress.py <levels/lesson_1.pak | xxd
00000000: 0b0b 0b0b 0404 0000 0a0a 0109 0d05 0502 ................
00000010: 0200 0100 0000 0101 0100 0009 0702 0209 ................
00000020: 1100 0125 0100 2309 0700 0009 0d1d 0124 ...%..#........$
00000030: 011d 0a0a 0105 0500 0100 2000 1b02 0200 .......... .....
00000040: 1a00 2009 0709 1100 011f 0033 001e 0100 .. ........3....
00000050: 2309 0709 0d23 0000 0023 0a0a 0105 0509 #....#...#......
00000060: 1100 011f 0200 1e01 0023 0907 0001 0200 .........#......
00000070: 1a00 0023 0000 1b00 0009 0709 0d01 1c01 ...#............
00000080: 1c0a 0a01 0105 0502 0200 0100 0001 0000 ................
00000090: 0107 0509 1101 2309 0704 0400 0100 0001 ......#.........
000000a0: 2109 0704 0409 0d01 010b 0b0b 0b08 0804 !...............
000000b0: 0402 0200 0608 0807 0707 0502 0202 0078 ...............x
000000c0: 0808 0707 0705 0202 0200 7008 0807 0707 ..........p.....
000000d0: 0502 0202 0064 0017 101e 1e1a 1901 0300 .....d..........
000000e0: 0e1a 1717 100e 1f01 0e13 141b 1e01 1f1a ................
000000f0: 0012 101f 011b 0c1e 1f01 1f13 1001 0e13 ................
00000100: 141b 001e 1a0e 1610 1f2d 0020 1e10 0116 .........-. ....
00000110: 1024 1e01 1f1a 011a 1b10 1900 0f1a 1a1d .$..............
00000120: 1e2d 0000
ومع ذلك ، فإن النتائج غير ملحوظة. بالطبع ، أصبح دفق البيانات الناتج أكثر انتشارًا من الأصل ، ولكن ليس كثيرًا. بالتأكيد لا يكفي لاحتواء جميع البيانات التي نتوقع العثور عليها. من الواضح أن نظام التفريغ هذا أبسط قليلاً من اللازم.إذا فحصنا بعناية الناتج الناتج ، فسنرى قريبًا أنه يبدأ بالعديد من وحدات البايت المتكررة. 0b
، 04
، 00
، 0a
- أنهم جميعا تحدث في أزواج. بالنظر إلى النسخة المضغوطة الأصلية ، سنرى أن جميع هذه الأزواج قد نشأت بسبب استبدال القاموس. لكن في هذه العملية ، لاحظنا على الفور أن كل هذه المعاني المكررة تتوافق أيضًا مع الإدخالات في القاموس. وهذا هو ، إذا قمنا بتطبيق القاموس مرة أخرى ، فسيتم توسيع البيانات مرة أخرى. ربما نحن لا تفريغ بما فيه الكفاية؟قد يكون تخميننا الأول هو إجراء تمريرة ثانية ، مع تطبيق كل إدخال قاموس مرة أخرى لتوسيع البيانات أكثر. قد يكون التخمين الثاني هو تنفيذ تمريرات متعددة مع القاموس ، مع تكرار العملية حتى يتم استبدال جميع وحدات البايت المشابهة لمفاتيح القاموس. ومع ذلك ، إذا ألقينا نظرة فاحصة على بنية القاموس ، فإننا ندرك أننا ببساطة نطبق إدخالات القاموس من اليمين إلى اليسار ، وليس من اليسار إلى اليمين ، عندما يتم توسيع جميع قيمنا في مسار واحد.إذا أخذنا هذه الفرضية ، يمكننا أن نرى بنية خوارزمية ضغط أكثر منطقية. يأخذ البرنامج البيانات المصدر ويقوم بمسحها ، ويبحث عن أكثر سلاسل البايت مزدوجة شيوعًا. ثم يستبدل التسلسل ثنائي البايت بقيمة بايت واحد غير موجود في البيانات. ثم يكرر هذه العملية ، ويواصلها حتى تحتوي البيانات على أكثر من تكرارات متتالية مزدوجة البايت. في الواقع ، خوارزمية الضغط هذه لها اسم: تُعرف باسم ضغط "إعادة الاقتران" ، وهو اختصار لـ "الأزواج العودية".(وقد يفسر هذا بعض الأنماط التي نراها في القاموس. أثناء الضغط ، يتم إنشاء القاموس من اليسار إلى اليمين ، لذلك عند تفريغه ينبغي تطبيقه من اليمين إلى اليسار. نظرًا لأن إدخالات القاموس تشير في كثير من الأحيان إلى الإدخالات السابقة ، فمن المنطقي أن تكون البايتتان الثانية والثالثة غالبًا أصغر من الأول.)على الرغم من أن إعادة الضغط لا تؤدي إلى نتائج رائعة للغاية ، إلا أنها تتمتع بميزة: يمكن تنفيذ برنامج إلغاء الضغط بأقل قدر من الشفرة. أنا شخصياً استخدمت إعادة الاقتران في بعض المواقف عندما كنت بحاجة لتقليل الحجم الإجمالي للبيانات المضغوطة ورمز إلغاء الضغط.
لذلك ، يمكننا إجراء تغيير في سطر واحد من برنامج Python لتطبيق القاموس من اليمين إلى اليسار:File decompress2.py: import struct import sys
إذا جربنا هذا الإصدار ، فسيكون الإخراج أكبر بكثير: $ python ./decompress2.py <levels/lesson_1.pak | xxd | less
00000000: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000010: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000020: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000030: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000040: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000050: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000060: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000070: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000080: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000090: 0000 0000 0000 0000 0000 0000 0000 0000 ................
000000a0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
000000b0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
000000c0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
000000d0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
000000e0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
000000f0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000100: 0000 0000 0000 0000 0000 0101 0101 0100 ................
00000110: 0101 0101 0100 0000 0000 0000 0000 0000 ................
00000120: 0000 0000 0000 0000 0000 0100 0000 0101 ................
00000130: 0100 0000 0100 0000 0000 0000 0000 0000 ................
00000140: 0000 0000 0000 0000 0000 0100 2300 0125 ............#..%
00000150: 0100 2300 0100 0000 0000 0000 0000 0000 ..#.............
00000160: 0000 0000 0000 0000 0101 0101 011d 0124 ...............$
00000170: 011d 0101 0101 0100 0000 0000 0000 0000 ................
الأسطر 1-24 / 93 (المزيد)
نرى الكثير من البايتات الفارغة في هذا الإخراج ، لكن هذا قد يتوافق مع بطاقة فارغة تقريبًا. يبدو أن وحدات البايت غير الصفرية يتم تجميعها بجانب بعضها البعض. نظرًا لأننا نأمل في العثور على بطاقة 32 × 32 ، فلنعد تهيئة الإخراج إلى 32 بايت لكل سطر:$ python ./decompress2.py <levels / lesson_1.pak | xxd -c32 | أقل
00000000: 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 ................................
00000020: 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 ............................
00000040: 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 ................................
00000060: 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 ................................
00000080: 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 ................................
000000a0: 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 ................................
000000c0: 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 ................................
000000e0: 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 ................................
00000100: 0000 0000 0000 0000 0000 0101 0101 0100 0101 0101 0100 0000 0000 0000 0000 0000 ................................
00000120: 0000 0000 0000 0000 0000 0100 0000 0101 0100 0000 0100 0000 0000 0000 0000 0000 ................................
00000140: 0000 0000 0000 0000 0000 0100 2300 0125 0100 2300 0100 0000 0000 0000 0000 0000 ............#..%..#.............
00000160: 0000 0000 0000 0000 0101 0101 011d 0124 011d 0101 0101 0100 0000 0000 0000 0000 ...............$................
00000180: 0000 0000 0000 0000 0100 2000 1b00 0000 0000 1a00 2000 0100 0000 0000 0000 0000 .......... ......... ...........
000001a0: 0000 0000 0000 0000 0100 2300 011f 0033 001e 0100 2300 0100 0000 0000 0000 0000 ..........#....3....#...........
000001c0: 0000 0000 0000 0000 0101 0101 0123 0000 0023 0101 0101 0100 0000 0000 0000 0000 .............#...#..............
000001e0: 0000 0000 0000 0000 0100 2300 011f 0000 001e 0100 2300 0100 0000 0000 0000 0000 ..........#.........#...........
00000200: 0000 0000 0000 0000 0100 0000 1a00 0023 0000 1b00 0000 0100 0000 0000 0000 0000 ...............#................
00000220: 0000 0000 0000 0000 0101 0101 0101 1c01 1c01 0101 0101 0100 0000 0000 0000 0000 ................................
00000240: 0000 0000 0000 0000 0000 0000 0100 0001 0000 0100 0000 0000 0000 0000 0000 0000 ................................
00000260: 0000 0000 0000 0000 0000 0000 0100 2301 2300 0100 0000 0000 0000 0000 0000 0000 ..............#.#...............
00000280: 0000 0000 0000 0000 0000 0000 0100 0001 2100 0100 0000 0000 0000 0000 0000 0000 ................!...............
000002a0: 0000 0000 0000 0000 0000 0000 0101 0101 0101 0100 0000 0000 0000 0000 0000 0000 ................................
000002c0: 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 ................................
000002e0: 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 ................................
lines 1-24/47 (more)
بعد النظر بعناية في أنماط القيم غير الصفرية ، سنرى أن الخريطة مرئية بوضوح في الإخراج. في الواقع ، يمكننا أن نجعل هذا النموذج أكثر وضوحًا باستخدام أداة التفريغ "الملونة" ، التي تعين لونًا لكل قيمة بايت ، مما يسهل البحث عن أنماط القيم المكررة:xcd
هذه أداة غير قياسية ، ولكن يمكنك تنزيلها من هنا . لاحظ خيار -r
الأداة المساعدة less
، الذي يخبرك بمسح تسلسل التحكم.قارن هذا بخريطة المستوى الأول المرسومة بالمروحة:بدون شك ، نرى بيانات خريطة المستوى. يمكنك أن تكون متأكدًا تمامًا من أننا حددنا خوارزمية التفريغ بشكل صحيح.تفسير البيانات
بمقارنة قيم البايت مع صورة الخريطة ، يمكننا تحديد ما 00
يشفر بلاطة فارغة 01
، 23
ويرمز للجدار ، ويشير إلى شريحة. 1A
يدل على الباب الأحمر ، 1B
- الباب الأزرق ، وهلم جرا. يمكننا تعيين قيم دقيقة للرقائق والمفاتيح والأبواب وجميع المربعات الأخرى التي تشكل خريطة المستوى بالكامل.الآن يمكننا الانتقال إلى المستوى التالي وإيجاد قيم البايت للعناصر التي تظهر هناك. وتابع مع المستويات التالية حتى نحصل على قائمة كاملة من قيم البايت وكائنات اللعبة التي يقومون بترميزها.كما اقترحنا في البداية ، تنتهي البطاقة تمامًا بعد 1024 بايت (عند الإزاحة 000003FF
).هذه المرة ، لإزالة أول 32 خطًا من التفريغ ، نستخدمهاsed
. نظرًا لأن لدينا 32 بايت لكل سطر ، فسنتخطى 1024 بايت الأولى.بعد بيانات الخريطة مباشرة ، يوجد تسلسل يبلغ 384 بايت (قيمه في الفاصل الزمني 00000400
- 0000057F
) ، وكلها تقريبًا تساوي الصفر ، لكن القيم غير الصفرية تقع أيضًا بينها. بعد ذلك يأتي نمط بايت مختلف تمامًا ، لذلك سيكون من المنطقي افتراض أن تسلسل 384 بايت هذا جزء منفصل.إذا نظرنا إلى عدد قليل من المستويات الأخرى ، سرعان ما نلاحظ هذا النمط. يتكون الجزء 384 بايت بالفعل من ثلاثة أقسام فرعية ، كل 128 بايت طويلة. يبدأ كل قسم فرعي ببضع وحدات غير صفرية متبوعة بأصفار تملأ الجزء المتبقي من القسم الفرعي.تحتوي بعض المستويات على الكثير من البيانات ؛ للآخرين (على سبيل المثال ، للمستوى الأول) فقط الحد الأدنى للغاية. بمقارنة المستويات مع بطاقاتهم ، سنلاحظ قريبًا أن كمية البيانات الموجودة في هذا الجزء من الملف مرتبطة مباشرة بعدد "mobs" لكل مستوى. في هذه الحالة ، يشمل عدد "الغوغاء" جميع المخلوقات على المستوى ، وكتل الأرض (التي لا تتحرك بشكل مستقل ، ولكن يمكن دفعها) والشخصية الرئيسية تشيب (اللاعب). بمعنى أنه لم يتم الإشارة إلى الغوغاء على الخريطة نفسها ، ولكن يتم ترميزها في هذه المخازن المؤقتة الثلاثة.بعد أن علمنا أن هذه الأقسام الفرعية الثلاثة تحتوي على بيانات على الغوغاء على المستوى ، لن يكون من الصعب للغاية معرفة ما هو موجود في كل قسم من الأقسام الفرعية.أول قسم فرعي 128 بايت هو قائمة بقيم البايت التي تحدد نوع الغوغاء. على سبيل المثال ، تبدو مخازن المستوى الثاني كما يلي: $ python ./decompress2.py <levels/lesson_2.pak | xxd | less
00000400: 0608 1c1c 0808 0000 0000 0000 0000 0000 ................
00000410: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000420: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000430: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000440: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000450: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000460: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000470: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000480: a870 98a0 6868 0000 0000 0000 0000 0000 .p..hh..........
00000490: 0000 0000 0000 0000 0000 0000 0000 0000 ................
000004a0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
000004b0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
000004c0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
000004d0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
000004e0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
000004f0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000500: 6060 6060 5868 0000 0000 0000 0000 0000 ````Xh..........
00000510: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000520: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000530: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000540: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000550: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000560: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000570: 0000 0000 0000 0000 0000 0000 0000 0000 ................
خطوط 64-87 / 93 (المزيد)
قارن هذا بخريطة مستوى:في هذا المستوى ، هناك ستة من الغوغاء: ثلاثة أخطاء وكتلان وشريحة. يحتوي المفتاح الفرعي 128 بايت الأول على بايت واحد 06
وثلاثة بايت 08
و وحدتي بايت 1C
. سيكون من المعقول أن نستنتج ما 06
تمثله Chip 08
- أي خطأ ، 1C
وكتلة.(استمرار لمقارنة ملفات البيانات من مستويات البطاقات والتعبئة في منطقتنا الغوغاء القاموس، سرعان ما تجد عيبا في هذه النظرية: إن رقاقة قد يتم الإشارة إلى أربع قيم مختلفة، وهي 04
، 05
، 06
أو07
. تحتوي هذه المجموعة من الرموز على كل الغوغاء. عندما ندرس القيم المختلفة بعناية ، فسوف نفهم في النهاية أن القيمة 0 أو 1 أو 2 أو 3 تُضاف إلى قيمة البايت التي تشير إلى النوع ، مع الإشارة إلى الاتجاه الأولي للغوغاء: الشمال أو الشرق أو الجنوب أو الغرب. هذا ، على سبيل المثال ، 06
تشير قيمة البايت إلى رقاقة تبحث جنوبًا.)أهمية القسمين الأخريين أقل وضوحا. ولكن بعد دراسة القيم المكررة في هذه الأقسام الفرعية ومقارنة البطاقات الخاصة بهذه الغوغاء ، نفهم أن البايتات في القسم الفرعي الثاني تخزن إحداثي X لكل غوغاء ، بينما تخزن البايتات في القسم الفرعي الثالث إحداثي Y لكل غوغاء. يعوق فهم هذا القرار حقيقة أن الإحداثيات المخزنة في هذه الأقسام الفرعية تحولت بالفعل 3 بتات إلى اليسار ، أي مضروبة في 8. هذه الحقيقة الصغيرة تشرح المجموعة "الزرقاء" ، التي وجدناها في إحصاء القيم. (الأسباب التي أدت إلى حدوث هذا التحول ليست واضحة ، ولكن على الأرجح يتم استخدام البتات الثلاثة الأدنى لتمثيل الرسوم المتحركة عند تحرك الغوغاء.)بعد التعامل مع هذا الجزء ، يمكننا الآن معرفة عدد ملفات البيانات التي لم يتبق منها سوى بضع بايتات:ملاحظة أنxxd
يقبل -s
قيمة ست عشرية للخيار .$ ل f في المستويات / * ؛ هل الثعبان. / decompress2.py <$ f | xxd -s 0x580 | sed-n 1p؛ عمله | أقل
00000580: 9001 0c17 1701 1120 1717 00 ....... ...
00000580: 0000 0c17 1b13 0c0d 101f 011e 1a20 1b00 ............. ..
00000580: f401 0c18 1e1f 101d 0f0c 1800 ............
00000580: 2c01 0c1b 0c1d 1f18 1019 1f00 ، ...........
00000580: 9001 0c1d 0e1f 140e 1117 1a22 00 ............ "
00000580: 2c01 0d0c 1717 1e01 1a01 1114 1d10 00، ..............
00000580: 2c01 0d10 220c 1d10 011a 1101 0d20 1200 ، ... "........ ..
00000580: 5802 0d17 1419 1600 X .......
00000580: 0000 0d17 1a0d 0f0c 190e 1000 ............
00000580: f401 0d17 1a0d 1910 1f00 ..........
00000580: f401 0d17 1a0e 1601 110c 0e1f 1a1d 2400 .............. $.
00000580: ee02 0d17 1a0e 1601 0d20 1e1f 101d 0114 ......... ......
00000580: 5802 0d17 1a0e 1601 1901 1d1a 1717 00 X..............
00000580: 5e01 0d17 1a0e 1601 1a20 1f00 ^........ ..
00000580: c201 0d17 1a0e 1601 0d20 1e1f 101d 00 ......... .....
00000580: 2c01 0d1a 2019 0e10 010e 141f 2400 ,... .......$.
00000580: 5000 0d1d 201e 1311 141d 1000 P... .......
00000580: e703 0e0c 1610 0122 0c17 1600 ......."....
00000580: 5802 0e0c 1e1f 1710 0118 1a0c 1f00 X.............
00000580: 8f01 0e0c 1f0c 0e1a 180d 1e00 ............
00000580: 0000 0e10 1717 0d17 1a0e 1610 0f00 1b1d ................
00000580: 2c01 0e13 0e13 0e13 141b 1e00 ,...........
00000580: 8f01 0e13 1417 1710 1d00 ..........
00000580: bc02 0e13 141b 1814 1910 00 ...........
lines 1-24/148 (more)
فحص الزوج الأول من البايتات في الباقي يلمح إلينا بسرعة أنها تحتوي على قيمة عدد صحيح 16 بت أخرى. إذا اعتبرناهم هكذا ، فإن غالبية القيم تظهر بترميز عشري كأرقام مستديرة: يستخدمالأمر بدلاً od
من -j
ذلك للانتقال إلى الإزاحة الأصلية -s
. تجدر الإشارة أيضًا إلى الأمر printf
: بالإضافة إلى توفير التنسيق ، فهي طريقة ملائمة لعرض النص دون تعليق سطر جديد في النهاية.$ ل f في المستويات / * ؛ هل printf "٪ -20s" $ f ؛ python ./decompress2.py <$ f | od -An -j 0x580 -tuS -N2؛ عمله | أقل
المستويات / all_full.pak 400
المستويات / alphabet.pak 0
المستويات / amsterda.pak 500
المستويات / apartmen.pak 300
المستويات / arcticfl.pak 400
المستويات / الكرات. 300
المستويات / beware_o.pak 300
المستويات / blink.pak 600
المستويات / blobdanc.pak 0
مستويات / blobnet.pak 500
المستويات / block_fa.pak 500
المستويات / block_ii.pak 750
المستويات / block_n_.pak 600
المستويات / block_ou.pak 350
المستويات / block.pak 450
المستويات / bounce_c.pak 300
مستويات / brushfir.pak 80
المستويات / cake_wal.pak 999
المستويات / castle_m.pak 600
المستويات / catacomb.pak 399
المستويات / cellbloc.pak 0
المستويات / chchchip.pak 300
مستويات / chiller.pak 399
مستويات / chipmine.pak 700
الأسطر 1-24 / 148 (المزيد)
إذا انتقلنا مرة أخرى إلى القائمة ، التي تم إنشاؤها أصلاً من البيانات التي توقعنا العثور عليها في الملفات ، فسوف ندرك أن هذا الرقم هو الوقت المناسب لإكمال المستوى (إذا كانت القيمة صفرية ، فلا يوجد حد زمني للمستوى).بعد هاتين البايتتين ، تصبح البيانات أكثر تقلبًا. حقيقة أنه بالنسبة لمعظم المستويات ، يوجد حوالي عشرة بايت في الملف تقيد محتوياتها بشكل خطير:$ python ./decompress2.py <levels / all_full.pak | xxd -s 0x0582
00000582: 0c17 1701 1120 1717 00 ..... ...
على سبيل المثال ، تبقى تسعة بايت فقط في هذا المستوى. نأخذ في الاعتبار هذا الحجم المحدود ، وكذلك حقيقة أن هذه البايتات التسعة تحتوي على أربعة تكرارات للقيمة 17
، يتم جمعها في أزواج اثنين. سنلاحظ على الفور أن نمط الأرقام 17
يتوافق مع نمط الحروف L
باسم المستوى "ALL FULL". يبلغ طول الاسم ثمانية أحرف ، لذلك غالبًا ما تكون البايت الفارغة في النهاية هي نهاية السطر. بعد أن اكتشفت ذلك ، يمكنك أن تفحص كل المستويات الأخرى بشكل تافه وأن تستخدم أسمائها لإنشاء قائمة كاملة من الشخصيات:00 | نهاية الخط |
01 | شريط الفضاء |
02 - 0B | أرقام 0-9 |
0C - 25 | رسائل من الألف إلى الياء |
26 - 30 | علامات الترقيم |
بالنسبة لمعظم المستويات ، ينتهي ملف البيانات هنا. ومع ذلك ، لا يزال لدى بضع عشرات من الاسم بيانات. إذا ألقينا نظرة على القائمة ، فسوف نرى أن هناك مستويات تحتوي على أزرار تلميح ، وأن هذه البيانات المتبقية تحتوي على نص سطر تلميح المستوى المشفر بنفس مجموعة الأحرف كأسماء المستوى.بعد ذلك ، وصلنا إلى نهاية جميع الملفات. الآن لدينا وصف كامل لمخطط هذه المستويات. اكتمال مهمتنا.الأعمال غير المكتملة
قد يلاحظ القارئ اليقظ أننا في البداية كنا نتوقع أن نجد عنصرين آخرين في هذه الملفات لم نلتقاه قط. سنشرح غياب الاثنين:العنصر الأول هو عدد الرقائق ، أي: إجمالي عدد الرقائق التي يجب على اللاعب جمعها من أجل المرور عبر موصل الرقائق. كما قلنا في البداية ، غالبًا ما يساوي العدد الإجمالي للرقائق على مستوى ، لكن هذا لا يحدث دائمًا. لذلك ، يجب الحصول على هذه البيانات بطريقة ما. يمكن العثور على الإجابة من خلال دراسة بطاقات من تلك المستويات التي توجد بها رقائق إضافية. اتضح أنه يتم استخدام قيمتين مختلفتين للإشارة إلى الرقائق. القيمة 23
التي وجدناها في البداية ، ولكن يتم استخدام القيمة أيضًا.31
تدل على شريحة لا تؤثر على المبلغ الإجمالي المطلوب لفتح موصل الشريحة. (ومع ذلك ، من وجهة نظر طريقة اللعب ، كلا النوعين من الرقائق متماثلان. إذا كان هناك نوع رقاقة واحد 31
على المستوى ، فلا يمكنك جمع أي عدد من الرقائق على المستوى.)العنصر الثاني هو كلمة مرور مستوى أربعة أحرف. ليس مخفياً في أي مكان في بيانات المستوى. لسوء الحظ ، لا يمكن حل هذه المشكلة عن طريق إجراء مزيد من التحقيق لملف البيانات. نحن مضطرون إلى استنتاج أن كلمات المرور يتم تخزينها ببساطة في مكان آخر. التفسير الأكثر ترجيحًا: يتم ترميزها في مكان ما في البرنامج نفسه. ومع ذلك ، أصبح من الواضح لاحقًا أنه لا يتم تخزين كلمات المرور مباشرةً. من الأشخاص المطلعين على الكود نفسه ، تعلمت أن كلمات المرور يتم إنشاؤها ديناميكيًا باستخدام مولد الأرقام العشوائية المزيفة الذي تمت تهيئته بتسلسل محدد. لذلك ، لا يمكن تغيير كلمات المرور إلى المستويات مباشرةً ، ويمكن القيام بذلك فقط عن طريق تغيير رمز المجمّع.خاتمة
عن طريق إجراء هندسة عكسية كاملة للبيانات الموجودة في ملفات المستوى ، يمكنني البدء في كتابة برنامج يمكنه تشفير وفك تشفير بيانات المستوى. بفضلها ، تمكنت من إنشاء محرر المستوى الذي طال انتظاره لإصدار Chip's Challenge for Lynx ، وقد أدى وجود هذه الأداة إلى زيادة قدرتي على دراسة منطق اللعبة إلى حد كبير ، وكذلك تحسين جودة مضاهاة اللعبة.لكن ... يجب أن أعترف أنني قمت شخصياً بالتطوير العكسي لملفات البيانات بطريقة غير موصوفة أعلاه. لقد بدأت من الطرف الآخر - مع تعريف بيانات السلسلة. بدأت بدراسة ملفات المستويات الثمانية الأولى. نظرًا لأنه يتم استدعاؤها من "LESSON 1" إلى "LESSON 8" ، فقد بحثت عن بيانات سلاسل فرعية متطابقة فيها. وكنت محظوظًا: لم يتم ضغط أي من هذه المستويات ، لذا يتم تخزين جميع الأسماء الثمانية في ملفات البيانات في شكلها الأصلي. بالطبع ، شعرت بالحرج لأن هذه الأسطر لم يتم تخزينها بأحرف ASCII ، ولكن ساعدني S المزدوج في الاسم في اكتشاف نمط تكررت في جميع ملفات البيانات الثمانية. وبعد العثور على الاسم ، تعرفت على ترميز الأحرف من الحروف والأرقام وحرف المسافة. ثم طبقت هذا الترميز على بقية الملف ، وبعد الاسم مباشرة رأيت خطوطًا سريعة ، وبدأت في مراقبة الحالات الشاذة:أداة مساعدة رائعة tr
تجعل من السهل تحويل مجموعة الأحرف الخاصة بك من ملفات البيانات إلى ASCII.$ tr '\ 001- \ 045' '0-9A-Z' <levels / lesson_1.pak | XXD
00000000: 4600 cb00 3000 0032 3030 3332 3235 3333 F ... 0..200322533
00000010: 3635 3537 0020 3820 2039 3636 4238 3846 6557. 8 966B88F
00000020: 0058 4a37 354d 3000 5737 4226 3746 2739 .XJ75M0.W7B & 7F'9
00000030: 3928 3533 2953 2027 2733 3042 2057 3532 9 (53) S '' 30B W52
00000040: 3730 3738 304a 3226 375a 2046 4a30 5752 70780J2 & 7Z FJ0WR
00000050: 2059 2052 4220 3537 0055 0050 3200 4f00 Y RB 57.U.P2.O.
00000060: 554a 2637 5400 3300 2946 4a57 5830 4642 UJ & 7T.3.) FJWX0FB
00000070: 2035 2637 544d 2946 4a37 4d4f 3058 3050 5 & 7TM) FJ7MO0X0P
00000080: 304a 5720 5120 5142 3835 3237 3020 3020 0JW Q QB85270 0
00000090: 2826 2058 4a33 3730 2056 4a33 5738 2727 (& XJ370 VJ3W8''
000000a0: 3933 3200 3439 3628 324d 7839 3628 324d 932.496(2Mx96(2M
000000b0: 7039 3628 324d 6400 4c45 5353 4f4e 2031 p96(2Md.LESSON 1
000000c0: 0043 4f4c 4c45 4354 2043 4849 5029 544f .COLLECT CHIP)TO
000000d0: 0047 4554 2050 4153 5420 5448 4520 4348 .GET PAST THE CH
000000e0: 4950 0053 4f43 4b45 542d 0055 5345 204b IP.SOCKET-.USE K
000000f0: 4559 2954 4f20 4f50 454e 0044 4f4f 5253 EY)TO OPEN.DOORS
00000100: 2d30 -0
على سبيل المثال ، يوجد في نص التعليمات مكانان يتم فيه استبدال تسلسل S والمسافة بالشريحة اليمنى. أعطتني هذه الحالات الشاذة أدلة كافية لحساب وجود الضغط بشكل استنتاجي والحصول على بعض المعلومات حول طبيعته. فيما بعد قمت بربط قيم البايت غير الطبيعية بحوادثها أقرب إلى بداية ملف البيانات. (على سبيل المثال ، في تفريغ الإزاحة الست عشرية الموضحة أعلاه ، 00000035
يوجد قوس الأيمن ، متبوعًا بحرف S كبير ومسافة.) من هذا ، قمت بحساب نظام الضغط بشكل مشابه للعملية الموضحة في المقالة. كل شيء آخر كان بسيط جدا.يبدو لي أنه يمكن للمرء أن يستخلص درسًا من هذا: لا توجد طريقة فريدة لفحص ملف بيانات غير مألوف. أي الأدوات التي تناسبك هي الأدوات الصحيحة للهندسة العكسية.