Phase 13: Tools & Protocols

Program Anatomisi: Bir ELF Okuyucu sıfırdan

Bir program, birbirine işaret eden bir dizi tablo. Yazdığınız bir okuyucu, bayt byte kontrol edebileceğiniz çıkış sağlar.

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ı.

SourceWhat it allowsConditions
17 U.S.C. 1201(f)Circumventing an access control to analyze a programLawful copy, interoperability purpose, information not readily available, no infringement
Directive 2009/24/EC Art. 5(3)Observing, studying, and testing a running programA right to use the copy, acts within normal loading and running
Directive 2009/24/EC Art. 6DecompilationLicensee, interoperability purpose, information not readily available, confined to the needed parts
Sega v. AccoladeDisassembly as fair useThe only way to reach the ideas and functional elements, a legitimate reason
Sony v. ConnectixIntermediate copies during reverse engineeringA 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şlanda 0x14- 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     8

Endeks 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_linkve sh_infosütunlar, tablolar arasındaki işaretlerdir. .rela.text- Evet .sh_info2 ve sh_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 ve sh_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_label

0 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 bayt 85 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 bir nopYükleyici eklendi böylece totalBaş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:

BuildWhere labels livesHow grade selectsHow grade_label reaches grade
-O0.data, five 8-byte pointers, five R_X86_64_64 recordsA jump table in .rodata: five R_X86_64_PC32 records pointing into .textA call with R_X86_64_PLT32 grade-0x4
-O1.rodata as labels.rel, five 32-bit relative entriesA value table at .rodata+0x14, one R_X86_64_PC32Inlined, no call
-O2Same as -O1Same as -O1Inlined, 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_LOAD

Tek 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 0x238

LC_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_REGULAR

Her 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:

IdeaELF, ledger-x86_64-O1.oMach-O, ledger-arm64-O1.o
File identitye_ident 7f 45 4c 46, class 64, data LSBmagic 0xfeedfacf
Machinee_machine 62, EM_X86_64cputype 0x100000c, CPU_TYPE_ARM64
File kinde_type 1, ET_RELfiletype 1, MH_OBJECT
Where the tables aree_shoff 0x428, 11 section headers5 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 1LC_SYMTAB symoff 1240, nsyms 17, names at stroff 1512
Symbol sizest_size: percent_change is 16 bytesNo 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 bytespercent_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.py

Demo şö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_LOAD

Bir 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:

  1. ElfFile._read_header()Sihir, sınıf, bayt sırası ve giriş boyutlarını kontrol eder, sonra başlığı açar.
  2. ElfFile._read_sections()ve read_cstring()Bölüm başlık tablosunu okuyun ve isimleri çözün e_shstrndx- Evet .
  3. ElfFile._read_symbols()bölünmüştür .st_info, isimleri çözüyor sh_link, ve rezerve edilen bölüm indekslerini okuyabilir hale getirir.
  4. ElfFile._read_relocations()Her zaman yürür.SHT_RELABölüm, bölünmeler r_info, ve 4.9 tabloda isim türleri.
  5. ElfFile.function_span()ve function_bytes()Bir simgeyi dosya aralığına ve içindeki baytlara dönüştürün.
  6. ElfFile.file_offset_map()Her yapıyı ofset olarak sıralar ve aktif olmayan alanı doldurur.
  7. ElfFile._read_program_headers()ve address_to_file_offset()Yürütülebilir işlemleri.
  8. 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.
  9. 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].name

Tuple 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_tagve d_val, ve isim DT_NEEDED- Evet .DT_STRTAB- Evet .DT_SYMTAB- Evet .DT_RELAveDT_JMPRELgABI "Dinamik Bölüm" sayfasından.
  • Defini edilmemiş sembolleri ile listelenir.st_shndx SHN_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.
  • Yakalamallvm-objdump -h -t -r -pve llvm-objdump -THer iki dosya için de metin code/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_PLT32ve R_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 byte 0x40beklenen 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 haritalar 0xb0- 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 üretir tiny-x86_64Bayt bayt
  • rakam modülü site/figures-re-32.jsledger-x86_64-O1.o

Üretim Başarısızlık Modları

FailureWhat the weak reading reportsWhat 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 tableGarbage names or an exceptione_shstrndx names the table, here section 1
sh_size trusted for SHT_NOBITSA 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 addressA wrong offset under a random baseThe file offset from PT_LOAD is the stable coordinate
A Mach-O section looked up by sectname aloneTwo __const sections collapse into onesegname,sectname is the name
A size field expected in nlist_64An exception or zero bytesThe 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

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.