
इंजेक्टेबल रीयल-मोड x86 डीबगर BIOS रिवर्स इंजीनियरिंग और सीरियल केबल के माध्यम से मनमाने रीयल-मोड कोड डीबगिंग के लिए, GDB एकीकरण और हार्डवेयर ब्रेकपॉइंट/वॉचपॉइंट समर्थन के साथ।
BREAD (BIOS Reverse Engineering & Advanced Debugger) एक 'इंजेक्टेबल' रियल-मोड x86 डीबगर है जो किसी अन्य पीसी से सीरियल केबल के माध्यम से वास्तविक हार्डवेयर पर मनमाने रियल-मोड कोड को डीबग कर सकता है।
BREAD लीगेसी BIOS को रिवर्स इंजीनियर करने के कई असफल प्रयासों से उभरा। यह देखते हुए कि अधिकांश - यदि सभी नहीं - BIOS विश्लेषण डिसअसेंबलर का उपयोग करके स्थिर रूप से किया जाता है, BIOS को समझना अत्यंत कठिन हो जाता है, क्योंकि किसी दिए गए कोड में रजिस्टरों या मेमोरी के मान जानने का कोई तरीका नहीं है।
इसके बावजूद, BREAD रियल-मोड में मनमाने कोड जैसे बूट करने योग्य कोड या DOS प्रोग्राम को भी डीबग कर सकता है।
त्वरित डेमो:
https://user-images.githubusercontent.com/8294550/217709970-9007a1e3-7352-470d-a22f-cbb5219d5547.mp4
BREAD के माध्यम से CPU स्ट्रिंग नाम बदलना
यह डीबगर दो भागों में विभाजित है: डीबगर (पूरी तरह से असेंबली में लिखा गया और डीबग किए जा रहे हार्डवेयर पर चल रहा है) और ब्रिज, जो C में लिखा गया है और Linux पर चल रहा है।
डीबगर इंजेक्टेबल कोड है, जो 16-बिट रियल-मोड में लिखा गया है, और इसे BIOS ROM या किसी अन्य रियल-मोड कोड में रखा जा सकता है। जब निष्पादित किया जाता है, तो यह उपयुक्त इंटरप्ट हैंडलर सेट करता है, प्रोसेसर को सिंगल-स्टेप मोड में डालता है, और सीरियल पोर्ट पर कमांड की प्रतीक्षा करता है।
दूसरी ओर, ब्रिज, डीबगर और GDB के बीच की कड़ी है। ब्रिज GDB के साथ TCP के माध्यम से संचार करता है और सीरियल पोर्ट के माध्यम से डीबगर को अनुरोध/प्रतिक्रिया अग्रेषित करता है। ब्रिज के पीछे का विचार GDB पैकेटों की जटिलता को दूर करना और मशीन के साथ संवाद करने के लिए एक सरल प्रोटोकॉल स्थापित करना है। इसके अलावा, सरल प्रोटोकॉल अंतिम कोड आकार को छोटा करने में सक्षम बनाता है, जिससे डीबगर को विभिन्न वातावरणों में इंजेक्ट करना आसान हो जाता है।
जैसा कि निम्नलिखित आरेख में दिखाया गया है:
+---------+ सरल पैकेट्स +----------+ GDB पैकेट्स +---------+
| |--------------->| |--------------->| |
| dbg | | bridge | | gdb |
|(real HW)|<---------------| (Linux) |<---------------| (Linux) |
+---------+ सीरियल +----------+ TCP +---------+
GDB स्टब को लागू करके, BREAD में कई विशेषताएं बॉक्स से बाहर आती हैं। निम्नलिखित कमांड समर्थित हैं:
GDB में एक कच्चे बाइनरी, जैसे BIOS, को रिवर्स इंजीनियर करने का अर्थ स्वचालित रूप से इसके मूल प्रतीक न होना है। हालांकि, जैसे-जैसे RE प्रक्रिया आगे बढ़ती है, उपयोगकर्ता/प्रोग्रामर/हैकर कोड के कुछ भागों की बेहतर समझ प्राप्त करता है, और IDA, Cutter, Ghidra जैसे स्थैतिक विश्लेषण उपकरण एनोटेशन, टिप्पणियां, फ़ंक्शन परिभाषाएं और अधिक जोड़ने की अनुमति देते हैं। ये संवर्द्धन उपयोगकर्ता की उत्पादकता को काफी बढ़ाते हैं।
इसे ध्यान में रखते हुए, प्रोजेक्ट में symbolify.py नामक एक सहायक Python स्क्रिप्ट है। प्रतीकों (पता लेबल) की एक सूची दी गई है, यह इन प्रतीकों के साथ एक न्यूनतम 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 कोड को शुरुआत (बूटब्लॉक) से डीबग करना संभव नहीं है, क्योंकि BREAD के सही ढंग से काम करने के लिए न्यूनतम सेटअप (जैसे RAM) आवश्यक है। हालांकि, CS:EIP को F000:FFF0 पर सेट करके "वार्म-रीबूट" करना संभव है। इस परिदृश्य में, BIOS आरंभीकरण को फिर से अनुसरण किया जा सकता है, क्योंकि BREAD पहले से ही ठीक से लोड है। कृपया ध्यान दें कि वार्म-रीबूट के दौरान BIOS आरंभीकरण का "कोड-पथ" कोल्ड-रीबूट से भिन्न हो सकता है और निष्पादन प्रवाह बिल्कुल समान नहीं हो सकता है।
निर्माण के लिए केवल GNU Make, एक C कंपाइलर (जैसे GCC, Clang, या TCC), NASM, और एक Linux मशीन की आवश्यकता होती है।
डीबगर के दो ऑपरेशन मोड हैं: पोलिंग (डिफ़ॉल्ट) और इंटरप्ट-आधारित:
पोलिंग मोड सबसे सरल दृष्टिकोण है और विभिन्न वातावरणों में अच्छी तरह से काम करना चाहिए। हालांकि, पोलिंग प्रकृति के कारण, उच्च CPU उपयोग होता है:
$ git clone https://github.com/Theldus/BREAD.git
$ cd BREAD/
$ make
इंटरप्ट-आधारित मोड लगातार पोलिंग करने के बजाय नया डेटा प्राप्त करने के लिए UART इंटरप्ट का उपयोग करके CPU उपयोग को अनुकूलित करता है। इसके परिणामस्वरूप CPU डीबगर से कमांड प्राप्त करने तक 'हॉल्ट' स्थिति में रहता है, और इस प्रकार, इसे CPU के 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) के स्थान पर रखा जाएगा और ROM में निम्नलिखित निर्देशों को मॉड्यूल के लिए एक फ़ार कॉल से बदल दिया गया है:
...
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 में सही स्थान पर डालने के लिए तैयार है।
BREAD के साथ DOS प्रोग्राम को डीबग करना थोड़ा मुश्किल है, लेकिन संभव है:
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 स्वयं भी शामिल है, को डीबग करने की अनुमति देता है।
ब्रिज डीबगर और 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 लाइसेंस के तहत लाइसेंस प्राप्त है। Davidson Francis द्वारा लिखित और (उम्मीद है) अन्य योगदानकर्ताओं द्वारा।
ब्रेकपॉइंट हार्डवेयर ब्रेकपॉइंट के रूप में लागू किए जाते हैं और इसलिए उपलब्ध ब्रेकपॉइंट की सीमित संख्या होती है। वर्तमान कार्यान्वयन में, एक समय में केवल 1 सक्रिय ब्रेकपॉइंट! ↩
हार्डवेयर वॉचपॉइंट (ब्रेकपॉइंट की तरह) भी एक समय में केवल एक ही समर्थित हैं। ↩
कृपया ध्यान दें कि डीबग रजिस्टर डिफ़ॉल्ट रूप से VM पर काम नहीं करते हैं। bochs के लिए, इसे --enable-x86-debugger=yes फ़्लैग के साथ संकलित करने की आवश्यकता है। Qemu के लिए, इसे KVM सक्षम के साथ चलाने की आवश्यकता है: --enable-kvm (make qemu पहले से ही ऐसा करता है)। ↩