تحليل ديناميكي لبرنامجك الخاص: إزالة الخطأ، والبصمات، وتأكيد المطالبات الدقيقة
Type: Build
Languages: Python, TypeScript, C, arm64 assembly, lldb command language
Prerequisites: Phase 13 · 33 (disassembly and control flow), Phase 13 · 35 (evidence, confidence, and unknowns)
Time: ~130 minutes
أهداف التعلم
- شرح ما يحتاجه جهاز إزالة الحذاء من نظام التشغيل على لينكس (
ptrace) و على macOS (ميكش مهمة و منفذ استثناء) ، و لماذا لا يمكن لشجرة مربع الرمال إطلاق إصلاحات. - حدد نقاط وقف البرمجيات باسم وعنوان قائمة، حدد نقطة مراقبة في الأجهزة، وقراءة الحدود التي يفرضها المعالج على كليهما.
- قم بتشغيل lldb في وضع اللحظة من Python مع خطة توقف تسجل السجلات والذاكرة والمتغيرات الإطارية في كل توقف، و تحليل النسخة مرة أخرى في خط زمني.
- تحقق من المطالبات الدقيقة من الدروس 32، 33، 34، و 41 ضد ذلك جدول زمني: سجلات الحجج، تعويضات الحقل البني، التي حافة فرع مشروط أخذ، والتي المكالمات حدثت.
- إدارة نفس الخطة ضد
-O2بناء، قراءة نقاط الانقطاع التي تحل ولا تضرب أبدا، وقول ما يفعل ولا يثبت. - حفظ وتحميل ملف جوهر، وتسمية ما يترك جوهر على شكل كومة.
- خريطة مجموعة الأوامر gdb و lldb، ووضع
strace،ltrace،bpftrace،dtraceوrrعلى النظام التشغيلي الصحيح مع نسخهم و تراخيصهم. - أوضح الحقائق التي تمتلك رمز macOS التي تسمح لـ lldb بتحريف ثنائي قمت ببناءه بنفسك ، والشروط التي ترفض تحتها.
المشكلة
الدرس 41 قراءة struct accountمن DWARF وقال balanceيجلس في التنافس 4. دراسة 33 بنيت الرسم البياني للسيطرة-جريان منclassifyوقال ان الفرع الأول يذهب الىreturn -1الدروس 32 قالت الحجج الأولى اثنين تصل فيx0وx1كل هذه الجملة كانت مشتقة من البايتات.
ثم يقرأ العميل نفس البرنامج ويقول "المكالمات الرئيسية"bump،classifyوsum_balances" الجدول الرمزي يردف الثلاثة، لذلك الجملة تبدو آمنة. تقوم ببناء البرنامج في-O2، تحديد نقطة وقف على كل وظيفة، وتشغيلها. العملية طباعة أربعة خطوطها والمخارج. كل نقطة وقف تقاريرhit count = 0. . .
الوكيل لم يكن مخطئاً بشأن المصدر كان مخطئاً بشأن البناء الذي سئل عنه قام المُجمّع بتحديد كلّ المكالمات الثلاثة و قام بتثبيتهاsum_balances(rows, 3)الطريقة الوحيدة لمعرفة ذلك لهذا الثنائي على هذه الآلة هي تشغيله تحت جهاز تحليل الحذاء والنظر.
هذه الدروس تبني حلقة من البحثaccount.cيقرأ القائمة، ويحدد جلسة واحدة من أجهزة الـ Ildb، ويقوم بتشغيلها، ويحدد كل مطالبة ثابتة على ما تظهر فيه التوقف.confirmedيعني توقف أظهر أن الادعاء صحيح لهذا الجريrefutedيعني توقف أظهرت أنها خاطئةnot-observedيعني أنه لا يوجد توقف يمكن اختباره، و السجل يقول لماذا.observedسجل مع السلطةcontrolled-replay. . .
جهاز إصلاح الحذاء هو ثلاثة خدمات النظام و جدول
يقوم جهاز إزالة الحذاء بأربع أشياء مع عملية لا يملك فيها رمزها. إنه يوقف العملية ، ويقرأ ويكتب سجلاته ، ويقرأ ويكتب ذاكرةاته ، ويُخبر عندما يتوقف العملية مرة أخرى. يقدم النجم كل هذه الأشياء كخدمة ، وتختلف الخدمات حسب النظام التشغيلي.
على لينكس الخدمة هيptrace(2). الصفحة اليدوية تقول أنها "توفير وسيلة يمكن بها لعملية واحدة (المعقب) مراقبة وتحكم تنفيذ عملية أخرى". PTRACE_TRACEMEيسمح للطفل بإعلان أن والده سيتعقبهاPTRACE_ATTACHيتخذ عملية غير مرتبطةPTRACE_PEEKDATAيقرأ "كلمة في العنوان" في ذكرى المتعثرPTRACE_GETREGSيقرأ السجلات وPTRACE_SINGLESTEPيدير تعليمات واحدة. وحدات وحدات ياما ملحقة: /proc/sys/kernel/yama/ptrace_scopeعند افتراضها 1 يعني "يجب أن يكون العملية المستهدفة نساً من المتصل"، 2 يعني أن إدارًا فقط يمكنه إضافة، و 3 يعني أن لا أحد يمكنه.
على macOS ptraceهو واجهة أصغر. قائمة أبل اليدوية PT_TRACE_ME،PT_ATTACHEXC،PT_STEP،PT_CONTINUEوPT_KILL، ويقول ذلك معPT_ATTACHEXC"إشارات الطفل تذهب إلى الوالد كإستثناءات من ماخ"ptraceعلى الإطلاق.debugserverمكالماتtask_for_pid()للحصول على ميناء مهم ماخ للعملية، ثم task_set_exception_ports()حتى تصل نقاط الانقطاع كرسائل استثناءtaskgatedيقرر من يمكن أن يتصلtask_for_pid. . .
يمكنك أن ترى تكلفة هذا التصميم في النص الذي يحفظه هذا الدروس. قذيفة الجلسة تعمل تحت صندوق رمال حزام المقعد. نفس الخطة التي تنتج 11 توقف خارج صندوق الرمل تنتج هذا داخلها:
(lldb) run
error: process exited with status -1 (attach failed ((os/kern) invalid address))لقد تم إنشاء العملية ثم لم يتم تبنيها، الخطأ يتعلق بحقوق المتحلّل، ولا يُقال شيء عن البرنامج، ويقوم الحزام بتسجيلها كفشل إطلاق، مع صفر توقف ولا حكم.
الجدول هو الجدول نقطة الانقطاع. يقوم جهاز التحليل بتذكر أي بايت التعليمات الذي استبدله في أي عنوان، حتى يتمكن من إعادة القائمة الأصلية عند الاستمرار، ويعد الضربات لكل موقع. breakpoint listيطبع الطاولة، والحزام يقرأها في نهاية كل جولة.
نقاط الانفصال: كلمة واحدة أو كلمة واحدة استبدلت
نقطة وقف البرمجيات هي تعليمات يعبث عليها المعالج، مكتوبة على أول بايت من التعليمات التي تريد التوقف عليها. على x86-64 هذا هو INT3، رمز الاختيارCC، طولها بعيد واحد، والتي تصف دليل إينتل بأنه "مقصود على استدعاء عامل استثناءات التحذير". على arm64 هو BRK #imm16، إرشاد أربعة بايتات "تسجل الاستثناء في ESR_ELx، باستخدام قيمة EC 0x3C".BRKالتعليم نفسه، ولهذا السبب تقرير توقف عن عنوان التعليم المبدل وليس بعد ذلك.
يحدد lldb نقاط الانقطاع باسم، أو باستخدام الملفات والخط أو العنوان.
(lldb) breakpoint set -n bump
Breakpoint 1: where = account-O0`bump + 12 at account.c:13:19, address = 0x00000001000004bcbump + 12هو التعليم الرابع، والثلاثة التي قبلها هيsub sp, sp, #0x10،str x0, [sp, #0x8]وstr w1, [sp, #0x4]، إعداد الإطار الذي يعود جدول DWARF للخط الافتتاحي للعمل. في التوقف، x0وx1لا يزال يحتفظ بالجدل، لأن المبدء فقط نسخها.
عن طريق العنوان، يحتاج lldb إلى معرفة ما هي الوحدة التي ينتمي العنوان.breakpoint set -a 0x100000504قبل وجود العملية هو عنوان الحملة بدون قسم خلفه، وفي الاختبار الأول ظلunresolvedخلال الجولة بأكملها معhit count = 0. الشكل الذي يستخدمه الحزام يسمي الوحدة:
(lldb) breakpoint set -a 0x100000504 -s account-O0
Breakpoint 4: address = 0x0000000100000504مع-sالرقم هو عنوان ملف داخل account-O0, إيلدبي يضيف شرائح الحمل ، وبعد الجري يقرأ الموقع resolved, hit count = 2. قائمة الدروس 33 تعطي عناوين الملفات، لذلك هذا هو النموذج الذي يربط قائمة ثابتة إلى توقف ديناميكي.
فك الشفرة باليد
أول فرع مشروط فيclassifyفي-O0هو الكلمة36f800a8في0x1000004f0، الذيllvm-objdumpبصمات مثلtbz w8, #0x1f, 0x100000504. فك رموزها من رموز Arm A64TBZ. . .
- البيت 31 هو
b5، الجزء العالي من عدد البيت. هو 0. - البط 30 إلى 25
011011، مجموعة اختبار وفرع. بيت 24 هو 0 ، الذي يختارTBZو ليسTBNZ. . . - البط 23 إلى 19 هي
b40، الخمسة أجزاء من عدد البيت:11111= 31 . معb5= 0 البيت المتبكر هو 31، البيت الإشارة من قيمة 32 بت. - البط 18 إلى 5
imm14:00000000000101= 5 . تعويض الفرع هوimm14 * 4= 20 =0x14، لذا الهدف هو0x1000004f0 + 0x14 = 0x100000504. . . - البيت 4 إلى 0
Rt:01000= 8، لذلك السجل هوw8. . .
w8تم تحميل تعليمات واحدة في وقت سابق من قبل ldr w8, [x8, #0x4]، وهوa->balanceفي التعويض 4. TBZفروع عندما تكون البيت صفر. يتم أخذ الفروع عندما تكون البيت واضحة، وهو balance >= 0. كتب المصدرif (a->balance < 0) return -1;، لذلك قام المُجمّع بإعكس الحالة: الحافة التي تم أخذها تخطي الحافةreturn -1و التدفق في0x1000004f4لذلك، فإن الادعاء الدولي الذي يحمل الحزام هو دعاء الحافة مع تعبير:a->balance >= 0إن لم يقعوا في المرحلة الثانية، فان المصعد للقسم الثاني يعمل بنفس الطريقةsubs w8, w8, #0xc8الحساباتbalance - 200وb.le 0x100000524يتم أخذها عندما يكون ذلك على أقصى حد صفر، وهو balance <= 200. . .
نقاط المراقبة هي أجهزة و قلة
نقطة مراقبة توقف العملية عند الوصول إلى ذاكرة في عنوان. لا يمكن أن تكون تعليمات استبدال ، لأن أي تعليمات في أي مكان قد تقوم بالوصول. وبالتالي المحالون تحمل مجموعة صغيرة من مقارنات العناوين. على x86-64 هي سجلات التحليل:DR0من خلالDR3يحمل "العناوين التي تصل إلى 4 نقاط وقف" وDR7يسمح لكل واحد ويحدد شرطه (إجراء، كتابة، أو قراءة-كتابة) وطولها (1, 2, 4 أو 8 بايت). على arm64 الأزواج هي DBGWVR<n>_EL1، الذي "يحتوي على العنوان الافتراضي للبيانات المستخدمة لتطابق نقاط المراقبة" ، و DBGWCR<n>_EL1ويقول دليل الذراع "إن تنفيذ يمكن أن يتضمن بين 2-64 نقطة مراقبة".
أبلغت الـ "إل بي دي" الرقم الذي تم إعطائه:
(lldb) watchpoint set variable a->balance
Watchpoint created: Watchpoint 1: addr = 0x16fdfe00c size = 4 state = enabled type = m
(lldb) watchpoint list
Number of supported hardware watchpoints: 4أربعة على هذه الآلة، العنوان المراقب0x16fdfe00cهوx0 + 4: مؤشر البنية في bumpتوقف + تعويض DWARF من balance. .size = 4هل هوint. .type = mعندما تنتهي الفتحات ، يرفض lldb نقطة المراقبة التالية ، ولأن منطقة غير متناحقة أو غير ذات قوة من اثنين يمكن أن تأخذ فتحتين ، فإن عدد نقاط المراقبة التي يمكنك تعيينها يمكن أن يكون أقل من أربعة.
تقرير نقاط مراقبة البيانات بعد الوصول. تقول دليل Intel "تكتبت تعليمات كتب الذاكرة تغطي البيانات الأصلية قبل أن يتم إنشاء استثناء التحليل الذي يولد من نقطة وقف البيانات" ، ويحافظ lldb على القيمة القديمة في ذاكرة الاحتفاظ الخاصة به للطباعة كل من:
Watchpoint 1 hit:
old value: 250
new value: 255
* thread #1, queue = 'com.apple.main-thread', stop reason = watchpoint 1
frame #0: 0x00000001000004d0 account-O0`bump(a=0x000000016fdfe008, by=5) at account.c:14:12توقفنا في0x1000004d0، التعليمات بعدstr w8, [x9, #0x4]في0x1000004ccهناك شيء واحد يستحق المعرفة قبل أن تجري تحليل الناتج مباشرة بعدwatchpoint set, بصمات الـ " إيل دي بي "Watchpoint 1 hit:مع قيمة قديمة فارغة وnew value: 250هذه القراءة الأولى، والحبل يتجاهل ضربة دون قيمة قديمة.
خطوات، وقف الركاب، وضع المجموعة
اللغة القائمة لـ "إل بي دي" هي<noun> <verb> [-options] [arguments]. الفعل المتحركين هيthread step-in(s()thread step-over(n()thread step-inst(si()thread step-inst-over(ni() وthread step-out(finish)thread until 12يذهب إلى خطthread return <expr>يعود مبكرا مع قيمة. الحزام لا يتحرك. إنه يتوقف عند العناوين المخطط لها ويسمح لقطعة توقف القيام بالقراءة:
target stop-hook add -o "register read x0 x1 pc" -o "memory read -s4 -fx -c24 $x0" -o "frame variable -P 1"-oيمكن تكرارها، والإرشادات تعمل في ترتيب في كل محطة.register readأسماء السجلين للجدل و العداد البرنامج. memory read -s4 -fx -c24 $x0طبع 24 كلمة أربعة بايت في hex من عنوان في x0، وهو 96 بايت، ثلاثة struct accountالصفوفframe variable -P 1طبع الحجج والمحليين ويشيد المؤشرات على مستوى واحد، لذلك aيظهر خمس مجالاتها
وضع الحزمة هو lldb -b- كتيب الكتاب يقول--batchيدير الأوامر من-s،-S،-oو-Oثم يتوقف، وإذا توقف أمر "بسبب إشارة أو حادث، سوف يعود إصلاح الحذاء إلى المشاركة التفاعلية".-s. واحد من الأمور:settings set interpreter.stop-command-source-on-error falseبدونها، أولcontinueبعد خروج العملية تنتهي الملف كله، وbreakpoint listلا يُجري أبداً، مع ذلك، بصمات الـ "إل دي بي"error: Process must be launched.و يستمر
هناك حقائق اثنتان حول شكل الخروج تحدد كيف يعمل المصفح. أولاً، يتم طباعة خروج الرمز بينما يدير الرمز، والتي Process N stoppedحجر معframe #0يتم طباعة الخط بعد ذلك. لذلك يقوم المصفح بتجميع السجل والذاكرة والخطوط المتغيرة إلى جزء معلق ويربط الجزء إلى الجزء التاليframe #0خط يراه، قطعة من برنامج العداد فيdyldفي_dyld_startهو توقف الدخول قبل تشغيل البرنامج، و لا frame #0ثانياً، نقطة انقطاع مع--auto-continueلا يبلغ عن توقفها على الإطلاق ، ولا يعمل لها كوكب توقف. استخدم الإصدار الأول من هذا الحزام التواصل الآلي ولم يسجل أي شيء.continueأوامر بدلاً من ذلك
الخطة تأتي من القائمة
Plan.commands()يُبني ملف الأوامر من llvm-objdump -dنص الثنائي سوف تعمل.account-O0:
breakpoint set -n bump
breakpoint set -n classify
breakpoint set -n sum_balances
breakpoint set -a 0x100000504 -s account-O0
breakpoint set -a 0x1000004f4 -s account-O0
breakpoint set -a 0x100000524 -s account-O0
breakpoint set -a 0x100000514 -s account-O0
target stop-hook add -o "register read x0 x1 pc" -o "memory read -s4 -fx -c24 $x0" -o "frame variable -P 1"
run
watchpoint set variable a->balance
watchpoint list
continue
...
breakpoint list
watchpoint list -vنقاط الانقسام الثلاثة اسم هي إدخالات الوظيفة. نقاط الانقسام الأربعة عنوان هي الهدف والانهيار من كل فرع مشروط الذي Listing.conditional_branches()يجد فيclassify: tbzفي0x1000004f0وb.leفي0x100000510. نقطة مراقبة في المحطة الأولى، والتي تتوقع الخطة أن تكونbump- 16continueويتبع الأوامر أكثر من أي حاجة تشغيل، والبقايا تفشل بلا ضرر بعد الخروج. الميزانية ليست معرفة حول البرنامج. انها السقف.
يتم بناء المطالبات من نفس المدخلات من قبل claims_for():
| Id | Kind | Claim | Source |
|---|---|---|---|
arg-registers | argument-register | at every entry the first arguments arrive in x0 and x1 | lesson 32 and AAPCS64 |
field-offsets | field-offset | struct account is 32 bytes with kind at 0, balance at 4, code at 8, rate at 16, label at 24 | lesson 41 and the DWARF dump |
branch-1 | branch-taken | tbz at 0x1000004f0 goes to 0x100000504 when a->balance >= 0, otherwise to 0x1000004f4 | lesson 33 edges and the lesson 34 lift |
branch-2 | branch-taken | b.le at 0x100000510 goes to 0x100000524 when a->balance <= 200, otherwise to 0x100000514 | lesson 33 edges and the lesson 34 lift |
calls | call-edge | main calls bump once, classify three times, sum_balances once | source text and nm |
store-once | store | bump writes a->balance once and the new value is the old value plus by | lesson 34 pseudocode |
stride | struct-stride | list[i].balance sits at list + 32*i + 4 | lesson 41 and the psABI aggregate rule |
output | behaviour | the program prints class 0 1 -1 and sum 315 | source arithmetic |
تعويضات المجال تأتي منstruct_layout()، والتي تقرأDW_TAG_structure_typeمعDW_AT_byte_size (0x20)والخمسةDW_TAG_memberالإدخالات معهم DW_AT_data_member_locationالخطة لا تكتب الرقم 4
الركض عند -0: 11 توقف
أول موقف هوbumpنقطة الانقطاع، لقد طبعت هذه قبل حظر التوقف:
x0 = 0x000000016fdfe008
x1 = 0x0000000000000005
pc = 0x00000001000004bc account-O0`bump + 12 at account.c:13:19
0x16fdfe008: 0x00000073 0x000000fa 0x00000009 0x00000000
0x16fdfe018: 0x00000000 0x3ff40000 0x00000719 0x00000001
0x16fdfe028: 0x0000006c 0xffffffd8 0x00000003 0x00000000
0x16fdfe038: 0x00000000 0x40000000 0x00000721 0x00000001
(account *) a = 0x000000016fdfe008 {
kind = 's'
balance = 250
code = 9
rate = 1.25
label = 0x0000000100000719 "savings"
}
(int) by = 5اقرأ ثمانية كلمات في الصف الأول ضد تعويضات DWARF. كلمة 0 في تعويض 0 هي 0x73، وهو's'،kindبايت، تليها ثلاثة بايتات التشغيل. كلمة 1 في التداول 4 هو 0xfa= 250، balanceكلمة 2 في التداول 8 هي0x9،code، أshortفي النصف السفلي مع اثنين من البايتات التدليك فوقها. الكلمة 3 في التدليك 12 هو التدليك قبل 8-بايت الموافقة doubleالكلمات 4 و 5 في المقاطعة 16 هي0x3ff4000000000000، وهو رمز IEEE-754 من 1.25. الكلمات 6 و 7 في التداول 24 هي 0x100000719،labelالمؤشر إلى__TEXTالكلمات الثمانية التالية تبدأ بـ0x6c= 'l'و0xffffffd8= -40: الصف الثالث، loanبعد 32 بايت كل رقم يوافقframe variable، لذا_check_layout()السجلاتfield-offsetsكـconfirmedفي هذه المحطة، ويقوم بذلك مرة أخرى في كل واحدة من الحادية عشر محطة حيثx0يحتوي على مؤشر هيكلي
x0متساويةaوx1متساويةby، لذاarg-registersيتم تأكيد هذا الإدخال. يُقارن الحزمة فقط الحجج المعلنة.classifyتوقفx1لا يزال يقرأ0x5، بقية منbumpوclassifyإذا كان هناك حجة واحدة، فلا شيء يقارن معه. السجل الذي لا يوجد فيه حجة هو الضجيج، والدليل 32 لا يقول شيئا عنه.
المحطة الثانية هي نقطة المراقبة القديمة 250، القيمة الجديدة 255،byضربة واحدة في كل جولة، لذلكstore-onceثم تأتي ثلاث تنشيطات منclassify، كل منها مع توقف دخول ووقف واحد أو اثنين من الحافة:
| Stop | Address | Stop reason | What it tests |
|---|---|---|---|
| 2 | 0x1000004e8 | breakpoint 2.1 | entry, a->balance = 100, x0 = a |
| 3 | 0x100000504 | breakpoint 4.1 | tbz taken, 100 >= 0, branch-1 confirmed |
| 4 | 0x100000524 | breakpoint 6.1 | b.le taken, 100 <= 200, branch-2 confirmed |
| 5 | 0x1000004e8 | breakpoint 2.1 | entry, a->balance = 255 |
| 6 | 0x100000504 | breakpoint 4.1 | tbz taken, 255 >= 0 |
| 7 | 0x100000514 | breakpoint 7.1 | b.le fell through, 255 > 200 |
| 8 | 0x1000004e8 | breakpoint 2.1 | entry, a->balance = -40 |
| 9 | 0x1000004f4 | breakpoint 5.1 | tbz fell through, -40 < 0 |
| 10 | 0x100000544 | breakpoint 3.1 | sum_balances entry, x1 = 3 |
_check_branches()مجموعة توقف في تنشيطات، يقرأ التوازن عند الدخول، وكل توقف الحافة يحسب الخلفي الاضافية تنبؤات الموقع. خمسة توقف الحافة، خمسة الاتفاقيات: branch-1تأكدت ثلاث مراتbranch-2مرتين. توقف 5 يظهر التوازن ك255, وهو القيمة التي رأت نقطة المراقبة مكتوبة. الجري هو برنامج واحد والدليل يربط.
في المحطة 10 قرأ الرف 24 كلمة منlist. ._check_stride()يأخذ كلمة 1، كلمة 9، و كلمة 17، والتي هي تعويضات 4، 36، و 68: 100، 255، و -40.classifyرأى في نفس العنوان، يجمعهم إلى 315، ويقارن المجموع مع sum 315خط البرنامج مطبوع. ثم العملية تخرج مع حالة 0 و breakpoint listيظهر عدد ضربات من 1، 3، 1 للأسمين و 2، 1، 1 للأحواض الأربعة. جميع المطالبات الثمانية تنتهيconfirmed، مع 25 سجل خلفهم، 23 منهم مقيد على وقف و 2 إلى المنتهية.
الركض عند -O2: توقف صفر
نفس المصدر في-O2هو برنامج مختلفllvm-objdump -dمنmainلديه أربعةblالتعليمات، كل printfبدلاً من المكالمات التي تلقاهاmov w8, #-0x1،mov w8, #0x1، صفر من خلالxzrوmov w8, #0x13bو هو 315 ، المحاسب حاسب كل نتيجة في وقت تحليل.nmقائمة مستمرة_bump،_classifyو_sum_balances، لأنها وظائف خارجية والنسخ خارج الخط يجب أن تكون موجودة لجميع الأشياء الأخرى للدعوة.DW_TAG_subprogramالإدخالات لكل الأربعة وظائف والصفر DW_TAG_inlined_subroutineالإدخالات، حتى معلومات التحليل لا تسجل الإدخال.
خطة هذا البناء لديها ثلاثة نقاط تغيير الاسم و لا نقطة تغيير الحافة.classifyفي-O2هو ستة تعليماتldr،cmp،cset،cmp،csinv،ret، بدون فرع مشروط فيه.-O0ليس لدى القائمة عنوان في هذا المجموعة الثنائية، ولا يحملها الحزام. المطالبة حول الحافة تنتمي إلى البناء الذي يحتوي على القائمة على الحافة.
السباق:
(lldb) breakpoint set -n bump
Breakpoint 1: where = account-O2`bump at account.c:13:16, address = 0x0000000100000460
(lldb) run
sizeof 32 align 8
offsets 0 4 8 16 24
class 0 1 -1
sum 315
Process 48444 exited with status = 0 (0x00000000)
(lldb) watchpoint set variable a->balance
error: Command requires a process which is currently stopped.
(lldb) breakpoint list
1: name = 'bump', locations = 1, resolved = 1, hit count = 0كل نقطة انقطاع حللت العنوان موجود، والرمز موجود، وكتب lldbBRKعدد القتلى هو صفر لأن لا يوجد دليل لم يصل إلى العنوان
| Claim | Verdict | Record |
|---|---|---|
calls | refuted | after a full run with exit status 0, entry stops bump 0, classify 0, sum_balances 0 |
output | confirmed | the four lines and exit status 0 |
arg-registers | not-observed | no stop tested it, hit counts all 0 |
field-offsets | not-observed | no stop tested it |
store-once | not-observed | no stop in bump, so the watchpoint was never set |
stride | not-observed | no stop in sum_balances |
كن دقيقاً حول ما يقوله الرد على ذلكmainيطلق على الوظائف الثلاثة في هذا البرنامج. لهذا الإعداد و لهذا الإطار لا يفعل، والحزام تحقق أن الإطار قد اكتمل، لأن عدد ضربات من الصفر قبل الخروج يعني فقط أن الإطار لم يصل إلى المكالمة بعد. لم يقل الادعاء أن الوظائف هي رمز ميت، ولا يقول الركض أيضا: يتم تصديرها ويمكن لشيء آخر أن يطلق عليها. الادعاءات الأربعة الأخرى لا تؤكد ولا تُردد. لا يزال الهيكلbalanceفي المكافأة 4 في هذا البناء، وال -O2يقول DWARF ذلك، ولكن لا توقف في هذا الركض قراءة الذاكرة لإظهار ذلك. not-observedو الدروس 35 تبقي المجهول مفتوحاً حتى يلاحظها الجري
النفايات الأساسية
الملف الأساسي هو ذاكرة العملية والسجلات المكتوبة على القرص، حتى يمكن لمعالج الحذاء القراءة برنامج توقف بعد أن يذهب البرنامج.core(5)يقول "عدد صغير من الإشارات التي تسبب انتهاء غير طبيعي لعملية أيضا تسبب تسجيل حالة العملية في النواة أن تكتب على القرص"/cores/core.pid"لا بد من أن العملية التي انتهت كانت لديها إذن كتابة في السجل، والسجل موجود"sysctlتقارير kern.coredump: 1وkern.corefile: /cores/core.%P،/coresموجود مع وضع drwxrwxr-t root admin، والقذيفةulimit -c0 هو لا يكتب أي جوهر النواة حتى ترفع الحد مع ulimit -c unlimitedأو على مستوى النظام مع launchctl limit core unlimited. . .
يكتب الـ "إيلدبي" "البرنامج" بناءً على الطلب دون وقوع حادثbumpنقطة الانقطاع:
(lldb) process save-core --style stack account-O0-stack.core
Saving 49152 bytes of data for memory region at 0x16fdf4000
Modified-memory or stack-memory only corefile created. This corefile may
not show library/framework/app binaries on a different system, or when
those binaries have been updated/modified. Copies are not included
in this corefile. Use --style full to include all process memory.الملف كان 53248 بايت، تحميلها يحتاج إلى الثنائي بجانبه:
(lldb) target create "./account-O0" --core "account-O0-stack.core"
(lldb) bt
* thread #1, stop reason = ESR_EC_BRK_AARCH64 (fault address: 0x0)
* frame #0: 0x00000001000004bc account-O0`bump(a=0x000000016fdfe008, by=5) at account.c:13:19
frame #1: 0x0000000100000640 account-O0`main at account.c:40:5
frame #2: 0x000000019a949d54 dyld`start + 7184
(lldb) frame variable *a
(account) *a = (kind = 's', balance = 250, code = 9, rate = 1.25, label = "")
(lldb) frame variable a->label
(const char *) a->label = 0x0000000100000719 ""الـ"ترايس" والحقول الكاملة غير متأثرة، لأنها تعيش على كومة التي حفظها النواةlabelبصمات كسلسلة فارغة مع مؤشرها غير مسدود0x100000719صحيحة . البايتات التي تشير إليها هي حرفية"savings"في__TEXTجزء من المفتاح، والعلبة على شكل كومة لا تشمل هذا القطاع. المؤشر هو ملاحظة. السلسلة غير متوفرة، ولا يختلق lldb. --style fullيحتوي على كل ذاكرة العملية، وكون جوهر كامل لهذا البرنامج 34 كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كيلو كESR_EC_BRK_AARCH64: النواة سجلت BRKاستثناء الذي أثار نقطة الانهيار
على لينكس يكتب النواة النواة وفقاً ل/proc/sys/kernel/core_pattern، نموذج مع %pلـ (PID)%eلسم القيادة%sللإشارة، و%tوذلك في الوقت الذي كان فيه|أنابيب النفايات إلى برنامج. نظام التوزيع يستخدم هذا الأنابيبsystemd-coredumpوcoredumpctl list،info،dumpوdebugاسترد ما كان مخزنًاgdb prog coreوlldb -c core progكلاهما يفتح النتيجة.
ج.د.بي و الـ "إل.د.بي" جنبا إلى جنب
يقوم المخططان بإصلاح نفس العمل مع كلمات مختلفة. COMMAND_MAPفي الحزام يحتوي على 35 صف، طبعت بواسطة python3 code/main.py commandsتلك التي تستخدمها هذه الدروس
| Task | gdb | lldb | lldb alias |
|---|---|---|---|
| break at a function | break bump | breakpoint set -n bump | b bump |
| break at an address in one module | break *0x1000004f0 | breakpoint set -a 0x1000004f0 -s account-O0 | |
| run commands at every stop | define hook-stop ... end | target stop-hook add -o '...' | |
| list breakpoints with hit counts | info breakpoints | breakpoint list | br l |
| watch a variable for writes | watch a->balance | watchpoint set variable a->balance | wa s v a->balance |
| list watchpoints | info watchpoints | watchpoint list -v | wa l -v |
| read one register | p $x0 | register read x0 | |
| read memory as words | x/8xw 0x16fdfe008 | memory read -s4 -fx -c8 0x16fdfe008 | x -s4 -fx -c8 0x16fdfe008 |
| show arguments | info args | frame variable --no-locals | |
| step one instruction | stepi | thread step-inst | si |
| run until the function returns | finish | thread step-out | finish |
| write a core file | gcore core | process save-core --style stack core | |
| open a core file | gdb account-O0 core | lldb -c core account-O0 |
الـ"إلـدـب"pهو اسم مستعار منdwim-print، الذي يطبع متغير بسيط عبر frame variableالطريق و يرتفع إلىexpressionلأي شيء آخرvهوframe variable- أفضلframe variableفي الحزام: يقرأ مواقع DWARF ولا يدير أبداً الرمز في جهاز التحليل.
الإصدارات والترخيصات في 2026-10-14. يحمل lldb رقم إصدار LLVM ، والإصدار الحالي llvm.org هو LLVM 23.1.3 من 2026-10-06, تحت ترخيص Apache 2.0 مع استثناءات LLVM.lldbأدوات خط القيادة على هذه السفينةlldb-1703.0.236.21، إعداد Apple الخاص ، مع Python 3.9.6 مدمج ، و Apple lldb بالفعل موقعة لإصلاح الحذاء. تم إصدار GDB 18.1 في 2026-09-25 تحت GPL-3.0 أو أحدث..gdb_index, أرك64FPMRو (بايتون)gdb.Corefileلا يوجد دعم أصلي لعمليات السيليكون من آبل حتى 18.1: تم نشر سلسلة 15 إصطلاحًا "دعم أرك64-دارون الأولي" في أكتوبر 2025 ، وفي فبراير 2026 وضع مديرًا طريقًا لإنقاذه من خلال تفويض حقوق الطبع والنشر والإصطلاحات BFD أولاً.gdbالصيغة لديها زجاجات Arm64 و هي بناء مع مزيف --target=x86_64-apple-darwin20ويقول طلب السحب الذي أضافهم "ليس من الممكن إصلاح الثنائيات aarch64-darwin عبر GDB". أي إصلاح غير أبل على macOS يحتاج أيضا إلى شهادة موقعة بنفسه و com.apple.security.cs.debuggerالحق في التأمينtask_for_pidالفشل مع "(os/kern) الفشل (0x5)". مكان gdb في هذه الدورة هو لينكس، حيث Debian مستقر السفينة gdb 16.3 ل arm64, و حيث rrحياة
التتبع: ما طلب البرنامج من النواة
جهاز إزالة الحذاء يظهر حالة البرنامج عند توقف. جهاز تعقب يظهر تأثير البرنامج على النظام دون إيقافه لفترة طويلة. الأدوات لكل نظام تشغيل.
| Tool | OS | Version | Licence | What it records |
|---|---|---|---|---|
strace | Linux | 7.2, 2026-08-18 | LGPL-2.1-or-later | system calls with arguments and return values |
ltrace | Linux | 0.7.3 last tarball, git snapshots in Debian | GPL-2.0 | dynamic library calls |
bpftrace | Linux | v0.27.0, 2026-09-10 | Apache-2.0 | kernel and user probes through eBPF |
dtrace and dtruss | macOS | Sun D 1.19 in macOS 26.2 | CDDL, from Sun's OpenSolaris | D probes; dtruss prints system calls |
dtrussيقول في دليلها " بما أن هذا يستخدم DTrace، فقط المستخدمين الذين لديهم امتيازات الجذر يمكن تشغيل هذه الأوامر".dtrace(1)يضيف أن "مع إعدادات حماية سلامة النظام الافتراضي، لا يسمح لبرامج D بالوصول إلى قيم عنوانات النواة أو محتوى ذاكرة النواة" وأن الإجراءات المدمرة غير مسموحة تحت SIP. sudo dtruss ./account-O0على هذه الآلة لم يتم تشغيلها في هذا الدروس، لأن الحزام يدير دون جذور، والدرس يكتب أن غير المحققة.writeلكل واحدprintfخط" هو سؤال تتبع، "هل x1إبق 5 في bump" هو سؤال إصلاحي كلاهما على كل مرة
تسجيل و إعادة تشغيل
rrتسجل عملية تشغيل مرة واحدة وتقوم بإعادة تشغيلها بشكل تحديدي تحت gdb ، مع تنفيذ عكسيا: حدد نقطة مراقبة على القيمة التي تهتم بها و reverse-contيذهب للخلف إلى الكتابة. يصف المشروع نفسه بأنه "أنت تسجل فشل مرة واحدة، ثم تحذير التسجيل، بشكل تحديد، في كل مرة تريدها". الإصدار الحالي هو 5.9.0 من 2025-02-13, تحت رخصة MIT مع حجر BSD-2-Clause. إنه لينكس فقط يحتاج إلى معدادات أداء الأجهزة لتعدل الفرع ، لذلك يعمل على Intel Nehalem ولاحقاً ، بعض نواة AMD Zen ، و "بعض آرش 64 المعماريات الصغيرة (مثل ARM Neoverse N1 أو سلسلة Apple Silicon M) " تحت نظام لينكس. داخل آلة افتراضية يجب أن تكون العدادات مجازية، ويقول ويكي المشروع "لا يوجد أي عملية افتراضية أخرى على ماك كذلك"، لذلك لا يمكن لـ Docker و Colima على هذه الآلة تشغيلها. "بيرنسكو" هي الخدمة التجارية التي تم بناؤها على تسجيلات
لدى GDB جهاز تسجيل برمجي خاص بهrecord fullتسجل "سجل تنفيذ العملية" مع حد افتراضي من 200،000 تعليمات، و record btraceيستخدم متابعة فرع Intel و "لا يسجل البيانات". لا توجد في lldb. على هذه آلة macOS ، النسخة المحفوظة من الحزام هي السجل: نفس الخطة على نفس الثنائي تنتج نفس التوقفات بنفس الترتيب ، وتختبر الجهاز ثابتة هذا الترتيب.
ما يثبته الجري
يثبت إدخال واحد على بناء واحد. يحدد معيار C السلوك غير المحدد بأنه "سلوك، عند استخدام بناء برنامج غير ناقل أو خاطئ أو بيانات خاطئة، لا تفرض هذه الوثيقة أي متطلبات"intيمكن طباعة نتائج مختلفة بشكل مشروع في -O0و-O2الدروس 36 تلبيت هذا معpercent_changeخلاياه التفجير. يغير UBSan السؤال إلى توقف:-fsanitize=signed-integer-overflow -fsanitize-trap=undefinedيجعل التدفق "ينفذ تعليمات فخ" التي تهبط في الـ "إل بي دي" كوقف مع سبب
يُحرك الرمز المُحسن أيضًا ما تريد مشاهدته. تقول دليل GDB "لقد أعيد تحديد الرمز الخاص بك من قبل المحسن؛ يُظهر لك جهاز التحليل ما هو موجود بالفعل" وأن المتغير المُحسن خارج الوجود هو واحد "GDB لا يراه أبدًا".<optimized out>، و مصدرها يفصل ذلك عن<unavailable>: تحسين "لا يوجد في البرنامج" غير متوفر "يحصل في البرنامج (على مستوى الآلة) ، ولكن GDB ليس لديه وسيلة للوصول إليه".<variable not available>عندما يكون عداد البرنامج خارج كل نطاق من قائمة مواقع DWARF المتغير. نقطة مراقبة على محلية تعيش في سجل لا تنطلق أبداً ، لأن سجل ليس ذاكرة.-O2يظهر هذا الحالة القاسية: تم طيّب الوظيفة بأكملها، والصمت الذي لم يُحدد أبداً لا يثبت شيئاً.
لذا فإن الحزام يكتب ثلاثة أنواع من السجلات ولا أبداً رابع. توقف اختبر الادعاء ووافق عليهconfirmedتوقف اختبرها و لم يوافقوا عليهrefutedدعوى لا يمكن اختبارهاnot-observedويعتبر دفتر الدراسة 35 القبول بالأثنين الأولينobservedسجلات مع السلطةcontrolled-replayويحافظ على الثالث كجهل مفتوح
إعادة تحديد الخطأ فقط على المباني الخاصة بك على macOS
على سليكون آبل، علامات الرابط لك.ld(1)يقول عن-adhoc_codesign: "الوضع الافتراضي لـ Apple Silicon binaries هو أن يتم تصميمها بشكل خاص". codesign -dvvv account-O0على طوابع الجهاز:
Format=Mach-O thin (arm64)
CodeDirectory v=20400 size=387 flags=0x20002(adhoc,linker-signed) hashes=9+0 location=embedded
Signature=adhoc
TeamIdentifier=not set0x20002هوCS_ADHOC(0x2) بالإضافةCS_LINKER_SIGNED(0x20000), "توقيع تلقائي من قبل الصلة" في عنوان توقيع الشفرة في شركة آبل. codesign -d --entitlements -لا يطبخ أي شيء بعد الاسم التنفيذي: لا يوجد حق في الثنائي. أطلقتها lldb ، وضعت كل نقطة انقطاع ، وضعت نقطة مراقبة في الأجهزة ، وكتبت جوهر ، مع DevToolsSecurity -statusالإبلاغ عن "وضع المطور غير فعال حاليا" و تمكين حماية سلامة النظام.
اثنين من الحقوق تسيطر على من يمكن تحديدها، ويعيشون على جانبي معارضة. com.apple.security.cs.debuggerهو للشاشة: "قيمة بولية تشير إلى ما إذا كان التطبيق هو شاشة ويمكن أن يرفق إلى عمليات أخرى أو الحصول على منفذ المهام".debugserverيحمل شكل خاص منه، وcodesign -d --entitlements -على قوائم نسخ أدوات خط الأوامر com.apple.private.cs.debugger،com.apple.private.set-exception-port،com.apple.private.thread-set-stateوcom.apple.private.logging.diagnostic. .com.apple.security.get-task-allowهو للهدف. يضخ Xcode في إعدادات التحليل ، ويجعل من الصادرة ، ويعرف شهادة الاعتماد الثنائية التي لا تزال تحتوي عليه. يعلن مهندسي آبل قاعدة الرفض: يتم رفض ميناء المهمة عندما يكون لدى الهدف الوقت المحدد للعمل تمكين وليس لديه get-task-allow- عاديclangإن لم يتم تشديد البناء ولا يُطلق أي حالة إذا وقعتِ على أيّ اتفاق معcodesign --options runtime, أعد توقيعهاcodesign -s - -f --entitlements get-task-allow.plistقبل إعادة التشغيل
ثلاثة حقائق أخرى تغلق القسم. أولاً، قد ترى حوار "تحتاج أدوات المطور Access إلى التحكم في عملية أخرى للتشغيل لإستمرار" للمرة الأولى في جلسة تسجيل الدخول. sudo DevToolsSecurity -enableيزيل كلمة المرور الإضافية لأعضاء adminأو_developerالمجموعات. ثانيا، لا تعطيل SIP لتثبيت رمزك الخاص. يقول Apple "لا تحتاج إلى تعطيل SIP لتشغيل وتثبيت التطبيقات من Xcode" ، والكوني الوحيد SIP الذي يلمس التثبيت هو واحد csrutil enable --without debugيسترخى، لعمليات الآخرين. ثالثا، صندوق الرمل في هذه الجلسة يمنعtask_for_pidتماماً، والصلبsandbox-O0.lldb.txtيظهر النتيجة، إنّ السباق الذي لا يستطيع البدء هو واقع بيئي، ويقول الحزام بأنه كذلك.
كل شيء في هذا الدروس ينطبق على البرامج التي قمت بتجميعها. نفس الأوامر سوف ترتبط مع عملية أخرى فقط إذا اختار مالكها، ولا شيء هنا يعلم كيفية تغيير ذلك.
بناءها
أطلق التجربة:
cd phases/13-tools-and-protocols/42-dynamic-analysis-of-your-own-program
python3 code/main.py
npx tsx code/main.tsإن الخروج هو تقرير JSON واحد على النصوص المحفوظة. لكل بناء يُدرّس نقاط الانقطاع مع حلّها ومعدّل الضربات، ومعدّل نقطة مراقبة الأجهزة، وكل توقف مع سجلاتها وحججها، وحالة الخروج، ومخرج البرنامج، والخطأ الذي تم طبعه، والادعاءات مع أحكامها وتسجيلات الأدلة وراءها. يضيف الرحلة الجوية للقاعدة، نتائج API Python، فشل صندوق الرمال، وأي أدوات خارجية غائبة على هذه الآلة.
الشكل المتوقع:
{
"program": "account.c",
"variants": {
"O0": {
"binary": "account-O0",
"breakpoints": {"1": {"spec": "name=bump", "address": "0x1000004bc", "resolved": true, "hits": 1}, "...": "..."},
"hardwareWatchpoints": 4,
"watchpoints": {"1": {"address": "0x16fdfe00c", "size": 4, "type": "m", "hits": [[250, 255]], "hitCount": 1}},
"stops": [{"index": 0, "pc": "0x1000004bc", "function": "bump", "kind": "entry", "registers": {"x0": "0x16fdfe008", "x1": "0x5"}, "...": "..."}],
"exitStatus": 0,
"claims": [{"id": "arg-registers", "verdict": "confirmed", "confirmed": 5, "refuted": 0, "not-observed": 0, "...": "..."}],
"evidence": [{"claim": "arg-registers", "verdict": "confirmed", "stop": 0, "observation": "bump: x0 = 0x16fdfe008 and a = 0x000000016fdfe008, x1 = 0x5 and by = 5"}]
},
"O2": {"stops": [], "claims": [{"id": "calls", "verdict": "refuted", "...": "..."}], "...": "..."}
},
"core": {"savedAt": {"function": "bump", "pc": "0x1000004bc"}, "labelString": "", "...": "..."},
"types": {"python": "3.9.6", "size": 32, "...": "..."},
"sandbox": {"stops": 0, "errors": ["process exited with status -1 (attach failed ((os/kern) invalid address))"]},
"absentTools": ["bpftrace", "gdb", "llvm-objdump", "ltrace", "rr", "strace"]
}اقرأcode/main.pyفي هذا الترتيب:
Listingيُحللllvm-objdump -dالنص في الوظائف والإرشادات، وFunction.conditional_branches()يجد الفروع مع أهدافها و التدفقاتstruct_layout()يقرأ الهيكل و إدخالات الأعضاء منllvm-dwarfdumpرسالةlift_condition()يُحوّل فرع مشروط إلى مُعلن، الدروس 34 الخطوة للـtbz،cbz، و النماذج المقارنة والفروعPlan.commands()يكتب ملف الأوامر lldb من القائمة، وPlan.lldb_command()هو دعوة اللحظةparse_transcript()يمر الخروج في الترتيب ويتم بناؤهStopالسجلات التي تحتوي على سجلات، والذاكرة، والمتغيرات، وقيم نقطة المراقبة، بالإضافة إلى جدول نقاط الانفصال وحالة الخروج.claims_for()يذكر الادعاءات الثمانية، وevaluate()تصنيفهم توقف عن طريق توقف مع_check_*وظائفrollup()يقلل من السجلات إلى حكم واحد لكل مطالبة، مع الرد على الاعتراف بالتحديد.capture()وcapture_extras()أنتجت الأجهزة ، وlive()يكرر الحلقة بأكملها على بناء جديد عندماclangوlldbيمكنه الركض
هناك أربعة أوامر أخرىpython3 code/main.py plan O0يطبخ ملف الأوامر لإنشاء. python3 code/main.py commandsيطبع الجدول الجي دي بي و الايل دي بي. python3 code/main.py toolsتقارير عن الأدوات الخارجيةPATH. .python3 code/main.py live O0تجميعات account.cفي دليل مؤقت، الخطط من قائمة جديدة، تشغيل lldb، وتصف المزاعم ضد ذلك التشغيل. داخل صندوق الرمال يبلغ أن lldb لم يتمكن من إطلاق العملية، مع الخطأ، وتخرج 1.
استخدمها
استخدم الحزام على برنامج خاص بك في هذا الترتيب.
- بناءه مع
-gو مستوى التحسين الذي ستقوم بإرسالها. احتفظ بالبائنري، dSYM أو DWARF، وllvm-objdump -dرسائل نصية معاً - اكتب المطالبات قبل أن ترشح. كل منها يطلق على وظيفة أو سجل أو عنوان أو تعويض، وقيمة أو حافة متوقعة، والدرس أو الوثيقة التي جاءت منها. المطالبة التي تكتبها بعد رؤية توقف يصف توقف ولا يختبر شيئا.
- إنشاء الخطة من القائمة وقراءتها. كل عنوان فيه يجب أن يتبع إلى سطر في القائمة. إذا لم يكن هناك خط كهذا في نقطة وقف، إزالتها.
- إشغلي مرة واحدة في وضع اللحظة و ابق النص مع خط الأوامر والإصدار من إصلاح في رأسه. النص هو الدليل. القرارات مشتقة منه، ويمكن لأي شخص إعادة استنتاجها.
- الدرجة، اقرأ كل
not-observedالسجل والسبب قبل قراءة التأكيدات. الأسباب تخبرك أي ادعاءات تحتاج إلى إدخال آخر، بناء آخر، أو نقطة وقف أخرى. - إدارة البناء المثالي مع نفس المطالبات. توقع نقاط وقف حل مع صفر ضربات حيث قام المحامل بالترتيب، والحفاظ على تلك المطالبات مفتوحة.
- إدراج السجلات المثبتة والمنكرة في دفتر الدروس 35
observed، السلطةcontrolled-replay،سجل واحد لكل توقف، وترك تلك التي لم تلاحظها كجهول مع إصدار عدد نقاط الاختراق
المختبر التفاعلي
المختبر "أ": قراءة توقف يدوياً
منcodeالإرشادات، مفتوحة Python:
cd phases/13-tools-and-protocols/42-dynamic-analysis-of-your-own-program/code
python3 -qأركض
from main import *
t = load_transcript("session-O0.lldb.txt")
s = t.stops[0]
hex(s.pc), s.function, s.reason, s.registers
[hex(w) for w in s.memory[:8]]
s.struct_pointer().fieldsقبل الخط الثاني، اكتب أي كلمة تتوقع أن تستحمل 250 و التي اثنين من الكلمات تستحمل 1.25. ثم تشغيل struct.unpack("<d", struct.pack("<II", s.memory[4], s.memory[5]))ثم انظرt.stops[1].watch_hitوt.stops[1].pc، و إبحث عن هذا العنوان فيaccount-O0.objdump.txt: هو التعليمات بعد المتجر.
المختبر ب: الفرع وخليفه
plan = make_plan("O0")
plan.branches()
classify = plan.listing.function("classify")
[lift_condition(classify, b, "a->balance")[0] for b in plan.branches()]
[(hex(s.pc), s.struct_pointer().field_int("balance")) for s in t.stops_in("classify")]تَطَابقُ كلّ توقفِ الحافة إلى توقفِ دخولِها وتقرّر، قبل أن تنظر إلى الأدلة، الذي خلفيّةِ التنبؤاتِ. ثمّ تشغيل [e.to_dict() for e in evaluate(plan, t, struct_layout(open("fixtures/account-O0.dwarfdump.txt").read(), "account")) if e.claim.startswith("branch")]و مقارنة.
المختبر "ج": السباق الذي لم يتوقف أبداً
o2 = load_transcript("session-O2.lldb.txt")
len(o2.stops), o2.exit_status, o2.hits()
o2.errors[:3]
make_plan("O2").branches()
rollup(evaluate(make_plan("O2"), o2, struct_layout(open("fixtures/account-O2.dwarfdump.txt").read(), "account")))شرح كل واحد not-observedبكلماتك الخاصة، ثم افتحaccount-O2.objdump.txtو العثور على الثابتات الأربعة في mainهذا استبدل المكالمات ثم عدDW_TAG_inlined_subroutineفيaccount-O2.dwarfdump.txtمعcount_tags(). . .
المختبر (د): كسر الأدلة
text = open("fixtures/session-O0.lldb.txt").read().replace("x1 = 0x0000000000000005", "x1 = 0x0000000000000006", 1)
broken = parse_transcript(text)
[e.to_dict() for e in evaluate(plan, broken, struct_layout(open("fixtures/account-O0.dwarfdump.txt").read(), "account")) if e.claim == "arg-registers"][:2]سجل واحد محرر يدور الأول arg-registersسجل إلىrefutedو الإرتفاع معها بينما البقية الأربعةconfirmed. ثم حذف old value: 250خط قبل نقطة المراقبة توقف ومراقبةstore-onceتسقط إلىnot-observed.المُحرم الذي لا يمكن جعله يفشل ليسُ بمُحرم
إذا استطاع الـ "إيلدبي" إطلاق عملية في قذفك، فانتهىpython3 main.py live O0وpython3 main.py live O2العناوين ستختلف عن المرافق، لكن الحكم لا يجب أن يختلف
مختبر التدريب
تمديد الحزام مع دعوى حول الحلقة في sum_balances. . .
المتطلبات:
- ابحث عن الحلقة الخلفية من
sum_balancesفي-O0إدراجها معconditional_branches()و أضف نقطة انقطاع في عنوان الحلقة. - إضافة مطالبة التي يقوم بها الرأس
count + 1أوقات للcount= 3، وهو الدرس 33 الحافة الخلفية زائد اختبار الخروج. - تمديد الركبة وقف أو خطة حتى الركبة تقرأ حلقة العداد سقف مكعب في كل توقف الرأس، وكتب
_check_loop()الذي يسجل تسلسل القيم العكسية. - استعادوا النسخة مع
python3 main.py capture O0على آلة حيث يمكن لـ Lldb إطلاقها وإعادة تشغيل مجموعة. اختبار الخطة سوف تفشل حتى يطابق ملف الأوامر الملتزمة الخطة الجديدة، والتي هي نقطة ذلك الاختبار. - إضافة اختبار يعدل قيمة معاكسة واحدة في النص و يؤكد أن الدعوى تتحول
refuted. . .
النجاح يعني أن التقرير يظهر أن العنوان يتوقف في ترتيب مع قيمها المضادة، وال -O2يحتفظ الركض الجديدnot-observedمع سبب أن الحلقة كانت متجهة أو مطوية.
درساً أساسياً
هذا الدروس يتضمنoutputs/skill-dynamic-analysis-of-your-own-program.mdاستخدمها لتحويل برنامج قمت بإنشائه، وإدراجها، وقائمة من المطالبات الثابتة إلى نسخة تحريف محفظة واحدة وحكم واحد لكل مطالبة، مع وقف الذي أدى إلى كل حكم وسبب كل مطالبة لم يتم مراعاتها.
تحقق من ذلك
إشغال التجربة والسلسلة التحديدية:
cd phases/13-tools-and-protocols/42-dynamic-analysis-of-your-own-program
python3 code/main.py
npx tsx code/main.ts
python3 -m unittest discover -s code/tests -vهذه الجزمة تعمل بدون إلكترونية إلكترونية، إنها تقرأ النصوص التي تم التقاطها مرة واحدة فيcode/fixtures/يجب أن تثبت التحقق:
- يجد المصفح المحدد الأدوات الأربعة مع نطاقاتها والفروع المشروطة الثانية من
classifyفي-O0مع الهدف والانهيار - المصعد
tbz w8, #0x1fهوa->balance >= 0ورفعsubsبالإضافةb.leهوa->balance <= 200 mainفي-O0مكالماتbump،classifyثلاث مرات، وsum_balancesوmainفي-O2مكالمات فقطprintf- x86-64
j<cc>الـ "منيومونيكا " مشروطة وjmpوcallqلا تكون - يُنتج المُسحّل DWARF بنية 32 بايت مع أعضاء عند 0، 4، 8، 16 و 24
- الموقع
-O2يحتوي DWARF على أربعة برامج فرعية و 0 إدخالات تحت الروتين المخطط لها، وnmلا يزال يردد الوظائف الثلاثة - خطة يساوي ملف الأوامر الملتزمة لكلا الإنشاءات ، وترانسكريبت رأس تسجل أمر lldb الذي قام بها
- الموقع
-O0النسخة لديها 11 توقف في الوظائف المتوقعة، وضع الخروج 0، وأربعة خطوط الخروج - إنتاج الرمز يرتبط بالموقف الذي يليها ، مع
x1= 5، الذاكرة تبدأ منx0، وسلسلة العلامة - جدول نقاط الانفصال يحمل قرار ويعد ضربات 1, 3, 1, 2, 1, 1, 1
- نقطة المراقبة هي 4 بايت من نوع تعديل في
x0 + 4، ضرب مرة واحدة مع القيمة القديمة 250 والقيمة الجديدة 255 ، و أربعة فتحات الأجهزة يتم الإبلاغ عنها - الموقع
-O2النسخة لديها صفر توقف، ثلاثة نقاط وقف مع صفر ضربات، لا نقطة مراقبة، وخطأ واحد لكل غير مستخدمcontinue - فشل الإطلاق في صندوق الرمال قبل أي توقف مع خطأ إصدار
- الرحلة الجوية والعودية في القاعدة تبقي العودة و
balance= 250 و يفقد السلسلة الخلفيةlabel - يقرأ API Python الخمسة الحقول ، حجم 32 ، ويتراوح الدالة دون عملية
- كتابة خطية مسجلة من x86-64 تحليل النص مع
rdi،rsiوrip - كل مطالبة تؤكد في
-O0مع سجلات 5، 11، 3، 2، 1، 1، 1، 1 - دليل الفروع يذكر القيمة والشروط والحافة
- دليل المجال المثبت يفك الكلمات من خلال ترتيب DWARF
- الدليل المخزن والخطوة تحمل القيمة القديمة والجديدة والتربية ثلاث صفات
- الموقع
-O2يرفض الجري حواف الدعوة ويترك الادعاءات الأخرى غير مشروعة - يتم رفض قيمة السجل المحررة ، لا يتم ملاحظة ضرب نقطة مراقبة تم حذفها ، ويمكنه رفض التدفق
- خريطة الأوامر لديها أشكال gdb و lldb لكل سطر، و الجدولية الأجهزة أسماء أربعة سجلات إزالة x86
- يذكر الظهور الأدوات الغائبة بدلاً من الفشل ، ويقول وضع المباشر عن الأدوات المفقودة باسمها
- البيانات الرقمية تأتي من كلا الجلستين وحدة الموقع تضم نفس البيانات
أساليب فشل الإنتاج
| Failure | What the weak report says | What the harness must prove |
|---|---|---|
| Resolved breakpoint with zero hits | "the function runs" or "the function is dead" | a full run with exit status, hit counts, and the verdict refuted for the call edge only |
| Register read at the wrong stop | "x1 = 5 inside classify" | compare only declared arguments at the entry stop |
| Struct read without offsets | a column of hex words | every word mapped to a DW_AT_data_member_location and compared with frame variable |
| Branch graded from the mnemonic | "tbz means taken when negative" | the lifted predicate from the encoding and the stop at the successor |
| Watchpoint on a register-resident local | "no write happened" | the watchpoint was set on an address and was hit, or the record is not-observed |
| Auto-continue breakpoints | a clean transcript with no stops | plain breakpoints, a stop hook, and a continue budget |
| Address breakpoint without a module | unresolved, hit count = 0 | -s MODULE so the listing address resolves to a load address |
| First command error ends the batch | no breakpoint list at the end | interpreter.stop-command-source-on-error false |
| Stack-style core read as full memory | label = "" reported as the program's value | the core style, the pointer, and the segment it points into |
| Sandboxed shell | "lldb is broken" | attach failed ((os/kern) invalid address) recorded as an environment fact |
Hardened target without get-task-allow | "macOS forbids debugging" | the two conditions from Apple's rule, and a re-sign of your own binary |
| Transcript discarded after grading | verdicts with no source | the transcript file, its command line, and the debugger version |
قاعدة العمليات
خطط توقفات من القائمة، تشغيل مرة واحدة في وضع اللحظة، حافظ على النص، وتقييم كل مطالبة ثابتة في كل توقف يمكن اختبارها. كتابة تأكيد أو رفض فقط من توقف، كتابة غير ملاحظ مع عدد ضربات والسبب، ولا تدع نقطة وقف حل في مكان لتشغيل. إعادة التشغيل فقط من المكونات التي قمت بها.
المزيد من القراءة
- LLDB Tutorial: command grammar, breakpoints, watchpoints, stop hooks
- lldb(1) manual page: --batch, --one-line, --source, --core
- GDB to LLDB Command Map
- LLDB Python Reference: SBTarget, SBFrame, SBValue, and the embedded interpreter
- LLDB Caveats: the Python build lldb links against
- LLVM 23.1.3 release
- GDB 18.1 release announcement, 2026-09-25
- GDB manual: Process Record and Replay
- GDB manual: Debugging Optimized Code
- GDB wiki: Permissions on Darwin, code signing a debugger
- gdb-patches: Initial AArch64-Darwin support series, October 2025
- Homebrew gdb formula pull request adding arm64 bottles
- ptrace(2), Linux manual page
- lldb debugserver MachTask.mm: task_for_pid and task_set_exception_ports
- Intel SDM Volume 2: INT n, INTO, INT3, INT1
- Intel SDM Volume 3: debug registers DR0 to DR7 (Debug, Branch Profile, TSC, and Resource Monitoring Features)
- Arm A64 ISA: BRK, Breakpoint instruction
- Arm A64 ISA: TBZ, Test bit and branch if zero
- Arm Architecture Reference Manual, D2.9 Watchpoint exceptions
- Apple ld(1): -adhoc_codesign
- Apple ld64 LinkEdit.hpp: CS_LINKER_SIGNED
- Apple: Debugging Tool Entitlement, com.apple.security.cs.debugger
- Apple: Hardened Runtime
- Apple: Resolving common notarization issues, the get-task-allow entitlement
- Apple: Disabling and Enabling System Integrity Protection
- Apple Developer Forums: debugserver may not get the taskport of a hardened process
- Apple DevToolsSecurity(8)
- Apple core(5)
- Apple Technical Note TN2124: Mac OS X Debugging Magic, core dumps
- lldb
process save-corehelp text - core(5), Linux manual page
- coredumpctl(1)
- Apple dtrace(1)
- Apple dtruss(1m)
- strace releases
- ltrace
- bpftrace CHANGELOG
- rr project
- rr 5.9.0 release
- rr wiki: Building And Installing, hardware and VM requirements
- Clang UndefinedBehaviorSanitizer
- ISO/IEC 9899:2024 working draft N3220, clause 3.5 undefined behavior
- DWARF Debugging Information Format Version 5
- Procedure Call Standard for the Arm 64-bit Architecture (AAPCS64)
- System V Application Binary Interface, AMD64 Architecture Processor Supplement
This free lesson is part of the AI Engineering from Scratch curriculum. Read the full explanation, run the lesson code, and verify the result in the interactive reader or from the repository source.
Browse the complete course catalog or open this lesson on GitHub.