في اليوم الذي وقعت فيه حالة حب مع الغمغمة

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

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

لم أقم بتحليل تنسيق BINI ، وأنا لست أول من تعلم كيفية تحريرها. لكنني لم أحب الأدوات الموجودة ، وكان لدي رؤيتي الخاصة لكيفية عملها. أفضل واجهة بنمط Unix ، على الرغم من أن اللعبة نفسها تعمل على Windows.

في ذلك الوقت ، تعرفت على الأدوات yacc (في الواقع Bison ) و lex (في الواقع المرن ) ، بالإضافة إلى Autoconf ، لذا فقد استخدمتها بالضبط. كان من المثير للاهتمام تجربة هذه الأدوات في الممارسة العملية ، على الرغم من أنني قلدت بتقليد مشاريع أخرى مفتوحة المصدر ، ولا أفهم سبب القيام بكل شيء بهذه الطريقة ، بأي طريقة أخرى. نظرًا لاستخدام yacc / lex وإنشاء برامج نصية للتكوين ، كان هناك حاجة لنظام تشبه Unix بالكامل. كل هذا مرئي في الإصدار الأصلي من البرامج .

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

إعادة بيع


في منتصف عام 2018 ، عدت إلى هذا المشروع. هل سبق لك أن نظرت إلى الكود القديم الخاص بك مع التفكير: ما رأيك حتى؟ تحولت صيغة INI الخاصة بي إلى أن تكون أكثر صلابة وصرامة من اللازم ، حيث تم تسجيل الثنائيات بطريقة مشكوك فيها ، ولم يعمل التجميع بشكل طبيعي.

بفضل عشر سنوات من الخبرة الإضافية ، عرفت بالتأكيد أنني سأكتب هذه الأدوات بشكل أفضل الآن. وفعلت ذلك في غضون بضعة أيام ، وأعدت كتابتهم من جديد. هذا الرمز الجديد الآن في الخيط الرئيسي على جيثب.

أحب أن أجعل كل شيء أبسط ما يمكن ، لذلك تخلصت من autoconf لصالح Makefile أبسط وأكثر قابلية للنقل . لا مزيد من yacc أو lex ، لكن المحلل اللغوي مكتوب باليد. يتم استخدام C المناسب فقط والمحمول ، والنتيجة بسيطة للغاية لدرجة أنني قمت بتجميع المشروع بأمر قصير واحد من Visual Studio ، وبالتالي فإن Makefile غير مطلوب حقًا. إذا قمت باستبدال stdint.h بـ typedef ، فيمكنك بناء وتشغيل أدوات binitools تحت DOS .

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

الغموض


لقد كنت مهتمًا بالتعثر لسنوات عديدة ، وخاصةً afl ( ضباب غامق أمريكي). لكنه لم يتقن ذلك ، على الرغم من أنه اختبر بعض الأدوات التي أستخدمها بانتظام. لكن الغموض لم يجد شيئًا رائعًا ، على الأقل قبل أن أستسلم. اختبرت مكتبة JSON الخاصة بي ولسبب ما لم أجد شيئًا. من الواضح أن محلل JSON الخاص بي لا يمكن أن يكون موثوقًا به ، أليس كذلك؟ لكن الغموض لم يظهر أي شيء. (كما اتضح فيما بعد ، أصبحت مكتبة JSON الخاصة بي موثوقة للغاية ، ويرجع الفضل في ذلك إلى حد كبير إلى جهود المجتمع!)

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

على افتراض أن لديك الأدوات اللازمة مثبتة (make ، gcc ، afl) ، إليك كيفية بدء عملية دمج binitools بسهولة:

 $ make CC=afl-gcc $ mkdir in out $ echo '[x]' > in/empty $ afl-fuzz -i in -o out -- ./bini 

تقبل الأداة المساعدة bini INI عند الإدخال وتصدر BINI ، لذلك من المثير للاهتمام التحقق من الإجراء العكسي غير unbini . نظرًا لأن unbini يحلل البيانات الثنائية البسيطة نسبيًا ، فإن fuzzer (ربما) ليس لديه ما يبحث عنه. ومع ذلك ، فقط في حالة ، راجعت ذلك على أي حال.



في هذا المثال ، قمت بتغيير المترجم الافتراضي إلى shell GCC for afl ( CC=afl-gcc ). هنا afl تستدعي دول مجلس التعاون الخليجي في الخلفية ، لكنها تضيف مجموعة الأدوات الخاصة بها إلى الملف الثنائي. عند الدمج ، تستخدم afl-fuzz مجموعة الأدوات هذه لمراقبة مسار تنفيذ البرنامج. تشرح وثائق afl التفاصيل الفنية.

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

النتيجة الأكثر إثارة للاهتمام ومخيفة هو تعطل البرنامج الكامل. عندما بدأت تشغيل fuzzer لأول مرة في binitools ، أظهر bini العديد من هذه الحوادث. في غضون دقائق ، اكتشف afl عددًا من الأخطاء الدقيقة والمثيرة في برنامجي ، والتي كانت مفيدة بشكل لا يصدق. عثر Fazzer على خطأ غير مرجح في مؤشر قديم ، والتحقق من ترتيب مختلف تخصيصات الذاكرة المختلفة. كان هذا الخطأ المحدد نقطة تحول جعلني أدرك قيمة الغمغمة.

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

إنشاء مجموعة اختبار


بمجرد إصلاح جميع الأخطاء التي اكتشفها fuzzer وضبط المحلل اللغوي في جميع المواقف الحدودية ، قمت بإجراء مجموعة من الاختبارات من حزمة بيانات fuzzer - وإن لم يكن ذلك مباشرةً.

أولاً ، ركضت fuzzer بالتوازي - تم شرح هذه العملية في وثائق afl - لذلك حصلت على الكثير من المدخلات الزائدة عن الحاجة. حسب التكرار ، أعني أن الإدخال مختلف ولكن له نفس مسار التنفيذ. لحسن الحظ ، لدى afl أداة للتعامل مع هذا: afl-cmin ، أداة لتقليل الصدفة. أنه يلغي المدخلات غير الضرورية.

ثانياً ، كان الكثير من هذه المدخلات أطول من اللازم لاستدعاء مسار التنفيذ الفريد. afl-tmin ، وهو minimizer لحالة الاختبار الذي قلل من حالة الاختبار ، afl-tmin .

قمت بفصل الإدخال الصحيح وغير الصحيح - وتحقق منه في المستودع. ألق نظرة على كل هذه المداخل الغبية التي اخترعها fuzzer بناءً على الحد الأدنى من المدخلات:


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

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

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


All Articles