BREAD (BIOS Reverse Engineering & Advanced Debugger) هو مصحح أخطاء x86 للوضع الحقيقي "قابل للحقن" ويمكنه تصحيح كود الوضع الحقيقي التعسفي (على HW الحقيقي) من كمبيوتر آخر عبر كابل تسلسلي.
ظهر BREAD بعد العديد من المحاولات الفاشلة لإجراء هندسة عكسية لنظام BIOS القديم. نظرًا لأن الغالبية العظمى من تحليل BIOS - إن لم يكن كله - يتم إجراؤه بشكل ثابت باستخدام أدوات التفكيك، فإن فهم BIOS يصبح صعبًا للغاية، حيث لا توجد طريقة لمعرفة قيمة السجلات أو الذاكرة في جزء معين من التعليمات البرمجية.
على الرغم من ذلك، يمكن لـ BREAD أيضًا تصحيح التعليمات البرمجية التعسفية في الوضع الحقيقي، مثل التعليمات البرمجية القابلة للتمهيد أو برامج DOS أيضًا.
عرض سريع:
تغيير اسم سلسلة وحدة المعالجة المركزية عبر BREAD
ينقسم مصحح الأخطاء هذا إلى جزأين: مصحح الأخطاء (مكتوب بالكامل في التجميع ويعمل على الأجهزة التي يتم تصحيح أخطائها) والجسر، المكتوب بلغة C ويعمل على Linux.
مصحح الأخطاء هو رمز قابل للحقن، مكتوب في الوضع الحقيقي 16 بت، ويمكن وضعه داخل BIOS ROM أو أي رمز آخر في الوضع الحقيقي. عند التنفيذ، يقوم بإعداد معالجات المقاطعة المناسبة، ويضع المعالج في وضع الخطوة الواحدة، وينتظر الأوامر على المنفذ التسلسلي.
الجسر، من ناحية أخرى، هو الرابط بين مصحح الأخطاء وGDB. يتصل الجسر بـ GDB عبر TCP ويعيد توجيه الطلبات/الاستجابات إلى مصحح الأخطاء من خلال المنفذ التسلسلي. الفكرة وراء الجسر هي إزالة تعقيد حزم GDB وإنشاء بروتوكول أبسط للتواصل مع الجهاز. بالإضافة إلى ذلك، يتيح البروتوكول الأبسط أن يكون حجم الكود النهائي أصغر، مما يسهل على مصحح الأخطاء أن يتم حقنه في بيئات مختلفة مختلفة.
كما هو مبين في الرسم البياني التالي:
+---------+ simple packets +----------+ GDB packets +---------+
| | --------------- > | | --------------- > | |
| dbg | | bridge | | gdb |
| ( real HW ) | <- -------------- | ( Linux ) | <- -------------- | ( Linux ) |
+---------+ serial +----------+ TCP +---------+
من خلال تنفيذ كعب قاعدة GDB، يتمتع BREAD بالعديد من الميزات الجاهزة. الأوامر التالية مدعومة:
الهندسة العكسية للثنائي الخام، مثل BIOS، في GDB تعني تلقائيًا عدم وجود رموزه الأصلية. ومع ذلك، مع تقدم عملية RE، يكتسب المستخدم/المبرمج/المتسلل فهمًا أفضل لأجزاء معينة من التعليمات البرمجية، وتسمح أدوات التحليل الثابتة مثل IDA وCutter وGhidra وغيرها بإضافة التعليقات التوضيحية والتعليقات وتعريفات الوظائف، وأكثر. تعمل هذه التحسينات على زيادة إنتاجية المستخدم بشكل كبير.
مع أخذ هذا بعين الاعتبار، يوجد نص بايثون مصاحب في المشروع يُسمى symbolify.py
. بالنظر إلى قائمة الرموز (تسمية العنوان)، فإنه يقوم بإنشاء ملف ELF صغير مع إضافة هذه الرموز. يمكن بعد ذلك تحميل ELF هذا في GDB لاحقًا واستخدامه لتبسيط عملية تصحيح الأخطاء بشكل كبير.
يمكن أن يتضمن ملف الرمز مسافات بيضاء وأسطرًا فارغة وتعليقات (#) وتعليقات على سطر العنوان. يمكن أن تكون العناوين بتنسيق عشري أو سداسي عشري، ويمكن أن تكون التسميات/الرموز (مفصولة بمسافة بيضاء واحدة أو أكثر) بالصيغة [a-z0-9_]+، كما في (يمكن العثور على مثال حقيقي في الرموز/ami_ipm41d3. رسالة قصيرة):
#
# This is a comment
#
0xdeadbeef my_symbol1
0x123 othersymbol # This function does xyz
# Example with decimal address
456 anotherone
على سبيل المثال، بالنظر إلى ملف الرمز المتوفر على الرموز/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 من البداية (كتلة التشغيل)، حيث يلزم وجود حد أدنى من الإعداد (مثل ذاكرة الوصول العشوائي) حتى يعمل BREAD بشكل صحيح. ومع ذلك، من الممكن إجراء "إعادة تشغيل دافئة" عن طريق ضبط CS:EIP على F000:FFF0
. في هذا السيناريو، يمكن متابعة تهيئة BIOS مرة أخرى، حيث تم بالفعل تحميل BREAD بشكل صحيح. يرجى ملاحظة أن "مسار التعليمات البرمجية" لتهيئة BIOS أثناء إعادة التشغيل الدافئ قد يكون مختلفًا عن إعادة التشغيل البارد وقد لا يكون تدفق التنفيذ هو نفسه تمامًا.
يتطلب البناء فقط GNU Make، ومترجم C (مثل GB، أو Clang، أو TCC)، وNASM، وجهاز Linux.
يحتوي مصحح الأخطاء على وضعين للتشغيل: الاستقصاء (الافتراضي) والقائم على المقاطعة:
يعد وضع الاستقصاء هو الأسلوب الأبسط وينبغي أن يعمل بشكل جيد في مجموعة متنوعة من البيئات. ومع ذلك، نظرًا لطبيعة الاستقصاء، هناك استخدام مرتفع لوحدة المعالجة المركزية:
$ git clone https://github.com/Theldus/BREAD.git
$ cd BREAD/
$ make
يعمل الوضع المعتمد على المقاطعة على تحسين استخدام وحدة المعالجة المركزية (CPU) من خلال استخدام مقاطعات UART لتلقي بيانات جديدة، بدلاً من الاستقصاء المستمر عنها. وينتج عن ذلك بقاء وحدة المعالجة المركزية في حالة "توقف" حتى تتلقى الأوامر من مصحح الأخطاء، وبالتالي منعها من استهلاك 100% من موارد وحدة المعالجة المركزية. ومع ذلك، نظرًا لعدم تمكين المقاطعات دائمًا، لم يتم تعيين هذا الوضع كخيار افتراضي:
$ git clone https://github.com/Theldus/BREAD.git
$ cd BREAD/
$ make UART_POLLING=no
يتطلب استخدام BREAD فقط كبلًا تسلسليًا (ونعم، تحتوي اللوحة الأم على رأس COM، تحقق من الدليل) وحقن الرمز في الموقع المناسب.
للحقن، يجب إجراء الحد الأدنى من التغييرات في dbg.asm (src الخاص بمصحح الأخطاء). يجب تغيير "ORG" الخاص بالرمز وكذلك كيفية إرجاع الرمز (ابحث عن " >> CHANGE_HERE <<
" في الكود الخاص بالأماكن التي تحتاج إلى تغيير).
باستخدام تراث AMI كمثال، حيث سيتم وضع وحدة مصحح الأخطاء في مكان شعار BIOS ( 0x108200
أو FFFF:8210
) وتم استبدال الإرشادات التالية في ذاكرة القراءة فقط باستدعاء بعيد للوحدة:
...
00017EF2 06 push es
00017EF3 1E push ds
00017EF4 07 pop es
00017EF5 8BD8 mov bx , ax - ┐ replaced by: call 0xFFFF : 0x8210 (dbg.bin)
00017EF7 B8024F mov ax , 0x4f02 - ┘
00017EFA CD10 int 0x10
00017EFC 07 pop es
00017EFD C3 ret
...
التصحيح التالي يكفي:
diff --git a/dbg.asm b/dbg.asm
index caedb70..88024d3 100644
--- a/dbg.asm
+++ b/dbg.asm
@@ -21,7 +21,7 @@
; SOFTWARE.
[BITS 16]
- [ORG 0x0000] ; >> CHANGE_HERE <<
+ [ORG 0x8210] ; >> CHANGE_HERE <<
%include "constants.inc"
@@ -140,8 +140,8 @@ _start:
; >> CHANGE_HERE <<
; Overwritten BIOS instructions below (if any)
- nop
- nop
+ mov ax, 0x4F02
+ int 0x10
nop
nop
من المهم ملاحظة أنه إذا قمت بتعديل بعض الإرشادات داخل ذاكرة القراءة فقط (ROM) الخاصة بك لاستدعاء رمز مصحح الأخطاء، فيجب استعادتها قبل العودة من مصحح الأخطاء.
سبب استبدال هاتين التعليماتتين هو أنه يتم تنفيذهما قبل أن يعرض BIOS الشعار على الشاشة مباشرةً، والذي أصبح الآن مصحح الأخطاء، مما يضمن بعض النقاط الأساسية:
قد يكون العثور على موقع جيد للاتصال بمصحح الأخطاء (حيث تمت تهيئة BIOS بالفعل بشكل كافٍ، ولكن ليس بعد فوات الأوان) أمرًا صعبًا، ولكنه ممكن.
بعد ذلك، يصبح dbg.bin
جاهزًا ليتم إدراجه في الموضع الصحيح في ذاكرة القراءة فقط (ROM).
يعد تصحيح أخطاء برامج DOS باستخدام BREAD أمرًا صعبًا بعض الشيء، ولكنه ممكن:
dbg.asm
بحيث يفهمه DOS كبرنامج DOS صالح:times
)int 0x20
)التصحيح التالي يعالج هذا:
diff --git a/dbg.asm b/dbg.asm
index caedb70..b042d35 100644
--- a/dbg.asm
+++ b/dbg.asm
@@ -21,7 +21,10 @@
; SOFTWARE.
[BITS 16]
- [ORG 0x0000] ; >> CHANGE_HERE <<
+ [ORG 0x100]
+
+ times 40*1024 db 0x90 ; keep some distance,
+ ; 40kB should be enough
%include "constants.inc"
@@ -140,7 +143,7 @@ _start:
; >> CHANGE_HERE <<
; Overwritten BIOS instructions below (if any)
- nop
+ int 0x20 ; DOS interrupt to exit process
nop
قم بإنشاء صورة مرنة FreeDOS (أو DOS) قابلة للتشغيل تحتوي على النواة والمحطة الطرفية فقط: KERNEL.SYS
و COMMAND.COM
. أضف أيضًا إلى هذه الصورة المرنة البرنامج المطلوب تصحيح أخطائه و DBG.COM
( dbg.bin
).
ويجب اتباع الخطوات التالية بعد إنشاء الصورة:
bridge
بالفعل (ارجع إلى القسم التالي للحصول على التعليمات).DBG.COM
.DBG.COM
بالمتابعة حتى تنتهي.من المهم ملاحظة أن DOS لا يمسح صورة العملية بعد خروجها. ونتيجة لذلك، يمكن تكوين مصحح الأخطاء مثل أي برنامج DOS آخر ويمكن تعيين نقاط التوقف المناسبة. تمتلئ بداية مصحح الأخطاء بـ NOPs، لذلك من المتوقع أن العملية الجديدة لن تحل محل ذاكرة مصحح الأخطاء، مما يسمح لها بمواصلة العمل حتى بعد أن يبدو أنها "اكتملت". يسمح هذا لـ BREAD بتصحيح أخطاء البرامج الأخرى، بما في ذلك DOS نفسه.
Bridge هو الرابط بين مصحح الأخطاء وGDB ويمكن استخدامه بطرق مختلفة، سواء على أجهزة حقيقية أو جهاز افتراضي.
معلماتها هي:
Usage: ./bridge [options]
Options:
-s Enable serial through socket, instead of device
-d <path> Replaces the default device path (/dev/ttyUSB0)
(does not work if -s is enabled)
-p <port> Serial port (as socket), default: 2345
-g <port> GDB port, default: 1234
-h This help
If no options are passed the default behavior is:
./bridge -d /dev/ttyUSB0 -g 1234
Minimal recommended usages:
./bridge -s (socket mode, serial on 2345 and GDB on 1234)
./bridge (device mode, serial on /dev/ttyUSB0 and GDB on 1234)
لاستخدامه على أجهزة حقيقية، ما عليك سوى استدعاءه بدون معلمات. اختياريًا، يمكنك تغيير مسار الجهاز باستخدام المعلمة -d
:
./bridge
أو ./bridge -d /path/to/device
)Single-stepped, you can now connect GDB!
ثم قم بتشغيل GDB: gdb
. للاستخدام في جهاز افتراضي، يتغير ترتيب التنفيذ قليلاً:
./bridge
أو ./bridge -d /path/to/device
)make bochs
أو make qemu
)Single-stepped, you can now connect GDB!
ثم قم بتشغيل GDB: gdb
.في كلتا الحالتين، تأكد من تشغيل GDB داخل المجلد الجذر BRIDGE، حيث توجد ملفات مساعدة في هذا المجلد لكي يعمل GDB بشكل صحيح في 16 بت.
BREAD مفتوح دائمًا للمجتمع ومستعد لقبول المساهمات، سواء فيما يتعلق بالمشكلات والوثائق والاختبار والميزات الجديدة وإصلاحات الأخطاء والأخطاء المطبعية وما إلى ذلك. مرحبًا بك على متن الطائرة.
BREAD مرخص بموجب ترخيص MIT. كتبه ديفيدسون فرانسيس و(نأمل) المساهمين الآخرين.
يتم تطبيق نقاط التوقف كنقاط توقف للأجهزة، وبالتالي يكون لها عدد محدود من نقاط التوقف المتاحة. في التنفيذ الحالي، هناك نقطة توقف نشطة واحدة فقط في كل مرة! ↩
يتم أيضًا دعم نقاط مراقبة الأجهزة (مثل نقاط التوقف) مرة واحدة فقط في كل مرة. ↩
يرجى ملاحظة أن سجلات تصحيح الأخطاء لا تعمل بشكل افتراضي على الأجهزة الافتراضية. بالنسبة لـ Bochs، يجب تجميعها باستخدام علامة --enable-x86-debugger=yes
. بالنسبة إلى Qemu، فإنه يحتاج إلى التشغيل مع تمكين KVM: --enable-kvm
( make qemu
يقوم بذلك بالفعل). ↩