آناتومی برنامه: یک خواننده ELF از ابتدا
Type: Build
Languages: Python (stdlib), C (fixture only)
Prerequisites: Phase 0 · 01 (Dev Environment) with clang installed, Python 3.10 or later, hexadecimal arithmetic
Time: ~120 minutes
اهداف یادگیری
- قانون مجوز برای هر هدف در این مسیر و چهار محدودیت 17 USC 1201 ((f) را بیان کنید.
- سرنخ ELF، جدول سرنخ بخش و هر دو جدول رشته را با
struct.unpack. . - رمزگشایی
.symtabنوشته ها و.relaسوابق با نام های gABI و x86-64 psABI - بائتهای دقیق یک تابع به نام را برگردانید و هر فایل تعویض شده را به ساختار مالک آن نقشه بزنید.
- سرنخ برنامه ها را بخونید و یک آدرس مجازی را به یک فایل تعویض شده از طریق نقشه برداری کنید
PT_LOAD. . - دستورات بارگذاری یک شیوی ماچ-او را بخوانید و ایده ELF را که هر یک از آنها مطابقت دارد نام ببرید.
قبل از شروع: مجوز و قانون
هر هدف در درس 32 تا 36 برنامه ای است که تو نوشتی و مرتب کردیcode/fixtures/ledger.cشما مالک منبع، دستور ساز و فایل های شیری هستید که آن را تولید کرده است. هیچ مجوز برای خواندن وجود ندارد، هیچ محافظت برای گذراندن و هیچ شخص ثالثی که کد آن را باز می کنید. آهنگ هرگز حفاظت از نرم افزار، چک مجوز یا برنامه هایی را که شما مالک نیستید پوشش نمی دهد.
اين قاعده وجود داره چون قانون از خلع كننده به عنوان کپي کردن استفاده ميکنه در شرکت "سیگا شرکت های محدود" v. Accolade, Inc.*، 977 F.2d 1510 (9th Cir. 1992) دادگاه تصمیم گرفت که کپی های واسطه ای که در هنگام تجزیه ساخته شده، کپی های تحت قانون حقوق چاپ هستند. سپس این دادگاه اعلام کرد که "در صورتی که خلع واحد راه برای دسترسی به ایده ها و عناصر کاربردی موجود در یک برنامه کامپیوتری که تحت حقوق مالکیت است و در صورتی که دلیل مشروع برای جستجوی چنین دسترسی وجود دارد، خلع یک استفاده عادلانه از اثر تحت حقوق مالکیت است، به عنوان یک موضوع قانونی".
پرونده دوم Sony Computer Entertainment, Inc. v. Connectix Corp. ، 203 F.3d 596 (9th Cir. 2000). Connectix در حالی که یک شبیه ساز ساخته بود، نسخه های BIOS PlayStation را کپی کرد و دادگاه این کپی ها را استفاده عادلانه داد. هر دو پرونده مطالعه را محافظت می کردند. هیچ یک از کد کپی شده در محصول نهایی محافظت نمی کردند.
دو قانون محدودیت ها را تعیین می کنند. 17 قانون ایالات متحده 1201 ((ف) اجازه می دهد تا کسی که "حق استفاده از نسخه ای از یک برنامه کامپیوتری را به طور قانونی بدست آورده است" کنترل دسترسی را دور کند "به منظور شناسایی و تجزیه و تحلیل عناصر برنامه ای که برای دستیابی به قابلیت همکاری یک برنامه کامپیوتری مستقل با سایر برنامه ها ضروری است". چهار دسته محدودیت: یک کپی قانونی، هدف قابلیت همکاری، عناصر که قبلاً "به راحتی در دسترس نبودند" و تجزیه و تحلیل که به طور دیگر نقض نمی کند. بخش 1201 ((f) تنها استثنا از قانون ضد تجاوز است. سوال کپی کردن با استفاده عادلانه باقی می ماند.
در اتحادیه اروپا، ماده 5 (2) ماده 5 (2) ماده 5 (2) ماده 5 (2) ماده 5 (2) ماده 5 (2) ماده 5 (2) ماده 5 (2) ماده 5 (2) ماده 5 (2) ماده 5 (2) ماده 5 (2) ماده 5 (2) ماده 5 (2) ماده 5 (2) ماده 5 (2) ماده 5 (2) ماده 5 (2) ماده 5 (2) ماده 5 (2) ماده 5 (2) ماده 5 (2) ماده 5 (2) ماده 5 (2) ماده 5 (3) ماده 5 (2) ماده 5 (2) ماده 5 (2) ماده 5 (2) ماده 5 (2) ماده 5 (3) ماده 5 (2) ماده 5 (2) ماده 5 (3) ماده 5 (2) ماده 5 (3) ماده 5 (2) ماده 5 (3) ماده 5 (2) ماده 5 (3) ماده 5 (2) ماده 5 (3) ماده 5 (3) ماده 5 (2) ماده 5 (3) ماده 5 (3) ماده 5 (3) ماده 5 (3) ماده 5 (3) ماده 5 (3) ماده 5 (3) ماده 5 (3) ماده 5 (3) ماده 5 (3) ماده 5 (3) ماده 5 (3) ماده 5 (3) ماده 5 (3) ماده 5 (3) ماده 5 (3) ماده 5 (3) ماده 5 (3) ماده 5 (3) ماده 5 (3) ماده 5 (3) ماده 5 (3) ماده 5 (3) ماده 5 (3) ماده 5 (3) ماده 5 (3) ماده 5 ماده 5 ماده 5 ماده 5 ماده 5 ماده 5 ماده 5 ماده 5 ماده 5 ماده 5 ماده 5 ماده 5 ماده 5 ماده 5 ماده 5 ماده 5 ماده 5 ماده 5 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6 ماده 6
ماده ۶ اجازه ی جمع آوری بدون اجازه صاحب حق را می دهد، زمانی که "حصول اطلاعات لازم برای دستیابی به قابلیت همکاری یک برنامه کامپیوتری مستقل ضروری است". شرایط مطابق با 1201 ((f) است. یک مجوزدار اعمال را انجام می دهد، اطلاعات قبلاً به راحتی در دسترس نبودند و اعمال در قسمت های لازم باقی می ماند. ماده 6 ((2) ممنوع است که از نتیجه برای هر هدف دیگر یا برای برنامه ای که در بیان به طور قابل توجهی مشابه است استفاده شود. ماده 8 شرایط قرارداد مخالف ماده 6 یا ماده 5 (3) را باطل می کند.
| Source | What it allows | Conditions |
|---|---|---|
| 17 U.S.C. 1201(f) | Circumventing an access control to analyze a program | Lawful copy, interoperability purpose, information not readily available, no infringement |
| Directive 2009/24/EC Art. 5(3) | Observing, studying, and testing a running program | A right to use the copy, acts within normal loading and running |
| Directive 2009/24/EC Art. 6 | Decompilation | Licensee, interoperability purpose, information not readily available, confined to the needed parts |
| Sega v. Accolade | Disassembly as fair use | The only way to reach the ideas and functional elements, a legitimate reason |
| Sony v. Connectix | Intermediate copies during reverse engineering | A non-infringing final product |
این یادداشت ها از قانون و نظرات آمده و مشاوره حقوقی نیستند. وقتی هدف آینده شما نیست، قبل از باز کردن پرونده و نگه داشتن گواهی با شواهد خود، مجوز نوشتاری دریافت کنید. بخش 2.1 از راهنمای مهندسی معکوس 101 به صورت کامل و پرونده های دادگاه اروپا اشاره دارد.
مشکل
تو جمع آوری کردیledger.cو فرار کردllvm-objdump -tدر مورد اين موضوع. فهرست هاي خروجيpercent_changeدر ارزش0000000000000000با اندازه0000000000000010. کدام 16 بایت از فایل 1768 بایت این تابع هستند؟ لیست همچنین نشان می دهدR_X86_64_PC32 .rodata+0x10در تعویض0x36و چهار بایت در آن نقطه در فایل صفر است. آنها چه چیزی خواهد شد و چرا اضافه شده است0x10وقتی میز شروع میشه0x14؟
rea پاسخ به سوالات طرح با inspect_binary_layout، و اون ابزار فقط در لينوکس x64 با pwntools 4.15.0 وجود دارهREA_PWNTOOLS_PYTHON. در اين مک از دست رفتهaddress_to_file_offsetنیاز به یک جلسه Ghidra و یک کپی کامل باایت های فایل اصلی با یک SHA-256 مطابقت دارد. inspect_artifactاین صفحه به عنوان یک صفحه نمایش داده شده است که در آن آن ها موجود است. این صفحه هیچ وقت جدول های داخل یک فایل شی را باز نمی کند. بنابراین شما خود خواننده را در حدود 950 خط پایتون باstruct.unpackو هيچ وابستگیي نداره.llvm-objdumpمتن یک بار به داخل چراغ ها ثبت شده
فایل ها جدول هایی هستند که به جدول ها اشاره می کنند
سیستم V gABI، فصل 4، شکل 4-1 دو دیدگاه از یک فایل را می کشد. دیدگاه لینک یک سرپرست ELF، یک جدول سرپرست برنامه، بخش ها و یک جدول سرپرست بخش مورد نیاز را دارد. نمایش اجرای دارای یک سرپرست ELF، یک جدول سرپرست برنامه مورد نیاز، بخش ها و یک جدول سرپرست بخش اختیاری است. gABI یک جمله را اضافه می کند که کل خواننده را تحت کنترل قرار می دهد: "تنها سرنخ ELF دارای موقعیت ثابت در فایل است". هر ساختار دیگری توسط یک آفسیت ذخیره شده در ساختار دیگری یافت می شود.
gABI همچنین انواع مقیاس را اصلاح می کند و می گوید که "ELF از هیچ میدان بیت استفاده نمی کند". Elf64_AddrوElf64_Off8 بایت استElf64_Half2 استElf64_Word4 است وElf64_Xwordx86-64 psABI، بخش 4.1.1، نیاز داردELFCLASS64،ELFDATA2LSB،e_machine EM_X86_64(62) وe_flags0. اندازه ی کوچک و دارای عرض ثابتstruct.unpackبا یک<پيشگويي هر سوابق رو در يک تماس ميخواد
اولين 64 بايت از ledger-x86_64-O1.o:
text00000000: 7f45 4c46 0201 0100 0000 0000 0000 0000 .ELF............
00000010: 0100 3e00 0100 0000 0000 0000 0000 0000 ..>.............
00000020: 0000 0000 0000 0000 2804 0000 0000 0000 ........(.......
00000030: 0000 0000 4000 0000 0000 4000 0b00 0100 ....@.....@.....اونها رو با صفحه "ELF Header" GABI ببينe_ident16 بايت است7f 45 4c 46(جادو) ، کلاس 2 (64-بایت) ، داده 1 (کوتاه-اندیان) ، نسخه 1، OS ABI 0 (سیستم V) و پوشاندن.
پسe_type 0x0001.ET_REL، یک شی قابل انتقالe_machine 0x003e62 استEM_X86_64.e_entryصفر است چون یک شی نقطه ورود ندارد. e_phoffصفر است چون یک شی هیچ عنوان برنامه ای ندارد.e_shoff.0x428, فایل تعویض جدول سرگویی بخش.e_ehsize 0x40،e_phentsize0e_phnum0e_shentsize 0x40،e_shnum 0x0b(۱۱ سرنخ) وe_shstrndx 0x0001. .
خواننده اون خط رو به يه تماس مي گيره:
pythonfields = struct.unpack_from("<HHIQQQIHHHHHH", data, 16)رشته فرمت، شکل gABI است که از چپ به راست خوانده می شود: دو نیم کلمه، یک کلمه، سه ارزش 8 بایت، یک کلمه و شش نیم کلمه. خواننده یک فایل را که ترتیب جادویی، کلاس یا بایت متفاوت است رد می کند و رد می کند.e_shentsizeیاe_phentsizeبا عرض اشتباه هر تعویض بعدی اشتباه می شود، بنابراین چک در بالای صفحه قرار دارد.
عنوان بخش ها و دو جدول رشته ای
هر عنوان بخش یک Elf64_Shdrاز 64 بایت (gABI شکل 4-8): sh_name،sh_type،sh_flags،sh_addr،sh_offset،sh_size،sh_link،sh_info،sh_addralignوsh_entsize. رشته فرمتي<IIQQQQIIQQ.sh_nameیک تعویض به بخش که شاخص آن e_shstrndx، و نامش در اولین بايت صفر به پایان می رسد.e_shstrndx1 است و clang نام آن بخش را .strtab، بنابراین یک جدول هم نام بخش و هم نام نماد را خدمت می کند.
خواننده 11 سر را از -O1 پرینت می کند:
textIdx Name Type Flags Offset Size Addr Link Info Align
0 NULL 0x0 0x0 0x0 0 0 0
1 .strtab STRTAB 0x368 0xbe 0x0 0 0 1
2 .text PROGBITS AX 0x40 0x9f 0x0 0 0 16
3 .rela.text RELA I 0x2a8 0x48 0x0 10 2 8
4 .rodata PROGBITS A 0xe0 0x50 0x0 0 0 8
5 .rela.rodata RELA I 0x2f0 0x78 0x0 10 4 8
6 .rodata.str1.1 PROGBITS AMS 0x130 0xa 0x0 0 0 1
7 .comment PROGBITS MS 0x13a 0x2f 0x0 0 0 1
8 .note.GNU-stack PROGBITS 0x169 0x0 0x0 0 0 1
9 .llvm_addrsig LLVM_ADDRSIG E 0x368 0x0 0x0 10 0 1
10 .symtab SYMTAB 0x170 0x138 0x0 1 9 8شاخص 0 عنوان صفر ذخیره شده است. .text159 بايت کد در فایل آفسه شده است0x40، درست بعد از سرش، با پرچمA(در حافظه اختصاص داده شده) وX(قابل اجرا).rodata80 بایت اطلاعات فقط برای خواندن را در اختیار دارد. .rodata.str1.1پنج رشته برچسب را نگه داردF\0D\0C\0B\0A\0، 10 بايت، با پرچمM(مربوط) وS(حافظه ها)
.commentنماد 47 بايت را در دست دارد\0Apple clang version 17.0.0 (clang-1700.6.3.2)\0..llvm_addrsigنوع داره0x6fff4c03، در محدوده خاص سیستم عاملSHT_LOOSبهSHT_HIOSکه ذخایر gABI و LLVM ELF.hاسمش رو به اون بزنه پرچمش رو به اون بزنهE.SHF_EXCLUDE، پس یه لینکر میذاره
.sh_linkوsh_infoستون ها نشان دهنده های بین جدول ها هستند. .rela.text.sh_info2 و sh_link10: بخش 2 را اصلاح می کند و نمادهای خود را از بخش 10 می گیرد..symtab.sh_link1، جدول رشته ای برای نام های نماد و sh_info9, شاخص اولین نماد غیر محلی. sh_addrصفر در هر ردیف است چون یک شی هنوز آدرس ندارد. لینکر آنها را اختصاص می دهد.
دو قانون gABI اجازه می دهد تا خواننده یک نقشه کامل از فایل را رسم کند. بخش ها "مقابل است و تعادل ندارند" و یک فایل "می تواند فضای غیرفعال را داشته باشد". خواننده هر ساختار را با تعویض طبقه بندی می کند و سوراخ ها را پر می کند:
text 0x0 +64 header ELF header
0x40 +159 section .text
0xdf +1 gap inactive space
0xe0 +80 section .rodata
0x130 +10 section .rodata.str1.1
0x13a +47 section .comment
0x169 +7 gap inactive space
0x170 +312 section .symtab
0x2a8 +72 section .rela.text
0x2f0 +120 section .rela.rodata
0x368 +190 section .strtab
0x426 +2 gap inactive space
0x428 +704 table section header tableسه شکاف به مجموع 10 بایت است و هر یک از آنها تعادل است..rodata.sh_addralign8، پس شروع میشه0xe0به جای0xdf..symtabخط 8 داره پس 7 بايت بعد ازش استفاده نشده.commentجدول سرنخ بخش خودش به 8 هموار شده
بخش های نوعSHT_NOBITSهیچ بائتی فایل را در اختیار نمی گیرد، بنابراین نقشه آنها را رد می کند. هیچ چیز در این شی دارای این نوع نیست..bssدر برنامه مرتبط هست و یک خواننده که به او اعتماد دارهsh_sizeاون جا نوشته شده که پرونده رو رد کرده
نماد: پیوند، نوع، بخش، ارزش، اندازه
یکElf64_Sym24 بایت است (گابای شکل 4-18): st_name،st_info،st_other،st_shndx،st_value،st_size، شکل<IBBHQQ.st_infoبسته بندی دو میدان: پیوند در چهار بیت بالا و نوع در چهار پایین. gABI نام های پیوند STB_LOCAL0STB_GLOBAL1، STB_WEAK۲، و انواعSTT_NOTYPE0STT_OBJECT1، STT_FUNC۲، STT_SECTION۳، STT_FILE۴. دو قطعه پایین ازst_otherدر نظر گرفتن دیدSTV_DEFAULT0 در تمام این جا
textIdx Value Size Bind Type Section Name
0 0x0 0 LOCAL NOTYPE *UND*
1 0x0 0 LOCAL FILE *ABS* ledger.c
2 0x0 20 LOCAL OBJECT .rodata labels.rel
3 0x0 0 LOCAL SECTION .rodata .rodata
4 0x2 2 LOCAL OBJECT .rodata.str1.1 .L.str.1
5 0x4 2 LOCAL OBJECT .rodata.str1.1 .L.str.2
6 0x6 2 LOCAL OBJECT .rodata.str1.1 .L.str.3
7 0x8 2 LOCAL OBJECT .rodata.str1.1 .L.str.4
8 0x0 0 LOCAL SECTION .rodata.str1.1 .rodata.str1.1
9 0x0 16 GLOBAL FUNC .text percent_change
10 0x10 46 GLOBAL FUNC .text grade
11 0x40 31 GLOBAL FUNC .text total
12 0x60 63 GLOBAL FUNC .text grade_labelشاخص 0 نماد غیر تعریف شده مخصوص است. st_shndxبخش تعریف کننده نماد را با سه مقدار ذخیره نام می دهد: SHN_UNDEF0SHN_ABS 0xfff1برای نام فایل وSHN_COMMON 0xfff2نماد بخش ها اسم خالی دارن، پسllvm-objdumpنام بخش را برای آنها چاپ می کند و خواننده نیز همین کار را می کند. قاعده gABI که "همه نمادها با پیوند STB_LOCAL قبل از نمادهای ضعیف و جهانی" به عنوان نه ردیف اول ظاهر می شود و .symtabثبت شده 9 در sh_infoبه همین دلیل
در یک شی قابل انتقالst_valueیک تعویض در داخل بخش نماد است، چونsh_addrصفر است. پس فایل تعویض یک تابعsh_offset + st_value - sh_addrو طولش همst_size:
pythonsection = elf.sections[symbol.st_shndx]
file_offset = section.sh_offset + (symbol.st_value - section.sh_addr)
code = elf.data[file_offset : file_offset + symbol.st_size]برایpercent_changeاینا0x40 + 0، و 16 بايت85 ff 74 09 29 fe 6b c6 64 99 f7 ff c3 31 c0 c3اندازه ها هم مثل شروع مهمهgradeدر .textتعویض0x3e. دو بايت66 90که دنباله دار هستندnopکمپایلر اضافه شده تاtotalشروع می شه0x40درسي 33 استفاده ميکنهst_sizeو درس 36 آنها را قبل از مقایسه بازسازی، پنهان می کند.
نقل مکان: معنی صفر بایت
یکElf64_Rela24 بایت است (گابای شکل 4-23): r_offset،r_infoو امضا شدهr_addend، شکل<QQq. گابي دوتا مي شودr_infoبا دو ماکرو: ELF64_R_SYM(i).i >> 32وELF64_R_TYPE(i).i & 0xffffffff. در پرونده ي قابل انتقالr_offsetیک تعویض در داخل بخش نامگذاری شده توسط جدول است sh_infoنام ها و فرمولا از x86-64 psABI، جدول 4.9 آمده است.Sارزش نماد است، Aاضافه کنندهPمحل که درست می شود وLورودی PLTR_X86_64_64.S + A،R_X86_64_PC32.S + A - PوR_X86_64_PLT32.L + A - P. .
-O1 8 تا رکورد داره
text .rela.text 0x36 R_X86_64_PC32 .rodata+0x10
.rela.text 0x84 R_X86_64_PC32 .rodata+0x24
.rela.text 0x93 R_X86_64_PC32 .rodata-0x4
.rela.rodata 0x0 R_X86_64_PC32 .rodata.str1.1
.rela.rodata 0x4 R_X86_64_PC32 .L.str.1+0x4
.rela.rodata 0x8 R_X86_64_PC32 .L.str.2+0x8
.rela.rodata 0xc R_X86_64_PC32 .L.str.3+0xc
.rela.rodata 0x10 R_X86_64_PC32 .L.str.4+0x10اولين رو از هم جدا کن0x36در داخلgrade، که در طولش0x10به0x3e. آموزش در0x33.48 8d 0d 00 00 00 00، یکleaاز یک آدرس مربوط به RIP، و تغییر مکان چهار بیت آن در 0x36. پردازنده، جابجا شدن را به آدرس دستورات بعدی اضافه می کند0x3a. .
کامپایلر میخواد نتیجه اش باشه.rodata + 0x14، جایی که یک جدول پنج ورودی با 32 بیت ارزش دارد 1, 2, 3, 4, 4پس ارزش ذخیره شده باید(.rodata + 0x14) - 0x3a. فرمولS + A - PباP = 0x36می دهد.rodata + A - 0x36. تنظیم دو برابر به دست می آیدA = 0x14 - 4 = 0x10. اضافه کردن هدف را از بین چهار بایت بین جابجایی و پایان دستورالعمل می برد.
این جدول اینه که چطور clang جمع آوری کردswitchدرgradeدر -O1 کد حساب می کنهscore / 10با ضرب از 0x66666667و دو تغییر، از 6 تخفیف می کند، با 4 مقایسه می کند و جدول را شاخص می کند. شیء -O1 ELF هیچ نماد برای جدول را حفظ نمی کند. مرجع نماد بخش است .rodataو فقط ساخت بازي 64 از همان منبع نامش را مي دهدl_switch.table.grade. دوين رکورد، در0x84درgrade_label, نقطه ها در جدول دوم با ارزش های 8 بایت در .rodata + 0x28سومين، در0x93، اشاره به.rodata + 0x0، آغازlabels.rel. .
پنج نفر.rela.rodataثبتات پر شودlabels.rel. هر ورودی در تعویض قرار داره4iو به دست آوردش اضافه می شود4i، پسS + A - Pبه عنوان آدرس رشته کاهش می یابد.grade_labelيه نوشته رو با movslq, پایه میز را به عقب می اندازد و اشاره را دارد. اولین رکورد نماد بخش را نام می دهد.rodata.str1.1با اضافه کردن 0 چون رشته Fاین اولین بائته ی این بخش است. بقیه نامها.L.str.1به.L.str.4، برچسب های محلی کامپایلر برایD،C،BوA. .
همان منبع به سه جدول مختلف در سه سطح بهینه سازی ترکیب می کند. خواننده تفاوت را بدون یک جداکننده نشان می دهد:
| Build | Where labels lives | How grade selects | How grade_label reaches grade |
|---|---|---|---|
| -O0 | .data, five 8-byte pointers, five R_X86_64_64 records | A jump table in .rodata: five R_X86_64_PC32 records pointing into .text | A call with R_X86_64_PLT32 grade-0x4 |
| -O1 | .rodata as labels.rel, five 32-bit relative entries | A value table at .rodata+0x14, one R_X86_64_PC32 | Inlined, no call |
| -O2 | Same as -O1 | Same as -O1 | Inlined, total grows to 140 bytes |
در -0switchيه ميز واقعي است که پنج تا ورودي به سمتش مياد.textدر+0x91،+0x8c،+0x87،+0x82و+0x86. تماس باgradeهمش رو حمل ميکنه-0x4به همین دلیل کهleaدر -O1 بهینه ساز دید که هر مورد ثابت را باز می گرداند و پرش ها را با جدول نتایج جایگزین کرد. درس 33 هر دو شکل را رمزنگاری می کند.
عنوان برنامه ها: دیدگاه بارگذاری
يه شيوه ي ي يابين جا نيست، پسe_phnumیک اجراء یا یک شی مشترک آنها را دارد و بارگذاریگر چیزی دیگر نمی خواند.Elf64_Phdr56 بایت است (نمای 5-1 GABI): p_type،p_flags،p_offset،p_vaddr،p_paddr،p_filesz،p_memsz،p_align، شکل<IIQQQQQQ. گابي سه قانون برايPT_LOAD. واردات . در بالا رفتن ظاهر می شوندp_vaddrدستور و یکp_memszبزرگتر از p_fileszیعنی صفر پر شدنp_vaddrهمگام استp_offsetماژولp_align. .
هر هدف در این آهنگ یک برنامه ای است که شما ساخته اید و هیچ لینکر در این مک تولید یک لینوکس اجرا از ledger.oپس اين دستگاهtiny-x86_64ازcode/fixtures/build_tiny_elf.py، که 736 بايت با struct.pack. فایل يه سرپرستي ELF، دو سرپرستي برنامه، يک 36 بايت داره_start، یک 3 بايت.rodata، یک جدول نماد و شش عنوان بخش_startنوشته شدهok\nدر لینوکس x86-64 اجرا می شود. اینجا یک فستور برای مسیر عنوان برنامه است.
textProgram headers:
PT_LOAD r-x offset=0x0 vaddr=0x400000 filesz=0x2e0 memsz=0x2e0 align=0x1000
PT_GNU_STACK rw- offset=0x0 vaddr=0x0 filesz=0x0 memsz=0x0 align=0x10
entry 0x4000b0 is file offset 0xb0 through PT_LOADتک آهنگPT_LOADنقشه کل فایل 736 بايت را در 0x400000. خواننده با پیدا کردن PT_LOADکه شامل آن و محاسبات استp_offset + (address - p_vaddr). برای نقطه ورود که0 + (0x4000b0 - 0x400000) = 0xb0، آغاز.text. .
اين حسابي پشت رياستaddress_to_file_offsetبراي يه هدف گيدرا، اون ابزار بازميگرده{address, file_offset}.برندگان هاپر هم برگردنprovider_file_offsetوimage_base_file_offset. ابزار نیاز به یک نسخه اصلی کامل از پرونده دارد و SHA-256 از آن نسخه باید با هدف مطابقت داشته باشد.inspect_binary_layoutیک خط برای حفظ اضافه می کند: " یک آدرس مرتبط آدرس زمان اجرا نیست". یک بارگذاری قابل اجرا مستقل از موقعیت در پایه ای که هسته انتخاب می کند ، و تعویض فایل تنها هماهنگی پایدار است.
عنوان های برنامه همچنین توضیح می دهند که چرا یک اجراگر خالی هنوز اجرا می شود. یک لینکر می تواند جدول عنوان بخش را کاملا حذف کند و e_shoffیک ابزار که بخش ها را حرکت می دهد هیچ چیز را نمی بیند. بارنده هرگز به آنها نگاه نمی کند.
ایده های مشابه در Mach-O
اپل هیچ مشخصات پروزی برای Mach-O را منتشر نمی کند. سرنخ های منبع XNU مشخصات هستند: EXTERNAL_HEADERS/mach-o/loader.h،nlist.hوreloc.h. سازشledger-arm64-O1.oهمش همینطورهledger.cبا ساختclang -target arm64-apple-macos -O1 -fno-asynchronous-unwind-tables -c. .
Amach_header_6432 بايت است: magic،cputype،cpusubtype،filetype،ncmds،sizeofcmds،flags،reservedجادويي اينه0xfeedfacf،MH_MAGIC_64.cputype.0x0100000c،CPU_TYPE_ARM64.filetype1 استMH_OBJECT.ncmds5 و sizeofcmds616 است: دستورات بارگیری 616 بایت را پس از سر و کله اش می گیرد و هر یک از آنها با شروع می شود{cmd, cmdsize}. پرچم0x2000.MH_SUBSECTIONS_VIA_SYMBOLS. .
textLoad commands:
0 LC_SEGMENT_64 cmdsize=472 at 0x20
1 LC_BUILD_VERSION cmdsize=24 at 0x1f8
2 LC_LINKER_OPTIMIZATION_HINT cmdsize=16 at 0x210
3 LC_SYMTAB cmdsize=24 at 0x220
4 LC_DYSYMTAB cmdsize=80 at 0x238LC_SEGMENT_64(0x19) پاسخ Mach-O برای هر دو جدول ELF است.segment_command_64.segname،vmaddr،vmsize،fileoff،filesize،maxprot،initprot،nsectsوflagsپسnsectsسوابق نوعsection_64،80 بايت هرکدوم، با همون دستور عمل کنينloader.hنظرش در موردMH_OBJECTمی گوید بخش ها در یک بخش نامناسب بدون پوشاندن بخش زندگی می کنند.fileoff648،filesize456 و پنج بخش
text Idx Section Addr Size Offset Reloff Nreloc Type
1 __TEXT,__text 0x0 0xd0 648 1104 6 S_REGULAR
2 __DATA,__const 0xd0 0x28 856 1152 5 S_REGULAR
3 __TEXT,__cstring 0xf8 0xa 896 0 0 S_CSTRING_LITERALS
4 __TEXT,__const 0x108 0x40 912 0 0 S_REGULAR
5 __LD,__compact_unwind 0x148 0x80 976 1192 4 S_REGULARهر بخش به بخش اصلی خود نام می دهدsegname، پس دو بخش به نام__constفرق داره چون یکی از اونا__DATA,__constو اوني ديگه__TEXT,__const. نقل مکان ها به هر بخش ازreloffوnreloc، جایی که ELF دارای یک جدا شده است.rela.Xبخش به هر هدف
هرکدومrelocation_info8 بايت است.r_addressاول می آید. یک فیلد 32 بیت بعد می آید، بسته بندی شده به عنوانr_symbolnum(24 بت)r_pcrel(1) r_length(2) ، r_extern(1) وr_type(4) شش__textسوابق سه جفت استARM64_RELOC_PAGE21وARM64_RELOC_PAGEOFF12، یک جفت برای هر میز مرجع.adrpو اضافهaddچه کاري ميکنه؟leaآره، پس هر مرجع به دو پرونده نیاز داره.__DATA,__constسوابق هاARM64_RELOC_UNSIGNEDباr_length3، پس هر یک از آنها یک شاخص مطلق 8 بایت را درست می کند.
در -O1 ساخت دست64 حفظ شدlabelsبه عنوان اشاره های ساده که در آن x86-64 ساخت یک جدول نسبی ساخته شده است.
LC_SYMTAB(0x2) نگه دارهsymoff1240،nsyms17،stroff1512،strsize176 و 1512 + 176، 1688 است، اندازه فایل. هر یک.nlist_6416 بايت است: n_strx،n_type،n_sect،n_desc،n_value.n_typeیه میدان کمی استN_TYPE(ماسک)0x0e) برابر به N_SECT(0xe) معنی آن در بخش تعریف شده است که حکم 1 استn_sect.N_EXT(0x01) یک نماد خارجی را نشان می دهد.
nlist_64برای گرفتن 24 بایت از_percent_changeخواننده آدرس علامت بعدی را در همان بخش می گیرد._gradeدر0x18. نماد C نماد اولي داره_اینجا، بنابراین تابع است_percent_change. .
LC_DYSYMTAB(0xb) همان قانون اول محلی را با ELF ثبت می کند sh_info.ilocalsym0nlocalsym13،iextdefsym13،nextdefsym۴.LC_BUILD_VERSION(0x32) ثبت شدهplatform1 (macOS) ، minos۲۶.۰ وsdk26.2 بسته بندی شدهXXXX.YY.ZZاین دستور است که دلیل این است که دستگاه arm64 به عنوان بائتهای انجام شده است. ساخت یک Mac با SDK دیگر یک فایل متفاوت تولید می کند.
. خوانندهcompare_formatsدو پرونده را کنار هم قرار می دهد:
| Idea | ELF, ledger-x86_64-O1.o | Mach-O, ledger-arm64-O1.o |
|---|---|---|
| File identity | e_ident 7f 45 4c 46, class 64, data LSB | magic 0xfeedfacf |
| Machine | e_machine 62, EM_X86_64 | cputype 0x100000c, CPU_TYPE_ARM64 |
| File kind | e_type 1, ET_REL | filetype 1, MH_OBJECT |
| Where the tables are | e_shoff 0x428, 11 section headers | 5 load commands, 616 bytes after the header |
| The code | .text at 0x40, size 0x9f, flags AX | __TEXT,__text at 648, size 0xd0, in the unnamed segment |
| Symbol names | .symtab (section 10), names in .strtab through sh_link 1 | LC_SYMTAB symoff 1240, nsyms 17, names at stroff 1512 |
| Symbol size | st_size: percent_change is 16 bytes | No field: next symbol minus this one, 24 bytes |
| Relocations | .rela.text, 3 records, R_X86_64_PC32 | __text reloff 1104, 6 records, PAGE21 and PAGEOFF12 |
| Function bytes | percent_change: 16 bytes at 0x40 | _percent_change: 24 bytes at 0x288 |
"آریا" میخواد "ماچ" رو از طریق "inspect_macho، که اجرا مي کنهotool -h -l،nm -gjU،dyld_info -imports -exportsوdwarfdump --uuidاز طریقxcrunخواننده شما به هیچکدوم از این ها نیاز نداره و همین رو میخوادsymoffوstroffاینotool -l. اثر انگشتopen_binaryابزار ریا ELF، MZ، PE و نازک یا جهانی Mach-O را از بایت های جادویی تشخیص می دهد.e_machineاین اولین 20 بایت از آنچه که شما در اینجا ساخته اید است. برای یک فایل بدون سر و کله، شما می دهیدformat: raw-binary.سازش و آدرس ورود. ارائه دهنده Ghidra یک دوگانه جهانی را رد می کند و از یک قطعه نازک می خواهد. خواننده شما همین کار را می کند وقتی می بیند0xcafebabe. .
آن را بسازید
خواننده استاندارد کتابخانه رو اجرا کن:
bashcd phases/13-tools-and-protocols/32-program-anatomy-elf-from-scratch
python3 code/main.pyنمایش نوشته شدهledger-x86_64-O1.oو سر و قسمت، بخش ها، نمادها، نقل مکان ها و نقشه تعویض فایل را چاپ می کند. سپس چهار رشته بایت تابع را چاپ می کند، می خواند ledger-arm64-O1.oو جدول مقایسه را چاپ می کند.tiny-x86_64. خط هاي انتظار شده:
textpercent_change: 16 bytes at file offset 0x40: 85 ff 74 09 29 fe 6b c6 64 99 f7 ff c3 31 c0 c3
grade: 46 bytes at file offset 0x50: 48 63 c7 48 69 c0 67 66 66 66 ...
entry 0x4000b0 is file offset 0xb0 through PT_LOADمسیر را برای خواندن یک فایل عبور کنید: python3 code/main.py code/fixtures/ledger-x86_64-O0.oخواننده از چهار بايت اول ELF یا Mach-O را انتخاب مي کند.
بخونcode/main.pyدر این ترتیب:
ElfFile._read_header()جادو، کلاس، ترتيب بايت و اندازه ورودي را چک مي کند، سپس سرگير را باز مي کند.ElfFile._read_sections()وread_cstring()جدول سرگوه بخش را بخوانید و نام ها را از طریق حل کنیدe_shstrndx. .ElfFile._read_symbols()تقسیم شدنst_info، اسم ها رو حل ميکنهsh_link، و شاخص های بخش رزرو شده را قابل خواندن نگه می دارد.ElfFile._read_relocations()هر بار راه میرهSHT_RELAبخش، تقسیمr_info, و انواع نام از جدول 4.9.ElfFile.function_span()وfunction_bytes()یک نماد را به یک دامنه فایل و بائتهای آن تبدیل کنید.ElfFile.file_offset_map()هر ساختار را با تعویض طبقه بندی می کند و فضای غیرفعال را پر می کند.ElfFile._read_program_headers()وaddress_to_file_offset()دستکاری های اجرا کنندهMachOFileسر و کله رو ميخواد، فرمان هاي بار رو ميخوادLC_SEGMENT_64با بخش های آن،LC_SYMTAB،LC_BUILD_VERSION، و نقل مکان در هر بخشcompare_formats()اسم ها در هر دو پرونده با ارزش های خوانده شده از هر یک از آنها به عنوان یک ایده است.
ازش استفاده کن
و درآمدی خواننده از درس های بعدی با from main import ElfFileبعد از اضافه کردن درسcodeفهرست به sys.pathدرس 33 ازش ميخوادfunction_bytesوrelocations_for(".text")دروس 35 به عنوان شواهد مشاهده شده، تولید آن را ثبت می کند. دروس 36 از آن می خواهد که تعویضات نقل مکان را قبل از مقایسه باایت ها پنهان کند.
خودتون را به C خودتون به یک شی با همانclang -target x86_64-unknown-linux-gnu -cفرمان و اجرا خواننده در آن. فقط زمانی که x86-64 psABI جدول 4.9 آن را لیست می کند نام نقل مکان را اضافه کنید. فقط زمانی که gABI یا زنجیره ابزار تولید کننده آن را مستند کند، یک نوع بخش را اضافه کنید. هنگامی که خواننده با یک مقدار که نام ندارد ملاقات می کند، تعداد را به جای حدس زدن به صورت شش اعشاری چاپ می کند.
آزمایشگاه تعاملی
آزمایشگاه A: سرنخ دست
ازcodeدایرکتوری، پیتون باز:
bashcd phases/13-tools-and-protocols/32-program-anatomy-elf-from-scratch/code
python3 -qراه رفتن:
pythonimport struct
from main import ElfFile, FIXTURES
data = (FIXTURES / "ledger-x86_64-O1.o").read_bytes()
data[:4], data[4], data[5]
struct.unpack_from("<HHIQQQIHHHHHH", data, 16)
elf = ElfFile(data)
elf.header.e_shoff, elf.header.e_shnum, elf.sections[elf.header.e_shstrndx].nameتوپل نشان میدهe_type1، e_machine۶۲، وe_shoff1064، که0x428. آخرين خط نشان ميده که بخش 1.strtab، اسم بخش ها رو نگه داره
آزمایشگاه B: جدول ارزش پشت سوئیچ
pythonrodata = elf.section_bytes(elf.section(".rodata"))
[int.from_bytes(rodata[i : i + 4], "little") for i in range(0x14, 0x28, 4)]
[int.from_bytes(rodata[i : i + 8], "little") for i in range(0x28, 0x50, 8)]
[r for r in elf.relocations_for(".text") if r.symbol == ".rodata"]هر دو جدول رو مطالعه کن1, 2, 3, 4, 4. با اونها رو چک کنledger.c.grade(65)حساب ها65 / 10 - 6 = 0و نوشته شده در 0 که 1 است، ارزش case 6.grade(100)نوشته شده در 4، که 4 است، ارزش مشترک توسط case 10وcase 9. .
آزمایشگاه C: سه ساخت یک منبع
pythonfor level in ("O0", "O1", "O2"):
obj = ElfFile.from_path(FIXTURES / f"ledger-x86_64-{level}.o")
print(level, [s.name for s in obj.sections if s.name in (".data", ".rodata")])
print(level, sorted({r.type_name for r in obj.relocations}))
print(level, {s.name: s.st_size for s in obj.symbols if s.type == "FUNC"})-0 رو به صورت -0.dataبخش و سه نوع نقل مکان..dataو يه نوعtotal66 بایت در -O0، 31 در -O1 و 140 در -O2 است.
آزمایشگاه D: همان عملکرد در Mach-O
pythonfrom main import MachOFile
macho = MachOFile.from_path(FIXTURES / "ledger-arm64-O1.o")
[(c.name, c.cmdsize) for c in macho.load_commands]
macho.function_span("_percent_change")
macho.function_bytes("_percent_change")[:4].hex()طول زمان 24 بایت در فایل 648 است. چهار بایت اولa0000034، شکل کوچولو اين آموزش ها0x340000a0، کهllvm-objdump -dچاپ شدهcbz w0, 0x14. اينمbefore == 0محافظ، همان تست که ساخت x86-64 به عنوان85 ff 74 09. .
آزمایشگاه تمرین
در یک ماشین لینوکس x86-64، ساخت clang -O1 -shared -fPIC code/fixtures/ledger.c -o libledger.soوclang -O1 code/fixtures/ledger.c -o ledger_mainبا یکmainشما اضافه می کنید، سپس خواننده را روی هر دو اجرا کنید.
الزامات:
- بخون
.dynsymبا همElf64_Symطرح، از زمانSHT_DYNSYM11 و سابقه هم بدون تغییر - بخون
.dynamicمثلElf64_Dynثبتd_tagوd_valو اسمشDT_NEEDED،DT_STRTAB،DT_SYMTAB،DT_RELAوDT_JMPRELاز صفحه gABI "قسم دینامیک" - نماد های نامحدود را با فهرست کنید
st_shndxSHN_UNDEFبه عنوان واردات وR_X86_64_JUMP_SLOTثبت شده در.rela.pltبه عنوان سایت های تماس که به آنها می رسد. - نقشه هرکدوم
PT_LOADبخش به بخش هایی که در آن است، با استفاده از قانون gABI که "یک بخش فایل شی حاوی یک یا چند بخش است". - ثابت کن
address_to_file_offsetکه بائتهای هر تابع تعریف شده در شی مشترک بائتهایی هستندfunction_bytesباز می گردد - دستگیرش کن
llvm-objdump -h -t -r -pوllvm-objdump -Tمتن برای هر دو فایل واردcode/fixtures/و خواننده را با آنها در آزمون مقایسه کنید.
موفقیت به این معنی است که لیست واردات، نقشه بخش و هر محدوده عملکرد با محصول ابزار ضبط شده موافق هستند، بدون اجرای ابزار در طول آزمایشات.
درس هنرپیشه
این درس به ما کمک می کندoutputs/skill-program-anatomy-elf-from-scratch.md. با کلوید، کورسور، کودکس، OpenClaw، هرمز، یا هر عامل دیگری که SKILL.md را می خواند استفاده کنید. این مهارت یک شی یا قابل اجرا را که کاربر در یک گزارش طرح ساخته است تبدیل می کند. گزارش دامنه فایل هر ساختار، نماد و نقل مکان را می دهد، با محصول ابزار ضبط شده بررسی می شود.
بررسی کنید
نمایش و مجموعه تعیین کننده را اجرا کنید:
bashcd phases/13-tools-and-protocols/32-program-anatomy-elf-from-scratch
python3 code/main.py
python3 -m unittest discover -s code/tests -vجنازه ثابت ميکنه:
- هر بخش، اسم، اندازه، آدرس و نوعي که با دستگير شده ها مطابقت داره
llvm-objdump -hمتن برای سه شی x86-64 و اجرا قابل انجام کوچک - هر ارزش نماد، پیوند، نوع، بخش، اندازه و نام با ضبط شده مطابقت دارد
llvm-objdump -tمتن - هر تعویض نقل مکان، نام نوع، نماد و اضافه با دستاورده ها مطابقت دارد
llvm-objdump -rمتن، از جملهR_X86_64_PLT32وR_X86_64_64در -O0 - هر دو سرپرست برنامه
tiny-x86_64با دستگير ها مطابقت داشته باشllvm-objdump -pمتن، و اشیاء هیچ percent_change16 بايت در تعویض فایل است0x40با بايت هاي انتظار مي رود و هر تابع در داخلش قرار داره.text- نقشه تعویض فایل هر بایت هر دستگاه را یک بار پوشش می دهد، با سه شکاف تعویض اشیاء -O1 در مکان های خود
- نقطه ورود
tiny-x86_64نقشه ها برای فایل های تعویض0xb0از طریقPT_LOAD، و یک شی قابل انتقال از نقشه برداری آدرس ها رد می کند - سرش ميچ-او، هر فرمان بار، بخش، پنج بخش
LC_SYMTABوLC_BUILD_VERSIONبا دستگير ها مطابقت داشته باش--private-headersمتن - نماد هاي ماش او و 15 تغيير مکاني با نيروهاي دستگير شده مطابقت ميکنه
llvm-objdumpمتن و_percent_change24 بایت در 648 آفسه - یک جادوی اشتباه، یک کلاس ۳۲ بت، یک فایل بزرگ، یک جدول کوتاه، یک دوگانه جهانی، و یک فایل منبع C همه بالا می روند.
FormatError build_tiny_elf.pyتکرار کرده استtiny-x86_64بایت برای بایت- ماژول ارقام
site/figures-re-32.jsبا کت های دقیقledger-x86_64-O1.o
روش های شکست تولید
| Failure | What the weak reading reports | What the reader must prove |
|---|---|---|
st_value used as a file offset | "percent_change is at 0" | Offset is sh_offset + st_value - sh_addr, here 0x40 |
| Padding counted as code | "grade is 48 bytes" | st_size is 46 and the 66 90 after it belongs to no symbol |
| Section names read from the wrong table | Garbage names or an exception | e_shstrndx names the table, here section 1 |
sh_size trusted for SHT_NOBITS | A read past the end of the file | .bss has no file bytes and the map skips it |
| Zero bytes read as the operand | "lea 0(%rip)" | The .rela.text record at 0x36 names .rodata+0x10 |
| Addend read as the target offset | "the table starts at 0x10" | S + A - P with the instruction end gives 0x14 |
| Sections walked on a stripped executable | "the file has no code" | PT_LOAD maps every address the loader uses |
| A linked address treated as a runtime address | A wrong offset under a random base | The file offset from PT_LOAD is the stable coordinate |
A Mach-O section looked up by sectname alone | Two __const sections collapse into one | segname,sectname is the name |
A size field expected in nlist_64 | An exception or zero bytes | The next symbol in the same section ends the function |
| A universal binary read as one Mach-O | "magic 0xcafebabe is unknown" | Refuse it and ask for a thinned slice |
| The arm64 object rebuilt on another Mac | "the fixture changed" | LC_BUILD_VERSION records the SDK, so the committed bytes are the fixture |
قاعده عملیاتی
در این بخش، شما می توانید یک عنوان را بخوانید که می توانید به آن اعتماد کنید، از تعویضات آن پیروی کنید و هر فیلدی را از مشخصات که آن را تعریف می کند نام دهید. محصول ابزار را که با آن مقایسه کرده اید به عنوان یک ابزار متعهد نگه دارید، بنابراین مقایسه بدون ابزار اجرا می شود. هنگامی که یک مقدار نامی در مشخصات ندارد، شماره را چاپ کنید. خواننده ای که می توانید باایت به باایت بررسی کنید، آن است که هر درس بعدی بر آن بنا می شود.
خواندن بیشتر
- System V gABI, chapter 4: Object FilesوELF Header،Sections،Symbol TableوRelocationصفحات
- System V gABI, chapter 5: Program HeaderوDynamic Linking
- System V ABI, AMD64 Architecture Processor Supplement, جدول 4.9 انواع نقل مکان
- Apple XNU
loader.h،nlist.hوreloc.h - 17 U.S.C. 1201وDirective 2009/24/EC
- Sega v. Accolade, 977 F.2d 1510وSony v. Connectix, 203 F.3d 596
- rea 6.5.0
docs/binary-diagnostics.mdوdocs/ghidra-provider.md
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.