ربط، تحميل، وتقطيع الثنائيات: PLT، GOT، الاستيراد، إصلاحات، وتحديد الوظيفة
Type: Build
Languages: Python, C, x86-64 and arm64 assembly
Prerequisites: Phase 13 · 32 (ELF from scratch), 33 (disassembly and control flow), 38 (calling conventions)
Time: ~160 minutes
أهداف التعلم
- تحدد من سجل نقل وترتيبات الرمز ما يفعله محرر الروابط منه: مسافة ثابتة، مدخل PLT مع فتحة GOT، أو نقل ديناميكي.
- اقرأ القسم الديناميكي، PLT، وال GOT من ELF المستقلة من الموقف التنفيذية واتبع مكالمة واحدة من خلالها مع صيغ psABI.
- قراءة كائن COFF (20 طابع فيديو الصور، إدخالات قسم 40 طابع، رموز 18 طائر، نقل 10 طائر) وإدخال، تصدير، وتحويل القاعدة من إطار صورة PE32+.
- فكّر سلسلة إصلاحات مقيدة من " ماخ " كلمة بعد كلمة و تطابق كل كلمة مع
dyld_info. . . - أوضح ما
stripإزالة ما تبقى في كل شكل، وإشغال البرنامج المزيف لإثبات أنه لا يزال يعمل. - استعادة وظيفة البدء من إصدار arm64 المزيل مع
LC_FUNCTION_STARTS، مسحات المبادئ، والهدف الدعوة، تسجيل كل طريقة ضد جدول الرمز من التوأم غير المقسم، وتسمية الوظائف مع توقيع على شكل FLIRT.
المشكلة
أنتِ ترتبطينmain.cضدlibledger.dylibو سأل وكيل ما هي وظائفهاmainمكالمات على0x1000005a0. العميل فك التعليمات وقرأbl 0x100000694هذه العنوان تحتوي على 12 بايتadrp x16،ldr x16, [x16, #0x38]،br x16- لقد تبع العميل الحمولة إلى0x100004038، قراءة ثمانية بايت 00 00 00 00 00 00 10 80وبلغت عن مكالمة من خلال مؤشر فاسد إلى مساحة النواة
ثمّ خلعتِ البناء الثابت لنفس البرنامج وسألتِ كم وظيفة لديهnm، رأى اسم واحد ،__mh_execute_headerو أجاب على واحدة
كلتا الإجابات تقرأ الملف كما لو كان العملية الجارية.0x100004038بيت 63 يقول الالتزام، اثني عشر بيت يقول أين التغيير التالي، والبيتات المنخفضة تقول أي واردة لالتزام._putsقبل ذلكmainالملف المزيف فقد جدول الرمز و احتفظ بهLC_FUNCTION_STARTS، والذي يرد جميع الوظائف الخمسة تبدأ في تسعة بايت
هذه الدروس تبني خمسة قراء في المكتبة القياسية بايثون. قراءة COFF لمتعلقاتنا الخاصة وقراءة PE32+ لإدخال وتصدير وتحويل القواعد الإدراجات. قراءة Mach-O لأوامر الحملة، والحذف، والجدول الرمز غير المباشر، سلسلة إصلاح السلاسل، وتصدير محاولة، التحقق كلمة مقابل كلمة dyld_info. قراءة ELF للقسم الديناميكي، PLT، وال GOT، بالإضافة إلى خطط الذي يقول ما لنسخة PIE من الأشياء لدينا سوف تخلق. عثور الوظيفة لإنشاء سلب مع أربعة طرق ونقطة لكل منها. مبدع توقيع على شكل FLIRT، مع البايتات الترحيل wildcarded و CRC16 من الذيل.
فقط ماخ-أو وصلات على هذه الآلة. الأجهزة التنفيذية arm64 والديليب تم ربطها، نزع، وتشغيل. الكائنات ELF و COFF مرتبة هنا مع clang -target، ولا يوجد محرر الروابط ELF أو PE مثبت ، لذلك تم كتابة الصور المرتبطة في هذه الصيغ بايت بوايت من خلال المواصفات من قبل نصوص Python في مجلد الإصلاحات. llvm-objdumpويقرأها، وكذلك مسح المكتبة، لم يتم تشغيلها، ويقول النص كذلك حيث يهم.
ما الذي يقوم به محرر الروابط مع نقل
دروس 32 قراءة سجلات الإنتقال والدرس 33 تطبيق S + A - Pإلىleaسجل يسمي مكانP، نوع، رمز Sو إضافةA. في الجدول 4.9 من x86-64 psABI يقدم صيغة واحدة لكل نوع ، مع Lللوضع الذي يقع فيه إدخال الرمز في PLT، G" لـ " الله " تعويضها " .GOTلـ عنوان GOT، و Bثلاث نتائج تغطي كائناتنا
يقوم محرر الرابط بتسجيل القيمة في الحقل ويتم إزالة السجل. R_X86_64_PC32على الرغم من أن هذه الصورة لا تتغير على الإطلاق، لا يهم عنوان الحمل.
يقوم محرر الروابط بإدخال PLT و GOT. R_X86_64_PLT32ضد وظيفة غير محددة هو هذه الحالة.L + A - P، المسافة إلى المدخل المدخل يقفز من خلال فتحةR_X86_64_JUMP_SLOTسجل في.rela.pltيخبر المصلح الديناميكي أن يملأ الفجوة.
محرر الروابط يحافظ على نقل لتحميل. R_X86_64_64هو عنوان مطلق ثمانية بايت، والجهاز التنفيذي المستقل عن الموقف لا يعرف عنوانه حتى يتم تحميله.R_X86_64_RELATIVEمع صيغةB + Aفي.rela.dyn. . .
main-x86_64-O1.oلديه 18 سجل: 14 في .text، 2 في .data.rel.roو 2 في.rodata. .plan_dynamic_link()فيcode/main.pyيطبق عليها القواعد الثلاثة مع ledger-x86_64-O1.o:
| Record | Count | Decision | Why |
|---|---|---|---|
R_X86_64_PC32 to .rodata, .L.str, fns, the table in .rodata | 16 | in place | the symbol is defined in the inputs |
R_X86_64_PLT32 to percent_change, grade, total, grade_label | 4 | direct, no PLT | defined in ledger-x86_64-O1.o, so the link editor reduces PLT32 to PC32 |
R_X86_64_PLT32 to puts twice and printf twice | 4 | PLT entry and GOT slot | undefined, provided by a shared library |
R_X86_64_64 to grade in fns[] | 2 | R_X86_64_RELATIVE | an absolute field in a PIE |
اثنين من الواردات تعطى.plt48 بايت (PLT0 + إدخالين من 16) ، a .got.plt40 بايت (ثلاثة فتحات محجوزة بالإضافة إلى اثنين) ، و .rela.plt48 بايت. السجلين النسبيين يعطون.rela.dynهذه الأرقام تأتي من المواصفات المطبقة على سجلاتناtiny-dyn-x86_64تنفيذ جزء PLT من هذه الخطة لنفس الواردات، وتتطابق أحجامها مع الخطة.
الكمبيوتر يكتب PLT32في كل مكالمة، المحددة أو غير المحددة، منذ النقاط الثنائية 2.31 (HJ Lu، 2018-02-13) و LLVM 7.0.0 (D43383, 2018-02-20).PLT32في كائن لذلك يقول "دعوة" ، ولا يقول أي شيء عن ما إذا كان إدخال PLT سيكون موجودا. يظهر نوعان آخران في بناءات أخرى. R_X86_64_GOTPCRELXوR_X86_64_REX_GOTPCRELX(G + GOT + A - P) طلب فتحة GOT بدون PLT ، والتي -fno-pltيُنتج ويمكن لمحرر الروابط أن يُسترخِل إلى إصدار مباشرleaأوcallعندما يكون الرمز محلي (psABI الملحق ب). R_X86_64_32وR_X86_64_32Sلا يمكن أن تحتوي على عنوان في PIE والربط يفشل. لا يوجد أي نوع في كائنينا.
القسم الديناميكي، PLT، والجوت
يقول الفصل 5 من gABI أن المفروض الذي يشارك في الربط الديناميكي لديه واحد PT_INTERPعنوان البرنامج، الذي يجب أن يسبق أي قطاع قابل للتحميل، و PT_DYNAMIC، والتي تغطي .dynamicالقسم. القسم هو مجموعة منElf64_Dynأزواج، علامة وقعت وقيمة، تنتهي ب DT_NULL. . .
tiny-dyn-x86_64هو 5184 بايت مكتوب من قبل build_tiny_dyn_elf.pyمن غابي و psABI.0x1000:
| Tag | Number | Value | Meaning |
|---|---|---|---|
DT_NEEDED | 1 | libc.so.6 | a string table offset, 0xb, the library to load |
DT_HASH | 4 | 0x1a0 | the SysV hash table |
DT_STRTAB | 5 | 0x188 | .dynstr |
DT_SYMTAB | 6 | 0x140 | .dynsym, 24 bytes per entry |
DT_STRSZ | 10 | 0x15 | 21 bytes of strings |
DT_SYMENT | 11 | 0x18 | 24 |
DT_PLTGOT | 3 | 0x10c0 | .got.plt |
DT_PLTRELSZ | 2 | 0x30 | two 24-byte records |
DT_PLTREL | 20 | 7 | the records are DT_RELA |
DT_JMPREL | 23 | 0x1b8 | .rela.plt |
DT_FLAGS_1 | 0x6ffffffb | 0x08000000 | DF_1_PIE, a GNU tag |
DT_NULL | 0 | 0 | end |
قال المُساعد لماذاDT_JMPRELهو منفصل: "يحتوي على عنوان إدخالات التنظيم المرتبطة فقط بطاولة ربط الإجراءات. إفصال هذه إدخالات التنظيم يسمح للمرتبط الديناميكي بتجاهلها أثناء بدء عملية، إذا تم تمكين التزامن الباكس". DF_BIND_NOWفيDT_FLAGS،DF_1_NOWفيDT_FLAGS_1أوLD_BIND_NOWفي البيئة تطفئ الارتباط البطيء والربط الديناميكي يعالجهم عند البدء.
PLT0 ومدخلات المجموعات
في قسم psABI 5.2 يعطي الشكلين في الشكل 5.2.
text 1f0: ff 35 d2 0e 00 00 pushq 0xed2(%rip) # 0x10c8 = GOT+8
1f6: ff 25 d4 0e 00 00 jmpq *0xed4(%rip) # 0x10d0 = GOT+16
1fc: 0f 1f 40 00 nopl (%rax)
200: ff 25 d2 0e 00 00 jmpq *0xed2(%rip) # 0x10d8 = GOT+24, the slot for puts
206: 68 00 00 00 00 pushq $0x0 # index 0 into DT_JMPREL
20b: e9 e0 ff ff ff jmp 0x1f0 # PLT0قم بالعدول مرة واحدة، التحرك النسبي للـ RIP هو النسبي للاتجاه التالي. 0x1f6 + 0xed2 = 0x10c8، وهو.got.plt + 8. .0x1fc + 0xed4 = 0x10d0، وهو.got.plt + 16. .0x206 + 0xed2 = 0x10d8، الفناء الرابع0x210 - 0x20 = 0x1f0كل مدخل هو 16 بايت، لذلك المدخل لexitيبدأ في0x210، يدفع$0x1، ويقرأ0x10e0. . .
الحكومة الوطنية تحفظ ثلاثة فتحات. psABI 5.2: الدخول الصفر يحمل عنوان _DYNAMICو "الدخولات الأولى والثانية في الجدول العالمي للتعويض أيضا محجوزة"..got.pltيقرأ0x1000, 0, 0, 0x206, 0x216. glibc يملأ الفناء الأول معstruct link_mapو فتحة اثنين مع _dl_runtime_resolveعندما تحميل الملف، فتحة ثلاثة تحملها0x206في الملف، وهو pushqداخلputs@plt. psABI: الفقرة "في البداية ... تحتوي على عنوان التعليمات التالية pushq".
لذا ، المكالمة الأولى تُجرىcall 0x200،jmpq *0x10d8أرض علىpushq $0،jmp 0x1f0يدفع GOT+8 ويقفز عبر GOT+16 إلى الحل، والذي يقرأ تسجيل 0 من DT_JMPREL،R_X86_64_JUMP_SLOT puts، يبدوputsأعلى فيlibc.so.6، يكتب عنوانها في0x10d8ثم يقفز هناك، والاتصال الثاني يبدأcall،jmpq *0x10d8ويقول ديبر إن "التكلفة العامة للجميع باستثناء المكالمة الأولى هي "فقط" قفزة واحدة غير مباشرة".%r11يشرح اختيار السجل في الدروس 38: إنه سجل خدش لذلك "الرمز في PLT لا يحتاج إلى تسريب أي سجلات".
موقع المكالمة في.textيقرأe8 d4 ff ff ffفي0x227و0x22c - 0x2c = 0x200. .llvm-objdumpبصماتcallq 0x200 <puts@plt>وسمي الإدخال من .rela.pltالسجل، وهذا هو كيف plt_entries()اسمها أيضاً
ما لا يوجد في الملف
الملف لا يحتوي على أيPT_GNU_RELRO. اقترح جاكوب جيلينك أن RELRO في قائمة المواد الثنائية في 2004-01-06: نقل القسمات المكتوبة فقط أثناء الإنتقال إلى بداية القطاع المكتوب ، بحيث يمكن للربط الديناميكي mprotectالنص الافتراضي في GNU ld يضع.init_array،.fini_array،.data.rel.ro،.dynamicو.gotداخل، ويحافظ فقط على ثلاثة فتحات محجوزة من.got.pltداخل، 24 بايت، لأن الفواح الأخرى مكتوبة بثرثرة.-z nowالمجموعاتDF_BIND_NOWويقوم بتحريك الكل.got.pltداخل، كامل RELRO، لأن لا شيء مكتوب بعد بدء._dl_protect_relroمكالماتmprotect(start, end - start, PROT_READ)على النطاق المتحبط بالصفحة-fno-pltيذهب خطوة أخرى على جانب المُجمّع: المكالمات تذهب عبر jmpq *name@GOTPCREL(%rip)ويقول دليل المجلس الخليجي أن "كل الرموز الخارجية يتم تحديدها في وقت الحمل".
الملف لا يحتوي على قسمات نسخة أيضاً.libc.so.6يضيف.gnu.versionواحدElfxx_Halfفي كل.dynsymالدخول، و.gnu.version_r، أVerneedلكل مكتبة معVernauxسجلات لكل اسم نسخة. printf@GLIBC_2.2.5الوسائل: إدخال printfفي.gnu.versionيحتوي على مؤشرVernauxالذيvna_otherيساوي هذا المؤشرvna_name"GLIBC_2.2.5" في الموقعVerneedالذيvn_fileهو "libc.so.6". مؤشر 0 هو محلي، 1 هو عالمي، وبيت 15 (VERSYM_HIDDEN،0x8000) يخفي نسخة من محرر الروابط.
جدول الـ " هاش " هو " SysV "DT_HASHمع علبة واحدة وسلسلة ثلاثية الدخول، 24 بايت.DT_HASHأوDT_GNU_HASHهذا الملف لا يصدر شيئاً، ولم يتم تشغيله هنا، لذا ما إذا كانت glibc تقبل جدول واحد السلة غير مؤكدة.
.dynsym , .symtab , و strip
-الـ (جابي) يُصنع.dynsymو.dynstrالقسمات المخصصة و.symtabو.strtabلا يتم تخصيص الرمزات غير المخصصة. يقرأ المصلح الديناميكي الزوجة الأولى وليس الثانية أبدا. "لا يستخدم جدول الرمز العادي في وقت التشغيل وبالتالي ليس من الضروري إزالة ثنائي منه. لا يؤثر على الأداء".
جينوstrip --strip-allيزيل.symtab،.strtab،.debug_*و.commentكل قسم تم تخصيصه يبقى:.interp،.dynsym،.dynstr،.gnu.hash،.gnu.version*،.rela.dyn،.rela.plt،.plt،.text،.rodata،.eh_frame،.init_array،.dynamic،.got،.got.plt،.data،.bssو الملاحظات ، رؤساء القسم يبقون ما لم تضيفوا--strip-section-headers(المادة 2.41) أو تشغيلllvm-strip --strip-sections. .--strip-debugيحتفظ بالرموز--only-keep-debugيكتب ملف التحليل الذي --add-gnu-debuglinkأسماء من المزيف، مع CRC32 في .gnu_debuglinkوld --build-idيكتب.note.gnu.build-idحتى يمكن لجهاز التحليل أن يطابق الاثنين.
كوف كائن
clang -target x86_64-pc-windows-msvc -O1 -fno-asynchronous-unwind-tables -cالمنتجةmain-x86_64.objوledger-x86_64.obj. صفحة تنسيق PE تعريف الكائن: رأس الملف 20 بايت، لا رأس اختياري، جدول قسم من إدخالات 40 بايت، جدول رمز من سجلات 18 بايت، وسجلات نقل 10 بايت لكل قسم.
CoffObjectفيcode/main.pyيقرأ كلّهما، رأس الملف يقول "آلة"0x8664، 10 أقسام ، 37 سجلات رمزيةSizeOfOptionalHeader0 أسماء الجدول.text،.data،.bss، ستة أقسام تسمى.rdataو/16اسم يبدأ بـ/هو تعويض عشري في جدول السلاسل ، والذي يبدأ بحجمها الخاص أربعة بايتات. /16يقرر.llvm_addrsig، هذا هو الاسمllvm-objdump -hبصمات
وتتضمن السجلات الـ 37 سجلات مساعدة. رمز القسم (طبقة التخزين 3) يحمل واحد يحمل طول القسم، وعدد الترحيل، ومجموع التحقق، واختيار COMDAT. خمسة من .rdataالقسمات لديها اختيار 2، "أي"، وتحتوي على حرفية سلسلة واحدة تحت اسم MSVC-المتخلف: ??_C@_04JDDJEAHN@even?$AA@هو سلسلة "حتى". محرر الروابط يحفظ نسخة واحدة من كل واحد. السجل الأخير هو.fileمع سجل مساعد "main.c". llvm-objdump -tطبع هذه على شكل[ 0]،AUX scnlen ...،[ 2]، وهكذا، تخطي المؤشرات المساعدة، والاختبار يُقارن مؤشرنا وأزواج الأسماء مع هذا النص.
عمليات الإنتقال: 14 IMAGE_REL_AMD64_REL32في.textو 4 IMAGE_REL_AMD64_ADDR64في.rdata. .REL32هو "ال عنوان النسبي 32-بيت من البايت بعد نقل". هذا هو R_X86_64_PC32مع-4المدمجة: سجل ELF يحمل إضافة -4لأن السجل لديه حقل إضافي، وسجل COFF ليس لديه أي. ledger-x86_64.objلديه ثلاثةREL32في0x36،0x84و0x93و الدروس 32ledger-x86_64-O1.oلديه ثلاثةR_X86_64_PC32في نفس ثلاث تعويضات. الرمز هو نفسه، والشكلات تختلف فقط في حيث يتم كتابة الأربعة. ADDR64هو عنوان مطلق ثمانية بايت،labelsجدول من المؤشرات السلكية، و يصبح نقل قاعدة في الصورة، كما R_X86_64_64يصبحR_X86_64_RELATIVE. . .
ELF Elf64_Rela | COFF relocation | Mach-O relocation_info | |
|---|---|---|---|
| record size | 24 bytes | 10 bytes | 8 bytes |
| fields | offset, type and symbol in r_info, addend | offset, symbol index, type | address, then a 32-bit word with symbolnum:24, pcrel:1, length:2, extern:1, type:4 |
| addend | explicit | none, lives in the field | none, or an ARM64_RELOC_ADDEND record before the real one |
the call to puts | R_X86_64_PLT32 puts-0x4 | IMAGE_REL_AMD64_REL32 puts | ARM64_RELOC_BRANCH26 _puts |
صورة البيئة: الواردات والصادرات، وإعادة توطين القاعدة
tiny-x86_64.exeهو 2560 بايت مكتوب من قبل build_tiny_pe.py. لديه نفس الوظيفة التي يفعله الـ " إلف " الصغيرputsوexitمنmsvcrt.dll. كما أنها تصدر اسم واحد وتحمل نقل قاعدة واحدة، لذلك الكتيبات الثلاثة التي تحتاج إلى هذا الدروس موجودة. pefile2024.8.26 و llvm-objdump -pكلاهما يقرأونه، وكلاهما يلتقطون أجهزة.
أبدأ في إدارة نظام التشغيل بالـ (إم إس دوس)MZو في الموازنة0x3cيحتفظe_lfanew،0x40في0x40التوقيعPE\0\0ثم عنوان ملف COFF: آلة0x8664، 4 أقسام ،SizeOfOptionalHeader240، الخصائص 0x22(صورة قابلة للتنفيذ، عنوان كبير معروف).Magicالمجال0x20b، مما يعني PE32+. AddressOfEntryPointهو0x1000،ImageBaseهو0x140000000،SectionAlignment 0x1000،FileAlignment 0x200،SizeOfImage 0x5000،Subsystem3 (المكعبة)DllCharacteristics 0x8160تُفكّرHIGH_ENTROPY_VA 0x0020،DYNAMIC_BASE 0x0040،NX_COMPAT 0x0100وTERMINAL_SERVER_AWARE 0x8000ثم 16 دليل بيانات من RVA والحجم. أربعة يتم تعيينها: تصدير في0x2080حجم0x4d, استيراد في0x2000حجم0x28, نقل القاعدة في0x4000حجم0xc، IAT في0x2040حجم0x18. . .
العناوين في الصورة هي RVAs، تعادلات من قاعدة الحمل. جدول القسم يرسمها إلى تعادلات الملف: .textالـ RVA 0x1000في الملف0x200،.rdataالـ RVA 0x2000في0x400،.dataالـ RVA 0x3000في0x600،.relocالـ RVA 0x4000في0x800. .PeImage.rva_to_offset()هذه الطاولة
دليل الواردات
في RVA 0x2000يقع صف من 20 بايت تصفيات الاستيراد تنتهي من صفرا وصف. لدينا واحد: استيراد البحث الجدول 0x2028, طابع زمني 0 ، سلسلة الشحن 0 ، اسم 0x2068, جدول العناوين التأمين 0x2040. اسم RVA يشير إلى "msvcrt.dll". جدول البحث يحتوي على إدخالات 64 بت تنتهي بفارق صفر. مجموعة بيت 63 تعني الاستيراد عن طريق ordinal، مع ordinal في بيت 15 إلى 0. بيت 63 واضح يعني بيت 30 إلى 0 هي RVA من إدخال الإشارة / اسم: إشارة 16 بت، "مؤشر في جدول إشارة اسم التصدير"، ثم اسم ASCII. لدينا 0x2058(مُشيرة 0، "مُضخم") و 0x2060(مؤشر 0، "خروج").
وتقول المواصفات أن IAT "مثل محتويات جدول البحث عن الاستيراد حتى يتم ربط الصورة".0x2040يحتوي على نفس RVA، والاختبار يختبر أن 24 بايت في 0x2028يساوي 24 بايت في 0x2040. "خلال الالتزام، يتم تغطية الإدخالات في جدول عناوين الاستيراد بالعناوين 32-بيت (لـ PE32) أو 64-بيت (لـ PE32+) للرموز التي يتم استيرادها". يقوم شحن Windows بذلك قبل أن يتم تشغيل نقطة الدخول. لا يوجد أي إرتباط كسيل ولا يوجد قطعة. يصل الرمز إلى فتحة مباشرة:
text 100b: ff 15 2f 10 00 00 call qword ptr [rip + 0x102f] # 0x1011 + 0x102f = 0x2040, the IAT slot of puts
1013: ff 15 2f 10 00 00 call qword ptr [rip + 0x102f] # 0x1019 + 0x102f = 0x2048, the IAT slot of exitFF /2هو الاتصال القريب غير المباشر. الإرشادات الثانية لها نفس البايتز لأن كل واحد يجلس ثمانية بايتات بعد الآخر ويقرأ فتحة ثمانية بايتات بعد الآخر. iat_calls()يُسكان قسم الرمز لـff 15، يضيف 6 و التنقل، و يسمى الفجوة من دليل الاستيراد.
دليل التصدير
في RVA 0x2080جدول دليل التصدير البايت 40: العلامات، العلامة الزمنية، النسخة، اسم RVA يشير إلى "tiny-x86_64.exe"، قاعدة ordinal 1, مدخل جدول العناوين 1، مؤشر الاسم. ثلاثة جداول تلي. جدول العناوين التصدير في 0x20a8يحمل RVA واحد0x1000. جدول مؤشر أسماء الصادرات في 0x20acيحتوي على RVA واحد إلى السلسلة "tiny_entry". الجدول العادي للتصدير في 0x20b0يحتوي على مؤشر واحد غير متحيز من 16 بت، 0. اسم kخرائط لتحديد الموقعordinals[k]، والرسالة المصدرة هيordinals[k] + ordinal base. المواصفات تضيف قاعدة لا نستخدمها: إدخال عنوان يشير إلى داخل قسم التصدير هو سلسلة المرسل مثل MYDLL.expfuncو الجدول الإشاري للأسماء "مرتبة باللغة للسماح بالبحث الثنائي".
نقل القاعدة
.relocتحتوي كل كتلة على صفحة RVA و حجم كتلة، ثم إدخالات 16 بت مع النوع في الأربعة بتات العليا و صفحة تعويض في اثني عشر أقل. كتلة واحدة لدينا: صفحة 0x3000، الحجم 12 ، المشاركة 0xa000، نوع 10IMAGE_REL_BASED_DIR64، تعويض 0 ، ومدخل التشغيل من النوع 0 ، IMAGE_REL_BASED_ABSOLUTE. ثمانية بايت في RVA 0x3000- لا .0x1400020d0، قاعدة الصورة بالإضافة إلى RVA من السلسلة "مرتبطة يدوياً". إذا وضع المحمول الصورة في مكان آخر ، فإنه يضيف الفرق إلى هذا الحقل. المواصفات: "ملفات الصورة لا تحتوي على نقل COFF ... تحتوي الصورة على معلومات نقل في شكل نقل قواعد في قسم .reloc (ما لم يكن الصورة لديها صفة IMAGE_FILE_RELOCS_STRIPPED). "/DYNAMICBASEيتم تشغيله افتراضيًا في link.exe الحالية ، لذلك الصور تحمل .relocوDYNAMIC_BASE. . .
صورة PE لا تملك جدول رموز لتمزيقها.PointerToSymbolTable"يجب أن يكون صفر لصور لأن معلومات إزالة اللغاز COFF قد انتهت" والأسماء تعيش في إدارة PDB منفصلة. ما يبقى في كل صورة هو دليل الاستيراد، دليل التصدير، و، بالنسبة للرمز 64 بت،.pdata، الذي RUNTIME_FUNCTIONالإدخالات تسمي بداية ونهاية كل وظيفة التي تمنح كومة أو تدعو وظيفة أخرى. وظائف ورقة غائبة منها.
صورة ماخ-أو: أوامر الحمل، والحزم، والجدول الإعلامي غير المباشر
ledger_mainهو المرتبط arm64 قابل للتنفيذ. otool -hطبع 19 أمر تحميل و العلامات 0x00200085. .MH_NOUNDEFS 0x1،MH_DYLDLINK 0x4،MH_TWOLEVEL 0x80وMH_PIE 0x200000المجموع إلى هذه القيمة.LC_SEGMENT_64(__PAGEZERO،__TEXT،__DATA_CONST،__LINKEDIT() اثنانLC_LOAD_DYLIB(@rpath/libledger.dylibإذاً/usr/lib/libSystem.B.dylibو واحد لكل واحدLC_DYLD_CHAINED_FIXUPS،LC_DYLD_EXPORTS_TRIE،LC_SYMTAB،LC_DYSYMTAB،LC_LOAD_DYLINKER،LC_UUID،LC_BUILD_VERSION،LC_SOURCE_VERSION،LC_MAIN،LC_RPATH،LC_FUNCTION_STARTS،LC_DATA_IN_CODEوLC_CODE_SIGNATURE. .MachoFile.command_names()يعود نفس القائمة بنفس الترتيبotool -l. . .
__TEXTيبدأ في0x100000000. .__textهو 256 بايت في 0x100000558،mainولا شيء آخر لأن وظائف الكتيب الأربعة تعيش في الديليب__stubsهو 84 بايت في 0x100000658، سبعة قطع من اثني عشر بايت.__DATA_CONSTيبدأ في0x100004000مع__got64 بايت، ثمانية فتحات مؤشرات من نوع القسمS_NON_LAZY_SYMBOL_POINTERSو__const، 32 بايت ،fns[]وnames[]الجداولmain.c. . .
قطعة واحدة، باليد
- الصفحة
_putsهو في0x100000694:
text 30 00 00 90 adrp x16, 0x100004000
10 1e 40 f9 ldr x16, [x16, #0x38]
00 02 1f d6 br x160x90000030هوadrpالسجل هو أقل خمسة بيتات، 16.immloهو 30 إلى 29 bits، 0. immhiهو 23 بت إلى 5، 1 القيمة هي immhi << 2 | immlo = 4، في صفحة من 4,096 بايت، لذلك الصفحة المستهدفة هي صفحة التعليمات،0x100000000، بالإضافة0x4000. .0xf9401e10هوldrمع تعويض غير مؤلف من 12 بت مقياس 8: 21 بت إلى 10 هي 7 × 8 هي 0x38، لذا فإن الحمل يقرأ0x100004038. .0xd61f0200هوbr x16. الـ (أرم64) في مصدر (إيد64) هو هذا بالضبطStubAtom. . .
أي اسم ينتمي إلى فتحة يأتي من جدول الرموز غير المباشرة. LC_DYSYMTABنقاط في 15 مؤشر في الجدول الرمزي.reserved1حيث تبدأ سلسلة المؤشرات و فيreserved2كم حجم كل قطعة (12).reserved1و 8 بايت لكل فتحة__stubsيبدأ عند مؤشر 0 و __gotفي المؤشر 7، لذلك إضافة الخرائط 5 إلى إدخال جدول الرمز 10 _puts، وفرق 7 من__gotخرائط لنفس الموقعotool -Ivيُطبع كلتا الركضات، وindirect_table()يُعدّ كل صف
مساحة الأسماء ذات مستويين
nm -mبصمات(undefined) external _puts (from libSystem)و(undefined) external _grade (from libledger)المكتبة تأتي من السجل الرمز نفسه.nlist.hيحددGET_LIBRARY_ORDINAL(n_desc)كقطعة 15 إلى 8 n_desc, 1 للمرة الأولى LC_LOAD_DYLIB، 2 للثانية ،0xfeللبحث الديناميكي0xffللقيام بذلكld(1): "مع -two_levelnamespace (المتخلفة) ، يبحث المصلح فقط عن dylibs على خط الأوامر للحصول على الرموز، والسجلات التي تم العثور على dylibs في. " nm_lines()يعطي الـ 12 سجل منn_type،n_sect،n_descوn_value، ويقارن الاختبار خطها الخارجي لخط مع الصورة التي تم القبض عليهاnm -m. إربط مع-Wl,-flat_namespaceو اقرأotool -hلرىMH_TWOLEVELيختفي قليلاً
إصلاحات سلسلة، بايت مقابل بايت
LC_DYLD_CHAINED_FIXUPSهوlinkedit_data_command: dataoff 0x8000،datasize 208208 بايت تتبعfixup-chains.h. . .
العنوان هو سبع حقل 32 بت: fixups_version 0،starts_offset 32،imports_offset 80،symbols_offset 112،imports_count 8،imports_format 1(DYLD_CHAINED_IMPORT()symbols_format 0(غير مضغوط) llvm-objdump --macho --chained-fixupsيطبخ هذه القيم السبع والاختبار يُقارنها.
في المكافأة 32dyld_chained_starts_in_image: seg_count 4و أربع تعويضات، واحدة لكل قطعة، صفر للقطعات بدون إصلاحات. فقط __DATA_CONSTلديه واحد.dyld_chained_starts_in_segmentيقرأsize 24،page_size 0x4000،pointer_format 6(DYLD_CHAINED_PTR_64_OFFSET، "الهدف هو vm تعويض") ،segment_offset 0x4000،max_valid_pointer 0،page_count 1، و واحد page_startمن 0. DYLD_CHAINED_PTR_START_NONE،0xffff، سيكون ذلك صفحة بدون مشاكل
السلسلة تبدأ عند تعويض الملف0x4000 + 0كل كلمة 64 بت تحمل شيئين: ماذا تكتب هناك، وأين الكلمة التالية.
text dyld_chained_ptr_64_bind ordinal:24 addend:8 reserved:19 next:12 bind:1 = 1
dyld_chained_ptr_64_rebase target:36 high8:8 reserved:7 next:12 bind:1 = 0اقرأ الكلمة الأولى0x8010000000000000-البيت 63 جاهز، إرباطnextهو 62 إلى 51 bits.0x002، في وحدات من 4 بايت، لذلك التصحيح التالي هو 8 بايت على.0x4058،0x00000000000006db63 نقطة خالية، إعادة البيع.nextهو 0 نهاية السلسلة الهدف هو0x6db, تعويض vm , و قاعدة الصورة 0x100000000يجعلها0x1000006db، السلسلة " غريب " في __cstringالكلمة التي كانت قبلها0x00100000000006d6، إعادة التأثيرnames[0]إلى "حتى".
المشي يزور اثني عشر كلمة: ثمانية يربط في __gotمع العددية 0 إلى 7 ، اثنين من الارتباطات في __constمع 0 (كلتا الإدخالات من fns[]نقطة في_grade) و إعفاءات الـnames[]. .chained_fixups()يعود لهم مع تعويضات الملفات والكلمات الخام، والاختبار يقارن اثني عشر (offset, raw)أزواج مع dyld_info -fixup_chain_details، التي تطبعraw: 0x8010000000000000 bind: (next: 002, bindOrdinal: 0x000000)لأول مرة
الجدول الإيرادات في الموازنة 80 يحتوي على ثمانية إدخالات 32 بت: lib_ordinal:8،weak_import:1،name_offset:23، مع الأسماء في المكافأة 112._grade،_grade_label،_percent_change،_totalمن المكتبة 1 و 4 إلى 7 هي ___stack_chk_fail،___stack_chk_guard،_printf،_putsمن المكتبة 2fixup_rows()يضم الجداول إلى الصفيان dyld_info -fixupsبصمات مثل __DATA_CONST __got 0x100004038 bind libSystem/_puts، والاختبار يقارن الاثني عشر
إذاً "المؤشر الفاسد" في0x100004038هو الإيراد 7. dyld يمر بهذه السلسلة مرة واحدة، عند الحمل، ويكتب عنوان _putsإلى الحفرة ووضعت جلسة WWDC22 التابعة لأبل الأمر بهذه الطريقة: "في موقع المؤشر 64 بت في DATA، تحتوي بعض البيتات على تعويض إلى موقع التصحيح التالي. كما تم إغلاقها هناك جزء يقول إذا كان الإصلاح هو التجميع أو إعادة القاعدة. " جعل Xcode 13 النموذج الافتراضي لهدف التنفيذ من macOS 12 أو iOS 15 ، و تمدد Xcode 13.3 إلى macOS 11 و iOS 13.4. لا يوجد أي إرتباط كسول في هذا النموذج. كل فتحة مقيدة قبلmainيذهب
التخطيط الذي استبدله
ledger_main_nochainهو نفس الرابط مع-Wl,-no_fixup_chains- لقد فعلتLC_DYLD_INFO_ONLYبدلاً من ذلك، مع أربع جداول مرموزة بالبايت: إعادة تأسيس 8 بايت، ربط 72، ربط كسولة 120, تصدير 64.__TEXT,__stub_helper(108 بايت) ، a __gotفقط 16 بايت تمتلكها___stack_chk_guardوdyld_stub_binderو__DATA,__la_symbol_ptr(56 بايت، سبعة فتحات).dyld_info -fixupsالقوائم السبع lazy-bindصفوف و سبعة قاعدات التي تشير كل فتحة كسولة إلى ترامبولينها الخاص في__stub_helper. ترامبولين يدفع مؤشر ويقفز إلىdyld_stub_binder، الذي يحل الاسم ويكتب الفجوة. وهذا هو PLT و GOT من القسم السابق في شكلات ماخ-او: __la_symbol_ptrهو.got.plt،__stub_helperهو PLT0 و pushq $indexوdyld_stub_binderهو_dl_runtime_resolve. .code/main.pyيبلغ عن التداعيات والأقسام في هذا الملف ولا يفكّر رموز التجميع؛ يطلب المختبر الممارس ذلك.
التجارة المصدرية
LC_DYLD_EXPORTS_TRIEكل عقدة هي حجم محطة uleb128 ، ثم ، إذا كان غير صفر ، العلامات uleb128 وتعويض uleb128 ، ثم بايت واحد من عدد الأطفال ، و لكل طفل ، حافة سلسلة وتعويض uleb128 عقدة. exports_trie()-إمشيlibledger.dylibإنه يعود_percent_change 0x410،_grade 0x428،_total 0x464،_grade_label 0x498، أربعة تعويضاتdyld_info -exportsبصمات على جهاز التنفيذ يعود__mh_execute_header،_fnsو_main. . .
أي شريط يترك
أبل strip(1)يقول أنه بدون خيارات على جهاز تنفيذي مرتبط بديناميكيا يتصرف مثل-u -r: حفظ الرموز غير المحددة والرموز المشار إليها ديناميكيا. -xيزيل الرموز المحلية-Nيزيل جدول الرمز والجدول السلكي بأكمله. "للكتب المشتركة الديناميكية، فإن الحد الأقصى من التخلص عادةً هو -x"، لأن "المركز الديناميكي يستخدم الرموز العالمية فقط".
لاحظها على أجهزةناstrip ledger_mainأخذLC_SYMTABمن 12 إلى 10 سجلات و تم إزالتها_main،_fnsو_names. البقاء على الواردات الثمانية غير المحددة، لأن الديلد بحاجة إلى أسمائهم لربطها، و__mh_execute_headerيبقى لأنه هوREFERENCED_DYNAMICALLYالسجل العاشر هو طعن من النوعN_OPTاسمهاradr://5614542، التي أدوات آبل تكتب حتى الطاولة ليست فارغة أبداًnmالبرنامج المزيف يطبخ نفس الخطوط الثلاثة التي كانت في الأصلية ويتم الخروج من 0.LC_FUNCTION_STARTSلا يزال لديه بداية واحدةmain. . .
strip libledger.dylibبدون أي خيار ذهب أبعد من المعتاد من الصفحة الرجل-x: LC_SYMTAB0 سجلات وnmلا يُطبخ "لا رموز" لا يزال محاكاة التصدير قائمة بالظروف الأربعة في نفس التداولات، لأنّ Dyld يربط عبر المحاكاة ولا يمرّ أبداًLC_SYMTAB. .LC_FUNCTION_STARTSلا يزال قائمة أربعة بدايات المختبر ج يربط جديدmain.cضد ديلب المنزلق لإظهار أن محرر الرابط يخدم أيضاً من قبل الـ "تري".
| What | ELF after --strip-all | PE image | Mach-O after strip |
|---|---|---|---|
| static symbol table | gone | never present in an image | local and defined names gone, undefined imports stay |
| names the loader binds | .dynsym and .dynstr stay | import directory stays | imports stay in LC_SYMTAB and in the chained imports table |
| exported names | .dynsym stays | export directory stays | exports trie stays |
| function starts | .eh_frame FDEs, unless built without unwind tables | .pdata for non-leaf functions | LC_FUNCTION_STARTS stays |
| debug information | .debug_* gone | in the PDB, never in the image | in the dSYM, never in the image |
| section or load command headers | stay unless --strip-section-headers | stay | stay |
العثور على الوظائف بدون رموز
static-arm64-O2الروابطmain-arm64-O2.oوledger-arm64-O2.oفي واحد يمكن تنفيذه مع خمسة وظائف في __text. .nm -nمن الملف غير المقسم هو الحقيقة الأساسية:_mainفي0x100000500(244 بايت) ، _percent_changeفي0x1000005f4(24) ، _gradeفي0x10000060c(60)_totalفي0x100000648(248)_grade_labelفي0x100000740(72) static-arm64-O2.strippedلا يوجدN_SECTالرمز داخل__text. .FunctionFinderيستخدم أربعة طرق عليه وscore()يُقارن كل قائمة بالحق
LC_FUNCTION_STARTS
نقاط القيادة عند تسعة بايت في __LINKEDIT: 80 0a f4 01 18 3c f8 01 00.تصميمها هو uleb128 deltas، أول من__TEXTالعنوان80 0aهو0x500، لذا البداية الأولى هي0x100000500. .f4 01هو0xf4، لذا0x1000005f4. .18يعطي0x10000060c،3cيعطي0x100000648،f8 01يعطي0x100000740و00إنهاء القائمة خمسة من خمسة، لا إيجابيات كاذبة، في-O0و في-O2. يكتب محرر الرابط هذه الجدول لـ (الـ (مُحاكم الطائرات و (مُخبر الحوادثstripغيدرا يقرأها في تحليل "ميك او وظيفة تبدأ"dyld_info -function_startsيطبعها ويعرضها ليفfunction_starts. . .
مسحات المنتهية
أنماط بداية Ghidra AARCH64 تبدأ مع fd 7b bf a9، وهوstp x29, x30, [sp, #-0x10]!في ملفاتنا يجد 0 من 5 في كلا المستويينsub sp, sp, #Nأولاً ثمstp x29, x30, [sp, #M]مع تعويض إيجابي، لذلك لا يحدث شكل المؤشر السابق.sub sp, sp, #imm،(word & 0xff0003ff) == 0xd10003ff, يجد 5 من 5 في -O0، حيث كل وظيفة لديها إطار، و 1 من 5 في -O2، حيثpercent_change،grade،totalوgrade_labelهي وظائف ورقة بدون إطار على الإطلاق. percent_changeيبدأ بـcbz w0, ....سكان المنتهي يُبيان ما اختار المُجمّع إصداره، ودرسة 38 أظهرت كم مرة هذا لا شيء.
لم يظهر أي من المسحات إيجابية خاطئة هنا لأن البرنامج صغير.55منpush rbpضرب داخل تعليمات أخرى، وهذا هو السبب في أن ملفات نمط Ghidra x86-64 يزوجون بعد نمط مثل 55 48 89 e5مع نمط مسبق مثل retأوnopيجب أن ينهي الوظيفة السابقة
الهدف المطلوب
كلّ شخصblفي__textاسم البدء في الوظيفة، لأن blفي منتصف الوظيفة شيء لا ينبعث منه المجمع لـ C.0x94000000مع تعويض كلمة مؤقّت 26 بتblفي0x1000005a0يقرأ3d 00 00 94: 0x3dهو 61، × 4 هو 0xf4، لذا الهدف هو0x100000694. فيledger_mainهذا هو القاعدة_putsفي البناء الثابتcall_targets()يجمع الأهداف داخل__text، يُسقطُ تلك التي تَهْبطُ في__stubs، و يضيف LC_MAINدخول، لأن لا شيء يدعوmainخمسة من خمسة على كلا المستويين. يبنى النواة (أندرييس، سلوينسكا، بوس، 2017) اكتشافها الوظيفي على هذه الفكرة للترميز الذي يولده المُجمّع، ويقيس أندرييس وغيرها في عام 2016 أن حدود الوظيفة هي الجزء الأصعب من التفكيك، أصعب من حدود التعليمات.
توقيع على شكل شكل شقق
يصف Hex-Rays FLIRT في وثائقها: نمط هو "ال 32 بايت الأول من وظيفة يتم فيها تسجيل جميع البايت المتغيرات"، وتأتي البايت المتغيرات من إصلاحات ومراجع خارجية، والأنماط تعيش في شجرة، وخارج النمط "نحسب CRC16 من البايتات بدءا من الموقف 33 حتى البايت المتغير الأول" وتخزين هذا الطول. يتم تفصل وظائف ذات نفس النمط و CRC عن بعضها البعض بواسطة بايت ذيل في موقع يختلفان فيه، ثم بالأسماء التي يشيرون إليها. تصادم لا شيء يفصل "يتم حلها بواسطة الذكاء الطبيعي". يتم استبعاد الوظائف القصيرة من أربع بايت دون مرجع خارجي، و"ملف التوقيع لا يحتوي على أي بايت من المكتبات الأصلية، باستثناء أسماء الوظائف".
signatures_from_object()يبنيه منledger-arm64-O2.oوmain-arm64-O2.o، الطريقة التي تقوم بها (فلاير) ببناءها من كائنات المكتبةrelocation_infoسجل يغطي كلمة واحدة أربعة بايت، وتلك البايت تصبح مختلفة..patترتيب الخط، الوثيقة في محلل ريزين، هو النمط، طول CRC، CRC16، طول الوظيفة، :0000والاسم، و^offset nameلكل اسم مرجع:
textA00000342800004B890C8052087D091B000DC01AC0035FD6 00 0000 0018 :0000 _percent_change
E8CC8C52C8CCAC72087C289B09FD7FD308FD62930801090B081900511F110071 04 9AAF 003C :0000 _grade ^0000 l_switch.table.grade ^0001 l_switch.table.grade
E8CC8C52C8CCAC72087C289B09FD7FD308FD62930801090B081900511F110071 04 9AAF 0048 :0000 _grade_label ^0000 l_switch.table.grade_label ^0001 l_switch.table.grade_label ^0002 _labels ^0003 _labels_percent_changeهو 24 بايت، لذلك نمطها هو وظيفة كاملة وطول CRC هو 0. _gradeهو 60 بايت مع إعادة توزيعه الأولى في بايت 36، لذلك CRC تغطي أربعة بايت..patاستخدام المقاطع: الكتلة0x8408, القيمة الأولي 0xffffثم تبادل البايتين. قيمته لسلسلة ASCII "123456789" هي 0x6e90، و أظافر الاختبار ذلك.
match_signatures()يختبر كل توقيع في كل وظيفة تبدأ من البناء المزيف، النمط أولا، ثم CRC.-O0كل الوظائف الخمسة تحصل على أسمائها ولا تتصادم أي شيء-O2الخطان فوق تصدمgrade_labelفي خطgrade، لذلك المهام المشتركة 36 بايت أولها وأربعة بايت CRC.0x10000060cو في0x100000740. الذيل يفرقهم: في 44 بايت_grade- نعم20و_grade_label- نعم28، وهوldr w0, [x9, w8, uxtw #2]ضدldr x8, [x9, w8, uxtw #3]و الطول 60 مقابل 72، و تقرير تسجل كل من الصدامات و البايت الذي قرر لهم. خمسة من خمسة أسماء، في كلا المستويين.
الاختبارtest_object_bytes_equal_linked_bytes_outside_relocated_wordsيُحقق من المفترض: كل بايت غير متغير من كل وظيفة كائن يساوي بايت في نفس التداخل في المُتَنَفِّذ المُرتبط، لذا قام محرر الرابط بتغيير الكلمات المُنقلة فقط. _totalلا يوجد إعادة التنقل ولا يوجد متغيرات البايت على الإطلاق
ما الذي تفعله الأدوات مع نفس الفكرة
IDA Pro 9.4 يُرسل توقيعات FLIRT لكل معالج و sigmakeأداة لبناءها؛ تغطي IDA Free x86 و x86-64 فقط و هي لاستخدام غير تجاري. لومينا هو خادم يقوم بتفتيش البيانات المعدنية للعمل بواسطة md5 من الوظيفة ويقفز الوظائف تحت 32 بايت. Ghidra 12.1.4 (Apache-2.0) لديه ID الوظيفة: hash كامل على mnemonics وأوضاع التعامل التي هي "قوية ضد التغييرات بسبب الربط" و hash محددة التي تضيف العمليات الثابتة، تسجل لكل تعليمات مطابقة، مع الوظائف الوالدية والطفل ككسر العلاقات؛ قواعد البيانات المرسومة تغطي مكتبات Visual Studio فقط، وتقوم بملء الخاص بك من ثنائيات الخاصة بك. يستخدم نينجا ثنائي 6.0 كريبتون (التجارية ، مع إصدار حر) WARP (Apache-2.0): وظيفة GUID هي UUIDv5 على GUIDs الكتلة الأساسية ، كل واحد UUIDv5 على البايتات الكتلة بعد الصفر "جميع التعليمات التي يمكن نقلها" وإسقاط NOPs ، مع المدعو ، والاتصال ، والظروف المجاورة كقيود. يطابق BinDiff 8 (Apache-2.0) الوظائف بين اثنين من الثنائيات مع ترتيب ثابت من الخطوات، من التطابق الهاشي من خلال مؤشرات MD الرسم البياني للتدفق إلى تطابق التسلسل، ويزن التشابه من الحواف المطابقة والكتلة والإرشادات، ومؤشر MD الرسم البياني للتدفق. يضيف الشفرة 3.4.3 (AGPL-3.0 فقط، IDA) نسبة عدد الأول الصغير، حيث تقوم كل خريطة من النمومات بتحديد عدد الأول والعمل هو النسبة، وهي بصمة أصابع متعددة المجموعات دون ترتيب. الرادار2 6.2.4 (LGPL-3.0) لديه توقيعات (zg،z/،zb) ويقرأ " فليرت ".sigالملفات مع zfs• الـ (LGPL-3.0) يخلق الـ (Rizin 0.9.1).patأو.sigمن ثنائي مفتوح مع Fcو تطبق واحدة مع Fs. . .
إيدا و Binary Ninja مغلقة ووصف هنا من وثائقهم. رادار2 و ريزين ليسوا مثبتين على هذه الآلة، وتستشهد أوامرهم من كتبهم ومصادرهم. الأدوات التي تشغلت على هذه الأجهزة هيclang،ld،llvm-objdump،otool،nm،stripوdyld_infoمن أدوات خط القيادة، و LIEF 1.0.0، و pyelftools 0.33, و الملف الشخصي 2024.8.26 من خلال المسبار. قرأ LIEF كل شكل هنا، بما في ذلك كائنات COFF، التي دعمتها منذ 0.17.0, وبلغت عن عشرة التزامات و اثنين من القواعد على ledger_main، نفس اثني عشر إصلاحات . أدوات اللوحة تقرأ ملفات ELF فقط . الملف رفضmain-x86_64.objمعPEFormatError: 'DOS Header magic not found.'وقرأ الصورة الصغيرة
بناءها
أطلق التجربة:
bashcd phases/13-tools-and-protocols/40-linking-loading-and-stripped-binaries
python3 code/main.pyإن المخرج هو تقرير JSON واحد مع خمسة أجزاء. elfيحتوي على حسابات نقل المواد الثانية، خطة الرابط، والقسم الديناميكي، PLT، و GOT من tiny-dyn-x86_64. .coffيحتوي على القسمات، والتحركات، والوظائف، والأسماء غير المحددة للجسمين. peيحتوي على العناوين والإداريات والإيرادات والصادرات وإعادة توطين القواعد والدعوات الدولية للعملات الدوليةtiny-x86_64.exe. .machoيحتفظledger_mainمع أجزائها، وتصميماتها المتسلسلة، وتصميماتها، والصادرات،ledger_main_nochain، و "ديليب" و تقارير "ستريبتين"identificationيحمل الحقيقة، الأساليب الأربعة مع النتائج، والوقائع مع اصطداماتها، في -O0و-O2. . .
الشكل المتوقع:
json{
"elf": {"linkPlan": {"pltSymbols": ["puts", "printf"], "sizes": {".plt": 48, ".got.plt": 40, ".rela.plt": 48, ".rela.dyn": 48}, "...": "..."}, "tiny-dyn-x86_64": {"...": "..."}},
"coff": {"main-x86_64.obj": {"...": "..."}},
"pe": {"imports": [{"dll": "msvcrt.dll", "iat": "0x2040", "...": "..."}], "...": "..."},
"macho": {"ledger_main": {"flags": "0x200085", "fixupRows": ["__DATA_CONST __got 0x100004000 bind libledger/_grade", "..."], "...": "..."}, "strip": {"...": "..."}},
"identification": {"O2": {"methods": {"functionStarts": {"recall": "5/5"}, "prologueSub": {"recall": "1/5"}, "...": "..."}, "correctNames": 5}}
}اقرأcode/main.pyفي هذا الترتيب:
ElfFileيقرأ العناوين، والقسمات، كل من الجداول الرمزية، والتحويلات، وdynamic_entries()،jump_slots()،plt_entries()وplt0()اقرأ الصورة المرتبطةplan_dynamic_link()يطبق الجدول 4.9 والقسم 5.2 على سجلات نقل الأشياء التي يمكن نقلها.CoffObjectيقرأ عنوان الملف، جدول القسم، رموز 18 بايت مع سجلاتها المساعدة، وإعادة التنظيمات 10 بايت.PeImageيقرأ إصدار MS-DOS، وقع PE، رأس اختياري، وإداريات البيانات، القسمات، وإداريات الاستيراد والصادرة، وإعادة توطين القاعدة، وiat_calls()التشريحاتff 15. . .MachoFileيقرأ أوامر الحمل، الشرائح،nlistالسجلات، الجدول العكسي الرمز، الصفوف، الإصلاحات المتسلسلة، محاولات الصادرات، وإعادة نقل الأشياء.FunctionFinderيدير أربعة الباحثين عن البداية وscore()يُقارن كلّ منهما بالحقّ.signatures_from_object()وmatch_signatures()بناء وتطبيق توقيعات على شكل FLIRT.- - نعم
parse_*تقوم الوظائف بقراءة نص الأداة المكتوبة التي تقارن الاختبارات معها.
المزيد من الأوامر:python3 code/main.py elf FILE،coff FILE،pe FILEوmacho FILEطبع تقرير واحدpython3 code/main.py plan OBJECT...يطبخ خطة الرابط لأي مجموعة من كائنات ELF. python3 code/main.py functions STRIPPED UNSTRIPPED OBJECT...يُسجل المجدين والتوقيعات على زوج آخر من بناءاتك الخاصة.
استخدمها
استخدم القراء بهذه الترتيب على برنامج قمت ببناءه
- اقرأ العناوين وسمي النموذج و النوع: المادة التي يمكن نقلها ، أو المكتبة المشتركة أو القارئ. يرفض القارئ ما لا يعرفه ، وهذا الرفض هو المعلومات.
- إدراج الواردات مع مقدميها:
DT_NEEDEDو.dynsym، أو جدول الواردات المتسلسلة وLC_LOAD_DYLIBكل مكالمة إلى اسم في تلك القائمة تترك الصورة - اتبع مكالمة واحدة يدوياً إلى فتحة الملف. اكتب بيانات المكالمة أو البيانات البطنية أو البيانات PLT وعنوان فتحة الملف والكلمة في الملف وما يكتبه المحمول هناك. إذا كانت الكلمة في الملف تبدو كالقمامة، فكّرها كإصلاح قبل أن تسميها قمامة.
- مقارنة جدول الرمز مع الصادرات و تبدأ الوظيفة. الأسماء التي يحتاجها المحمول للبقاء على قيد الحياة
stripأسماء فقط يحتاج إصلاحي لا. قل إلى أي مجموعة اسم مفقود ينتمي. - في البناء المزول، تشغيل العثورات التي يمكنك تشغيلها وتسجيلها ضد التوأم غير المزول بينما لا تزال لديك واحدة. تفضل الجداول التي كتبها محرر الروابط،
LC_FUNCTION_STARTS،.pdataأو.eh_frame، على أنماط البايت، واستخدام أهداف المكالمة لملء الفجوات. - قم ببناء توقيعات من الأشياء الخاصة بك وتطبيقها، سجل كل اصطدام والبايت الذي يفرقه. اسم يدعي توقيعان هو مجهول حتى النيل أو اسم مرجع يقرره.
احتفظ بنص الأداة المكتوبة مع الملفات. عندما تتغير سلسلة الأدوات، استعاد الاستيلاء عليها، لأن قائمة تغير هي جهاز تغير.
المختبر التفاعلي
المختبر (أ): عملية التشغيل المباشر باليد
منcodeالإرشادات، مفتوحة Python:
bashcd phases/13-tools-and-protocols/40-linking-loading-and-stripped-binaries/code
python3 -qأركض
pythonfrom main import *
elf = load_elf("tiny-dyn-x86_64")
[(e.tag_name, e.text) for e in elf.dynamic_entries()]
elf.plt0()
[(e.symbol, hex(e.address), hex(e.slot), hex(e.slot_initial)) for e in elf.plt_entries()]
[hex(w) for w in elf.got_plt_words()]قبل كل مكالمة، احسب الإجابة من البايتات في النص أعلاه. ثم تغيير IMPORTSفيbuild_tiny_dyn_elf.pyإلى ثلاثة أسماء، وإعادة بناء، وتنبؤ بحجم جديد من .plt،.got.pltو.rela.pltقبل أن تقرأهم
المختبر ب: إصلاح واحد مقيد
pythonexe = load_macho("ledger_main")
stub = next(s for s in exe.stubs() if s.symbol == "_puts")
stub.lines()
raw = int.from_bytes(exe.read(stub.slot, 8), "little")
hex(raw), raw >> 63, (raw >> 51) & 0xfff, raw & 0xffffff
[(i.index, i.name, i.library) for i in exe.chained_fixups()["imports"]]فك كلمة الارتباط على الورق أولاً:nextفي حقل في وحدات من أربعة بايت، والنظامي. ثم قراءة الكلمة ثمانية بايت أكثر وتأكيد أنها التصحيح التالية. ثم قراءة الكلمة الأخيرة من السلسلة في0x100004058و اشرح لماذاnextهو صفر.
المختبر (ج): إزالة العصا وإعادة ربطها
في دليل الأجهزة، ربط برنامج جديد مع ديليب المنزف تماما:
bashcd phases/13-tools-and-protocols/40-linking-loading-and-stripped-binaries/code/fixtures
clang -target arm64-apple-macos -O1 main.c -L. -lledger.stripped -Wl,-rpath,@executable_path -o /tmp/relinked
nm /tmp/relinked | grep ' U '
dyld_info -exports libledger.stripped.dylibالارتباط ينجح مع مكتبةLC_SYMTABفهي فارغة، لأن محرر الروابط يقرأ محاولة التصدير. ثم تشغيلpython3 ../main.py macho libledger.stripped.dylibو مقارنةsymtab،exportsوfunctionStartsثم قم بتخلع البناء الثابت-Nبدلاً من ذلك وشاهد ماذاdyld_info -function_startsلا يزال هناك بصمات
المختبر (د): التصادم
pythonfrom main import *
stripped = load_macho("static-arm64-O2.stripped")
sigs = signatures_from_object(load_macho("ledger-arm64-O2.o"))
[s.pat_line() for s in sigs]
starts = FunctionFinder(stripped).function_starts()
match_signatures(sigs, stripped, starts)اقرأ سجلات التصادمين ثم قم بتجميع توقيعات منledger-arm64-O0.o، تطابقهم ضدstatic-arm64-O0.strippedو أوضح من.patالخطوط لماذا لا يحدث أي اصطدام في-O0. ثم اقصرPATTERN_LENGTHإلى 16 و أعد عدد الوظائف التي تتصادم
مختبر التدريب
فك رموز الجداول القائمة على الالتزام القديمة وإضافة متكن PE.
المتطلبات:
- فك رموز الـ
LC_DYLD_INFO_ONLYإعادة قاعدة، ربط، والبائس ربط سيلات الكودledger_main_nochainإلى صفوف مع القطعة، القسم، العنوان، النوع، والهدف، وجعلهم يساوي 21 صف في المقاطعةdyld_info -fixupsرسالة - إضافة
PT_GNU_RELROعنوان البرنامج وDT_FLAGSمعDF_BIND_NOWإلىbuild_tiny_dyn_elf.py، تحرك.got.pltداخل النطاق المغطى، واصنعllvm-objdump -pأظهر الرأس الجديد. قل في إضافات README أي جزء من كامل RELRO هذا يعيد التنظيم والتي يمكن أن تقوم بها فقط glibc. - اكتب
.pdataقراءة لصور PE32+ ومعثر وظيفة يستخدمRUNTIME_FUNCTIONابدأ العناوين. ابني كائن معclang -target x86_64-pc-windows-msvc -cبدون-fno-asynchronous-unwind-tables، اقرأها.pdataالتنقلات، وتشير إلى أي من وظائفها هي وظائف ورقة يتم إغلاقها في الجدول. - اكتب اختبار لكل قارئ يُقارن مع نص الأداة المسجل.
النجاح يعنيpython3 code/main.py macho ledger_main_nochainيطبع نفس 21 صفوف إصلاحيةdyld_infoبصمات، ELF الصغير يحمل PT_GNU_RELROهذاllvm-objdumpالقوائم، والـ.pdataيسمي العثور الوظائف التي يمكنه القيام بها ويرسل الوظائف التي لا يمكنها القيام بها.
درساً أساسياً
هذا الدروس يتضمنoutputs/skill-linking-loading-and-stripped-binaries.mdاستخدمها لتحويل برنامج قمت بإنشائه إلى قائمة من الواردات مع مقدميهم، مكالمة واحدة تتبع إلى فتحة يقوم محمولها، بيان ما stripويتطلب التوأم غير المحرز كحقيقة الأرض، قائمة أداة قبض على كل مطالبة، و سجل كل تصادم.
تحقق من ذلك
إشغال التجربة والسلسلة التحديدية:
bashcd phases/13-tools-and-protocols/40-linking-loading-and-stripped-binaries
python3 code/main.py
python3 code/tests/test_main.pyيجب أن تثبت التحقق:
- القسم الديناميكي
tiny-dyn-x86_64المزادllvm-objdump -pالتجديف على التجديفDT_FLAGS_1يحملDF_1_PIE - الاثنان
R_X86_64_JUMP_SLOTالسجلات متطابقةllvm-objdump -Rو اسمهاputsوexit - PLT0 يدفع GOT+8 ويقفز عبر GOT+16, كل مدخل يقرأ فتحة ويدفع مؤشرها، وكل فتحة تحتوي على عنوان خاص بها
pushq - القسمات و عناوين البرنامج من مباراة ELF الصغيرة
llvm-objdump -h -p - سجلات نقل كيانات الـ ELF المتطابقة
llvm-objdump -r - خطة الرابط تجد رمزين PLT، أربعة مكالمات مباشرة، إعادة توطينين نسبي، وحجم الصورة الصغيرة
- جدول قسم COFF، 37 سجلات رمزية مع سجلات مساعدة، وتطابق عمليات نقل
llvm-objdump -h -t -rو/16يقرر.llvm_addrsig - ثلاثة كوف
REL32سجلاتledgerاجلس في الـ 3 الـ 11PC32التكافؤ - الرؤوس المخصصة للشخصيات،
DllCharacteristics، وتطابق إداريات البياناتllvm-objdump -p - دليل الواردات، إدخالات الإشارة/الأسم، ومطابقة IAT
llvm-objdumpوpefile، و IAT يساوي جدول البحث قبل الالتزام - دليل التصدير و
DIR64رمز نقل القاعدة، وسمي المؤشر الذي تم نقله السلسلة "مرتبطة يدوياً" ff 15في RVA0x100bيصل إلى فتحة IAT منputs- العلامات ماك-أو، أسماء الأوامر التسعة عشر، والمكتبات المقابلة
otool - السبعة من المواد تُفكّر إلى الثمانية
__gotفتحات و مطابقات الجدول المباشر للرموزotool -Iv - الصور المرتبطة بالرأس تتطابق
llvm-objdump --macho --chained-fixups - المشي السريع يعيد كل الكلمات الخامة الـ 12
dyld_info -fixup_chain_details - صفوف التثبيت و جدول الواردات تتطابق
dyld_info -fixupsو-imports - الصادرات تتماشى
dyld_info -exports" للدليل " و " لنسخه " المزروعة " . nm_lines()متساويةnm -mللنسخة القابلة للتنفيذ والنسخة المزورة منهاstripإزالة_main،_fnsو_names، احتفظت بـ 9 أسماء و 1radr://5614542طعن، والبرنامج المزيف يطبخ نفس الخروج- التخطيط المتكرر لديه
LC_DYLD_INFO_ONLY، سبعة مؤشرات كسولة ، و تقاعد في__stub_helper - أوراق نقل الأجسام arm64 تتطابق
llvm-objdump -r LC_FUNCTION_STARTSيعود خمسة من خمسة على المستويين و يساويnmوdyld_info -function_starts- الموقع
sub spالفحص يجد خمسة من خمسة في-O0وواحد من خمسة في-O2، وstpالفحص الإطار لا يجد أي blالأهداف بالإضافة إلىLC_MAINدخول تعويض خمسة من خمسة- التوقيعات تسمي جميع الوظائف الخمسة على كلا المستويين وتسجل
gradeالتصادم مقسم بالبايت 44 - إن CRC16 لـ "123456789" هي
0x6e90، والثمانية بايت تحت زوج الحمل GOT منmainهي متغيرات - كل بايت كائن غير متغير يساوي بايت المرتبط
- تقرير المعلومات التجريبية والرسومية نفس العناوين، ووضع الوحدة الموقع نفس البيانات في ES5
أساليب فشل الإنتاج
| Failure | What the weak tool reports | What the analysis must prove |
|---|---|---|
A bl or call lands in a stub | "calls an unnamed function at 0x694" | the stub bytes, the slot it loads, and the import the slot binds |
| A GOT word read from the file | "corrupt pointer 0x8010000000000007" | bit 63 is bind, next is 2, ordinal 7 is _puts from library 2 |
PLT32 in an object | "every call goes through the PLT" | defined symbols are reduced to a direct distance, only undefined ones get an entry |
| Partial RELRO read as full | "GOT is read-only" | .got.plt slots after the three reserved ones sit outside PT_GNU_RELRO without -z now |
COFF REL32 compared with ELF PC32 | "the Windows build has different relocations" | same offsets, the -4 lives in the ELF addend and in the COFF field |
| IAT read before load | "imports point at strings" | the IAT equals the lookup table until the loader binds, and the strings are hint/name entries |
| A stripped dylib | "no symbols, nothing exported" | the exports trie and LC_FUNCTION_STARTS list every function |
nm on a stripped executable | "one function" | LC_FUNCTION_STARTS lists five starts, nine bytes long |
A prologue scan at -O2 | "one function" | four leaf functions have no frame, call targets and the starts table find them |
| Two functions share 36 bytes | "grade_label is grade" | the tail byte at 44 and the lengths 60 and 72 split the collision |
| A tool run on another machine's build | the numbers in this text | recapture every listing from your own fixtures |
قاعدة العمليات
اتبع كل مكالمة خارجية من موقع المكالمة إلى فتحة يقوم محملها محملها، و فك كلمة في تلك الفتحة كما يحددها النموذج قبل أن تدعوها عنوان. إخلع الملابس فقط بعد أن تسجل ما يحتاجه الحمولة تبدأ وظيفة استعادة من الجداول التي كتبها محرر الروابط قبل مسح البايت، وتسجيل كل العثور ضد إثنان مع الرموز، وتسجيل كل تصادم توقيع مع البايت الذي يفرقه.
المزيد من القراءة
- System V gABI, chapter 5: Program Loading and Dynamic Linking
- System V Application Binary Interface, AMD64 Architecture Processor Supplement: section 5.2 and Table 4.9
- Drepper, "How To Write Shared Libraries", version 4.1.2
- Drepper, "ELF Symbol Versioning"
- Jelinek, "Little hardening DSOs/executables against exploits", binutils list, 2004-01-06
- Taylor, "Linker relro", 2008
- binutils commit bd7ab16b: PLT32 for calls, 2018-02-13وLLVM D43383
- GNU Binutils: stripوllvm-objcopy
- Microsoft, PE Format
- CALL instruction reference, FF /2
- Apple, mach-o/loader.hوmach-o/nlist.h
- Apple dyld, mach-o/fixup-chains.h
- Apple ld64, stub_arm64.hpp
- Xcode 13 release notes: chained fixupsوWWDC22 session 110362, "Link fast: Improve build and launch times"
- strip(1)،ld(1)وdyld(1)
- Hex-Rays, IDA F.L.I.R.T. Technology: In-DepthوLumina overview
- Ghidra Function ID helpوAARCH64 function start patterns
- Vector 35, WARPوBinary Ninja WARP guide
- BinDiff manualوBinDiff source
- Diaphora
- rizin pat.c, the .pat parser،radare2 flirt.cوradare2 book: Zignatures
- Andriesse et al., "An In-Depth Analysis of Disassembly on Full-Scale x86/x64 Binaries" (USENIX Security 2016)وAndriesse, Slowinska, Bos, "Compiler-Agnostic Function Detection in Binaries" (EuroS&P 2017)
- Bao et al., ByteWeight (USENIX Security 2014)وShin, Song, Moazzezi, "Recognizing Functions in Binaries with Neural Networks" (USENIX Security 2015)
- LIEF changelog،pyelftoolsوpefile
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.