Program Anatomisi: Bir ELF Okuyucu sıfırdan
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
Öğrenme Hedefleri
- Bu pistteki her hedef için izin kuralı ve 17 U.S.C. 1201 f'nin dört sınırını belirtin.
- ELF başlığı, bölüm başlığı tablosunu ve her iki dizilenin de tablosunu ile okuyun.
struct.unpack- Evet . - Çözümleme
.symtabNotlar ve.relagABI ve x86-64 psABI isimleri ile kayıtlar. - Adlı bir fonksiyonun tam baytlarını geri gönderin ve her dosyayı sahip olduğu yapıya haritasınız.
- Program başlıklarını okuyun ve bir dosya için bir sanal adresi haritasını oluşturun.
PT_LOAD- Evet . - Mach-O nesnesinin yük komutlarını oku ve her birinin eşleşen ELF fikri adını ver.
Başlamadan Önce: Yetki ve Kanun
32 ila 36 derslerindeki her hedef senin yazdığın ve oluşturduğun bir program.code/fixtures/ledger.cBu yazılımın kaynağı, kompiliör komutası ve oluşturduğu nesne dosyaları sizin. Okumak için lisans yok, geçmek için koruma yok, ve kodunu açan üçüncü taraf yoktur.
Kural var çünkü yasa kopyalanmayı kopyalama olarak görüyor. Sega Enterprises Ltd. V. Accolade, Inc., 977 F.2d 1510 (9th Cir. 1992), mahkeme, sökülürken yapılan ara kopyaların Telif Hakları Kanunu uyarınca kopya olduğunu kararlaştırdı. Daha sonra, "Mücutlık hakkı korunan bir bilgisayar programında yer alan fikirlere ve işlevsel unsurlara erişmenin tek yolu ve bu erişimi talep etmek için meşru bir neden olduğu yerlerde, muhakkak ki, muhakkak ki, muhakkak ki bu yasal bir konudur".
İkinci dava Sony Computer Entertainment, Inc. v. Connectix Corp. , 203 F.3d 596 (9th Cir. 2000). Connectix bir emülatör inşa ederken PlayStation BIOS kopyalandı ve mahkeme bu kopyaları adil kullanım olarak gördü.
İki kanun sınırları belirliyor. 17 U.S.C. 1201 ((f) "bir bilgisayar programının kopyasını kullanma hakkını yasal olarak elde eden" bir kişiye erişim kontrolünü "tek amaçla bağımsız olarak oluşturulan bir bilgisayar programının diğer programlarla birlikte etkileşime geçmesi için gerekli olan programın öğelerini tanımlamak ve analiz etmek için" elden geçirme hakkı verir. Dört sınırlı yığın: yasal bir kopya, birlikte çalışabilme amacı, zaten "sadece mevcut olmayan" öğeler ve başka türlü ihlal edilmeyen analizler. 1201 (f) bölümü, sadece kaçakçılık önleme kuralına istisna. Kopyalama sorusu adil bir şekilde kullanılır.
Avrupa Birliği'nde, 2009/24/EK Direktifi'nin 5*3) maddesinde çalışma kapsamaktadır. Kopyayı kullanma hakkı olan bir kişi "programın herhangi bir öğesinin temelinde bulunan fikirleri ve ilkeleri belirlemek için programın işleyişini gözlemleyebilir, inceleyebilir veya test edebilir".
Madde 6'da, "Bağımsız olarak oluşturulan bir bilgisayar programının birlikte çalışması için gerekli olan bilgileri elde etmek için gerekli olan" durumlarda, hak sahibi'nin izni olmadan kopyalanma izin verilir. Şartlar 1201 ((f) ile aynıdır. Bir lisans sahibi, eylemleri yapar, bilgi zaten kolayca kullanılamıyordu ve eylemler gerekli parçalar içinde kalır. Madde 6 ((2) sonucu herhangi bir başka amaç veya ifade açısından önemli ölçüde benzer bir program için kullanmayı yasaklar. Madde 8'de, 6. veya 5. (3) maddelere aykırı sözleşme şartları geçersiz kılındı.
| 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 |
Bu notlar, statülerden ve görüşlerden gelir ve yasal tavsiyeler değildir. Gelecekte bir hedef sizin değilse dosyayı açmadan önce yazılı bir yetki alın ve yetkiyi kanıtlarınızla birlikte tutun.
Sorun
- Sen topladın .
ledger.cVe koştu .llvm-objdump -t- Çıktı listeler.percent_changedeğerinde0000000000000000büyüklüğü ile00000000000000101.768 baytlı dosyanın hangi 16 baytı fonksiyonu var?R_X86_64_PC32 .rodata+0x10- Yapabilir miyim ?0x36Bu da bir diğer şey. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .0x10Masa başlanda0x14- Ne ?
rea düzen sorularını cevaplar inspect_binary_layout, ve bu araç sadece Linux x64 ' te var ve 4.15.0 ' un arkasında pwntools var .REA_PWNTOOLS_PYTHONBu Mac'te yok.address_to_file_offsetGhidra oturumuna ve orijinal dosya bytesinin tamamı kopyasına ihtiyaç duyar.inspect_artifactBu, bir nesne dosyasının içindeki tabloları asla açmaz. Bu yüzden okuyucuyu kendiniz Python'un yaklaşık 950 satırı ile yazırsınız.struct.unpackTestler her tabloyu karşılaştırır.llvm-objdumpBir kez fotoğraflara yerleştirilmiş metin.
Dosya, Masalara Göre Göre Görülen Tablolardır
Sistem V gABI, bölüm 4, Resim 4-1, bir dosyanın iki görünümünü çizer. Bağlantı görünümünde ELF başlığı, seçmeli bir program başlığı tablosu, bölümler ve gerekli bir bölüm başlığı tablosu bulunur. Çalışma görünümünde ELF başlığı, gerekli program başlığı tablosu, bölümler ve seçmeli bir bölüm başlığı tablosu bulunur. GABI, tüm okuyucuyu yönlendiren bir cümle ekler: "Yalnızca ELF başlığı dosyada sabit bir konuma sahiptir". Diğer tüm yapılar başka bir yapıda saklanan bir ofset tarafından bulunur.
GABI ayrıca skalar türlerini de düzeltir ve "ELF bit alanlarını kullanmaz" diyor. Elf64_Addrve Elf64_Off8 byte.Elf64_Half2'dir.Elf64_Word4'dir ve Elf64_Xword8. x86-64 psABI, bölüm 4.1.1' ü talep eder.ELFCLASS64- Evet .ELFDATA2LSB- Evet .e_machine EM_X86_64(62) ve e_flags0. Sıkı genişliğe sahip küçük endian ortalama struct.unpackbir <Önceden her kayıt tek bir çağrıda okunuyor.
İlk 64 byte 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 ....@.....@.....Onları GABI "ELF Header" sayfasına karşı oku.e_ident16 byte .7f 45 4c 46(Sihir), sınıf 2 (64-bit), veri 1 (çocuk-endyan), sürüm 1, OS ABI 0 (Sistem V), ve dolgu.
O zaman ...e_type 0x0001- Evet .ET_REL, yer değiştirilebilir bir nesne.e_machine 0x003e62'dir.EM_X86_64- Evet .e_entrybir nesne giriş noktası olmadığı için sıfırdır. e_phoffbir nesne program başlıkları olmadığı için sıfırdır. e_shoff- Evet .0x428, bölüm başlık tablosunun dosya sıfırlaması. Son satır tutar e_ehsize 0x40- Evet .e_phentsize0 , e_phnum0 , e_shentsize 0x40- Evet .e_shnum 0x0b(11 başlık) vee_shstrndx 0x0001- Evet .
Okuyucu bu satırı tek bir çağrıya dönüştürüyor:
pythonfields = struct.unpack_from("<HHIQQQIHHHHHH", data, 16)Format dizilisi, soldan sağa okunan gABI figürüdür: iki yarım kelime, bir kelime, üç 8 bayt değerleri, bir kelime ve altı yarım kelime. Okuyucu sihirli, sınıf veya bayt sırası farklı bir dosyayı reddeder ve reddeder.e_shentsizeveya e_phentsizeYanlış genişlik, her sonraki geçişin yanlış olmasını sağlar, bu yüzden çek en üstte yer alır.
Bölüm Başlıkları ve İki İpi Tablo
Her bölüm başlığı bir Elf64_Shdr64 byte (gABI Resim 4-8): sh_name- Evet .sh_type- Evet .sh_flags- Evet .sh_addr- Evet .sh_offset- Evet .sh_size- Evet .sh_link- Evet .sh_info- Evet .sh_addralignvesh_entsizeFormat dizilisi <IIQQQQIIQQ- Evet .sh_nameindeksinin bölüme bir karşılığıdır e_shstrndxBu nesne, 0 byte ile biter.e_shstrndx1'dir ve clang bu bölgeyi adlandırıyor..strtab, yani bir masa hem bölüm isimlerini hem de sembol isimlerini sunuyor.
Okuyucu -O1 nesnesinin 11 başlığını basıyor:
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 8Endeks 0, rezerve edilen sıfır başlık. .textDosya ofset'te 159 byte kod içerir 0x40Başlıktan hemen sonra bayraklarla.A(hüye hafızasında yerleştirilmiştir) ve X(Yapılabilir). .rodata80 byte sadece okunur verileri içerir. .rodata.str1.15 etiket ipini tutuyor F\0D\0C\0B\0A\0, 10 byte, bayraklarla M(kemiş) ve S- Evet.
.comment47 baytlı bir kompiliör damgasını taşıyor \0Apple clang version 17.0.0 (clang-1700.6.3.2)\0- Evet ..llvm_addrsigtürü vardır .0x6fff4c03, işletim sistemi spesifik aralığında SHT_LOOS- ...SHT_HIOSGABİ'nin rezervleri ve LLVM'in ELF.hBayrağı.E- Evet .SHF_EXCLUDE, yani bir bağlantılayıcı düşürüyor.
- Evet .
sh_linkvesh_infosütunlar, tablolar arasındaki işaretlerdir..rela.text- Evet .sh_info2 vesh_link10: Bölüm 2' yi patch eder ve sembollerini bölüm 10' dan alır..symtab- Evet .sh_link1, sembol isimleri için dizileri tablo vesh_info9, ilk yerel olmayan sembolün indeksi.sh_addrBu sayede, bir nesneyi tanımlayan bir nesne, her satırda sıfırdır.
İki gABI kuralı, okuyucuya dosyanın tam bir haritasını çizmesine izin verir. Bölümler "ait ve üst üste geçmez" ve bir dosya "aktiv olmayan alan içerebilir". Okuyucu her yapıyı ofset ile sıralar ve delikleri doldurur:
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Üç boşluk toplam 10 byte ve her biri bir uyumdur. .rodata- Evet .sh_addralign8, yani 0xe0- Hayır .0xdf- Evet ..symtab8 düzeni var, yani 7 byte kullanılmaz..commentBölüm başlık tablosunun kendisi 8.
Tip bölümleri SHT_NOBITSBu nesneyi hiç bir şey bu tür değil..bssBağlı bir programda yapar ve güvenen bir okuyucu sh_sizeDosyanın arkasında okur.
Simgeler: Bağlama, Tip, Bölüm, Değer, Boyut
Bir Elf64_Sym24 byte (gABI Şekil 4-18): st_name- Evet .st_info- Evet .st_other- Evet .st_shndx- Evet .st_value- Evet .st_size, biçimlendirme<IBBHQQ- Evet .st_infoİki alan içerir: yüksek dört bitte bağlama ve düşük dörtte tür.STB_LOCAL0 , STB_GLOBAL1 , STB_WEAK2 ve türleriSTT_NOTYPE0 , STT_OBJECT1 , STT_FUNC2 , STT_SECTION3 , STT_FILE4. Düşük iki bit st_otherGörülebilirliği korumak,STV_DEFAULTBurada 0'dur.
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_label0 indeks, belirlenmemiş sembolden oluşan bir simgedir. st_shndxSimgeyi tanımlayan bölümün isimleri, üç değerle rezerve edilir: SHN_UNDEF0 , SHN_ABS 0xfff1dosya adı için ve SHN_COMMON 0xfff2Bölüm sembollerinin boş bir adı var, bu yüzden llvm-objdumpGABI kuralının "STB_LOCAL bağlayan tüm sembollerin zayıf ve küresel sembollerden önce" olduğu ilk dokuz satır olarak gösterilmektedir..symtab9 'da kaydedildi .sh_infoBu yüzden.
Yer değiştirilebilir bir nesneye .st_valuesimgelenin bölümünde bir sıfırlama, çünkü sh_addrBu işlevin dosya sıfırdır.sh_offset + st_value - sh_addr, ve uzunluğu 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]- Evet .
percent_change- Evet .0x40 + 0, ve 16 bayt85 ff 74 09 29 fe 6b c6 64 99 f7 ff c3 31 c0 c3Boyutlar da başlangıç kadar önemli.grade.text- Değerlendirme0x3eİki bayt .66 90Bu da birnopYükleyici eklendi böylecetotalBaşlıyor .0x40Bu iki bayt hiçbir simgeye ait değil.st_sizeBir söküntü için durma koşulları olarak, ve ders 36 yeniden inşaat karşılaştırmadan önce onları maske eder.
Yer değiştirme: sıfır baytların anlamı
Bir Elf64_Rela24 byte (gABI Şekil 4-23): r_offset- Evet .r_info, ve imzalanmış birr_addend, biçimlendirme<QQq- GABİ bölünüyor .r_infoİki makro ile: ELF64_R_SYM(i)- Evet .i >> 32ve ELF64_R_TYPE(i)- Evet .i & 0xffffffff- Yer değiştirilebilir bir dosyada .r_offsettabloya ait olan bölümde bir sıfırlama sh_infoTip isimleri ve formülleri x86-64 psABI, Tablo 4.9'dan gelir.Ssimge değeridir, AEkleme, PDökülmekte olan yer ve LPLT girişini. R_X86_64_64- Evet .S + A- Evet .R_X86_64_PC32- Evet .S + A - PveR_X86_64_PLT32- Evet .L + A - P- Evet .
-O1 nesnesinin sekiz kayıtları vardır:
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İlkini ayır.0x36İçinde yatıyor .grade, ki genişliyor .0x10- ...0x3e- Eğitimleri0x33- Evet .48 8d 0d 00 00 00 00, a leaRIP'ye göre bir adresin 4 baytlı yer değiştirmesi 0x36. İşlemci, yer değiştirmeyi bir sonraki talimatın adresine ekler.0x3a- Evet .
Yükleyici sonuçları .rodata + 0x14, burada 32 bit değerli beş giriş tablosu 1, 2, 3, 4, 4Yani depolanmış değer(.rodata + 0x14) - 0x3a- FormulaS + A - P- Evet .P = 0x36ver ..rodata + A - 0x36İki eşit verenin ayarlanmasıA = 0x14 - 4 = 0x10Ekleme, hedef ofset'i ekleyerek, talimatın yer değiştirilmesi ile sona ermesi arasındaki dört byteyi taşıyor.
Bu tablo , clang ' ın switchİçeridegrade-O1'de.score / 10 çarpı ile0x66666667-O1 ELF nesnesi, tablo için hiçbir sembol tutmuyor. İpucu bölüm sembolüdür .rodataEk olarak bir ekleme ve sadece aynı kaynaklı arm64 yapı onu isimlendirir.l_switch.table.gradeİkinci kayıt,0x84İçeridegrade_label, 8 bayt değerlerinin ikinci bir tablosunda noktaları .rodata + 0x28Üçüncü,0x93, .rodata + 0x0, labels.rel- Evet .
Beş kişi ..rela.rodataKayıtlar doldur labels.relHer giriş , karşılaştırıldığında yer alır .4iVe eklenir.4i- Evet .S + A - Pdizinin adresine eksi tablo tabanına indirir. grade_label ile bir giriş okuyormovslq, tablo tabanını geri ekler ve işaretçi vardır. İlk kayıt bölüm sembolü isimlendirir.rodata.str1.10 ekleyerek, çünkü dizileri FBu bölümün ilk baytıdır..L.str.1- ....L.str.4, kompilatörün yerel etiketleri için D- Evet .C- Evet .BveA- Evet .
Aynı kaynak üç optimizasyon seviyesiyle üç farklı tabloya bir araya getiriyor. Okuyucu, ayrıştırıcı olmadan farkı gösterir:
| 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 |
-0'da switch5 girişinin geriye doğru yönlendirdiği gerçek bir atlama masası..text- ...+0x91- Evet .+0x8c- Evet .+0x87- Evet .+0x82ve+0x86- Telefonla .gradeAynı şeyi taşıyor.-0x4Bu nedenle, lea-O1'de optimizer her durumun sabit bir değer gönderdiğini gördü ve sıçramaları sonuçlar tablosu ile değiştirdi.
Program Başlıkları: Kayıtçı'nın Görüşü
Yer değiştirilebilir bir nesne program başlıkları yoktur, bu yüzden e_phnumBu, bir ekle edilebilir veya paylaşılan nesne tarafından kullanılır ve yüklemeci başka bir şey okumamaktadır.Elf64_Phdr56 byte (gABI Şekil 5-1): p_type- Evet .p_flags- Evet .p_offset- Evet .p_vaddr- Evet .p_paddr- Evet .p_filesz- Evet .p_memsz- Evet .p_align, biçimlendirme<IIQQQQQQGABI , üç kural belirler .PT_LOADGirişler yükseliş olarak görünür.p_vaddrBir de bir de birp_memszp_fileszsıfır dolma anlamına gelir.p_vaddr ile eşleşirp_offsetmodulo p_align- Evet .
Bu parçadaki her hedef sizin oluşturduğunuz bir program. Bu Mac'teki hiçbir bağlantı cihazı Linux'un çalıştırılabilir bir cihazı üretmez.ledger.o- Yani , bu cihaz ...tiny-x86_64- ...den geliyor .code/fixtures/build_tiny_elf.py, 736 byte ile yazıyor struct.packDosyada bir ELF başlığı, iki program başlığı ve 36 baytlı bir başlık var._start, 3 baytlı ..rodata, bir simge tablosu ve altı bölüm başlığı._startYazar ok\nLinux'ta x86-64 çalıştırılır. Burada program başlığı yoluna bir ayar vardır.
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_LOADTek kişiPT_LOAD736 baytlı dosyanın tümünü haritalar 0x400000. Okuyucu , PT_LOADBu bilgi ve bilgi içerir.p_offset + (address - p_vaddr)Giriş noktası için .0 + (0x4000b0 - 0x400000) = 0xb0, .text- Evet .
Bu, rea'nın arkasındaki hesaplama.address_to_file_offsetGhidra hedefi için, bu araç geri döner.{address, file_offset}Hopper sağlayıcısı da geri dönüyor .provider_file_offsetve image_base_file_offset. Araç dosyanın tam bir orijinal kopyasına ihtiyaç duyar ve bu kopyanın SHA-256'sı hedefe eşleşmelidir.inspect_binary_layout"Bir bağlantılı adres çalıştırma zamanı adresi değildir". Bir konum bağımsız çalıştırılabilir yükler çekirdeğin seçtiği bir taban, ve dosya ofset tek sabit koordinat.
Program başlıkları ayrıca çıkarılmış bir yürütülebilir hala neden çalışmakta olduğunu açıklar. Bir bağlantılayıcı bölüm başlık tablosunu tamamen silebilir ve e_shoffBir araç bölümleri yürürken hiçbir şey görmez.
Mach-O'da Aynı Fikirler
Apple Mach-O için bir proza spesifikasyonu yayınlamıyor. XNU kaynağının başlıkları spesifikasyon: EXTERNAL_HEADERS/mach-o/loader.h- Evet .nlist.hvereloc.h- Düzeltme .ledger-arm64-O1.oAynı şey .ledger.cile inşa edilmişclang -target arm64-apple-macos -O1 -fno-asynchronous-unwind-tables -c- Evet .
A.mach_header_6432 byte: magic- Evet .cputype- Evet .cpusubtype- Evet .filetype- Evet .ncmds- Evet .sizeofcmds- Evet .flags- Evet .reservedBüyü ise ...0xfeedfacf- Evet .MH_MAGIC_64- Evet .cputype- Evet .0x0100000c- Evet .CPU_TYPE_ARM64- Evet .filetype1'dir.MH_OBJECT- Evet .ncmds5 ve sizeofcmds616: yükleme komutları başlıktan sonra 616 byteyi yer alır ve her biri ile başlar.{cmd, cmdsize}Bayrak .0x2000- Evet .MH_SUBSECTIONS_VIA_SYMBOLS- Evet .
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) iki ELF tablosuna da Mach-O cevabı.segment_command_64- Evet .segname- Evet .vmaddr- Evet .vmsize- Evet .fileoff- Evet .filesize- Evet .maxprot- Evet .initprot- Evet .nsectsveflags- O zaman ...nsectsTip kayıtları section_64, 80 byte her biri, aynı komuta ile takip edin.loader.hyorum yapın MH_OBJECTBölümlerin "Üzerinde bir bölüm olmayan bir bölümde" yaşadığını söylüyor.fileoff648, filesize456 ve beş bölüm.
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_REGULARHer bölüm ana bölümünü segname, bu yüzden iki bölümden bahsediyorum .__constFarklıdır çünkü biri __DATA,__constVe diğerleri .__TEXT,__constYer değiştirmeler bölgeye göre yapılır.reloffve nreloc, ELF ' nin ayrı bir .rela.XHedef başına bölme.
Her biri .relocation_info8 byte.r_address32 bitlik bir alan daha sonra,r_symbolnum(24 bit),r_pcrel(1) r_length(2) r_extern(1) ve r_type(4) Altı __textKayıtlar üç çift .ARM64_RELOC_PAGE21ve ARM64_RELOC_PAGEOFF12, bir çift bir masa referansı.adrpEk olarak .addNe yaparsın x86-64 leaYani her referans iki kayıt gerektirir.__DATA,__constKayıtlar ARM64_RELOC_UNSIGNED- Evet .r_length3, böylece her biri 8 bayt mutlak göstergeyi düzeltir.
-O1'de , arm64 yapı tutuldu .labelsx86-64 yapısının görevi bir tablo oluşturduğu düz göstergeleri olarak.
LC_SYMTAB(0x2) tutulur symoff1240'a kadar.nsyms17, stroff1512'de.strsize176 ve 1512 artı 176 1688'dir.nlist_6416 byte: n_strx- Evet .n_type- Evet .n_sect- Evet .n_desc- Evet .n_value- Evet .n_typeBiraz alan.N_TYPE(maske)0x0e) eşittir N_SECT(0xe) 1 tabanlı sırası olan bölümde tanımlanan anlamına gelir.n_sect- Evet .N_EXT(0x01) bir dış sembolü işaret eder.
nlist_6424 byte'yi elde etmek için._percent_changeokuyucu aynı bölümde bir sonraki simge adresini alır. _grade- ...0x18C sembolleri önde taşıyor ._Burada, yani işlev _percent_change- Evet .
LC_DYSYMTAB(0xb) ELF ile aynı yerel ilk kural kaydedilir sh_info- Evet .ilocalsym0 , nlocalsym13, iextdefsym13, nextdefsym4.LC_BUILD_VERSION(0x32) kayıtlar platform1 (macOS), minos26.0 ve sdk26.2 paketlenmiş gibiXXXX.YY.ZZBu komut, arm64 cihazının byte olarak devreye alınmasının nedeni.
Okuyucu'nun compare_formatsiki dosyayı yan yana koyuyor:
| 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 |
rea Mach-O'yu okuyor.inspect_macho, ki devam ediyor .otool -h -l- Evet .nm -gjU- Evet .dyld_info -imports -exportsvedwarfdump --uuid- Evet .xcrunOkuyucuya hiç bir şeye gerek yok ve aynı şeyi okuyor.symoffve stroffBu ...otool -lParmak izleri.open_binaryREA aracı ELF, MZ, PE ve ince veya evrensel Mach-O'yu sihirli baytlardan algılar.e_machineBu, burada oluşturduğunuz şeyin ilk 20 bytesidir. Başlıksız bir dosya için,format: raw-binaryGhidra sağlayıcısı evrensel bir ikili göndermeyi reddediyor ve ince bir parça istiyor.0xcafebabe- Evet .
Yapın
Standart kütüphane okuyucuyu çalıştır:
bashcd phases/13-tools-and-protocols/32-program-anatomy-elf-from-scratch
python3 code/main.pyDemo şöyle diyor:ledger-x86_64-O1.ove başlığı, bölümleri, sembollerini, yer değiştirmeleri ve dosya ofset haritasını yazdırır.ledger-arm64-O1.oProgram başlıkları ile biter.tiny-x86_64Beklenen çizgiler:
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_LOADBir dosyayı okuman için bir yol geçin: python3 code/main.py code/fixtures/ledger-x86_64-O0.oOkuyucu ilk dört bayttan ELF veya Mach-O seçer.
Oku code/main.pyBu sırada:
ElfFile._read_header()Sihir, sınıf, bayt sırası ve giriş boyutlarını kontrol eder, sonra başlığı açar.ElfFile._read_sections()veread_cstring()Bölüm başlık tablosunu okuyun ve isimleri çözüne_shstrndx- Evet .ElfFile._read_symbols()bölünmüştür .st_info, isimleri çözüyorsh_link, ve rezerve edilen bölüm indekslerini okuyabilir hale getirir.ElfFile._read_relocations()Her zaman yürür.SHT_RELABölüm, bölünmelerr_info, ve 4.9 tabloda isim türleri.ElfFile.function_span()vefunction_bytes()Bir simgeyi dosya aralığına ve içindeki baytlara dönüştürün.ElfFile.file_offset_map()Her yapıyı ofset olarak sıralar ve aktif olmayan alanı doldurur.ElfFile._read_program_headers()veaddress_to_file_offset()Yürütülebilir işlemleri.MachOFileBaşlığı okuyor, yük komutları,LC_SEGMENT_64Bölümleri ile birlikte,LC_SYMTAB- Evet .LC_BUILD_VERSION, ve bölge başına yer değiştirme.compare_formats()Her iki dosyada aynı fikirleri, her birinden değerleri okuyarak isimlendiriyor.
Kullan
Daha sonraki derslerden okuyucuyu from main import ElfFileBu dersi ekledikten sonra codeDizinsys.pathDers 33 bunu istiyor .function_bytesve relocations_for(".text").35 ders, çıkışını gözlemli kanıt olarak kaydeder.36 ders, byte karşılaştırmasından önce maske yapılması için yer değiştirme karşılığını ister.
Kendi C ' yi aynı nesne ile bir nesneye oluştur .clang -target x86_64-unknown-linux-gnu -cBu sayede, bir bölüm adı eklenir. Bu sayede, bir bölüm adı eklenir. Bu sayede, bir bölüm adı eklenir.
İnteraktif Laboratuvar
Laboratuvar A: başlık elden
- Evet .
codeDizin, açık Python:
bashcd phases/13-tools-and-protocols/32-program-anatomy-elf-from-scratch/code
python3 -qÇık:
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].nameTuple gösterilerie_type1 , e_machine62, ve e_shoff1064, yani 0x428Son satır 1. bölümün gösterdiği gibi..strtabBölüm isimlerini taşıyor.
Laboratuvar B: anahtarın arkasındaki değer tablosu
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"]Her iki tablo da okuyucu .1, 2, 3, 4, 4- Karşılarına bak .ledger.c- Evet .grade(65)hesaplamalar65 / 10 - 6 = 0ve 0'u okuyor, bu da 1'in değeri case 6- Evet .grade(100)4 yazısı, yani 4 , paylaşılan değer .case 10ve case 9- Evet .
Laboratuvar C: tek kaynağın üç yapılandırması
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"})-O0 nesnesinin bir .data-O1 ve -O2 nesneleri hiçbir .dataVe bir tür.total-O0'da 66 byte, -O1'de 31 ve -O2'de 140 byte.
Laboratuvar D: Mach-O'da aynı işlev
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()Dosya 648'in geçici süresi 24 byte. İlk dört byte a0000034, küçük bir eğitim biçimi .0x340000a0, ki llvm-objdump -d gibi izlercbz w0, 0x14Bu da o .before == 0koruyucu, x86-64 yapıdaki aynı test.85 ff 74 09- Evet .
Pratik Laboratuvar
Okuyucuyu bağlantılı bir programa uzatın.clang -O1 -shared -fPIC code/fixtures/ledger.c -o libledger.sove clang -O1 code/fixtures/ledger.c -o ledger_mainbir maineklersiniz, sonra da okuyucuyu her ikisine de çalıştırırsınız.
Gereksinimler:
- Oku
.dynsymAynı şeyle.Elf64_Symdüzenlenmesi,SHT_DYNSYM11'e kadar ve kayıt değişmedi. - Oku
.dynamic- Evet .Elf64_DynKayıtlard_tagved_val, ve isimDT_NEEDED- Evet .DT_STRTAB- Evet .DT_SYMTAB- Evet .DT_RELAveDT_JMPRELgABI "Dinamik Bölüm" sayfasından. - Defini edilmemiş sembolleri ile listelenir.
st_shndxSHN_UNDEFÜlkeler ve ülkelerR_X86_64_JUMP_SLOTKayıtlar.rela.pltBu yüzden, bu telefonları onlara ulaştıran siteler olarak kullanıyoruz. - Her yeri haritada .
PT_LOADgABI kuralını kullanarak, "bir nesne dosyası segmenti bir veya daha fazla bölüm içerir". - Bunu kanıtla .
address_to_file_offsetpaylaşılan nesnede her tanımlanmış fonksiyonun baytları baytlardır.function_bytesGeri dönüyor. - Yakalama
llvm-objdump -h -t -r -pvellvm-objdump -THer iki dosya için de metincode/fixtures/Okuyucuyu onlarla testlerde karşılaştır.
Başarılı olmak, ithalat listesi, segment haritası ve her fonksiyon aralığı, testler sırasında araç çalıştırılmadan kaydedilen araç çıkışı ile uyumlu anlamına gelir.
Ders Artifakti
Bu ders bize çok yararlı .outputs/skill-program-anatomy-elf-from-scratch.md. Claude, Cursor, Codex, OpenClaw, Hermes veya SKILL.md okuyan herhangi bir ajanla kullanın. Bu beceri, kullanıcının oluşturduğu bir düzen raporu haline getirilen bir kompile edilen nesne veya yürütülebilirliği dönüştürür. Rapor, her yapının, sembolün ve yer değiştirmenin dosya aralığını, yakaladığı araç çıkışına karşı kontrol eder.
Kontrol et
Demo ve Deterministik Suite'i çalıştır:
bashcd phases/13-tools-and-protocols/32-program-anatomy-elf-from-scratch
python3 code/main.py
python3 -m unittest discover -s code/tests -v- Suit kanıtlıyor:
- Her bölümde, isim, boyut, adres ve türü yakaladıklarla eşleşir.
llvm-objdump -hÜç x86-64 nesne ve küçük çalıştırılabilir için metin - Her simge değeri, bağlayıcı, türü, bölümü, boyutu ve adı yakaladıklarla eşleşir
llvm-objdump -tmetin - Her yer değiştirme ofset, tip adı, sembol ve ekleme, yakaladıklarla eşleşir.
llvm-objdump -rmetin, dahil olmak üzereR_X86_64_PLT32veR_X86_64_64-O0 - Program başlıkları
tiny-x86_64Tutuklanmakla eşleşir .llvm-objdump -pmetin, ve nesneler hiçbir percent_changedosya sıfırlaması 16 byte0x40beklenen baytlarla ve her fonksiyonun içinde olduğu.text- Dosya ofset haritası, her sabitlemenin her baytını bir kez kapsar ve -O1 nesnesinin üç uyum boşluğu yerlerinde yer alır
- giriş noktası
tiny-x86_64Dosyaların karıştırılması için haritalar0xb0- Evet .PT_LOAD, ve yer değiştirilebilir bir nesne adres haritasını reddediyor - Mach-O başlığı, her yük komutu, bölümü, beş bölümü,
LC_SYMTABveLC_BUILD_VERSIONTutuklanmakla eşleşir .--private-headersmetin - Mach-O simgelerinin ve 15 yer değiştirme olayının , yakaladıkları ile uyumlu olduğunu .
llvm-objdumpmetin ve_percent_change648'de 24 byte - Yanlış bir sihir, 32 bitlik bir sınıf, büyük bir indian dosyası, kısaltılmış bir masa, evrensel bir ikili ve C kaynak dosyası hepsi yükselmektedir.
FormatError build_tiny_elf.pyYapılanları yeniden üretirtiny-x86_64Bayt bayt- rakam modülü
site/figures-re-32.jsledger-x86_64-O1.o
Üretim Başarısızlık Modları
| 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 |
İşlem Kuralı
Güvendiğiniz başlığı okuyun, sahip olduğu ofsetleri takip edin ve tanımlayan spesifikasyondan her alanın adını verin. Karşılaştırdığınız araç çıkışını bağlı bir ayar olarak tutun, böylece karşılaştırma aracı olmadan çalışır. Bir değerin spesifikasyonda adı yoksa, sayıyı yazdırın.
Daha Fazla Okumak
- System V gABI, chapter 4: Object Filesve ELF Header- Evet .Sections- Evet .Symbol TableveRelocationsayfalar
- System V gABI, chapter 5: Program Headerve Dynamic Linking
- System V ABI, AMD64 Architecture Processor SupplementTablo 4.9 Yer değiştirme türleri
- Apple XNU
loader.h- Evet .nlist.hvereloc.h - 17 U.S.C. 1201ve Directive 2009/24/EC
- Sega v. Accolade, 977 F.2d 1510ve Sony v. Connectix, 203 F.3d 596
- rea 6.5.0
docs/binary-diagnostics.mdvedocs/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.