gdb (GNU Debugger)أداة gdb هي المصحح القياسي والأقوى (Standard Debugger) في أنظمة Linux/Unix. إذا كانت الأدوات السابقة مثل strace و ltrace تسمح لك بمراقبة البرنامج من الخارج فقط عبر تتبع النداءات، فإن gdb يمنحك سلطة مطلقة للتحكم في البرنامج من الداخل.
باستخدام gdb يمكنك إيقاف البرنامج عند أي سطر كود أو تعليمة تجميع (Assembly Instruction)، فحص محتويات الذاكرة العشوائية (RAM)، قراءة وتعديل قيم مسجلات المعالج (CPU Registers)، بل وتغيير مسار تنفيذ البرنامج بالكامل أثناء تشغيله لحقن ثغرة أو تخطي نظام حماية. هذه العملية تسمى Dynamic Debugging (التصحيح الديناميكي المتقدم).
مثل أدوات التتبع الديناميكي، يعتمد gdb بشكل مكثف على نداء النظام ptrace. ولكن آلية إيقاف البرنامج عنده ذكية جداً:
عندما تطلب من gdb وضع نقطة توقف (Breakpoint) عند عنوان معين في الذاكرة، فإنه يقوم سرياً باستبدال البايت الأصلي لكود الآلة عند ذلك العنوان ببايت خاص يسمى Breakpoint Instruction (في معالجات x86/x64 يكون هذا البايت هو 0xCC المقابل لأمر التوقف INT 3).
عندما يصل المعالج (CPU) أثناء تنفيذ البرنامج إلى هذا البايت 0xCC، فإنه يطلق استثناءً (Exception) وينقل التحكم فوراً إلى gdb الذي يعيد البايت الأصلي مؤقتاً ليعرض لك حالة البرنامج، وينتظر منك الأمر التالي.
لأننا التزمنا بعدم استخدام الجداول، سأعرض لك أهم أوامر gdb مقسمة حسب الوظيفة كقائمة منسقة ومباشرة:
gdb ./vulnerable_binary: لفتح الملف التنفيذي داخل البيئة وتحضيره.run أو اختصاراً r: لبدء تشغيل البرنامج من البداية حتى ينتهي أو يصطدم بنقطة توقف.run AAAABBBB: لتشغيل البرنامج وتمرير مدخلات له كمعاملات سطر أوامر (Command Line Arguments).break main أو b main: لوضع نقطة توقف عند بداية دالة main.break *0x00401122 أو b *0x00401122: لوضع نقطة توقف عند عنوان ذاكرة محدد بالهكس. علامة النجمة ضرورية جداً لإخبار gdb أنك تمرر عنواناً مباشراً وليس اسم دالة.info breakpoints أو i b: لعرض جميع نقاط التوقف التي وضعتها وأرقامها المعرفية.delete 1 أو d 1: لحذف نقطة التوقف رقم 1.next أو n: لتنفيذ السطر الحالي بلغة عالية المستوى (مثل C) والانتقال للسطر التالي. إذا كان السطر يحتوي على استدعاء دالة، فإنه ينفذها بالكامل دون الدخول في تفاصيلها (Step Over).step أو s: مثل أمر next تماماً، ولكنه إذا وجد استدعاء دالة فإنه يدخل في تفاصيلها سطرًا بسطر (Step Into).nexti أو ni: لتنفيذ تعليمة لغة تجميع (Assembly Instruction) واحدة فقط والانتقال للتالية (تخطي الدوال). هذا الأمر هو الأهم عندما لا تملك الكود المصدري.stepi أو si: لتنفيذ تعليمة لغة تجميع واحدة والدخول داخل أي دالة مستدعاة.continue أو c: لاستئناف تشغيل البرنامج بشكل طبيعي بأقصى سرعة حتى يصل لنقطة التوقف التالية أو ينتهي البرنامج.info registers أو i r: لعرض القيم الحالية المخزنة داخل مسجلات المعالج (مثل RAX, RBP, RSP, RIP).print $rax أو p $rax: لطباعة قيمة مسجل معين فقط (هنا مسجل RAX) بالنظامين العشري والستة عشري.x/<format> <address> (اختصار لـ Examine): الأداة الجوهرية لفحص محتويات الذاكرة العشوائية.
x/24wx $rsp تعني: افحص الذاكرة بدءاً من عنوان مسجل الـ Stack Pointer واطبع 24 وحدة بيانات، على أن تكون كل وحدة بحجم الكلمة (Word أي 4 بايت ويرمز لها بـ w) ومعروضة بتنسيق الستة عشري (x).x/s 0x402000: لعرض البيانات الموجودة عند هذا العنوان كتصنيف نصي (String ويرمز له بـ s).عندما تحاول استغلال ثغرة في الذاكرة مثل Buffer Overflow، فإنك تستخدم gdb لـ:
RIP (أو EIP في أنظمة 32-bit). تقوم بحقن نمط فريد (Unique Pattern) ثم تفحص قيمة المسجل عبر p $rip لتحديد مكان السقوط والانهيار.x/100gx $rsp.