
مصحح أخطاء x86 قابل للحقن في الوضع الحقيقي للهندسة العكسية للـ BIOS وتصحيح أخطاء كود الوضع الحقيقي التعسفي عبر كابل تسلسلي، مع تكامل GDB ودعم نقاط التوقف/نقاط المراقبة العتادية.
BREAD (BIOS Reverse Engineering & Advanced Debugger) هو مصحح أخطاء x86 حقني يعمل في الوضع الحقيقي، ويمكنه تصحيح أي كود في الوضع الحقيقي (على أجهزة حقيقية) من جهاز كمبيوتر آخر عبر كابل تسلسلي.
نشأ BREAD من العديد من المحاولات الفاشلة لهندسة BIOS العكسية القديمة. نظرًا لأن الغالبية العظمى - إن لم يكن كل - تحليل BIOS يتم بشكل ثابت باستخدام مفككات (disassemblers)، يصبح فهم BIOS صعبًا للغاية، إذ لا توجد طريقة لمعرفة قيمة السجلات أو الذاكرة في جزء معين من الكود.
على الرغم من ذلك، يمكن لـ BREAD أيضًا تصحيح أي كود عشوائي في الوضع الحقيقي، مثل الكود القابل للإقلاع أو برامج DOS أيضًا.
عرض سريع:
https://user-images.githubusercontent.com/8294550/217709970-9007a1e3-7352-470d-a22f-cbb5219d5547.mp4
ينقسم هذا المصحح إلى جزئين: المصحح (مكتوب بالكامل بلغة التجميع ويعمل على الجهاز الذي يتم تصحيحه) والجسر (bridge)، مكتوب بلغة C ويعمل على Linux.
المصحح هو الكود القابل للحقن، مكتوب في وضع حقيقي 16 بت، ويمكن وضعه داخل ROM BIOS أو أي كود وضع حقيقي آخر. عند تنفيذه، يقوم بإعداد معالجات المقاطعة المناسبة، ويضع المعالج في وضع الخطوة الواحدة (single-step)، وينتظر الأوامر على المنفذ التسلسلي.
أما الجسر، فهو الرابط بين المصحح و GDB. يتصل الجسر مع GDB عبر TCP ويقوم بتوجيه الطلبات/الاستجابات إلى المصحح عبر المنفذ التسلسلي. الفكرة من وراء الجسر هي إزالة تعقيد حزم GDB وإنشاء بروتوكول أبسط للاتصال بالجهاز. بالإضافة إلى ذلك، يسمح البروتوكول الأبسط بأن يكون حجم الكود النهائي أصغر، مما يسهل حقن المصحح في بيئات مختلفة.
كما هو موضح في المخطط التالي:
+---------+ حزم بسيطة +----------+ حزم GDB +---------+
| |--------------->| |--------------->| |
| dbg | | bridge | | gdb |
|(جهاز حقيقي)|<---------------| (Linux) |<---------------| (Linux) |
+---------+ تسلسلي +----------+ TCP +---------+
من خلال تنفيذ كعب GDB، يوفر BREAD العديد من الميزات مباشرة. الأوامر التالية مدعومة:
هندسة ثنائية خام، مثل BIOS، في GDB تعني تلقائيًا عدم وجود رموزها الأصلية. ومع ذلك، مع تقدم عملية الهندسة العكسية، يكتسب المستخدم/المبرمج/المخترق فهمًا أفضل لأجزاء معينة من الكود، وتسمح أدوات التحليل الثابتة مثل IDA و Cutter و Ghidra وغيرها بإضافة تعليقات توضيحية وتعليقات وتعريفات دالة والمزيد. هذه التحسينات تعزز بشكل كبير إنتاجية المستخدم.
مع أخذ هذا في الاعتبار، يوجد نص برمجي مساعد بلغة Python في المشروع يسمى symbolify.py. بالنظر إلى قائمة الرموز (عنوان تسمية)، يقوم بإنشاء ملف ELF صغير مع إضافة هذه الرموز. يمكن بعد ذلك تحميل هذا ELF في GDB لاحقًا واستخدامه لتبسيط عملية التصحيح بشكل كبير.
يمكن أن يتضمن ملف الرموز مسافات بيضاء، أسطر فارغة، تعليقات (#)، وتعليقات على سطر العنوان. يمكن أن تكون العناوين بتنسيق عشري أو ست عشري، ويمكن أن تكون التسميات/الرموز (مفصولة بحرف مسافة بيضاء واحد أو أكثر) على الشكل [a-z0-9_]+، كما في (يمكن العثور على مثال حقيقي في symbols/ami_ipm41d3.txt):
#
# هذا تعليق
#
0xdeadbeef my_symbol1
0x123 othersymbol # هذه الدالة تقوم بـ xyz
# مثال مع عنوان عشري
456 anotherone
على سبيل المثال، بالنظر إلى ملف الرموز المتاح في symbols/ami_ipm41d3.txt، يمكن للمستخدم القيام بشيء مثل:
$ ./simbolify.py symbols/ami_ipm41d3.txt ip41symbols.elf
ثم تحميله في GDB كما يلي:
(gdb) add-symbol-file ip41symbols.elf 0
add symbol table from file "ip41symbols.elf" at
.text_addr = 0x0
(y or n) y
Reading symbols from ip41symbols.elf...
(No debugging symbols found in ip41symbols.elf)
(gdb) p cseg_
cseg_change_video_mode_logo cseg_get_cpuname
(gdb) p cseg_
لاحظ أنه حتى الإكمال التلقائي لـ GDB يعمل كما هو متوقع، أليس مذهلاً؟
كم عددها؟ نعم. نظرًا لأن الكود الذي يتم تصحيحه لا يدرك أنه يتم تصحيحه، يمكن أن يتداخل مع المصحح بعدة طرق، على سبيل المثال لا الحصر:
القفز إلى الوضع المحمي: إذا تحول الكود الذي يتم تصحيحه إلى الوضع المحمي، فإن هياكل معالجات المقاطعة وما إلى ذلك تتغير ولن يتم استدعاء المصحح بعد الآن في تلك النقطة من الكود. ومع ذلك، من الممكن أن تسمح القفزة العائدة إلى الوضع الحقيقي (مع استعادة الحالة السابقة بالكامل) للمصحح بالعمل مرة أخرى.
تغييرات IDT: إذا قام الكود الذي يتم تصحيحه بتغيير IDT أو عنوان قاعدته لأي سبب، فلن يتم استدعاء معالجات المصحح بشكل صحيح.
المكدس: يستخدم BREAD مكدسًا ويفترض أنه موجود! لا ينبغي إدراجه في مواقع لم يتم فيها تكوين المكدس بعد.
لتصحيح BIOS، هناك قيود أخرى مثل: لا يمكن تصحيح كود BIOS من البداية (bootblock)، حيث أن إعدادًا أدنى (مثل RAM) مطلوب لكي يعمل BREAD بشكل صحيح. ومع ذلك، من الممكن إجراء "إعادة تشغيل دافئة" عن طريق تعيين CS:EIP إلى F000:FFF0. في هذا السيناريو، يمكن متابعة تهيئة BIOS مرة أخرى، حيث أن BREAD قد تم تحميله بشكل صحيح. يرجى ملاحظة أن "مسار الكود" لتهيئة BIOS أثناء إعادة التشغيل الدافئة قد يكون مختلفًا عن إعادة التشغيل الباردة وقد لا يكون تدفق التنفيذ هو نفسه تمامًا.
البناء يتطلب فقط GNU Make، ومترجم C (مثل GCC أو Clang أو TCC)، و NASM، وجهاز Linux.
للمصحح وضعان للتشغيل: وضع الاستقصاء (الافتراضي) ووضع قائم على المقاطعة:
وضع الاستقصاء هو النهج الأبسط ويجب أن يعمل بشكل جيد في مجموعة متنوعة من البيئات. ومع ذلك، نظرًا لطبيعة الاستقصاء، هناك استخدام مرتفع لوحدة المعالجة المركزية:
$ git clone https://github.com/Theldus/BREAD.git
$ cd BREAD/
$ make
الوضع القائم على المقاطعة يحسن استخدام وحدة المعالجة المركزية عن طريق استخدام مقاطعات UART لاستقبال بيانات جديدة، بدلاً من الاستقصاء المستمر عنها. يؤدي ذلك إلى بقاء وحدة المعالجة المركزية في حالة "توقف" حتى تتلقى أوامر من المصحح، وبالتالي منعها من استهلاك 100% من موارد وحدة المعالجة المركزية. ومع ذلك، نظرًا لأن المقاطعات ليست ممكّنة دائمًا، لا يتم تعيين هذا الوضع كخيار افتراضي:
$ git clone https://github.com/Theldus/BREAD.git
$ cd BREAD/
$ make UART_POLLING=no
استخدام BREAD يتطلب فقط كابلًا تسلسليًا (ونعم، اللوحة الأم لديك تحتوي على رأس COM، تحقق من الدليل) وحقن الكود في الموقع المناسب.
للحقن، يجب إجراء تغييرات طفيفة في dbg.asm (مصدر المصحح). يجب تغيير 'ORG' للكود وكذلك كيفية عودة الكود (ابحث عن ">> CHANGE_HERE <<" في الكود للأماكن التي تحتاج إلى تغيير).