التفكيك والتحكم في التدفق: من البايت إلى الرسم البياني يمكنك التحقق منه
Type: Build
Languages: Python, C, x86-64 assembly
Prerequisites: Phase 13 · 32 (ELF from scratch)
Time: ~150 minutes
أهداف التعلم
- فكّر مجموعة إرشادات x86-64 في إطارات الكتيب من القواعد في إدارة إدارة الإنترنت (Intel SDM) الجدول 2، الفصل 2.
- إثبات المفكّر ضد
llvm-objdump -d" على كل تعليم " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي " أي - اجري مسح خطي وهبوط متكرر على نفس البايتات و اشرح أين كل واحد خاطئ.
- قطع التعليمات إلى كتلة أساسية وبناء رسمية التحكم في التدفق مع حواف مدرجة.
- احسب المهيمنين المباشرين باستخدام خوارزمية كوبر و هارفي وكينيدي وتجد حلقات من الحواف الخلفية
- استعادة أهداف قفزة غير مباشرة من إدخالات إعادة التوطين التي تشير إلى جدولها.
المشكلة
لقد قمت بتجميعهاledger.cفي-O0و سألت عميل ماgradeلقد اتصل العميل بتفكيك، وقرأ القائمة، و أجاب أن الوظيفة تنتهي فيjmpq *%raxبعد ذلك كتب رمزًا مزيفًا مع واحدreturn 0. . .
أنت تفتح نفس الشيء فيllvm-objdump -dو نرى أربع كتلة بعد القفز التي تخزن 4، 3، 2 و 1. كانت في القائمة كلها. لا شيء في القائمة يقول كيفية التنفيذ يصل إليها، لذلك العاملة معالجتها كرمز ميت.
لا يذكر أي تعليمات يتم تشغيلها ، في أي ترتيب ، أو من أين. للإجابة عليها تحتاج إلى ثلاث هيكلات أخرى: البايتات التي يمكن أن تصل إلى تدفق التحكم ، والكتلة الأساسية ، والحواف بينها.
لا يوجد في rea 6.5.0 جهاز تفكيك خاص به. للتحليل العميق، يطلب منك README أن تجلب Ghidra، Hopper، أو IDA. للتفتيش خارج الاتصال، يمكن لـ rea أيضاً أن تدعو إلى غنو objdump أو Rizin الذي تقوم بتثبيته. يوقف الرسم البياني للدعوة في أهداف غير حلولة: "المكالمات/المكالمات تحتوي فقط على وظائف حلولة.... لا يزال التدفق الحاسوبي غير المستهدف مجهول". هذا الدروس يُبني الجزء الذي يُنتج هذه الحقائق، بحيث يمكنك أن تحكم عليها عندما تقرأ أداة.
نمط التعليمات لديه قاعدة واحدة لكل مجال
إنديل SDM الجدول 2، الشكل 2-1 يعطي ترتيب البايت لكل تعليمات. أولاً تأتي المضافات الاختيارية، ثم واحد إلى ثلاثة بايتات رمز الخيار، ثم بايت ModRM الاختيارية وبايت SIB الاختيارية. يتبع تحرك من 0, 1 ، 2 ، أو 4 بايت ، ثم مباشرة من 0, 1 ، 2 ، أو 4 بايت. التعليمات الكاملة هي 15 بايت على الأكثر (SDM 2.3.11). يصف أجهزة AMD APM المجلد 3، الفصل 1، القواعد 1.1 إلى 1.7 نفس النموذج في صفحات أقل ويعتبر ذلك تحقيقاً متقاطعاً جيداً.
كل حقل يقرر ما يعنيه الحقل التالي. لذلك فإن المفكّر سلسلة من القرارات الصغيرة، وكل قرار لديه جملة في الدليل وراءه.
المُستَبْدَلَاتِ تأتي في أربع مجموعاتِ
الميزانية 2.1.1 قائمة بالمواعيد المسبقة القديمة.F0القفل والإضافات المتكررةF2وF3المجموعة 2 تمتلك الستة قطاعات المُتجاوزة (2Eهو%cs) و إشارات الفرع. المجموعة 3 هي 66، والحجم التشغيلي.67، حجم العنوان يُغلب.
يدل المذكرة على أنه يمكن استخدام إضافة واحدة من كل مجموعة.ledger-x86_64-O0.oفي التكافؤ0xf2يقرأ66 66 66 66 66 2e 0f 1f 84 00 00 00 00 00، خمسة نسخ من66في صفllvm-objdumpيطبعها على شكلnopw %cs:(%rax,%rax)كما يفعل المقرر الذي تقوم ببناءه. يقوم المقرر بجمع كل قبل البايت الذي يراه ويسأل فيما بعد أي منها يهم.
66لتوثيقات SSE هو مقدمة إلزامية التي تختار رمز الخيار، لذلك 66 0f ef c0هوpxor %xmm0, %xmm0و ليس 16 بت أي شيء.F3 0f 6fهوmovdqu. يبحث المفكّر أولاً عن زوج من المُضافات الإلزامية والبايت الثاني من رمز الاختيار. فقط عندما لا يكون هناك إدخال في هذا الزوج66يعني أوبراندي 16 بت
REX يضيف أربعة بيتات ويجب أن يجلس بجانب رمز الاختيار
SDM 2.2.1 تعريف REX بايت واحد من 40إلى4Fمع الشكل0100WRXB. .Wيختار أوبراند 64 بتRيوسع مجال تنظيم ModRM. Xيوسع حقل مؤشر SIB. Bيمتد حقل ModRM r/m، وحقل SIB الأساسي، أو السجل في البايت في رمز الخيار (الجدول 2-4). يجب أن يأتي REX مباشرة قبل رمز الخيار أو البايتات الهروبية، ويجب تجاهل REX يليها ضيف سابق.
في48 89 e5البايت48هل REX مع Wالمجموعة، رمز الخيار89هوMOV r/m, rو (مدرم)e5يختار%rspفي%rbpإزالة REX و نفس البايتات هيmovl %esp, %ebp. . .
REX تغير أسماء سجلات البايت أيضا. مع وجود REX، قم بتحديد الرموز 4 إلى 7 %spl،%bpl،%silو%dilبدلاً من%ah،%ch،%dhو%bhوذلك هو السبب في أن جهاز القيادة يحتفظ بمجملتين
الرمز البياني يعيش في خريطتين
رمز عمل واحد بايت هو الحالة الشائعة.0Fيفر من خريطة بثنائي بايت، و 0F 38أو0F 3Aيستخدم بعض الرموز الرقمية حقل ModRM reg كثلاثة بثات أخرى من الرموز الرقمية، مكتوبة /digitفي الدليل83 /7هوcmpمع 8 بتات مباشرة، 83 /5هوsubوFF /4هو غير مباشرjmp. . .
المُشفّر فيcode/main.pyتغطي الرموز في الأربعة كائنات ثابتة بالإضافة إلى عدد قليل من الجيران. القائمة هي الأشكال الحسابية الثمانية،mov،lea،movslq،movzbl،imul, النقبات ,testوidiv. يستمر معpush،pop،call،ret، القفزات المشروطة الـ16، والمتعددة البايتاتnopو خمسة رموز SSE2 أي رموز أخرى ترتفعDecodeErrorمع العنوان والسبب، وحدة فك المعلومات التي تخمن أنها أسوأ من تلك التي تتوقف
تقوم ModRM و SIB باختيار النقاط التنفيذية
الميزانية الدولية 2.1.3 تقسم الميزانية الدولية للعمل الدولي إلى mod(بيتس 7:6)reg(5:3) و r/m(2:0) mod = 11يعني r/m هو سجل. mod = 00،01و10متوسط الذاكرة بدون تحرك، تحرك 8 بتات، أو تحرك 32 بتات. r/m = 100يعني أن بايت SIB يلي.scale(7: 6)index(5:3) و base(2: 0) ، ومؤشر 100يعني عدم وجود سجل مؤشر.
صفين خاصين في الجدول 2-2 في وضع 64 بت. mod = 00معr/m = 101ليس كذلك(%rbp). هو تحرك 32 بت بالنسبة إلى التعليمات التالية (SDM 2.2.1.6). mod = 00مع قاعدة SIB 101هو تحرك 32 بت بدون سجل أساسي.
خذ8d 44 08 faمنgradeفي-O0- رمز البيانات8Dهوlea. مودر ام44هو01 000 100: تحرك 8 بتات، reg %eaxو تليها SIB08هو00 001 000: مقياس 1، مؤشر %rcx، قاعدة%rax. البايت التنقلfaهو -6 . نص AT&T هو leal -0x6(%rax,%rcx), %eax، الذي يحسبscore / 10 - 6، مؤشر المفتاح
النقل هو المكان الذي يكتب فيه محرر الروابط
48 8d 0d 00 00 00 00هوleaq (%rip), %rcx. أربعة البايتات الصفر هي حقل التحرك من أوبراند ذات علاقة RIP. في كائن قابل للتنقل هم حاملي الموقع..rela.textفي التكافؤ0x69يقولR_X86_64_PC32.rodata-0x4. تحدد المادة (المجملة 4.9) R_X86_64_PC32كـS + A - P: عنوان الرمز زائد الإضافة ناقص عنوان الحقل.
المعالج يضيف الحقل إلى عنوان التعليمات التالية، لذلك الهدف النهائي هو S + A + (end - P)لهذا السببleaالحقل هو الأربعة بايت الأخيرة، لذلك end - Pهو 4 والهدف هو .rodata + (-4) + 4 =.rodata + 0x0. تعوض إضافة -4 هذه الأربعة بايتس مسبقاً. كل أوبراند مقارنة مع RIP في الأجهزة المصممة يحمل هذا -4 داخل إضافة، لذلك.rodata+0x10على-O1 leaالوسائل.rodata+0x14. . .
يقوم المقرر بتسجيل تعديل حقل التحرك داخل كل تعليمة. resolve_reference()يبحث ذلك الموازنة في جدول التنقل وتقرير حيث سوف تشير العملية بعد ربط.leaq (%rip), %rcxإلى الطاولة في.rodata+0x0" . "
الأجهزة الفورية 32 بت على الأكثر، باستثناء استثناء واحد
SDM 2.2.1.5 تقول البقاء الفوري في أقصى 32 بت في وضع 64 بت وتكون إشارة تمتد. الاستثناء هو MOV r64, imm64(B8+rمعREX.W), الذي يسميه النصوصية AT&T movabsq. القرآن يقرأ 8 بتات مباشرة كما هو موقوف لعدد (83 c1 faهوaddl $-0x6, %ecx) و غير مؤهلة لعدة التحولات و اختيارات التشويش (pshufd $0xeeهذا التقسيم يطابق ماllvm-objdumpبصمات
بالقرب من الفروعpushوpopافتراضية على أوبراند 64 بت وتجاهل 66(SDM 2.2.1.7) لهذا السبب يطلق عليها المُشعّر اسمهاpushq،popq،callqوretqبدون قراءة مقدمة حجم الأوبران
فك التعليمات اليدوية
قبل أن تثق بالرمز، فك رمزاً48 69 c0 67 66 66 66منgradeعلى الورق
48هو في40إلى4F، لذا هو REX معW = 1حجم الاوبراند 64 بت69هوIMUL r, r/m, imm32. . .c0هو ModRM11 000 000: نموذج التسجيل، reg%rax, r/m%rax. . .- الأربعة البايتات التالية هي الفورية، القليل من الإنديان:
0x66666667. . . - طلب AT&T هو فوري، المصدر، الوجهة:
imulq $0x66666667, %rax, %rax. . .
هذا سبعة بايت، و llvm-objdumpيُطبق نفس النص. الثابت هو المتبادل الذي يسمح للصوت أن يُقسم بـ 10 مع مضاعفة و sarq $0x22سوف تقابل هذا الزوج مرة أخرى في الدروس 34، حيث يقوم المصعد بتحويله مرة أخرى إلىscore / 10. . .
يقوم المفكّر بهذه الخطوات الخمسة فيDecoder.decode()،_State.modrm()و_State.finish()اقرأها بالترتيب الذي تمت فيه وافق كل فرع على قسم SDM المشار إليه في سلسلة الوثائق
التصفية الخطية
يصف شوارز وديبرا واندروز التصفح الخطي في القسم 3.1 من "إزالة الرمز التنفيذي المراجعة" (WCRE 2002). تبدأ في أول بايت من قسم الرمز ، وتفكيك تعليمات واحدة ، وتستمر في بايت بعد ذلك ، وتوقف في النهاية. objdumpيعمل بهذه الطريقة، وكذلك يفعل llvm-objdump. . .
pythondef linear_sweep(code: bytes, start: int = 0, end: int | None = None) -> SweepResult:
position = start
while position < end:
try:
instruction = decode(code[:end], position)
except DecodeError:
undecodable.append((position, code[position]))
position += 1
continue
instructions.append(instruction)
position = instruction.endالطريقة لديها وضع فشل واحد، والرسالة الرسم 1 يظهر ذلك. يتم فك تشفير البايتات التي هي بيانات كإرشادات، ثم يفتقد المفكّر حدود الإرشادات الحقيقية لفترة. في x86 عادة ما يعيد التزام المقرر في غضون بضع بايت، وهذا ما يجعل من الصعب رؤية الخطأ. الاكتشاف يحدث فقط على رمز غير صالح، ونتائج الورقة هي أن هذه الخوارزميات "فشل صامتًا".
التصفح هو أيضاً الوسيلة في هذا الدروس يجب أن يتفق المفكّر معllvm-objdump -dعلى كل تعليمات كل جهاز، وllvm-objdumpهو نفسه مسح خطي. النص المسجل يعيش فيcode/fixtures/*.objdump.txt. يختبر الاختبار كل سطر إلى عنوان، وبايت، ونص، ويترك التعليقات بعد #ويتقارن. الأجسام الأربعة تحتوي على 86، 56، 84، و 11 تعليمات، 237 في الإجمال، و المفكّر يطابق كلّهم.
التراجع المتكرر
في القسم 3.2 من نفس الورقة، يسمى الطريقة الثانية التقاطع الترددي. ابدأ في نقطة دخول معروفة وقطع الرمز حتى يتم نقل التحكم. بالنسبة للقفز المباشر، استمر في الهدف. بالنسبة لفرع مشروط، استمر في الهدف والإرشاد التالي، وللقراءة، استمر بعد ذلك، لأن الدعوة تعود. بالنسبة للعودة، توقف. احتفظ بمجموعة من العناوين التي زارها حتى ينتهي المشي.
pythonif instruction.flow == "branch":
worklist.append(instruction.target)
address = instruction.end
elif instruction.flow == "indirect-jump":
table = resolver(instruction, instructions) if resolver else None
if table is None:
unresolved.append(instruction)
breakوضع الفشل هو العكس. القفز غير المباشر ليس له هدف في بايتاته. يتوقف المشي هناك، ويبقى كل كتلة يمكن الوصول إليها فقط من خلال القفز مجهولة. يظهر الشكل 2 في الورقة خطرًا ثانيًا: مجموعة هدفي مشتقة من عمليات نقل تشير إلى منتصف تعليمات. النزول في main.pyيُسجل هذه الحالة كصراع ويقف تلك المسارة، لذا فإن التداخل مرئي بدلاً من فك رمزها بصمت مرتين.
قياس أندرييس وغيرها تسعة مفصلات على 981 محمولاً من المجموعات الثنائية التي تم إنشاؤها (USENIX Security 2016). كانت البيانات الداخلية والرقم المتداخلة نادرة في إنتاج المحمول الحقيقي من المفترض في الأدب، وكانت البدائية الصعبة استرداد حدود الوظيفة. ضع كلا النتائج في اعتبارك. فخ الفخ في القسم التالي نادر في إنتاج المحمول. قفزة غير مباشرة في gradeهو شائع
مصنع حيث يكون التصفح خاطئ والنزول صحيح
code/fixtures/sweep-trap.sهو 24 بايت من الجمعية المكتوبة يدويا مع جدول أربعة بايت داخل جسم الوظيفة.movzbl (%rcx,%rax), %eaxويقفز فوقها
asmlookup:
movslq %edi, %rax
leaq .Ltable(%rip), %rcx
movzbl (%rcx,%rax), %eax
jmp .Ldone
.Ltable:
.byte 0x31, 0xc0, 0x90, 0x48
.Ldone:
addl $1, %eax
retqبناءه معclang -target x86_64-unknown-linux-gnu -c sweep-trap.sتم اختيار الأبيط الأربعة من الجدول بحيث يتم فكها كإرشادات معقولة31 c0هوxorl %eax, %eax. .90هوnop. .48هو إضافة REX، لذلك الجذب يجمعها مع ثلاث بايت من الحقيقي addl $1, %eaxو بصماتaddq $0x1, %rax- المعتقلينllvm-objdumpالنتائج تظهر بالضبط أن:
text e: eb 04 jmp 0x14 <lookup+0x14>
10: 31 c0 xorl %eax, %eax
12: 90 nop
13: 48 83 c0 01 addq $0x1, %rax
17: c3 retqلا شيء في هذه القائمة يبدو مكسوراً كل بايت تم فك رمزه كل مخطط حقيقيretqالقائمة لا تزال خاطئة في ثلاثة طرق.0xeالأهداف0x14، ولا توجد تعليمات في القائمة تبدأ في 0x14. التعليمات التي تعمل في0x14هو 32 بت addl، والقائمة تظهر 64 بت addqقبل بايت واحد. البايتات الأربعة في الجدول تظهر كرمز.
التراجع المتكرر منlookupالرمز يقرأ ستة تعليمات، ويتبع القفز إلى 0x14و تقاريرaddl $0x1, %eaxلا يُدعي أيّ من الأربعة بايتات من الجدول ولا أيّ من الأثمانية بايتات من الملفات السابقةplus_one. .trap_report()فيmain.pyيطبخ المقارنة:
json"linearSweep": {
"instructions": 9,
"atJumpTarget": {"starts": "0x13", "text": "addq\t$0x1, %rax", "startsAtTarget": false},
"claimedDataBytes": 4,
"claimedPaddingBytes": 8
},
"recursiveDescent": {
"instructions": 6,
"atJumpTarget": "addl\t$0x1, %eax",
"claimedDataBytes": 0,
"claimedPaddingBytes": 0
}الاختبارtest_sweep_and_objdump_share_the_same_mistakeيثبت ذلكllvm-objdumpطباعةaddq $0x1, %raxفي0x13الـ"أوراكل" هو مسح خطي ويحتوي على نقطة عمياء. الاتفاق مع الـ"أوراكل" يتحقق من المفكّر، و"الـ"إنزال" يتحقق من إرشادات التشغيل.
الكتل الأساسية
حدد ألين الكتلة الأساسية في "تحليل التدفقات التحكمية" (1970). إنها تسلسل من التعليمات التي يتم إدخالها فقط عند أول تعليماتها وتتركها فقط في آخرها. يعيد ياكدان وغيره من إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إعادة إ إعادة إعادة إعادة إعادة إ إ إ إعادة إعادة إ إ إ إ إ إ إ إ إ إ إ إ إ إ إ إعادة إ إ إ إ إ إ إ إ إ إ إ إ إ إ إ إ إ إ إعادة إ إ إ إ إ إ إ إ إ إ إ إ إ إ إ إ إ إ إ إ إ إ إ
إن البناء هو مسح مزدوج عبر التعليمات المفكورة ، يتم فرزها حسب العنوان. يجمع المرور الأول القادة. المرور هو الدخول أو هدف الفرع أو القفز ، أو هدف الطاولة ، أو التعليمات بعد أي نقل التحكم. يبدأ المرور الثاني كتلة جديدة في كل رئيسي وفي كل فجوة في تسلسل العنوان.
pythonfor instruction in self.instructions:
if instruction.address in leaders or current is None or current.end != instruction.address:
current = Block(instruction.address, [])
self.blocks[instruction.address] = current
current.instructions.append(instruction)الحواف تتبع من آخر تعليمات كل كتلة. العودة ليس لها خلف. قفزة واحدة، علامة jumpفروع مشروطة لها اثنين، معلقتtakenوfallthrough. إن تعليمات بسيطة في نهاية كتلة تتراوح إلى كتلة أخرى، والتي تحدث عندما يكون التعليمات التالية هدفاً فرعياً.tableأو لا يوجد، ويتم تسجيل الكتلة فيunresolved. . .
هناك حقائق أخرى تظهر من نفس المسح.gradeفي-O0وتنتج خمسة منها. كتلة يقع عنوانها المبدئي داخل تعليمات قائمة طريقة أخرى هي حالة التداخل من الفخ.
الرسم البياني للتدفقات التحكمية للجميع
totalفي-O1هو 31 بايت و 13 تعليمات:
text 40: 85 f6 testl %esi, %esi
42: 7e 18 jle 0x5c <total+0x1c>
44: 89 f1 movl %esi, %ecx
46: 31 d2 xorl %edx, %edx
48: 31 c0 xorl %eax, %eax
4a: 66 0f 1f 44 00 00 nopw (%rax,%rax)
50: 03 04 97 addl (%rdi,%rdx,4), %eax
53: 48 ff c2 incq %rdx
56: 48 39 d1 cmpq %rdx, %rcx
59: 75 f5 jne 0x50 <total+0x10>
5b: c3 retq
5c: 31 c0 xorl %eax, %eax
5e: c3 retqالقادة هم0x40(الدخول)0x44(بعدjle()0x50(هدف المجموعة)jne()0x5b(بعدjne() و0x5c(هدف المجموعة)jleخمسة كتلة، خمسة حواف:
| From | To | Kind | Why |
|---|---|---|---|
0x40 | 0x44 | fallthrough | jle not taken, count is positive |
0x40 | 0x5c | taken | count is zero or negative, return 0 |
0x44 | 0x50 | fallthrough | the nopw is alignment inside the block, execution passes through it |
0x50 | 0x50 | taken | jne 0x50, the loop |
0x50 | 0x5b | fallthrough | loop exit, return the sum |
الـ6 بايتnopwفي0x4aهو التدليك حتى أن الحلقة رأس في 0x50يقع على حدود 16 بايت. يمكن الوصول إليها، لذلك تنتمي إلى الحدود0x44- البايت الواحدnopفي0x5fبعد آخر مرةretqو هو أيضا التدليك، ولا شيء يصل إليه. التدفق على الوظيفة يجعل حجر السادس لها، وunreachable()يُدرّسُه.
في-O2نفس الوظيفة هي 140 بايت مع حلقين.0x80يضيف ثمانية قيم لكل مرسل مع paddd، والباقي من السكالار يذهب إلى0xc0. المكتب لديه حوافين خلفية0x80 -> 0x80و0xc0 -> 0xc0و مجموعة الاختبارات تحقق في كليهما
السيطرة على الخوارزمية المتكررة
- حجر
dيهيمن على كتلةbعندما كل طريق من المدخل إلىbيمر من خلالd. السيطرة المباشرة علىbهو المهيمن علىbهذا أقرب إلىb. هذه الفكرة تعود إلى Prosser (1959) إلى Allen (1970) ، و هي هي الهيكل الذي يجب أن يضع الدروس 34ifوwhileصحيحاً
كتب كوبر و هارفي وكينيدي "الخوارزمية السائدة والسريعة" (2001). يجادلون بأن صيغة تدفق البيانات ، التي تعمل في الترتيبات اللاحقة العكسية مع تمثيل ضيق ، تضرب لينغواير-تارجان (1979) على الرسوم البيانية تحت حوالي ألف عقدة. ورقتهم تقول أن هدفها "ليس تقديم خوارزمية جديدة". يظهر أن الخوارزميات البسيطة يمكن أن تفوز في الممارسة العملية و هي أدوات تعليمية جيدة. وظيفة CFG لا يكون لديها أبدا ألف عقدة.
pythondef intersect(first: int, second: int) -> int:
while first != second:
while number[first] > number[second]:
first = idom[first]
while number[second] > number[first]:
second = idom[second]
return first
changed = True
while changed:
changed = False
for start in order[1:]:
processed = [p for p in self.predecessors(start) if p in idom]
candidate = processed[0]
for other in processed[1:]:
candidate = intersect(other, candidate)
if idom.get(start) != candidate:
idom[start] = candidate
changed = Truenumberيحتوي على وضع بعد ترتيب العكس لكل كتلة، مع الدخول عند 0. intersectيمر أصابعين فوق شجرة المهيمنة الحالية حتى يلتقيون.<هذا الرمز يستخدم أرقام بعد ترتيب العكس، لذلك المقارنة هي >كل شيء آخر يتبع رمزية الورقة
لأجلtotalفي-O1النتيجة هيidom(0x44) = 0x40،idom(0x50) = 0x44،idom(0x5b) = 0x50وidom(0x5c) = 0x40- الحافة0x50 -> 0x50هو حافة خلفية لأن هدفها يهيمن على مصدرها، و حافة خلفية تعبر عن حلقة (ألن 1970). back_edges()يعيد ذلك الاختبارtest_dominators_on_a_hand_built_diamond_with_a_loopيستخدم نفس الشفرة على تسلسل 16 بايت مع نقطة اتصال، لذلك intersectيرى اثنين من أسلافه
الجدول القادم في درجة -O0
في-O0كلانغ يجمع switchفيgradeإلى لغة جدول القفز المستقل عن الموقف:
text 5d: 83 e8 04 subl $0x4, %eax
60: 77 38 ja 0x9a <grade+0x6a>
62: 48 8b 45 f0 movq -0x10(%rbp), %rax
66: 48 8d 0d 00 00 00 00 leaq (%rip), %rcx
0000000000000069: R_X86_64_PC32 .rodata-0x4
6d: 48 63 04 81 movslq (%rcx,%rax,4), %rax
71: 48 01 c8 addq %rcx, %rax
74: ff e0 jmpq *%raxاقرأها على أنها أربعة حقائقsubl $0x4يليها jaهو إختبار الحدود: المؤشرات فوق 4 تذهب إلى حالة الافتراض في 0x9a، لذا الجدول لديه خمس إدخالات.leaqمع إعادة توطينها يضع عنوان الجدول في %rcx، والقسم السابق أظهر أن العنوان هو .rodata+0x0- المُؤمنونmovslqتحميل إدخال مؤلف من 32 بت مقياس 4 addqيضيف قاعدة الجدول، لذلك كل مدخل هو المسافة من الجدول إلى كتلة الحجرة.
في ملف الكائن كل بايت من الجدول هو صفر. الأهداف موجودة فقط كتحويلات في .rela.rodata:
textRELOCATION RECORDS FOR [.rodata]:
OFFSET TYPE VALUE
0000000000000000 R_X86_64_PC32 .text+0x91
0000000000000004 R_X86_64_PC32 .text+0x8c
0000000000000008 R_X86_64_PC32 .text+0x87
000000000000000c R_X86_64_PC32 .text+0x82
0000000000000010 R_X86_64_PC32 .text+0x86التطبيقS + A - Pللدخولkفي التكافؤ4kالقيمة المخزنة هي.text + A - (.rodata + 4k)، والرقم يضيف .rodata + 0لذلك الهدف هو.text + A - 4k. دخول 0 هو 0x91 - 0 = 0x91, المدخل 1 هو 0x8c - 4 = 0x88، والدخل 2 هو 0x87 - 8 = 0x7fالإدخالات 3 و 4 هي0x82 - 12 = 0x76و0x86 - 16 = 0x76الحالات السادسة والسبعة والثمانية والنابعة والعاشرة منscore / 10اذهب إلى الكتل التي تخزن 1، 2، 3، 4 و 4. وهذا يطابق خط المصدر C للخط، بما في ذلك الحالة المشتركة ل 9 و 10.
recover_table()يُفعل هذا الحسابjump_table_resolver()يطابق الإيقاع العكسيjmpq. إنه يجدaddqالذي جعل السجل،movslqالذي تحمله،leaqالذي جعل القاعدة والحدودja. ثم يدفع الانخفاض الأهداف الأربعة المختلفة ويتم الاستمرار. يكون للجهاز التنفيذي المرتبط القيم النهائية في الجدول ولا توجد إعادة التنقل. ثم تقوم نفس الوظيفة بقراءة الإدخالات الموقعة بـ 32 بت وإضافة عنوان الجدول ، و evidenceتسجل المسار الذي أدى إلى تحقيق الأهداف.
أرقام gradeفي-O0، منpython3 code/main.py cfg code/fixtures/ledger-x86_64-O0.o grade:
| Method | Instructions | Blocks | Edges | Bytes | What is wrong |
|---|---|---|---|---|---|
| linear sweep | 34 | 9 | 7 | 118 code + 10 padding | 5 unreachable blocks, no edge into the four case blocks, padding decoded as nopw |
| recursive descent | 25 | 3 | 2 | 82 code | 36 code bytes never reached, jmpq *%rax at 0x74 unresolved |
| descent with the table | 33 | 8 | 11 | 118 code | nothing |
مع الطاولة شجرة المهيمنة كاملة.idomمن كل كتلة قضية0x62، الحجر مع القفز.idomمن0xa1هو المدخل0x30، لأن المسار الافتراضي من 0x9aيصل0xa1بدون أن تمر0x62. . .
ما الذي فعله المُحسّن لتحديد الدرجة عند -O1
سترىgradeووصف بأنه مفاتح يجمع إلى جدول قفزة. البايتات تقول شيئا أكثر تحديدا.-O1و-O2لا يوجد قفزة غير مباشرةgradeعلى الإطلاق:
text 2c: 83 f9 04 cmpl $0x4, %ecx
2f: 77 0c ja 0x3d <grade+0x2d>
31: 89 c8 movl %ecx, %eax
33: 48 8d 0d 00 00 00 00 leaq (%rip), %rcx
0000000000000036: R_X86_64_PC32 .rodata+0x10
3a: 8b 04 81 movl (%rcx,%rax,4), %eax
3d: c3 retqإضافة للانتقال0x10+ 4 المعتاد يضع الطاولة في .rodata+0x14الحمل هو ساديmovl، والبايتات هناك01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00 04 00 00 00. استبدل المجمع خمسة كتلة الحالة مع جدول من خمسة قيم العودة. هناك نقل، جدول، ومراقبة الحدود. تدفق التحكم هو ثلاثة كتلة وثلاث حواف مع فرع مشروط واحد. find_value_tables()يبلغ عن ذلك كـvalue-tableمع إدخالات [1, 2, 3, 4, 4]وevidence: "bytes". . .
grade_labelفي-O2يذهب خطوة أخرىlabelsتعدد من خمسة مؤشرات السلك أصبح جدول من خمسة 32 بت تعويضات في .rodata+0x0كل مدخل يحمل رقمR_X86_64_PC32إعادة التنقل ضد قسم السطر، ويتم إضافة عنوان الجدول إلى الإدخال الذي تحمله. recover_table()يقرأ تلك الإنتقالات الخمسة ويعود تعويضات0, 2, 4, 6, 8في.rodata.str1.1، التي تحملF،D،C،BوA. نفس الرياضيات التي تعيد جدول القفز تعيد جدول البيانات النسبية ، لأن كليهماS + A - P" في مُعَيَّن " معين .
الدروس من الإعدادين هي تسمية ما تظهر البايتات. جدول القفز يغير الرسم البياني للتدفق التحكم. جدول القيمة يغير الحمل. دعوة كل من "المفتاح" يخفي الفرق الذي يجب أن يعيد إعادة بناء الدروس 34.
ما الذي يعطيك إياه و (غيدرا) هنا
rea 6.5.0 مع مزود Ghidra 12.1.4 يعيد نص إدراج وظيفة من procedure_assemblyكحبل واحدinspect_native_instructionيعيد البايتات والطول ونص المقرر، وعلامات التشغيل، والإشارات المطبوعة، وتدفق تعليمات واحدة، مع تعويض مصنف من رمز p. xrefsيعيد العناوين المصدرة الفريدة المرتبة من Ghidra ReferenceManager.getReferencesTo. .analyze_functionيحمل جهاز CFG و bodyمن الوظيفة. procedure_infoيقدم الهيئة على أنها نطاقات شاملة ويحذر من أن "الفجوات والذيل المشترك يجب ألا يعاملون كبايتات مملوكة".
كل هذه الحقائق هي نزول غيدرا التكراري بالإضافة إلى المحللين، المتسلسلين.instruction_flow_evidence،function_referencesوjump_table_evidenceإذاً، فإن النتيجة تسمي القواعد التي صنعتها. ما لا يمكن أن تفعله هو إظهار عملها. عندما لا تجد غيدرا الجدول، فإن التدفق "غير معروف"، وتفتقر كتلة الحالة من المُتصلين، والمدعوين، والسي إف جي.
لديك الآن العمل، إركضpython3 code/main.py cfgعلى نفس الموضوع ومقارنة البلوك بدء، الحافة العد، والجدول المردد مع ما يعيد المقدم. حيث أنها توافق لديك اثنين من مشتقات مستقلة. حيث أنها تختلف لديك عنوان محدد للنظر في، وهو ما الدروس 35 تسجل كجهل.
ملاحظة على المفكّر كبرنامج. Capstone 6.0.0-Alpha10 (2026-07-21) ثابت خارج الحدود يقرأ ويكتب تحت CVE-2026-55893 و CVE-2026-55894, و 5.0.9 (2026-05-28) إصلاحات CVE محمولة خلفيا. يقرأ المفكّر بايتات لم يكتبها. الحدّ الـ15 بايت، تحقق التقطيع في _Cursor.byte()، ورفض تخمين حماية رمز غير معروفmain.pyمن المدخل الذي لم يتم بناؤه له
بناءها
أطلق التجربة:
bashcd phases/13-tools-and-protocols/33-disassembly-and-control-flow
python3 code/main.pyإن الخروج هو تقرير JSON واحد. لكل كائن ثابت يعطي اتفاقية الوهم. لكل وظيفة يعطي تعليمات، كتلة، وحافة العد تحت الأساليب الثلاثة. كما يدرج أيضا كتلة غير قابلة للوصول، قفزات غير مباشرة غير حل، حواف خلفية، السيطرة الفورية، وأي جداول. ينتهي مع مقارنة الفخ.
الشكل المتوقع:
json{
"fixtures": {
"ledger-x86_64-O0.o": {
"oracle": "86/86 instructions agree with llvm-objdump",
"functions": {
"grade": {
"range": "0x30-0xa6",
"paddingBytes": 10,
"linearSweep": {"instructions": 34, "blocks": 9, "edges": 7, "unreachableBlocks": ["0x76", "0x7f", "0x88", "0x91", "0xa6"], "...": "..."},
"recursiveDescent": {"instructions": 25, "blocks": 3, "edges": 2, "unresolvedIndirectJumps": ["0x74"], "...": "..."},
"recursiveDescentWithTables": {"instructions": 33, "blocks": 8, "edges": 11, "...": "..."},
"tables": [{"kind": "jump-table", "table": ".rodata+0x0", "count": 5, "entries": ["0x91", "0x88", "0x7f", "0x76", "0x76"], "evidence": "relocation", "...": "..."}]
}
}
}
},
"sweepTrap": {"...": "..."}
}اقرأcode/main.pyفي هذا الترتيب:
ElfObjectيقرأ عناوين القسم والرموز.rela.*الإدخالات، وsymbolize()اسم عنوان الطريقllvm-objdump-لا، لاDecoder.decode()يقرأ المواعيد المسبقة و REX، ثم يرسل على خريطة رمز الخيارات ذات بايت واحد أو بايتين._State.modrm()يقرأ ModRM، SIB، والتحرك، ويُسجل تعويضات حقل التحرك.resolve_reference()يطبقS + A - Pإلى إعادة التأهيل في مكان النزوح أو في ساحة مباشرة.linear_sweep()وrecursive_descent()هي الطرق الثنائية، كل منها مع فشله المسجل.recover_table()وjump_table_resolver()قراءة الجدول من خلال عمليات إعادة توزيعه أو بايتاته.ControlFlowGraphيُبني القادة والبلوك والحواف المخطوطة، وdominators()هو الخوارزمية التكرارية.analyze_function()يستخدم كل طريقة على وظيفة واحدة وcompare_with_oracle()يُفحص المُشفّر
هناك أمرين آخرينpython3 code/main.py disasm FILE.oيطبخ قائمة مع هدف الإنتقال بعد كل تعليمات لديها واحدة. python3 code/main.py cfg FILE.o FUNCTIONيطبخ تحليل وظيفة واحدة.
استخدمها
استخدم الأساليب الثلاثة في هذا الترتيب على كائن جديد.
- أبدأ بمقارنة النبي صلى الله عليه وسلم أولاً
llvm-objdumpعلى تعليمات بسيطة لديها خطأ، وكل هيكل لاحقا يرثها. - قم بتشغيل النزول الترددي من كل رمز وظيفة وقراءة
unresolvedكل دخول هو قفزة أو مكالمة غير مباشرة التي لا تملك أهداف - لكل قفزة غير حل، ابحث عن لغة الجدول والتحويل الذي يشير إليها. إذا لم يتماشى لغة الجدول، اقرأ البايت قبل القفزة واكتب ما ترى. هذه الملاحظة هي الأولى غير معروفة في الدروس 35.
- مقارنة المصفوفة والتنزيل بايت بـ بايت. البايتات التي فقط المصفوفة تدعي أن تكون ملء أو بيانات. البايتات التي لا تدعي لا يمكن الوصول إليها أو تنتمي إلى وظيفة لم تجدها.
- قم ببناء CFG من الهبوط مع الجداول التي تم استعادتها وقراءة الحواف الخلفية. كل واحد هو حلقة التي سوف تتحول الدروس 34 في
whileأوfor. . .
احتفظ بملفات النص في النص مع الأشياء. عندما تنتقل إلى نسخة مُحاملة جديدة، أعد تسجيلها، لأن قائمة تغيير هي جهاز تغيير.
المختبر التفاعلي
المختبر (أ): فك الرمز على الورق، ثم تحقق
منcodeالإرشادات، مفتوحة Python:
bashcd phases/13-tools-and-protocols/33-disassembly-and-control-flow/code
python3 -qأركض
pythonfrom main import *
decode(bytes.fromhex("48 8d 0d 00 00 00 00"), 0).text
decode(bytes.fromhex("8d 44 08 fa"), 0).text
decode(bytes.fromhex("66 66 66 66 66 2e 0f 1f 84 00 00 00 00 00"), 0).lengthاكتب ما تتوقعه قبل كل مكالمة ثم إزالة الـ48من الأول وشاهد تغيير حجم العرض ثم حاولdecode(bytes.fromhex("0f 0a"), 0)و قراءة DecodeError. . .
المختبر ب: إعادة التأهيل على موقع
pythonelf = load_fixture("ledger-x86_64-O0.o")
code = elf.section(".text").data
lea = decode(code, 0x66)
lea.displacement_offset
resolve_reference(elf, lea)المرجع يقول.rodata+0x0. تغيير العنوان إلى0x116فيgrade_labelو قراءة المرجع في .dataثم فك رموز الـcallqفي0x10eو تحلّي الأمر، الحقل المباشر يحملR_X86_64_PLT32 grade-0x4، والهدف المحدد هو0x30، وهوgrade. . .
المختبر (ج): شاهد أن الطرق لا تتفق
pythontrap = load_fixture("sweep-trap.o")
text = trap.section(".text").data
[i.text for i in linear_sweep(text, 0, 0x20).instructions]
[i.text for i in recursive_descent(text, [0], 0x20).sorted_instructions()]عد التعليمات في كل قائمة و اعثر على تلك التي تبدأ من 0x13ثم قم بتغيير آخر بايت في الجدولsweep-trap.sمن0x48إلى0xe8، إعادة بناءها، و إعادة تشغيلها مرة أخرىaddlو retqإلىcallqمع هدف خارج القسم يبدو أن الوظيفة لا تعود
المختبر D: المهيمنين يدوياً
pythonanalysis = analyze_function(load_fixture("ledger-x86_64-O1.o"), "total")
analysis.cfg.edges
analysis.cfg.reverse_postorder()
analysis.cfg.dominators()
analysis.cfg.back_edges()رسم الخمسة كتلة على الورق وحساب السيطرة قبل أن ننظر إلى النتيجة ثم تشغيل نفس الخطوط الأربعة علىgradeفي-O0و اشرح لماذاidom(0xa1)هو0x30و ليس0x62. . .
مختبر التدريب
إضافة موقع مكالمة غير مباشرة إلى الأجهزة وتوسيع التحليل.
المتطلبات:
- اكتب وظيفة C التي تدعو من خلال مؤشر وظيفة مختارة من صف صغير. بنيها في
-O0و-O1و إلتزام الأشياء معهمllvm-objdumpرسالة - اجعل المفكّر يتوافق مع الـ (أوراكول) على كلتا الأشياء. أضف رموزًا فقط عندما تكون هناك تعليمات في الجهاز تحتاج إليها.
- التمديد
recursive_descent()لذا ، يقوم مكالمة غير مباشرة بتسجيل موقع المكالمة فيcallsمعNoneكهدف و يستمر وراءه - اكتب حللا يقرأ صف المؤشرين للعمل من خلال عمليات إعادة توطينها ويعطي تقارير عن المشاركات المحتملة.
- إضافة مجموعة الدعوات إلى تقرير الوظيفة على النحو
indirectCallsمع نوع الأدلة لكل هدف - اكتب اختبار يفشل عندما يعيد القرار هدفاً ليس رمزًا للعمل.
النجاح يعنيpython3 code/main.py cfgاسم كل مكالمة ممكنة مع نقل يثبت ذلك، ومقارنة الوساطة لا تزال تنتقل على كل كائن.
درساً أساسياً
هذا الدروس يتضمنoutputs/skill-disassembly-and-control-flow.md. استخدمها لتحويل اسم كائن وسم عمل إلى قائمة معينة ، والجبرات ، والحواف المطبوعة ، والهيمنات ، وكل نقل غير حل. يتطلب مقارنة النبي ، وطريقتي التفكيك ، وحساب البايت ، ودليل نقل لكل جدول تم استعادته ، وإصدار ما ظل مجهولا.
تحقق من ذلك
إشغال التجربة والسلسلة التحديدية:
bashcd phases/13-tools-and-protocols/33-disassembly-and-control-flow
python3 code/main.py
python3 code/tests/test_main.pyيجب أن تثبت التحقق:
- المُشفّر يوافق على
llvm-objdump -dعلى جميع 237 تعليمات الأربعة كائنات ثابتة - REX.W يختار 64 بتة العمليات و REX.R و REX.B تمديد حقل السجل
- ModRM و SIB يعطي أوبراند ذاكرة AT&T، بما في ذلك النطاق، المؤشر، والتحرك الموقع
- يتم تسجيل التحرك النسبي لـ RIP مع تعويضات مجالها داخل التعليمات
- المشاريع المتكررة القديمة و إعادة تشكيل القسم مثل القرآن
- إلزامي
66يختار المقبل رمز SSE opcode ويترك السجلات 32-bit - 8-bit الفورية يتم تمديد الإشارة للحسابات وغير الموقع لعدة التحولات
- إرشاد أكثر من 15 بايت، إرشاد قصير، ورفع رمز الاختيار غير معروف
DecodeError R_X86_64_PC32علىleaيقرر على الطاولة وR_X86_64_PLT32علىcallيُحلّلُ إلى الـ (كالي)- المصفاة تقوم بتفكيك جدول الفخ كرمز ولا توجد تعليمات في الهدف القفز
- السقوط لا يطالب أبداً بالطاولة أو بالملابس ويقرأ
addl $0x1, %eaxفي الهدف llvm-objdumpيرتكب نفس الخطأ الذي يرتكبه المفتاح- يتم تسجيل دخول داخل تعليمات كصراع
- قفزة غير مباشرة غير حل توقف الهبوط وتدخل في قائمة
totalفي-O1لديه خمسة كتلة، خمسة حواف مدرجة، واحد حافة خلفية، والهيمنة المنتظرة مباشرة- الماس المصمم يدويا مع حلقة يعطي المهيمنين الخوارزمية تحدد
totalفي-O2لديه حلقينgradeفي-O0يُعطى طاولة قفزة خمس دخولات من.rela.rodataمع أهداف0x91, 0x88, 0x7f, 0x76, 0x76- بدون الطاولة
gradeفي-O0لديه ثلاث كتلة و قفزة واحدة غير حل gradeفي-O1و-O2هو جدول القيمة[1, 2, 3, 4, 4]بدون قفزة غير مباشرةgrade_labelفي-O2يقرأ جدول سلسلة نسبي من خلال خمسة عمليات نقل- الرقم التفاعلي يضم نفس البيانات التي ينتجها التحليل
أساليب فشل الإنتاج
| Failure | What the weak tool reports | What the analysis must prove |
|---|---|---|
Data inside .text | A plausible listing with every byte decoded | The descent claims no data byte and the jump target starts an instruction |
| Padding after the last return | A nopw instruction at the end of the function | The bytes are unreachable from the entry |
| Indirect jump through a table | "jmp rax" and four dead blocks | Table address from the lea relocation, entry count from the bounds check, targets from .rela.rodata |
| Switch turned into a value table | "a jump table with a relocation" | A movl load from .rodata+0x14, three blocks, no indirect transfer |
| Call with a zero displacement | callq 0x113, the next instruction | R_X86_64_PLT32 grade-0x4 resolves to 0x30 |
| Decoder guesses an unknown opcode | A mnemonic that looks right | DecodeError with the address, and the sweep records the byte |
| Instruction longer than 15 bytes | A very long "instruction" | The cursor raises before reading byte 16 |
| Branch into the middle of an instruction | Two overlapping listings | A conflict record with both addresses |
| Loop reported as straight-line code | No while in the pseudocode | A back edge whose target dominates its source |
| Oracle captured from a different compiler | Tests pass on stale text | The fixture README names the exact compiler and command |
قاعدة العمليات
فكّر ضدّ أوراكول، وتحكم تدفق المشي من إدخالات معروفة، وتحسب كل بايت كما يُزعم، أو ملء، أو بيانات، أو غير قابل للوصول. استعاد كل هدف غير مباشر من إعادة التنقل أو جدول يمكنك الإشارة إليه. سجّل الباقي كغير معروف، مع العنوان.
المزيد من القراءة
- Intel 64 and IA-32 Architectures Software Developer's Manual, Volume 2, Chapter 2: Instruction Format
- AMD64 Architecture Programmer's Manual Volume 3, Chapter 1: Instruction Encoding
- System V Application Binary Interface, AMD64 Architecture Processor Supplement, Table 4.9: Relocation Types
- Schwarz, Debray, Andrews, "Disassembly of Executable Code Revisited" (WCRE 2002)
- Andriesse et al., "An In-Depth Analysis of Disassembly on Full-Scale x86/x64 Binaries" (USENIX Security 2016)
- Allen, "Control Flow Analysis" (1970)
- Cooper, Harvey, Kennedy, "A Simple, Fast Dominance Algorithm" (2001)
- Lengauer, Tarjan, "A fast algorithm for finding dominators in a flowgraph" (1979)
- Capstone 6.0.0-Alpha10 release notes
- 6.5.0 مصدر في التج
3b79af2أن هذا الدروس يصف:README.md،docs/ghidra-provider.md،docs/ghidra-dos.md،docs/reverse-engineering-tools.md،bridge/ghidra/ReaGhidraBridge.java،src/contracts/officialToolContracts.ts،src/ghidra/GhidraAnalysisProfile.ts. . .
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.