Verileri ve Tipleri Geri Almak: Strukt, Array, C++ Nesneleri ve DWARF
a->balancebir tip masası olduğunda ve *(int *)(param_1 + 4)Bu ders, girişlerden tablo oluşturur, DWARF 5'ten gerçek olanı okuyor ve iki tarafın nerede anlaştığını gösterir.Type: Build
Languages: Python, TypeScript, C, C++, x86-64 assembly
Prerequisites: Phase 13 · 33 (disassembly and control flow), Phase 13 · 34 (lifting to pseudocode)
Time: ~140 minutes
Öğrenme Hedefleri
- Intel SDM adresleme kurallarından bir struct üyesi erişimi temel kayıt artı yer değiştirme olarak ve bir dizi erişimi temel artı indeks zaman adım olarak okuyun.
- Sistem V uyum kurallarını el ile uygulayın
struct accountve her bir boytu açıklayın. - Bir listedeki erişimi her bir alanın karşılığı, genişliği, türleri, bir adım ve boyutuna çeviren bir tip kurtarma geçitini oluşturun.
- DWARF 5 okuyucuyu yaz
.debug_info- Evet ..debug_abbrev- Evet ..debug_str_offsetsve.debug_addrYer değiştirilebilir bir nesneyi kontrol ederekllvm-dwarfdump- Evet . - Geri alınan düzenle DWARF düzenle karşılaştırın ve her farkı eksik kanıt, birleşik erişim veya kanıtlanamayan bir tür olarak adlandırın.
- Bir Itanium C ++ vtable, tipinfo ve yer değiştirme isimlerini okuyun ve bir yuvaya sanal bir çağrı çözün.
Sorun
- Sen topladın .
account.c- Hayır .-gBir ajanı ne diye sordu.bumpAjan nesneyi Ghidra 12.1.4'e gönderdi ve geri aldı:
void bump(long param_1,int param_2)
{
*(int *)(param_1 + 4) = param_2 + *(int *)(param_1 + 4);
return;
}Aynı kaynakla oluşturulmuş .-gAynı dekompilerden verilmiş:
int bump(account *a,int by)
{
a->balance = by + a->balance;
return a->balance;
}Bu nedenle, bu tabloyla ilgili olarak, bir dizi metin, bir dizi metin, bir dizi metin, bir dizi metin, bir dizi metin, bir dizi metin, bir dizi metin, bir dizi metin, bir dizi metin, bir dizi metin, bir dizi metin, bir dizi metin, bir dizi metin, bir dizi metin, bir dizi metin, bir dizi metin, bir dizi metin, bir dizi metin, bir dizi metin, bir dizi metin, bir dizi metin, bir dizi metin, bir dizi metin, bir dizi metin, bir dizi metin, bir dizi metin, bir dizi metin, bir dizi metin, bir dizi metin, bir dizi metin, bir dizi metin, bir dizi metin, bir dizi metin, bir dizi metin, bir dizi metin, bir dizi metin, bir metin, bir metin, bir metin, bir metin, bir metin, bir metin, bir metin, bir metin, bir metin, bir metin, bir metin, bir metin, bir metin, bir metin, bir metin, bir metin, bir metin, bir metin, bir metin, bir metin, bir metin, bir metin, bir metin, bir metin, bir metin, bir metin, bir metin, bir metin, bir metin, bir metin, bir metin, bir metin, bir metin, bir metin, bir metin, bir metin, bir metin, bir metin, bir metin, bir metin, bir metin, bir metin, bir metin, metin, metin, metin, metin, metin, metin, metin, metin, metin, metin, metin, metin, metin, metin, metin, metin, metin, metin, metin, metin, metin, metin, metin, metin, metin, metin, metin, metin, metin, metin, metin, metin, metin, metin, metin, metin, metin, metin, metin, metin, metin, metin, metin, metin, metin, metin, metin, metin, metin, metinintAdı:balanceBu olmadan , dekompiler sadece 8 byt genişliğinde bir şey geldiğini bilir .%rdive 4 byte'de +4Okuduk ve yazıldılar.
Bu tabloda her şeyin kaynağı vardır.addl 0x4(%rax), %ecxGenişlik 4 ından geliyor .lİsim bir DW_TAG_memberGiriş .debug_infoBu ders iki yarı oluşturur: talimatların kanıtladığı şeyi geri alan bir geçiş ve kompiliörün kaydettiği şeyi çözen bir okuyucu.
Üyelik Erişimi Deplaşması
Intel SDM Cild 1, bölüm 3.7.5, etkin adresin olarak tanımlandığını belirtir.Offset = Base + (Index * Scale) + Displacement, 1, 2, 4 veya 8'lik bir ölçekle ve 8, 16 veya 32 bitlik bir yer değiştirme ile.RIP + Displacement. Strukt üye erişiminde, indekssiz ilk form kullanılır: temel kayıt, nesnenin adresini ve yer değiştirme üye sıfırını içerir. Bir dizide erişim indeks terimini ekler ve ölçek veya ayrı bir katılımcı eleman boyutunu taşır.
Al .bump- ...-O0- Listeden .llvm-objdump -d -rBu da şöyle:
4: 48 89 7d f8 movq %rdi, -0x8(%rbp)
e: 48 8b 45 f8 movq -0x8(%rbp), %rax
12: 03 48 04 addl 0x4(%rax), %ecx
15: 89 48 04 movl %ecx, 0x4(%rax)Çözümleme03 48 04Dersin kuralları ile 33.03- Evet .ADD r32, r/m32ModRM 48- Evet .01 001 000- Evet .mod = 018 bitlik bir yer değiştirme ile bir hafıza operandı anlamına gelir. reg = 001- Evet .%ecx- Evet .r/m = 000- Evet .%rax- Deplacement byte 04- Kural 4 byte okur .%rax + 4ve onları %ecx- Ne ?lİleği llvm-objdumpBaskılar operand boyutunda, bu yüzden alanın genişliği 4 byte.%raxÇizgilik boşluğundan geldi .-0x8(%rbp), ve bu boşluk aldı .%rdi, Sistem V çağrı kuralı altında ilk tam sayı argümanı. Üç gerçek aşağıda: ilk parametresi bir işaretçi, işaret ettiği nesne 4 byet bir alanı vardır ofset 4, ve bumpO alanı okuyor ve yazıyor.
Bu baytlarda hiçbir şey söylemiyor .int- Evet .balanceveyastruct accountHiçbir şey nesnenin ne kadar büyük olduğunu söylemez.bump- Evet .
Sistem V düzen kuralları
x86-64 psABI, bölüm 3.1.2, toplamalar için üç kural verir. "Yönemler ve sendikalar en sıkı bir şekilde uyumlu bileşenlerinin hizalanmasını varsayırlar. Her üye uygun hizalanma ile en düşük mevcut ofset'e atılır. Her nesnenin boyutu her zaman nesnenin hizalanmasının katııdır". Aynı belgenin 3.1 Şekili skalar boyutları ve hizalanmaları verir: char1 ve 1'i,short2 ve 2, int4 ve 4'e.double8 ve 8, herhangi bir işaretçi 8 ve 8.
Kurallar, yapılara uygulanmalıdır.account.c- ...
struct account {
char kind;
int balance;
short code;
double rate;
const char *label;
};| Member | Size | Alignment | Lowest offset that fits | Padding before |
|---|---|---|---|---|
kind | 1 | 1 | 0 | 0 |
balance | 4 | 4 | 4 | 3 (bytes 1 to 3) |
code | 2 | 2 | 8 | 0 |
rate | 8 | 8 | 16 | 6 (bytes 10 to 15) |
label | 8 | 8 | 24 | 0 |
Son üye 32'de sona erer. En sıkı düzeltme 8, 32'nin de 8'in katı olması, bu nedenle boyutu 32'dir.
Üyelerin sırası büyüklüğü belirler.double, pointer, int, short, char0'u, 8'i, 16'u, 20'i, 22'i ve 23'e kadar döngülmüş olan 23'e kadar tamamlanır. sysv_layout()İçeridecode/main.pyPython'un üç kuralını uyguladığında her iki sonuç da elde edilir.ctypesmodül, host kompozisyonunun bir kuralı ile aynı kuralları uyguluyor ve ctypes.sizeof- Evet .ctypes.alignment, ve .offsetHer alanın 32, 8 ve 0, 4, 8, 16, 24Bu makineyi oluşturduğunuzda programın kendisi üçüncü kopyayı basıyor:
sizeof 32 align 8
kind 0 balance 4 code 8 rate 16 label 24
sum 315 tag -3 projected 318.75 label_length 7Bu yerel yapı, arm64'dir. Arm AAPCS64 bölüm 5.10.1'de aynı toplam kuralları belirtir ve Apple'ın arm64 belgesi bu beş skalar boyutunu korur, bu nedenle rakamlar iki hedefte uyumlu olur.-target x86_64-unknown-linux-gnuBöylece ders 33'ten ELF okuyucu ve aşağıdaki DWARF okuyucu bunları üzerinde çalışacak.
Yapıların Geri kalanını Elden Oku
bumpDiğer dört işlev account.cDiğerleri de farklı bir talimat formunu gösterir.account-x86_64-O0-g.objdump.txt- Evet .
sum_balancesStruktörün bir diziyi indeksize eder:
51: 48 8b 45 f8 movq -0x8(%rbp), %rax
55: 48 63 4d ec movslq -0x14(%rbp), %rcx
59: 48 c1 e1 05 shlq $0x5, %rcx
5d: 48 01 c8 addq %rcx, %rax
60: 8b 40 04 movl 0x4(%rax), %eax%rax- Evet .list- Evet .%rcx- Evet .i64 bitlik bir işaretle uzanmış ve shlq $0x532 çarpı çarpı.addq- Evet .%rax- Evet .list + 32 * i, ve yük 4 byet olarak öğenin 4 ' ün tersine okur iBu nesnede, yapı boyutunun bir sayı olarak görünen tek yer var.0x49- ...jmp 0x49- ...0x72- Evet .
tagİki dar alanı okuyor:
8c: 0f be 00 movsbl (%rax), %eax
98: 0f bf 48 08 movswl 0x8(%rax), %ecxmovsbl0'da bir byte yükler ve onu işaretle uzatır, bu yüzden 0'daki alan imzalı 1 byte tam bir sayıdır. movswl8. ofset'te iki byte yüklerken, işaret uzantısı ile, 8. alanın imzalanan 2 byte tam bir sayı olduğu ortaya çıkar.-O1İlk erişim cmpb $0x6c, (%rdi), yine de 0'da 1 byte diyor ve işaret hakkında hiçbir şey söylemiyor.
projectedBir kayganı okuyor:
cc: f2 0f 2a 40 04 cvtsi2sdl 0x4(%rax), %xmm0
d5: f2 0f 59 40 10 mulsd 0x10(%rax), %xmm0cvtsi2sdl32 bitlik imzalanan bir tam sayıyı bellekten çiftine dönüştürür. Bu da ofset 4'ün imzalanan 4 byet tam sayıyı taşıdığını doğruluyor. mulsdhafızadan iki katına çarpır, bu yüzden 16'nın 8 baytlı bir akış noktası değeri var.
label_lengthbir işaretçi okuyor ve sonra takip ediyor:
f3: 48 8b 40 18 movq 0x18(%rax), %rax
f7: 48 63 4d f4 movslq -0xc(%rbp), %rcx
fb: 80 3c 08 00 cmpb $0x0, (%rax,%rcx)- Evet .
movqBu 8 byte bir bütün sayıdır.cmpbBir adres olarak kullanılan bir değer bir işaretçi.cmpbindekste 1 byte okuyor%rcxBu yüzden de-O1işaretçi ileriye doğruleaq 0x1(%rcx), %rcxBu da bir yandan da bir yandan da bir yandan da bir yandan da bir yandan da bir yandan da bir yandan da bir yandan da bir yandan da bir yandan da bir yandan da bir yandan da bir yandan da bir yandan da bir yandan da bir yandan da bir yandan da bir yandan da bir yandan da bir yandan da bir yandan da bir yandan da bir yandan da bir yandan da bir yandan da bir yandan da bir yandan da bir yandan da bir yandan da bir yandan da bir yandan da bir yandan da bir yandan da bir yandan da bir yandan da bir yandan da bir yandan da bir yandan da bir yandan da bir yandan da bir yandan da bir yandan da bir yandan da bir yandan da bir yandan da bir yandan da bir yandan da bir yandan da bir yandan da bir yandan da bir yandan da bir yandan da bir yandan da bir yandan da bir yandan da bir yandan da bir yandan da bir yandan da bir yandan da bir yandan da bir yandan da bir yandan da bir yandan da bir yandan da da da bir yandan da bir yandan da da da bir yandan da da bir yandan da bir yandan da da bir da bir da bir da bir da bir da bir da bir da bir da bir da bir da bir da bir da da da da da bir da bir da bir da bir da da da bir da bir da bir da
Beş fonksiyonu bir araya getirirsek yapı { i8 at 0, i32 at 4, i16 at 8, f64 at 16, pointer at 24 }1 ila 3 ve 10 ila 15 arasındaki boşluklar, psABI kurallarının öngördüğü dolgulama şeklidir.const char *, 0 ' da imzalanan baytın açıklanmış türü , kaynak tarafından yazılmıştır char, ve beş fonksiyonun aynı tipte olduğunu.main, ki bir çerçeve nesnesini tüm beşine geçirir.-O0Çağrı siteleri bunu görülebilir hale getirir: leaq -0x70(%rbp), %rdiArdından addq $0x20, %rdiDaha öncecallq bumpveaddq $0x40, %rdiDaha öncecallq tag- 0'luk oranı.0x20ve0x40Bir dizi üç unsur ve memcpy0x60Bu, 32'nin üç elementini oluşturan 96 byt'tır.
Geçit
trace_function()İçeridecode/main.pyBir tutıcı 64 bitlik bir kayıt veya bir çerçeve boşluğu gibi -0x8(%rbp)Her sahibin üç değerden biri vardır.Pointerbir adımın bir adımın bir bilinmeyen katı sayısı eklendiğini kaydettiği bir kök artı sabit bir sıfır artı bir adımlar kümesi.IndexBilinmeyen bir değerin tam bir sayısıdır.
Devlet , altı Sistem V argüman kayıtlarının olması ile başlar .Pointer("param0", 0)- Evet .Pointer("param5", 0)O zaman her talimat bir kuralı uyguluyor:
- A.
movq64 bitlik bir kayıt ve çerçeve boşluğu arasındaki değer kopyalanır. 32 bitlik veya daha dar bir kayıt kayıt bir kayıt yaparIndex1 ölçeği ile. - A.
leaqBir işaretleme tabanı ile bir işaretleme oluşturur ve bir indeks kaydı ile eklerscale * index_scale- Yapacak adımlar.leaqKimin tabanı%rbpveya%rspBir çerçeve nesnesinin adını verir, bu nasıl olur?mainGeçitlerrows- Evet . shlq $nBirIndexölçeğini çarpır2^nveimulq $kçarpık- Evet .addqBir işaretçi ve bir gösterge göstergeyi tutar ve gösterge ölçeğini adımlara ekler.addq $kBir işaretçiye karşı çıkışını hareket ettirir ve bu kayıtların bir artışı olarak da kaydedilir.- Bir çağrı, her aramacı kaydedilen kayıtları bilinmez hale getirir ve işaretleri tutan argüman kayıtları bir arama sitesi olarak kaydedilir.
- Bas kayıtları bir işaretçiyi taşıyan her hafıza operandı bir
Accessİzleyenin kökü ve sıfırı, genişliği ve türü ile mnemonik ve bir depo bayrağı.
Genişlik ve tür sadece mnemonikten gelir.access_width()haritalar b- Evet .w- Evet .lveq1, 2, 4 ve 8 bayt için sufişler, movsblve movswlİmza edilen dar yüklere,movzblİmza edilmemiş olanlara,sdve ss8 byet ve 4 byet yüzenlere verilen skalar SSE talimatları, cvtsi2sdlİmzalanmış 4 bayt tam sayı kaynağına ve 16 bayt movapsBir vektöre aile. leaqve nopFormular erişim değildir.
if memory.base in FRAME_REGISTERS:
return Pointer(f"frame({memory.base}{format_signed(memory.displacement) if memory.displacement else ''})", 0)
base = env.get(memory.base)
if not isinstance(base, Pointer):
return Unknown
strides = set(base.strides)
if memory.index is not None:
index_value = env.get(memory.index)
strides.add((index_value.scale if isinstance(index_value, Index) else 1) * memory.scale)
return Pointer(base.root, base.offset + memory.displacement, frozenset(strides), base.origin)Bir işaretçi aracılığıyla 8 byte yüklenmesi, köken adı alanı oluşturan yeni bir işaretçi üretir, örneğin param0->0x18Bu değer daha sonra bir temel olarak kullanıldığında veya leaqBu kural, "8 byte"den "pointer"e çeviren 24'i karşılaştırır.
Dört çeşit adım kanıtı
infer_layout()Her tanık fonksiyonunun erişimlerini birleştirir ve her bir adımın ürettiği talimat ile birlikte dört kaynağından bir araya getirir.
| Kind | Where it comes from | Build that shows it |
|---|---|---|
scale | an Index with scale above 1 reached a pointer, from shlq $0x5 or imulq | -O0, shlq $0x5, %rcx at 0x59 |
loop-step | a 64-bit increment of the base or index register inside a loop that also contains the access | -O1, addq $0x20, %rdx at 0x24 |
offset-delta | same-width accesses through one base register inside one loop, at offsets a constant apart | -O2, eight loads 32 apart |
call-site | the offsets at which one frame object is passed to the witness functions | -O0, main passes rows, rows+0x20, rows+0x40 |
Bir döngü geriye döner .jmpBu kural, addl $0x1, %eaxBu ilerliyor.i- ...-O0, çünkü 32 bit bir eklemedir ve erişim geçmediği için %eaxSon adım, bulunduğu her değerin en büyük ortak bölücüdür.
-O2'deki vektörlü döngü
- Evet .
-O2Klang vektörlersum_balances- Çubuk vücudu .0x50- ...0xaaBir geçit için sekiz element okuyor:
3b: 48 8d 87 e4 00 00 00 leaq 0xe4(%rdi), %rax
50: 66 0f 6e 50 80 movd -0x80(%rax), %xmm2
55: 66 0f 6e 98 60 ff ff ff movd -0xa0(%rax), %xmm3
61: 66 0f 6e 90 40 ff ff ff movd -0xc0(%rax), %xmm2
69: 66 0f 6e a0 20 ff ff ff movd -0xe0(%rax), %xmm4
7d: 66 0f 6e 10 movd (%rax), %xmm2
81: 66 0f 6e 58 e0 movd -0x20(%rax), %xmm3
8a: 66 0f 6e 50 c0 movd -0x40(%rax), %xmm2
8f: 66 0f 6e 60 a0 movd -0x60(%rax), %xmm4
a0: 48 05 00 01 00 00 addq $0x100, %rax
aa: 75 a4 jne 0x50 <sum_balances+0x40>%raxBaşlıyor .list + 0xe4Sekiz yer değiştirme yükleri karıştırmalara yerleştirir .0x04, 0x24, 0x44, 0x64, 0x84, 0xa4, 0xc4, 0xe4-list, ki 4, 36, 68, 100, 132, 164, 196, 228- Artış 0x100Bu sayede, bir geçit, görüldüğü gibi, 8 alan ve en az 232 bytelik bir yapı rapor eder.
fold_unrolled()Bir döngü içinde, aynı genişliğe sahip erişimleri bir baz kayıtçası üzerinden alın.dEğer döngü adımları k * dVe tam olarak var.kAyrılıklı sıyrılar, döngü çözüldü.kZamanlar ve her erişim aynı eleman erişimini ofset olarak (offset - lowest) mod d- İşte .256 = 8 * 328 kargoyla kargoya 4'e katılır ve adımlar kaydedilmektedir.offset-delta 32- Evet .unrolled 32veloop-step 256- Skalare kalıntı döngüsü0xe0ekliyor scale 32-shlq $0x5, %rdxve loop-step 32-addq $0x20, %rdxBeş değerin en büyük ortak bölücü 32'dir.
Şekilde katlanmayı kapatan bir düğme var.-O2Bu durum, bu halka olmadan yer değiştirme güvenen bir okuyucu tarafından görülen bir durumdur.
DWARF 5 Bytes'ten
- Evet .
-gyapılandırmaaccount.cSekiz tane defekt bölümü var..debug_info493 byte..debug_abbrev179,.debug_str230,.debug_str_offsets124,.debug_addr112,.debug_line312,.debug_line_str12, ve.debug_frame264. DWARF 5 standart listelerinin kullanılması.debug_str_offsets- Evet ..debug_addrve.debug_line_str5. versiyonda yeni olarak.code/main.pyİlk beş tane lazım.
Birim başlığı
Standartın 7.5.1.1 bölümünde, toplam birim başlığı verilmiştir: unit_length- Evet .version- Evet .unit_type- Evet .address_sizevedebug_abbrev_offsetİlk 12 byte ..debug_infoBunlar:
e9 01 00 00 unit_length = 0x1e9 = 489
05 00 version = 5
01 unit_type = DW_UT_compile
08 address_size = 8
00 00 00 00 debug_abbrev_offset = 0- Evet .
unit_typeBu sayede, bir sonraki birim4 + 489 = 493Bölümün sonunda.llvm-dwarfdump --debug-infoAynı beş değerleri yazdırırCompile Unit:İlk test bu çizgiyi inceliyor ve karşılaştırıyor.
Kısacalar
Bölüm 2.1'de, her defuggerleme bilgisi girişinin "bir tanımlama etiketinden ve bir dizi özelliğinden oluştuğu" belirtilmektedir..debug_abbrevBu nesne 14 açıklamada bulunur. Burada iki önemli konu vardır:
[12] DW_TAG_structure_type DW_CHILDREN_yes
DW_AT_name DW_FORM_strx1
DW_AT_byte_size DW_FORM_data1
DW_AT_decl_file DW_FORM_data1
DW_AT_decl_line DW_FORM_data1
[13] DW_TAG_member DW_CHILDREN_no
DW_AT_name DW_FORM_strx1
DW_AT_type DW_FORM_ref4
DW_AT_decl_file DW_FORM_data1
DW_AT_decl_line DW_FORM_data1
DW_AT_data_member_location DW_FORM_data1parse_abbreviations()ULEB128 kodu, ULEB128 etiket, bir çocuk bayt, sonra bir 0, 07.5.6 bölümünde form kodlamaları bulunmaktadır: data11 byte.ref4birim ile ilişkili 4 baytlı bir sıfırlama. strx1dizileri karşılaştırma tablosuna 1 baytlı bir indeks. sec_offsetve data44 byte.addrxadres tablosuna ULEB128 indeksi olarak yerleştirilmiştir. exprlocULEB128 uzunluğu ve bu çok bayt, ve flag_presentHiç bayt yok.
Balanç üyesini el ile çöz
için yapı girişaccount- Ofset'ten başlıyor.0x195Baytlarla 0c 15 20 00 03Kısaca 12'e ait bir kod, yapı.DW_AT_nameİndeks dizilisi 0x15- Evet .DW_AT_byte_size- Evet .0x20= 32,DW_AT_decl_file0 ve DW_AT_decl_line3'ün çizgisi.struct account {Kaynağı.
İkinci üye girişleri 0x1a3- Evet .0d 10 88 01 00 00 00 05 04- ...
0d13 kısaltması, bir üye.10- Evet .DW_AT_name- Evet .strx1: string index 16.88 01 00 00- Evet .DW_AT_type- Evet .ref4: karşılığı0x188Birim başlangıcından itibaren, yaniDW_TAG_base_typeAdı:int- Evet .00- Evet .DW_AT_decl_fileve05- Evet .DW_AT_decl_line: 5 satıraccount.c- Evet .04- Evet .DW_AT_data_member_location: 4.
Bölüm 5.7.6 'da bir üye girişinin "ya DW_AT_data_member_location özelliğine veya DW_AT_data_bit_offset özelliğine sahip olabileceğini" ve tam bir sayı sabitinin "aşıyan bir varlıktan itibaren baytlarda karşılaştırıldığını" belirtmektedir. Bu bayt, addl 0x4(%rax), %ecxvecompare_layouts()Bu iki şeyi bir kenara koyan fonksiyon.
İpi ve adresler iki tabloda geçer
İpiş indeks 16 bir sıfırlama değil .debug_strOkuyucu alır .DW_AT_str_offsets_base8 olan birim girişinden 4 bayt girişini okur 8 + 4 * 16İçeride.debug_str_offsetsBu giriş , "balance"İçeride.debug_strAynı indirektlik adreslere de geçerlidir.sum_balancesBaytlarla başlar 07 07: kısaltma 7, bir alt program, sonra DW_AT_low_pc- Evet .addrxindeks 7. 8 baytlı giriş 8 + 8 * 7İçeride.debug_addr- Evet .0x30, fonksiyonun adresi .textBu tabloda ilk altı giriş, altı satırlı yazılardaki adreslerdir.
Yer değiştirilebilir bir nesnedeki hiçbir tablo son değerlerini tutmaz..debug_str_offsetsDosyada sıfırdır ve .rela.debug_str_offsets29'u tutmaktadır.R_X86_64_32.debug_strEk olarak gerçek tazminat ile..debug_addr13'ü var.R_X86_64_64.textve .rodata.str1.1- Evet ..debug_infoDört farklı yer değiştirme sistemi vardır: kısaltma ofset,DW_AT_str_offsets_base- Evet ..debug_str_offsets+0x8- Evet .DW_AT_stmt_listveDW_AT_addr_base- Evet ..debug_addr+0x8- Evet .ElfObject.relocated()uygulanır .S + AOkur bir bölüm analizini yapmadan önce 32 bit ve 64 bit türleri için. Bir bölüm simgesi bir nesne dosyasında 0 değerine sahiptir, bu nedenle alan eklemeyi alır. llvm-dwarfdumpaynı çözünürlükte, bu yüzden çıkış her çözünürlükte isim ve adresleri gösterir.
Tipler, fonksiyonlar ve yerler
DwarfInfo.type_name()Bu da ...DW_AT_typeİpuçlar.labelüye noktaları bir DW_TAG_pointer_type, bir DW_TAG_const_type, bu da charBas tip, bu yüzden isim const char *- Evet .type_size()DevamıDW_AT_byte_sizeTemel türler ve yapılarda, bir işaretçi için 8 ve bir dizi için element boyutunun sayısını çarpı. type_kind()DW_AT_encodingÜst tipten intbir işaretle, veyafloat, ve için bir işaretçi tipipointer, bu da karşılaştırma kullanan kelime birikimi.
DwarfInfo.functions()Altı okur.DW_TAG_subprogramGirişler.bump0x0- ...0x21, yani DW_AT_low_pcartı DW_AT_high_pcUzunluğu. Çerçeve tabanı DW_OP_reg6 RBP, ve parametre akonum ifadesini içerir 91 78- Evet .DW_OP_fbregSLEB128 değeri ile 0x78Bu da çerçeve boşluğu.-0x8(%rbp)listelenmenin depolanması%rdiAdresinde0x4Debug bilgisi ve talimatlar aynı boşluğu iki taraftan açıklar.
İki Yöntemin Karşılaştırılması
compare_layouts()DWARF üyelerini yürütür ve aynı sıyrıtındaki çıkarılan bir alanı arar.no-evidenceFarklı boyutlu bir alansize-mismatchAlanı hiç geri alınmamış bir işaretçi üyesipointer-unproven. Anıtlamalarda tam sayı aritmetik kullanıldığı bir float gibi, aynı fikirde olmayan bir tür, kind-mismatchÜyelik veya yastık içinden başlayan yerleşik alanlar unexpectedBoyut adımları karşılaştırır .DW_AT_byte_size- Evet .
Üç yapı için numaralar, python3 code/main.py- ...
| Build | Accesses through the first parameter | Fields | Stride evidence | Members matched | Size |
|---|---|---|---|---|---|
-O0 | 11 | 5 | scale 32, call-site 32, call-site 64 | 5 of 5 | 32, match |
-O1 | 9 | 5 | loop-step 32 | 5 of 5 | 32, match |
-O2 | 17 | 5 after folding, 12 before | offset-delta 32, unrolled 32, loop-step 256, scale 32, loop-step 32 | 5 of 5 | 32, match |
Sonuçlanan boşluklar her yapıdaki 1 ila 3 ve 10 ila 15 bytlerdir ve bunlar tam olarak DWARF düzeninin doldurma bytleri.natural_alignment_ok()Kayıtlar.
İşlevleri birbiriyle ekle ve resim değişir.bumpTek başına düzen 4'ün karşılaştırmasında bir alan, boyut bilinmemektedir ve alt sınır 8'dir. sum_balancesadımını ekler.tag0 ve 8 oranlarını ekler. projected16'da yüzen ekliyor.label_length24'e işaretçi ekler ve işaretçi olduğunu kanıtlar. main-O0Ve hiçbir şey eklemez.-O1ve -O2Bu sayede her yapılandırma için bu sırayı geçiyor.
Üç şey ulaşılmaz.İsimler talimatlarda bulunmuyor.const- Bu talimatlarda bulunmuyor.labelsadece "elementler 1 byte ayrı" leaq 0x1(%rcx), %rcx ile uyumlu olan string döngüsündecharve ileunsigned charve _Bool- Ne ?-O10'da byte işaretinin bilinmemesi, çünkü cmpb $0x6c, (%rdi)- Uzatmadan karşılaştırmalar yapılıyor.int8Orada vei8- ...-O0, nerede movsblBu gerçekler ders 35 kayıtları bilinmeyen olarak.
C++ nesneleri
shapes.cpptanımlar ShapeSanal bir yıkıcı, saf bir sanal area(), ve sanal bir name()veRect, Shapeve iki ekliyor.intItanium C++ ABI, clang'ın x86-64 Linux'ta bunları nasıl düzenlediğini yönetir.
Bozuk isimler
ABI'nin 5.1.2 bölümünde "Bir isim, öntanımla bozulur" der._ZBu nesne 19 simgeye sahip._ZNK4Rect4areaEvBu da şöyle:_ZO zaman ...NYatağındaki isim için,Kconst üyesi işlevi için, 4Rectve 4areauzunlukla belirlenmiş kaynak isimleri olarak, EYatağındaki isim kapatmak için vevboş bir parametrelist için. llvm-cxxfiltParmak iziRect::area() const- Evet ._Z8describeRK5Shapei- Evet .describe(Shape const&, int)- Evet .Rreferans olarak kullanılır.K- sabit.5Shapesınıfı.i- Evet .int5.1.4 bölümdeki özel isimler _ZTVbir masa için,_ZTItipinfo için ve _ZTSTypeinfo isimleri dizisi için.C1- Evet .C2- Evet .D0- Evet .D1veD2- Evet ._ZN4RectD0Evve _ZN4RectD2Evİkisi de basılı .Rect::~Rect(): silme yıkıcısı ve temel nesne yıkıcısı bir kaynak adı paylaşırlar.
Demanglerİçeridecode/main.pyBu alt kümeni ve 5.1.5.2 bölümünün yapı tipi kodlarını kapsar.S_5.1.10 bölümünün yerine getirilmesi. Test tüm 19 isimle çalıştırılır ve yakalananlarla karşılaştırılır.llvm-cxxfilt --no-strip-underscoreoutput. macOS'ta bayrak önemli, araç varsayılan bir önde gelen altını çıkarır ve ardından tanımamaktadır Z8describe...- Evet .
Yer değiştirmelerinden kaynaklanan
ABI'nin 2.5.2 bölümünde, sanal bir tabloun bileşenleri sırayla listelenir: sanal çağrı sıfırlamaları, sanal temel sıfırlamaları, üstteki sıfırlama, tipinfo işaretçisi ve ardından sanal fonksiyon işaretçileri. Adres noktası, bir nesnenin vptr değerinin, ilk fonksiyon işaretçisinin adresidir (2.6.2 bölüm). RectSanal tabanı yok, bu yüzden tablo üst, tipinfo ve fonksiyonlara karşılaştırılmıştır.
Simge ._ZTV4RectBu, 0x30 byte, 6 8-byte boşluk..rela.data.rel.ro._ZTV4Rect- ...
0000000000000008 R_X86_64_64 _ZTI4Rect
0000000000000010 R_X86_64_64 _ZN4RectD2Ev
0000000000000018 R_X86_64_64 _ZN4RectD0Ev
0000000000000020 R_X86_64_64 _ZNK4Rect4areaEv
0000000000000028 R_X86_64_64 _ZNK4Rect4nameEvSlot 0, sıfırla yukarıya kadar sıfırlanır ve yer değiştirilmeye ihtiyaç duymaz. Slot 1 tipinfo.+16Oradan, 0'luk slot tam nesne yıkıcısı, 1'lik slot silme yıkıcısı, 2'lik slot.area, 3. bölümname- Evet .read_vtable()simge boyutunu, aralığında yer değiştirmeleri ve sıfırı sıfırdan okur.ShapeMasanın şekli aynı .__cxa_pure_virtual2. slot'da, saf bir sanal çağrıya ulaşan çalıştırma süresi fonksiyonu.
RTTI
Bölüm 2.9.4'te std::type_infobir vptr olarak, mangled tip adına bir işaretçi ile takip edilir ve elde edilen sınıfları listeler. _ZTI4RectÜç yer değiştirdi: _ZTVN10__cxxabiv120__si_class_type_infoE+0x10- Evet ._ZTS4Rectve_ZTI5ShapeBirincisi , Vtable ' deki__si_class_type_info, ABI'nin sıfır sıfırında tek bir kamu-virtual olmayan tabanlı bir tip için kullandığı sınıf ve +0x10Bu vtable'nin adres noktası.4Rect, ki bu da kırık tip adı ve name()Bir .type_infoÜçüncü, taban._ZTI5Shapekullanımı __class_type_infoVe bir temel işaretçisi yok.read_typeinfo()Bu üç gerçeği geri verir.
Sanal çağrı
describe- ...-O0Bir sanal çağrı yapar:
f: 48 8b 7d f8 movq -0x8(%rbp), %rdi
13: 48 8b 07 movq (%rdi), %rax
16: ff 50 10 callq *0x10(%rax)İlk .movqİkinci birinde, nesneyi vptr olarak adlandırılan 0'da 8 byte okuyor, çünkü ABI onu ilk olarak sanal fonksiyonlu bir sınıfa yerleştirir.callqGeçer.0x10VPTR'den geçti ve0x10 / 82. bölüm.find_virtual_calls()İhtiyacımız olan iki adayla aynı şekilde konuşur ve raporlar verir: Rect::area() const-_ZTV4Rectve __cxa_pure_virtual-_ZTV5ShapeHangisi çalışacak, nesneye bağlıdır, bu yüzden arama dolaylı.
DWARF aynı bölgeyi kaydeder.DW_TAG_subprogram için girişlerareaHer iki sınıf da taşıyıcıdır.DW_AT_vtable_elem_location (DW_OP_constu 0x2)- Evet .name3 numaralı slot taşıyor, destruktorlar 0 numaralı slot taşıyor.Rect- birDW_TAG_structure_type16 bytelik bir DW_TAG_inheritance için girişShape0 ve üyeleri w8 ve hSaat 12.Shape8 byte bir yapay üye ile _vptr$Shapeİki bağımsız türeç 2 bölümü verir area: yer değiştirme tablosu ve hatalama girişleri.
- Evet .
-O1nesne hiçbir şekilde vtable'a sahip değil.mainyapılarıRectYerel olarak, böylece derleyici çözüldür.name()vedescribe(r, 2)Bu yüzden tablo yayınlanmıyor.describeHala içerirmovq (%rdi), %raxvecallq *0x10(%rax), çünkü parametri herhangi bir olabilir .ShapeKayıp bir vtable bir nesne dosyası hakkında bir gerçek ve testtest_the_optimised_object_keeps_the_call_pattern_but_drops_the_vtablesİki yarısını da kontrol eder.
Microsoft ABI'nin bu detayların her biriyle farklılık gösterir.?Ve çağrı konvansiyonunu kodlayacağız.void test(void)C++ olarak oluşturulmuştur.__cdecl- Evet .?test@@ZAXXZRTTI konumlandırıcısı vfptr[-1]bir _RTTICompleteObjectLocator, ve semboller ??_7bir vftable ve ??_R4Microsoft'un kendi belgeleri "kompilatör satıcıları arasında C++ isimlendirme standartları yoktur" diyor.
Araçlar Nasıl Yenilen Türleri Tanıtır
Aşağıdaki her satırda bir çıkış adı var . Bu makine üzerinde üretilmiş ve içeriye kaydedilmiş .code/fixtures/tools/Burada kurulmayan aletler için satırlar, belgeleri anlatır ve böyle söyler.
| Tool | Version, licence | Where types live | On our fixtures |
|---|---|---|---|
| Ghidra | 12.1.4, 2026-09-21, Apache-2.0 | Data Type Manager with built-in, user-defined, and derived types; .gdt archives; types used by a program are copied into it | With DWARF: /DWARF/account.c/account size=32 align=8 and a->balance = by + a->balance;. Without: *(int *)(param_1 + 4) with long param_1, tag(char *param_1), and *(char **)(param_1 + 0x18) |
| IDA Pro | 9.4, 2026-07-13, commercial; IDA Free is non-commercial with a cloud decompiler for x86 only | Local Types window, .til type libraries, DWARF and PDB import into a dwarf or pdb folder | Not installed. Documentation only |
| Binary Ninja | 6.0, 2026-09-03, commercial with a Free edition | Type editing, header import and export, type archives, debug info from DWARF and PDB, type libraries, platform types | Not installed. Documentation only |
| radare2 | 6.2.4, 2026-10-05, LGPLv3 | ts, tsc, afvd | ts and tsc account printed nothing for the relocatable object with bin.dbginfo=true; pdf shows mov dword [rax + 4], ecx with source line comments from DWARF |
| rizin | 0.9.1, 2026-06-29, LGPL core | ts, tsc, afvd | tsc account printed struct account { char kind; int balance; short code; double rate; const char *label; }; from DWARF; the build without -g lists only its bundled header types |
| lldb | lldb-1703.0.236.21 | image lookup -t | byte-size = 32 and the full compiler_type text from the ELF object |
| gdb | not installed here | ptype /o prints "the offsets and sizes of fields in a struct" with holes marked | Documentation only |
| pyelftools | 0.33, public domain | Python API over .debug_info | A 43-line script prints the same five members and unit version 5 |
| llvm-dwarfdump | Apple LLVM 17.0.0 | --debug-info, --debug-abbrev, --debug-str-offsets | The oracle for every reader test |
Ghidra'nın DWARF olmadan çıkışı aynı iki kuralın çalışmasını gösterir.char * parametreye kadartagÇünkü işlev bir bayt ile karşılaştırılır ve char **Sahaya çıkmak için0x18Çünkü ...label_lengthyüklenmiş değerden ayrılır ve baytları karşılaştırır. Bu dersdeki geçiş aynı iki kuraldır: talimattan genişlik, işaretten işaret. Ghidra'nın dekompiler belgesinde, p-kod varnodlarının "her zaman onlarla ilişkili bir veri türüne sahip olmadığı" ve dekompilerin "sonunda yayım yoluyla resmi bir veri türü tahsis ettiği" belirtiliyor. "Konsant göstericileri yerleştir" seçeneği aynı yayılımı sabitlere uzattır. Ghidra 12.1 bu ders için kullanılan cihazlarda kullanılamayan bitfield recovery'i dekompilerle birlikte ekledi.
Ghidra'nın Veri Tipi Yöneticisi, IDA'nın Yerel Tipleri, Binary Ninja'nın Arşivleri ve Rizin'in tdBu sayede, tüm bu programlar, geri alınan düzenden struct'u yazıp, dekompiler yeniden gönderilmesini izleyebilirsiniz.
rea 6.5.0 Ghidra sağlayıcısı ile dekompiler metnini bir kayıt olarak gönderir. Ghidra satırındaki her şey ona uygulanır ve kayıt, tür isimlerini üreten kanıt olmadan taşıyor.python3 code/main.py inferAynı listeye girerek de bu kanıtı bulmuşsunuz.
Tip İflası Araştırmaları
Bu geçiş, bir tip çıkarma sisteminin kısıtlama-genreasyon yarısını, bir parametre ile elde edilen bir yapı ile sınırlı olarak gösterir.
- TIE (Lee, Avgerinos, Brumley, NDSS 2011) tip yeniden yapılandırmasını kısıtlama üretimi olarak çerçeveledi ve ikili bir IR üzerinde çözme, muhafazakârlığı ve hassasiyeti ölçtü ve 87 temel programda ticari bir dekompilerle karşılaştırdı.
- Retypd (Noonan, Loginov, Cok, PLDI 2016) alt türleme, polimorfizm ve rekürsiv türler ekledi ve gösterge yeniden yapılandırılması rapor edildi
constGrammaTech'in adı altında açık kaynaklı bir uygulamada mevcuttur. - OSPREY (Zhang ve diğerleri, IEEE S&P 2021) her bellek konumunu rastgele bir değişken olarak değerlendirir ve en olası türleri çıkarır.
- DIRTY (Chen ve diğerleri, USENIX Security 2022) ve ReSym (Xie ve diğerleri, CCS 2024) öğrenilmiş modellerle işlem sonrası dekompiler çıkışı ve ReSym, tam yapı düzenleri üzerinde 72,9% doğruluk raporları.
- RecStruct (Chen ve diğerleri, USENIX Security 2026), akış-ansız sembolik yürütme ve yığın boşlukları üzerinde birlik-görme ile, bir dekompiler olmadan yuvalanmış structs kurtarır.
Bu dersin geçişinin atladığı ilk şey, yığın yapısı.-O1- Evet .main- Devam ediyor .rowsDüğme ve içe yazılmış kod üzerinde yazıyor .0x24(%rsp)- Evet .0x40(%rsp)- Evet .0x48(%rsp)- Evet .0x30(%rsp)ve0x38(%rsp)Bu modül 32 oranları 4, 0, 8, 16 ve 24'dir. Aynı yapı, ekmek için parametre yoktur.
Yapın
Demo çalıştır:
cd phases/13-tools-and-protocols/41-recovering-data-and-types
python3 code/main.py
npx tsx code/main.tsÇıktı bir JSON raporu beş parçalı. dwarfBirim başlığı, yapıları ve fonksiyonlarını içerir.-O0- Ne? - Ne?oraclespsABI kural düzenini içerir, ctypesdüzen, DWARF düzen, yerel çalıştırma ve agreeBayrak.buildserişimleri, çıkarılan düzen ve üç nesnenin her biri için karşılaştırmayı içerir. cxxİsimleri, her iki vtables, her iki tipinfo kayıtlarını, sanal çağrı ve DWARF yuvalarını içerir. toolsKaydedilen araç çıkışlarını listeler.
Beklenen şekil:
{
"dwarf": {"unit": {"unitLength": 489, "version": 5, "unitType": "DW_UT_compile", "addressSize": 8, "abbrevOffset": 0}, "...": "..."},
"oracles": {"agree": true, "psabiRules": {"size": 32, "align": 8, "...": "..."}, "...": "..."},
"builds": {
"O0": {"layout": {"stride": 32, "size": 32, "gaps": [1, 2, 3, 10, 11, 12, 13, 14, 15], "...": "..."}, "comparison": {"matches": 5, "members": 5, "...": "..."}},
"O1": {"...": "..."},
"O2": {"...": "..."}
},
"cxx": {"virtualCalls": [{"slot": 2, "candidates": {"Shape": "__cxa_pure_virtual", "Rect": "_ZNK4Rect4areaEv"}, "...": "..."}], "...": "..."}
}Oku code/main.pyBu sırada:
ElfObjectBölümleri, sembolleri ve yer değiştirmeleri okuyor verelocated()uygulanır .S + ABir hata düzeltme bölümüne.parse_abbreviations()veDwarfInfo._parse_unit()Başlığı, kısaltma tablosunu ve girişleri okuyun ve sonra çözünstrxveaddrxİki masa üzerinden.DwarfInfo.struct_layouts()vefunctions()GirişleriStructLayoutveFunctionInfoKayıtlar,type_name()- Evet .type_size()vetype_kind()- Evet .parse_listing()- Evet .parse_memory()veaccess_width()Okuyun .llvm-objdumpMesaj.trace_function()- Abstrakt devlet makinesi vefold_unrolled()veinfer_layout()Girişleri bir düzenle dönüştürün.compare_layouts()Durum satırlarını oluşturur vesysv_layout()vectypes_layout()Diğer iki peygamber.Demangler- Evet .read_vtable()- Evet .read_typeinfo()vefind_virtual_calls()C++ nesnesini kapsar.
Dört emir daha var.python3 code/main.py dwarf FILE.oDWARF 5'in yer değiştirilebilir nesnelerinin yapıları ve fonksiyonlarını yazdırır. python3 code/main.py infer LISTING.txt [FILE.o]Bir listeye geçiş yapar ve bir nesne ile DWARF'e karşı karşılaştırır. python3 code/main.py demangle NAME...ve python3 code/main.py vtable FILE.oC++'in yarısını kapsar.
Kullan
Kendin inşa ettiğin yeni bir nesne üzerine:
- Yakalama
llvm-objdump -d -rBu, bir nesneyi bir kenara bırakıp, bir nesneyi bir kenara bırakır. - Çık .
python3 code/main.py inferBir işlev eksikse, parametri bir hafıza operandına asla ulaşmadı veya geçiş izlemeyen bir yol boyunca birine ulaştı. - Her alanı erişim sayısıyla okuyun. Bir erişim bir tanık.
- - Devamı kanıtını oku.
call-siteveyascaleGiriş güçlü.loop-stepDiğer bir türden bir giriş zayıfdır, çünkü bir yuvarlak bir döngü birkaç elementin adımını bildirir.unrolledGörünür, katılamayı elden kontrol et. - Eğer
-goluşturmak var, karşılaştırmayı çalıştırmak ve olmayan her satırı okumakmatch- A.no-evidenceSatır bir üye adını verir.unexpectedGeçit yerleştiremediği bir giriş için sıra isimleri. - Düzenlemeyi araçınızın tip yöneticisine bir yapı olarak yazın ve hangi alanların, isimlerin ve türlerin tahmin olduğunu kaydetin.
Üç müjdeciyi erişime bırak.ctypes, ve DWARF yazdığınız bir düzenle ilgili anlaşmazlıklarda, bir paketli yapı, bir bit alanı veya farklı kurallar ile bir hedef bulmuşsunuz.
İnteraktif Laboratuvar
Laboratuvar A: Üye girişini çöz
- Evet .
codeDizin, açık Python:
cd phases/13-tools-and-protocols/41-recovering-data-and-types/code
python3 -qÇık:
from main import *
elf = load_object("account-x86_64-O0-g.o")
dwarf = DwarfInfo(elf)
dwarf.header()
elf.section(".debug_info").data[0x1a3:0x1ac].hex(" ")
dwarf.dies[0x1a3].attributes
dwarf.type_name(dwarf.type_of(dwarf.dies[0x1a3]))Sözlüğü incelemeden önce dokuz bayt kağıt üzerinde 13 kısaltma ile çöz.elf.section(".debug_str_offsets").data[8:16].hex(), ve yer değiştirilmiş olan,elf.relocated(".debug_str_offsets")[8:16].hex(), ve farkı açıkla.
Laboratuvar B: bir fonksiyonu izle
listing = load_listing("account-x86_64-O0-g.objdump.txt")
trace = trace_function("label_length", listing["label_length"])
[(hex(a.address), a.root, a.offset, a.width, a.kind) for a in trace.accesses]
trace.pointer_uses
trace_function("sum_balances", listing["sum_balances"]).loopsİndirmeden önce, hangi erişimden, ofset 24'in bir işaretçi olduğunu kanıtladığını yazın.-O1listeler ve bulma leaq 0x1(%rcx), %rcxİçeridetrace.increments- Evet .
Laboratuvar C: vektörlü döngüyü katlayın
o2 = load_listing("account-x86_64-O2-g.objdump.txt")
trace = trace_function("sum_balances", o2["sum_balances"])
sorted({a.offset for a in trace.accesses})
folded, evidence = fold_unrolled(trace, trace.accesses)
sorted({a.offset for a in folded}), [(e.kind, e.value, e.text) for e in evidence]O zaman koş .analyze_build("O2", fold=False)["comparison"]Ve sayın unexpectedDönüş kuralını değiştir, böylece döngü adımının tam olarak olması gerekir.k * d- Evet .kEn az 2 ve onaylayın ki -O1Bir giriş ve 32 adımla, yalnız kalır.
Laboratuvar D: tablo ve isimler
shapes = load_object("shapes-x86_64-O0-g.o")
read_vtable(shapes, "_ZTV4Rect")
read_typeinfo(shapes, "_ZTI4Rect")
demangle("_ZNK4Rect4areaEv"), demangle("_Z8describeRK5Shapei")
find_virtual_calls(load_listing("shapes-x86_64-O0-g.objdump.txt"), [read_vtable(shapes, "_ZTV5Shape"), read_vtable(shapes, "_ZTV4Rect")])Çizgiyi hesapla callq *0x10(%rax)Kağıt üzerinde, sonra onaylayın.DwarfInfo(shapes).struct("Rect").virtual_methods- O zaman yüklen .shapes-x86_64-O1-g.oVe denemek için .read_vtable- Bu kitapta oku.ElfErrorSimgolu neden yok olduğunu açıklayın.
Pratik Laboratuvar
Düğüm yapılarını geri alın main- ...-O1- Evet .
Gereksinimler:
- Uzaklaştırma
trace_function()Yani çerçeve boşluğu bölgesi bir kök olabilir.movapsDükkanlar dolsun0x0(%rsp)- ...0x5f(%rsp)-.data.rel.ro, 96 baytlı başlangıçlayıcıdırrows- Evet . - İçeride girişleri atfedin
0x24(%rsp)- Evet .0x40(%rsp)- Evet .0x48(%rsp)- Evet .0x30(%rsp)ve0x38(%rsp)Bu kök ve rapor onların modulo bir aday adım karşılaştırır. - 32 adımını döngüden çıkarın
0x120- ...0x12c, nerede%r144'te başlar ve ilerler.0x20- ... ...0x64, ve bu döngünün bağlandığını göster .0x643 element artı alan sıfırlaması. - Sonuçları DWARF düzenle karşılaştırın ve beş maçın içine girin ve hangi türlerin kanıtlanmamış olduğunu kaydetin.
- Çerçeve kökü bir parametre atfedildiğinde başarısız olan bir test ekleyin.
Başarılı olmak demektir .python3 code/main.py infer- ...-O1listeler listeleri mainÇizgi kökü olan bir tanık olarak ve karşılaştırma beş eşleşme gösterir main- Yalnız.
Ders Artifakti
Bu ders de içerir .outputs/skill-recovering-data-and-types.md. Bir listeni ve seçmeli bir defag yapısını bir alan için kanıtlı bir yapı düzeni, kaynaklarıyla bir adım, DWARF ile karşılaştırma ve bilinmeyenlerin bir listesine dönüştürmek için kullanın.
Kontrol et
Demo ve Deterministik Suite'i çalıştır:
cd phases/13-tools-and-protocols/41-recovering-data-and-types
python3 code/main.py
npx tsx code/main.ts
python3 -m unittest discover -s code/tests -vVerifikasyon, şunu kanıtlamalıdır:
- Birim başlığı, 14 kısaltma ve beş üyelik karşılığı
llvm-dwarfdump - Adlar yer değiştirilenler üzerinden gelir.
.debug_str_offsetsMasanın ve biriminstr_offsets_baseveaddr_base8 - Altı fonksiyon aralıkları taşır.
DW_OP_reg6 RBPveDW_OP_fbregToplama gösterdiği yerler ve düşük adresleri listelenme fonksiyonuna eşittir başlıyor - olmadan bir nesne
.debug_infoLEB128 dekodörleri ile birlikte0x78-8 olarak pyelftoolsvelldbOkuyucu ile aynı düzeni bas- psABI kuralları,
ctypes, DWARF ve yerli çalıştırma karşılığı verir0, 4, 8, 16, 24ve 32 boyutlu, ve yeniden düzenlenmiş yapı 24 byte. - listesi analizci 13 talimat okuyor
bump, bağlayıcılarR_X86_64_PLT32 bump-0x4Telefonla görüşme için0x194, ve geriye atlamayı bulur .0x72 - AT&T metni ve mnemonikten hafıza operandları, genişlikleri, türleri ve işaretleri çözünür
bump- ...-O0İzleyiciyi çerçeve boşluğundan geri okuyor ve 4'ün tersine üç kez girişi yapar.- Bu adımın sebebi
shlq $0x5- ...-O0,addq $0x20, %rdx- ...-O1, ve sekiz yükten 256 adımla katlanmış olarak-O2 label_lengthGöstericini, referansı geri çevirerek, ofset 24'te göstericini kanıtlar.main0'da bir çerçeve nesnesini geçiyor.0x20ve0x40- ...-O0Ve sadeceprintf- ...-O1- Her yapı 5 üyenin 5'ine 32 boyutlu ve DWARF dolguya eşit boşluklarla eşittir.
-O232 byte ve boyutsuz yedi alan rapor oluştur- Demangler tüm 19'u yeniden üretiyor.
llvm-cxxfiltipler - hem vtables hem de tipinfo kayıtları ve sanal çağrı.
0x162 numaralı yuva'ya karar ver. DWARF kabul ediyor. -O1C++ nesnesi çağrı kalıbını korur ve vtable yoktur- Ghidra, rizin ve radar2 fotoğrafları, DWARF ile yapı ve olmadan ham karşılaştırmalar gösteriyor.
- İnteraktif rakam analizle elde edilen aynı verileri içerir.
Üretim Başarısızlık Modları
| Failure | What the weak tool reports | What the analysis must prove |
|---|---|---|
| Unrolled loop | Eight fields 32 bytes apart and a 232-byte struct | Loop step 256 equals 8 times the offset delta 32, so one field at offset 4 |
| Stride read from one increment | sizeof 256 from addq $0x100 | The greatest common divisor of every stride source, with the scalar tail's addq $0x20 |
| 8-byte field called a pointer | void * at offset 24 | A later access uses the loaded value as a base, or the kind stays int64 |
| Pointer called an integer | long at offset 24 | The dereference at 0xfb, recorded as pointer proof |
| Narrow compare | char with a sign | cmpb carries no sign; only movsbl or movzbl does |
| Five functions assumed to share a type | One struct with no evidence of aliasing | Call sites in main that pass one frame object, or an explicit analyst assertion |
Strings read from .debug_str by index | Garbage names from a relocatable object | str_offsets_base plus the relocated .debug_str_offsets entry |
| Addresses read as numbers | low_pc of 0 for every function | addrx through the relocated .debug_addr table |
| DWARF 4 object | A reader that misparses the header | Version check on the second header field; 4 has no unit_type byte |
| Missing vtable | "the class has no virtual functions" | The -O1 object inlined every call; the -O0 object has the table and its relocations |
| Vtable slot read from the wrong base | Off-by-two slot numbers | The address point is 16 bytes past the symbol, after offset-to-top and typeinfo |
| Mangled name stripped | Z8describeRK5Shapei left as is | --no-strip-underscore on macOS, or a demangler that checks for _Z |
| Struct typed from one tool's guess | char *param_1 taken as the field type | The width and the dereference, written as evidence, and the name marked as a guess |
İşlem Kuralı
Bir girişten bir alan ve bir döngüden bir adım geri alın ve talimatı her birine bağlayın. Bir atışın ötesine güvenmeden önce, çözülmemiş döngüleri katlayın. DWARF'ı kısaltma ve yer değiştirilmiş tabloları üzerinden oku, hiçbir zaman bir dizilme bölümüne indeksleme yapmayın. Alanla alan karşılaştır ve her üyeyi kanıtsız, kanıtlanmamış her işaretçi ve her isim bilinmeyen olarak yaz. C++ nesnesinde, vtable'yi yer değiştirmelerinden ve çağradan boşluğu okuyun, sonra okuduğunuz boşluğu onaylamak için DWARF kullanın.
Daha Fazla Okumak
- System V Application Binary Interface, AMD64 Architecture Processor Supplement, section 3.1.2: Data Representation
- Procedure Call Standard for the Arm 64-bit Architecture (AAPCS64), section 5.10: Composite Types
- Writing ARM64 code for Apple platforms
- Intel 64 and IA-32 Architectures Software Developer's Manual, Volume 1, section 3.7.5: Specifying an Offset
- DWARF Debugging Information Format Version 5, sections 2.1, 5.7.6, 7.5.1.1, 7.5.3, 7.5.6
- Itanium C++ ABI, sections 2.5 (Virtual Table Layout), 2.9 (Run-Time Type Information), 5.1 (External Names)
- Microsoft Learn: Decorated namesve Name Decoration
- llvm-dwarfdump command guideve llvm-cxxfilt command guide
- Python ctypes: Structure layout, sizeof, alignment
- Ghidra Data Type Manager- Evet .Decompiler Concepts- Evet .Decompiler OptionsveGhidra 12.1.4 release
- IDA 9.1 user guide: Types- Evet .IDA 9.4 release postveIDA Free
- Binary Ninja user guide: Typesve Binary Ninja 6.0 Krypton
- radare2 6.2.4 releaseve rizin v0.9.1 release
- GDB manual: Examining the Symbol Table, ptype /o
- pyelftools on PyPI
- Lee, Avgerinos, Brumley, "TIE: Principled Reverse Engineering of Types in Binary Programs" (NDSS 2011)
- Noonan, Loginov, Cok, "Polymorphic Type Inference for Machine Code" (PLDI 2016)ve the retypd repository
- Zhang et al., "OSPREY: Recovery of Variable and Data Structure via Probabilistic Analysis for Stripped Binary" (IEEE S&P 2021)
- Chen et al., "Augmenting Decompiler Output with Learned Variable Names and Types" (USENIX Security 2022)
- Xie et al., "ReSym: Harnessing LLMs to Recover Variable and Data Structure Symbols from Stripped Binaries" (CCS 2024)
- Chen et al., "RecStruct" (USENIX Security 2026)
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.