Çıkarma ve Kontrol Akışı: Bytes'ten Grafeye Kontrol Edilebilir
Type: Build
Languages: Python, C, x86-64 assembly
Prerequisites: Phase 13 · 32 (ELF from scratch)
Time: ~150 minutes
Öğrenme Hedefleri
- Lider ayarlarında x86-64 talimat alt kümesini Intel SDM 2. cilt 2. bölümdeki kurallardan çözün.
- Dekodörü
llvm-objdump -dHer yapıda bir emir verilir. - Aynı baytlar üzerinde bir çizgi tarama ve bir geri dönüşlü düşüş yapın ve her birinin nerede yanlış olduğunu açıklayın.
- talimatları temel bloklara ayırıp, kenarları yazılmış bir kontrol akışı grafik oluşturun.
- Cooper, Harvey ve Kennedy'nin tekrarlama algoritması ile hemen hakimleri hesaplayın ve arka kenarlardan döngüleri bulun.
- Meseleye atlayan yer değiştirme girişlerinden dolaylı bir atlamanın hedeflerini geri alın.
Sorun
- Sen topladın .
ledger.c- ...-O0Bir ajanı ne diye sordu.gradeAjan bir monteyici aradı, listeyi okudu ve fonksiyonun sonunda olduğunu söyledi.jmpq *%raxSonra bir tek kodla pseudokod yazdı.return 0- Evet .
Aynı şeyi açarsın.llvm-objdump -dBu listede hiçbir şey, nasıl uygulanması onlara ulaşır, bu yüzden araç onları ölü kod olarak görüyor.
Bir liste sadece bir soruya cevap verir: hangi talimat hangi talimat biçiminde baytlar. Hangi talimatların hangi sırada çalışıldığını veya nereden çalışıldığını söylemez.
rea 6.5.0'da kendi parçacığı yok. Derin analiz için, README'nin Ghidra, Hopper veya IDA'yı getirmenizi ister. Çevrimiçi denetim için, rea ayrıca yüklediğiniz bir GNU objdump veya Rizin'i arayabilir. Çağrı grafikleri çözülmemiş hedeflerde durur: "Çalışmalar/Çalışmalar yalnızca çözülmüş fonksiyonları içerir.... Hedefsiz bilgisayar akışı bilinmiyor". Bu ders, bu gerçekleri oluşturan bölümü oluşturur, böylece bir araç bunları rapor ettiğinde onları yargılayabilirsiniz.
Eğitim biçiminde her alan için bir kural vardır
Intel SDM Cild 2, Şekil 2-1 her talimatın bayt sırasını verir. Önce seçmeli önbellekler, sonra bir ila üç opcode byte, sonra seçmeli bir ModRM byte ve seçmeli bir SIB byte. 0, 1, 2 veya 4 byte bir yer değiştirme ardından, hemen sonra 0, 1, 2 veya 4 byte bir yer değiştirme. Tüm talimat en fazla 15 byte (SDM 2.3.11) olacaktır. AMD APM Cilt 3, Bölüm 1, Bölüm 1.1 ila 1.7 aynı biçimi daha az sayfalarda tanımlar ve iyi bir çapraz kontroldür.
Her alan, bir sonraki alanın ne anlama geldiğini belirler. Bu nedenle bir dekodör küçük kararlar zinciridir ve her kararın arkasında el kitabında bir cümle vardır.
Önceden 4 grupta yer alır.
SDM 2.1.1'de eski önlükler listelenir.F0kilitleme ve tekrarlama önlemleri F2ve F3. Grup 2 altı segmentin üst kısmını tutar (2E- Evet .%cs) ve dal ipuçları.66Grup 4'ün,67, adres boyutunun üstesinden gelmek.
Bu nedenle, bu konularda, bir gruptan bir önbölü kullanılabilir.ledger-x86_64-O0.o- Yapabilir miyim ?0xf2okur 66 66 66 66 66 2e 0f 1f 84 00 00 00 00 00, beş kopyası66Bir sırada.llvm-objdumpBu şekilde basıyor .nopw %cs:(%rax,%rax)Bu kodlayıcı, gördüğü her bir önbaytı toplayıp hangisinin önemli olduğunu sorar.
66SSE talimatları için, opkodu seçen zorunlu bir önlüktür.66 0f ef c0- Evet .pxor %xmm0, %xmm016 bit bir şey değil.F3 0f 6f- Evet .movdqu. Dekodör öncelikle zorunlu önbellek ve ikinci opcode byte çiftini arar.6616 bitlik bir operand demek.
REX dört bit ekler ve opkodun yanında oturmalıdır
SDM 2.2.1 REX 'i bir byte olarak tanımlar .40- ...4Fşekliyle0100WRXB- Evet .W64 bitlik bir operand seçer. RModRM reg alanını genişletiyor. XSIB indeksi alanını genişletiyor. BModRM r/m alanını, SIB taban alanını veya opcode bytesindeki kayıt alanını (Tablo 2-4) genişletir. REX, opcode veya kaçış bytesinden hemen önce gelmelidir ve REX'in ardından eski bir önlük ile izlenmesi ihmal edilir.
İçeri48 89 e5byte48REX ile W- Sınıf.89- Evet .MOV r/m, r, ve ModRM e5seçilir %rsp- ...%rbp- REX ' i çıkar ve aynı baytlar movl %esp, %ebp- Evet .
REX, bayt kayıtlarının isimlerini de değiştirir.%spl- Evet .%bpl- Evet .%silve%dilyerine%ah- Evet .%ch- Evet .%dhve%bhDekodör bu nedenle iki masa tutar.
Opkodlar iki haritada yer alır
Bir baytlı bir opkod yaygın durumdur. 0Fİki baytlı haritaya kaçıyor ve 0F 38veya 0F 3ABu nedenle, bazı opkodlar, ModRM reg alanını üç opkod bit olarak kullanır./digitBu el kitabında.83 /7- Evet .cmp8 bitlik bir anlık ile.83 /5- Evet .subveFF /4- indirektjmp- Evet .
- Çözücü .
code/main.pyDört sabit nesneden oluşan kodları ve birkaç komşuyu kapsar.mov- Evet .lea- Evet .movslq- Evet .movzbl- Evet .imul, vardiyalar,testveidiv- Devam ediyor .push- Evet .pop- Evet .call- Evet .ret, on altı şartlı sıçrayış, çok baytlınop, ve beş SSE2 opkodu.DecodeErrorTahmin eden bir dekodör durduğundan daha kötü.
ModRM ve SIB operandları seçer
SDM 2.1.3 ModRM'yi ikiye bölüyor.mod(bits 7:6), reg(5:3), ve r/m(2:0).mod = 11r/m bir kayıt anlamına gelir. mod = 00- Evet .01ve10Değişiklik olmayan ortalama bellek, 8 bitlik bir değişiklik veya 32 bitlik bir değişiklik. r/m = 100SIB'nin bir baytı takip ettiği anlamına gelir.scale- 7:6'da.index(5:3), ve base(2:0) ve 100İndeks kayıtları yok demektir.
Tablo 2-2'deki iki özel satır 64 bit modunda geçer. mod = 00- Evet .r/m = 101- Hayır .(%rbp). Bir sonraki talimata göre 32 bitlik bir kaydırma (SDM 2.2.1.6). mod = 00SIB tabanı ile 10132 bitlik bir yer değiştirme, temel kayıt yok.
Al .8d 44 08 fa-grade- ...-O0- Opkod .8D- Evet .leaModRM 44- Evet .01 000 100: 8 bitlik bir yer değiştirme, reg %eax, ve bir SIB takip eder.08- Evet .00 001 000: ölçek 1, indeks %rcx, tabanı %rax- Deplacement bytefaAT&T'nin metni -6'dır.leal -0x6(%rax,%rcx), %eax, hesaplar yapar .score / 10 - 6, anahtar indeksini.
Yer değiştirme, bağlantı editörünün yazdığı yer
48 8d 0d 00 00 00 00- Evet .leaq (%rip), %rcxDört sıfır bayt, bir RIP-süre ilişkili operandın yer değiştirme alanıdır..rela.text- Yapabilir miyim ?0x69diyor kiR_X86_64_PC32.rodata-0x4. PsABI (Tablo 4.9) tanımlar R_X86_64_PC32- Evet .S + A - P: simge adresi artı eksi alan adresi.
İşlemci, alanı bir sonraki talimatın adresine ekler, böylece son hedef S + A + (end - P)Bu yüzden .leaAlan son dört bayt, yani end - P4 ve hedef .rodata + (-4) + 4 =.rodata + 0x0-4'ün eklenmesi bu dört baytı önceden telafi eder..rodata+0x10- ...-O1 lea- Yöntemleri.rodata+0x14- Evet .
Dekoder, her talimatın içinde yer değiştirme alanının sıyrılmasını kaydeder. resolve_reference()Bu işlevi, dönüşüm tablolarında karşılaştırılan ve operandın bağlantı sonrasında gösterdiği raporlar.leaq (%rip), %rcx"Mesa'ya".rodata+0x0" ...bir şey.
Bir istisna dışında en fazla 32 bit olan anlıklar
SDM 2.2.1.5 64 bit modunda en fazla 32 bit kalıp işaret uzattığı belirtilir.MOV r64, imm64(B8+r- Evet .REX.W), AT&T'nin sözcükleri olarak adlandırılır.movabsq. Dekodör 8 bitlik imzayı okuyor.83 c1 fa- Evet .addl $-0x6, %ecx) ve vardiya sayımları ve karıştırma seçicileri için imzalanmamış olarak (pshufd $0xeeBu bölünme neyle eşleşir ?llvm-objdumpParmak izleri.
Yakın dallar,pushvepop64 bit işletim sistemine varsayılan ve görmezden gelen 66Bu nedenle, dekodörler bu isimleri kullanıyor.pushq- Evet .popq- Evet .callqveretqOperand boyutlu bir önbellek okumadan.
Bir talimatı elden çözün
Kodu güvenmeden önce, şifreyi çöz.48 69 c0 67 66 66 66-gradeKağıt üzerinde.
48İçeride .40- ...4F, yani REX ileW = 1Operand boyutu 64 bit.69- Evet .IMUL r, r/m, imm32- Evet .c0ModRM' dır.11 000 000: kayıt formı, reg%rax, r/m%rax- Evet .- Sonraki dört bayt hemen, küçük endian:
0x66666667- Evet . - AT&T siparişi hemen, kaynağı, hedefi:
imulq $0x66666667, %rax, %rax- Evet .
Bu yedi bayt ve llvm-objdumpDevamı, klang'ı 10 ile çarpma ile bölünmesine izin veren karşılıklıdır.sarq $0x2234'e kaydırılan 34'ün ardından, 34'ün ardından, 34'ün sonunda tekrar bu çiftle karşılaşırsınız.score / 10- Evet .
Dekodör bu beş adımı yapar.Decoder.decode()- Evet ._State.modrm()ve_State.finish(). Onları bu sırada okuyun ve her dalı dosya dizisinde adı verilen SDM bölümüne eşleştirin.
Düzsel Arama
Schwarz, Debray ve Andrews, "Executable Code Revisited" (WCRE 2002) bölümünün 3.1'ünde doğrusal tarama tanımlıyor.objdumpBu şekilde çalışır, ve öyle de yapar llvm-objdump- Evet .
pythondef linear_sweep(code: bytes, start: int = 0, end: int | None = None) -> SweepResult:
position = start
while position < end:
try:
instruction = decode(code[:end], position)
except DecodeError:
undecodable.append((position, code[position]))
position += 1
continue
instructions.append(instruction)
position = instruction.endBu yöntemin bir hata moduna sahip ve bu, kağıtın 1. resminde gösterilmiştir. Veriler olan baytlar talimat olarak çözülür ve dekodör bir süre için gerçek talimat sınırlarını kaçırır. X86'de dekodör genellikle birkaç bayt içinde yeniden senkronize olur, bu da hatayı görmek zorlaştırır. Bu algılama sadece geçersiz bir opkod üzerinde gerçekleşir ve makale sonuçları bu algoritmaların "sessizçe başarısız" olduğu.
Bu ders için de tarama bir oracle.llvm-objdump -dHer bir düzenin talimatlarına göre vellvm-objdumpKaydedilen metin, içinde yaşar.code/fixtures/*.objdump.txt. Test, her satırı adres, bayt ve metne olarak analiz eder, yorumları #Dört nesne 86, 56, 84 ve 11 talimatları içerir, toplamda 237 ve dekodör hepsine eşittir.
Tekrar Düşüş
Aynı kağıtın 3.2 bölümünde ikinci yöntem rekürsiv geçiş olarak adlandırılır. Bilinen bir giriş noktasından başlayarak kontrol aktarımına kadar kodlamayı başlatın. Doğrudan atlamak için hedefe devam edin. Şartlı bir dal için hem hedefe hem de bir sonraki talimata devam edin, ve bir çağrı için, aradan sonra devam edin, çünkü arayan geri döner. Bir dönüş için durun. Yürüyüşün sona ermesi için ziyaret edilen adresler bir dizi tutun.
pythonif instruction.flow == "branch":
worklist.append(instruction.target)
address = instruction.end
elif instruction.flow == "indirect-jump":
table = resolver(instruction, instructions) if resolver else None
if table is None:
unresolved.append(instruction)
breakBaşarısızlık modunun tersidir. Doğrudan atlamak bytesinde hedef yoktur. Yürüyüş orada durur ve atlamak yoluyla ulaşılabilecek her blok bilinmez kalır. Kağıtın 2. Şekili ikinci bir tehlikeni gösterir: bir talimatın ortasına işaret eden yer değiştirmelerden elde edilen bir hedef kümesi.main.pyBu olayı bir çatışma olarak kaydetir ve bu yolu durdurur. Böylece üst üstelik iki kez sessiz olarak çözülmek yerine, örtüşme görünür.
Andriesse ve diğerleri 981 kompiler tarafından oluşturulan ikili üzerinde dokuz parçacık ölçtüler (USENIX Security 2016). İç hatlı veriler ve üst üste geçiş kodları, literatürün öngördüğü gerçek kompiler çıkışında daha nadirdi ve sert primitif işlev sınırları kurtarma idi. Her iki bulguyu da aklınızda tutun.gradeBu çok yaygın.
Yanlış Yıkış ve Doğru Düşüş Yapılan Bir Çevre
code/fixtures/sweep-trap.sBu, 24 byte el yazılı bir diziyle birlikte işlev bedeninin içinde dört bytelik bir tablo ile oluşur.movzbl (%rcx,%rax), %eaxve atlar.
asmlookup:
movslq %edi, %rax
leaq .Ltable(%rip), %rcx
movzbl (%rcx,%rax), %eax
jmp .Ldone
.Ltable:
.byte 0x31, 0xc0, 0x90, 0x48
.Ldone:
addl $1, %eax
retqBunu yapın .clang -target x86_64-unknown-linux-gnu -c sweep-trap.s.Dört tablo baytı, doğru talimatlar olarak çözülmesi için seçildi.31 c0- Evet .xorl %eax, %eax- Evet .90- Evet .nop- Evet .48REX önlüğü, bu yüzden tarama gerçekin üç baytıyla birleşiyor.addl $1, %eaxve parmak iziaddq $0x1, %raxTutuklananlar .llvm-objdumpçıkış tam olarak şunu gösteriyor:
text e: eb 04 jmp 0x14 <lookup+0x14>
10: 31 c0 xorl %eax, %eax
12: 90 nop
13: 48 83 c0 01 addq $0x1, %rax
17: c3 retqBu listede hiçbir şey kırılmış görünmüyor. her bayt çözüldü, her mnemonik gerçek ve retqBu listede üç yönde yanlış bir şey var.0xehedefler0x14, ve listede hiçbir talimat 0x14- Bu talimatın başında .0x1432 bit .addl, ve listede 64 bit bir gösterir addqTablo'nun dört baytı kod olarak görünür.
lookupSimbol altı talimatı çözüyor, atlamayı takip eder 0x14ve raporlar addl $0x1, %eaxBu tabloda dört bayttan ve sekiz dolgu bayttan hiçbirini iddia etmiyor .plus_one- Evet .trap_report()İçeridemain.pykarşılaştırmayı basıyor:
json"linearSweep": {
"instructions": 9,
"atJumpTarget": {"starts": "0x13", "text": "addq\t$0x1, %rax", "startsAtTarget": false},
"claimedDataBytes": 4,
"claimedPaddingBytes": 8
},
"recursiveDescent": {
"instructions": 6,
"atJumpTarget": "addl\t$0x1, %eax",
"claimedDataBytes": 0,
"claimedPaddingBytes": 0
}Test .test_sweep_and_objdump_share_the_same_mistakeBunu iddia ediyor.llvm-objdumpbasılıaddq $0x1, %rax- ...0x13Orakel, doğrusal bir tarama ve kör noktasını miras alır.
Temel Bloklar
Allen, "Control Flow Analysis" (1970) adlı kitapta temel blokları tanımladı. İlk talimatlarında ve sonlarında girilen talimatların bir dizi. Yakdan ve diğerleri bu yöntemi "No More Gotos" (NDSS 2015) bölümünün II.A.2'nde yeniden oluşturdular.
Yapı, kodlanmış talimatların üzerinde iki geçiş taraması, adresle sıralamaktadır. İlk geçiş liderleri toplar. Bir lider giriş, bir dal veya atlama hedefi, bir masa hedefi veya herhangi bir kontrol aktarımından sonra talimatdır. İkinci geçiş, her liderde ve adres dizisindeki her boşlukta yeni bir blok başlatır.
pythonfor instruction in self.instructions:
if instruction.address in leaders or current is None or current.end != instruction.address:
current = Block(instruction.address, [])
self.blocks[instruction.address] = current
current.instructions.append(instruction)Bir dönüşün bir varisi yoktur. Bir sıçrama bir tane, etiketlenmiş jumpŞartlı bir dalın iki tane var.takenve fallthroughBir blokun sonunda bir basit talimat, bir sonraki blokta gerçekleşir.table, ya da hiç, ve blok kaydedildi unresolved- Evet .
Aynı tarayıcıdan iki gerçek daha çıktı. Giriş ulaşamadığı bir blok ulaşılmaz ve tarama geçti.grade- ...-O0Başlangıç adresi başka bir yöntemin listesi talimatına giren bir blok, tuzaktan gelen örtüşme durumudur.
Toplamın Kontrol-Akış Grafiği
total- ...-O131 byte ve 13 talimat:
text 40: 85 f6 testl %esi, %esi
42: 7e 18 jle 0x5c <total+0x1c>
44: 89 f1 movl %esi, %ecx
46: 31 d2 xorl %edx, %edx
48: 31 c0 xorl %eax, %eax
4a: 66 0f 1f 44 00 00 nopw (%rax,%rax)
50: 03 04 97 addl (%rdi,%rdx,4), %eax
53: 48 ff c2 incq %rdx
56: 48 39 d1 cmpq %rdx, %rcx
59: 75 f5 jne 0x50 <total+0x10>
5b: c3 retq
5c: 31 c0 xorl %eax, %eax
5e: c3 retqLiderler 0x40(Giriş),0x44(Şimdikijle), 0x50(Tamamı:jne), 0x5b(Şimdikijne), ve 0x5c(Tamamı:jleBeş blok, beş kenar:
| From | To | Kind | Why |
|---|---|---|---|
0x40 | 0x44 | fallthrough | jle not taken, count is positive |
0x40 | 0x5c | taken | count is zero or negative, return 0 |
0x44 | 0x50 | fallthrough | the nopw is alignment inside the block, execution passes through it |
0x50 | 0x50 | taken | jne 0x50, the loop |
0x50 | 0x5b | fallthrough | loop exit, return the sum |
Altı baytlı .nopw- ...0x4aBu da bir başlıca şey.0x5016 baytlık bir sınırda yer alır.0x44- Bir bayt .nop- ...0x5fSonuncudan sonra.retqBu işlev için altıncı blok oluşturur veunreachable()- Listede.
- Evet .
-O2Aynı fonksiyon 140 byte ile iki döngü ile.0x80ile geçiş başına sekiz değer eklerpaddd, ve kalıntı kalıntıları0xc0CFG'nin iki arka kenarı var.0x80 -> 0x80ve0xc0 -> 0xc0, ve test süiti her ikisini de kontrol ediyor.
İteratif Algoritmle Yöneticiler
Bir blok .dBir blokta hakimdir.bGirişten bGeçer d- ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...bbBu en yakın yer .bBu kavram Prosser (1959) ile Allen (1970) arasında geçer ve bu, ders 34'ün yerleştirmesi gereken yapıdır.ifve whileDoğru.
Cooper, Harvey ve Kennedy "A Simple, Fast Dominance Algorithm" (2001) yazmışlardır. Onlar, kompakt bir temsil ile ters post sırada çalıştırılan veri akışı formülasyonunun yaklaşık bin düğüm altında grafiklerde Lengauer-Tarjan (1979) 'ı yendiğini savunuyorlar. Makalelerinin amacı "yeni bir algoritma sunmak değil" diyor. Basit algoritmaların pratikte kazanabileceğini ve iyi öğretim araçları olduğunu gösterir. Bir CFG fonksiyonunun neredeyse hiç bin düğümü yoktur.
pythondef intersect(first: int, second: int) -> int:
while first != second:
while number[first] > number[second]:
first = idom[first]
while number[second] > number[first]:
second = idom[second]
return first
changed = True
while changed:
changed = False
for start in order[1:]:
processed = [p for p in self.predecessors(start) if p in idom]
candidate = processed[0]
for other in processed[1:]:
candidate = intersect(other, candidate)
if idom.get(start) != candidate:
idom[start] = candidate
changed = Truenumber0'da giriş ile her blokun ters sıradan pozisyonunu tutar. intersectKağıt, karşılaşmalarına kadar iki parmağını geçerli dominanto ağaçtan yukarı yürütür.<Bu kod ters sipariş sonrası sayıları kullanıyor, bu yüzden karşılaştırma >Diğer her şey gazetenin sahte koduna uyar.
- Evet .
total- ...-O1Sonuç şu:idom(0x44) = 0x40- Evet .idom(0x50) = 0x44- Evet .idom(0x5b) = 0x50veidom(0x5c) = 0x40- Kenarlık .0x50 -> 0x50Arka kenar, hedefinin kaynağı üzerinde egemenlik gösterdiği için bir arka kenardır (Allen 1970).back_edges()Test geri döndürür.test_dominators_on_a_hand_built_diamond_with_a_loop16 baytlı bir dizide aynı kodu birleştirme noktasıyla çalıştırır.intersectİki öncekini görür.
-O0'da sınıflandırılmış atlama masası
- Evet .
-O0clangswitchİçeridegradepozisyon bağımsız atlama masası diline:
text 5d: 83 e8 04 subl $0x4, %eax
60: 77 38 ja 0x9a <grade+0x6a>
62: 48 8b 45 f0 movq -0x10(%rbp), %rax
66: 48 8d 0d 00 00 00 00 leaq (%rip), %rcx
0000000000000069: R_X86_64_PC32 .rodata-0x4
6d: 48 63 04 81 movslq (%rcx,%rax,4), %rax
71: 48 01 c8 addq %rcx, %rax
74: ff e0 jmpq *%raxDört gerçek olarak okuyun.subl $0x4Ardından jasınır kontrolü: 4'ten yüksek indeksler varsayılan durumuna geçer 0x9aBu yüzden masada beş giriş var.leaqyer değiştirmesiyle birlikte tablo adresini %rcx, ve önceki bölümde adresin gösterildiği .rodata+0x0- Ne ?movslqimzalanmış 32 bit giriş yükler.addqtablo tabanını ekler, böylece her giriş, tabloyla kapı blokuna arasındaki mesafedir.
Nesne dosyasında her tablo bytesinin değeri sıfırdır..rela.rodata- ...
textRELOCATION RECORDS FOR [.rodata]:
OFFSET TYPE VALUE
0000000000000000 R_X86_64_PC32 .text+0x91
0000000000000004 R_X86_64_PC32 .text+0x8c
0000000000000008 R_X86_64_PC32 .text+0x87
000000000000000c R_X86_64_PC32 .text+0x82
0000000000000010 R_X86_64_PC32 .text+0x86UygulaS + A - PGiriş içink- Yapabilir miyim ?4kKaydedilen değer .text + A - (.rodata + 4k), ve kod ekliyor .rodata + 0- Ama hedef....text + A - 4kGiriş 0 0x91 - 0 = 0x91, giriş 1 0x8c - 4 = 0x88, ve giriş 2 0x87 - 8 = 0x7f3. ve 4. bölümler 0x82 - 12 = 0x76ve 0x86 - 16 = 0x76- 6, 7, 8, 9 ve 10 olaylarıscore / 101, 2, 3, 4 ve 4'ü depolayan bloklara gidin. Bu, 9 ve 10 için paylaşılan hücre dahil olmak üzere, C kaynağı hattı için bir çizgi eşleşir.
recover_table()Bu hesaplama yapar.jump_table_resolver()İdiomla tersine eşleşir.jmpq- Bu , addqBu kayıtta,movslqBu yüklenmiş, leaqBu da temel ve sınırları oluşturdu.ja. Aşağı düşüş daha sonra dört farklı hedefi zorlar ve devam eder. Bağlı bir yürütülebilir tablonun son değerlerine sahiptir ve yer değiştirilmez. Aynı işlev daha sonra imzalanan 32 bit girişleri okuyor ve tablo adresini ekler ve evidencehedeflerin hangi yolla ortaya çıktığını kaydetir.
için numaralargrade- ...-O0, python3 code/main.py cfg code/fixtures/ledger-x86_64-O0.o grade- ...
| Method | Instructions | Blocks | Edges | Bytes | What is wrong |
|---|---|---|---|---|---|
| linear sweep | 34 | 9 | 7 | 118 code + 10 padding | 5 unreachable blocks, no edge into the four case blocks, padding decoded as nopw |
| recursive descent | 25 | 3 | 2 | 82 code | 36 code bytes never reached, jmpq *%rax at 0x74 unresolved |
| descent with the table | 33 | 8 | 11 | 118 code | nothing |
Masada, egemenlik ağacı tamamlanmıştır.idomHer bir dava blokunun 0x62, atlama ile blok.idom0xa1Giriş .0x30, çünkü 0x9aulaşır 0xa1Geçmeden 0x62- Evet .
Optimizer -O1 değerini nasıl değerlendirdi ?
Görürsün .gradeBu bir düğme olarak tanımlanır ve bir atlama tablosuna birleştirir.-O1ve -O2İçeri indirek atlama yok.gradeHiç değilse:
text 2c: 83 f9 04 cmpl $0x4, %ecx
2f: 77 0c ja 0x3d <grade+0x2d>
31: 89 c8 movl %ecx, %eax
33: 48 8d 0d 00 00 00 00 leaq (%rip), %rcx
0000000000000036: R_X86_64_PC32 .rodata+0x10
3a: 8b 04 81 movl (%rcx,%rax,4), %eax
3d: c3 retqYer değiştirme eklemesi0x10+ 4 ' ü sıralama masayı .rodata+0x14- Yük düz bir yer .movl, ve orada bulunan baytlar 01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00 04 00 00 00. Kompilatör beş durum bloklarını beş geri dönüş değerlerinin bir tablosu ile değiştirdi. Yer değiştirme, bir tablo ve sınır kontrolü vardır. Kontrol akışı üç blok ve üç kenar, bir koşullu dal ile oluşur. find_value_tables()Raporları bir value-tableNotlar ile birlikte[1, 2, 3, 4, 4]ve evidence: "bytes"- Evet .
grade_label- ...-O2Bir adım daha ileriye gidiyor.labels5 string pointers array'i 5 32-bit offset tablosu haline getirdi..rodata+0x0Her giriş bir R_X86_64_PC32İpuç bölümüne karşı yer değiştirme ve işlev yüklediği girişine tablo adresi ekler. recover_table()Bu beş yer değiştirme ve geri dönüş karşılığı .0, 2, 4, 6, 8- ....rodata.str1.1, ki tutulur .F- Evet .D- Evet .C- Evet .BveA. Aynı aritmetik bir atlama tablosunu geri alan bir nispeten veri tablosunu geri alan, çünkü her ikisi de S + A - PBilinen bir ödülden.
Bu iki yapıtan alınan ders, bytes'in gösterdiği şeyi adlandırmaktır. Bir atlama tablosu kontrol akışı grafiğini değiştirir. Bir değer tablosu yükü değiştirir. Her ikisini de "açıştırıcı" olarak adlandırmak, ders 34'ün yeniden yapılandırması gereken farkı gizler.
Rea ve Ghidra'nın sana verdiği şey
rea 6.5.0 ile Ghidra 12.1.4 sağlayıcısı bir fonksiyonun listelenme metnini gönderir procedure_assemblyBir ip gibi.inspect_native_instructionbir talimatın bytesini, uzunluğunu, dekodör metnini, operand işaretlerini, yazılmış referansları ve akışını gönderir, geri dönüş p-koddan sınıflandırılır. xrefsGhidra'nın sıralanmış eşsiz kaynak adreslerini gönderir ReferenceManager.getReferencesTo- Evet .analyze_functionCFG'yi taşıyor ve bodyBu fonksiyonun.procedure_info"Kaflar ve ortak kuyruqlar, sahip olduğu baytlar olarak değerlendirilmemelidir" diye uyarıyor.
Bu gerçeklerin her biri Ghidra'nın geri dönüşlü kökenliliği ve analizleri ile serileştirilmiştir.instruction_flow_evidence- Evet .function_referencesvejump_table_evidenceBu nedenle bir sonuç, onu üreten kuralları belirler. Yapamadığı şey, çalışmalarını göstermektir. Ghidra tablosunu bulamadığında akış "bilinmez" ve aramacılardan, çağrılardan ve CFG'den durum blokları eksik olur.
Artık işin var.python3 code/main.py cfgBu sayede, bu sayede, bu sayede, bu sayede, bu sayede, bu sayede, bu sayede, bu sayede, bu sayede, bu sayede, bu sayede, bu sayede, bu sayede, bu sayede, bu sayede, bu sayede, bu sayede, bu sayede, bu sayede, bu sayede, bu sayede, bu sayede, bu sayede, bu sayede, bu sayede, bu sayede, bu sayede, bu sayede, bu sayede, bu sayede, bu sayede, bu sayede, bu sayede, bu sayede, bu sayede, bu sayede, bu sayede, bu sayede, bu sayede, bu sayede, bu sayede, bu sayede, bu sayede, bu sayede, bu sayede, bu sayede, bu sayede, bu sayede, bu sayede, bu sayede, bu sayede, bu sayede, bu sayede, bu sayede, bu sayede, bu sayede, bu sayede, bu sayede, bu sayede, bu sayede, bu sayede, bu sayede, bu sayede, bu sayede, bu sayede, bu sayede, bu sayede, ve bu sayede, bu sayede, bu sayede, bu sayede, bu sayede, bu sayede, eğer, bu sayede, bu sayede, bu sayede, eğer, bu sayede, eğer, bu sayede, eğer, bu sayede, eğer, bu sayede, eğer, eğer, bu sayede, eğer, eğer, bu sayede, eğer, eğer, eğer bu sayede, eğer, eğer, eğer, eğer, eğer, eğer, bu sayede, eğer, eğer, eğer, eğer, eğer, eğer, eğer, eğer, eğer, eğer, eğer, eğer, eğer, eğer, eğer, varsa, eğer, eğer, eğer, eğer, eğer, eğer, eğer, varsa, eğer, eğer, eğer, eğer, eğer, eğer, varsa, eğer, eğer, eğer, eğer, eğer, eğer, eğer, varsa, varsa, eğer, eğer, varsa, eğer, eğer, eğer, varsa, eğer, eğer, eğer, eğer, eğer, eğer, eğer, eğer, eğer, eğer, eğer, eğer, varsa, varsa, eğer, varsa, eğer, eğer, varsa, eğer, eğer, eğer,
Bir program olarak dekodör üzerinde bir not. Capstone 6.0.0-Alpha10 (2026-07-21) sınır dışı sabit okuma ve yazma CVE-2026-55893 ve CVE-2026-55894 altında ve 5.0.9 (2026-05-28) arka planlı CVE ayarları altında yazılır. Bir dekodör yazmadığı baytları okuyor. 15 bayt sınır, kesim kontrolü _Cursor.byte(), ve bilinmeyen bir opkod koruma tahmin etmeyi reddetmek .main.pyBu yüzden yapılmamış.
Yapın
Demo çalıştır:
bashcd phases/13-tools-and-protocols/33-disassembly-and-control-flow
python3 code/main.pyÇıktı bir JSON raporu. Her sabit nesne için oracle anlaşması verir. Her işlev için üç yöntem altında talimat, blok ve kenar sayıları verir. Ayrıca erişilemez blokları, çözülmemiş dolaylı sıçrayışları, arka kenarları, hemen egemenleri ve herhangi bir tabloyu listeler. Tuzak karşılaştırması ile sona erer.
Beklenen şekil:
json{
"fixtures": {
"ledger-x86_64-O0.o": {
"oracle": "86/86 instructions agree with llvm-objdump",
"functions": {
"grade": {
"range": "0x30-0xa6",
"paddingBytes": 10,
"linearSweep": {"instructions": 34, "blocks": 9, "edges": 7, "unreachableBlocks": ["0x76", "0x7f", "0x88", "0x91", "0xa6"], "...": "..."},
"recursiveDescent": {"instructions": 25, "blocks": 3, "edges": 2, "unresolvedIndirectJumps": ["0x74"], "...": "..."},
"recursiveDescentWithTables": {"instructions": 33, "blocks": 8, "edges": 11, "...": "..."},
"tables": [{"kind": "jump-table", "table": ".rodata+0x0", "count": 5, "entries": ["0x91", "0x88", "0x7f", "0x76", "0x76"], "evidence": "relocation", "...": "..."}]
}
}
}
},
"sweepTrap": {"...": "..."}
}Oku code/main.pyBu sırada:
ElfObjectbölüm başlıklarını, sembollerini ve.rela.*girişler vesymbolize()Yolun bir adresini belirler.llvm-objdump- Evet.Decoder.decode()Öntanımları ve REX'i okuyor, sonra bir ya da iki baytlı opkod haritasına gönderir._State.modrm()ModRM, SIB ve yer değiştirme kaydını okuyor ve yer değiştirme alanının gidermesini kaydeder.resolve_reference()uygulanır .S + A - PYer değiştirme veya yakın bir alanda yer değiştirme.linear_sweep()verecursive_descent()Her iki yöntemin başarısızlığı kaydedilmiştir.recover_table()vejump_table_resolver()Bir tabloyu yer değiştirmeleri veya baytları ile okuyun.ControlFlowGraphliderler, bloklar ve tiplenmiş kenarlar oluşturur vedominators()Bu iteratif algoritma.analyze_function()her yöntemi tek bir fonksiyonla çalışır vecompare_with_oracle()Dekodörü kontrol eder.
İki emir daha var.python3 code/main.py disasm FILE.oHer talimatın ardından yer değiştirme hedefi ile bir liste basar. python3 code/main.py cfg FILE.o FUNCTIONBir fonksiyonun analizini basar.
Kullan
Yeni bir nesne için bu sırada üç yöntemi kullanın.
- Önce oracle karşılaştırmasını yap.
llvm-objdumpBir basit talimatın bir hata olduğu ve sonraki tüm yapıların ona miras aldığı bir hata olduğu. - Her fonksiyon simgesinden geri dönüşlü düşüşü çalıştırın ve okuyun
unresolvedHer giriş, hedeflerinizin olmadığı bir indirek atlama veya çağrıdır. - Her atlama için, masa dilini ve ona işaret eden yer değiştirmeyi araştırın. Eğer dil eşleşmezse, atlamadan önce baytları okuyun ve gördüğünüzü notu yazın.
- Arama ve düşüş baytını bayt ile karşılaştırın. Sadece arama iddiaları olan baytlar doldurma veya veri. Hiçbir iddiaları erişilemez olan veya bulunmadığı bir fonksiyona ait olan baytlar.
- CFG'yi geri alınan tablolarla aşağıdan inşa edin ve arka kenarları okuyun.
whileveyafor- Evet .
Orakel metin dosyalarını nesnelerle birlikte tutun. Yeni bir kompiliç versiyonuna geçtiğinizde, onları yeniden yakalayın, çünkü değiştirilmiş bir liste değişmiş bir konudur.
İnteraktif Laboratuvar
Laboratuvar A: Kağıt üzerinde çözünür, sonra kontrol edin.
- Evet .
codeDizin, açık Python:
bashcd phases/13-tools-and-protocols/33-disassembly-and-control-flow/code
python3 -qÇık:
pythonfrom main import *
decode(bytes.fromhex("48 8d 0d 00 00 00 00"), 0).text
decode(bytes.fromhex("8d 44 08 fa"), 0).text
decode(bytes.fromhex("66 66 66 66 66 2e 0f 1f 84 00 00 00 00 00"), 0).lengthHer çağrıdan önce beklediğini yaz ve sonra 48İlkinden sonra operand boyutunun değişmesini izle.decode(bytes.fromhex("0f 0a"), 0)Ve oku DecodeError- Evet .
Laboratuvar B: bir yerleşim
pythonelf = load_fixture("ledger-x86_64-O0.o")
code = elf.section(".text").data
lea = decode(code, 0x66)
lea.displacement_offset
resolve_reference(elf, lea)Referans şöyle diyor:.rodata+0x0Adresini değiştir .0x116İçeridegrade_labelve referansı okuyun..data- Sonra da callq- ...0x10eHemen alanın taşıdığıR_X86_64_PLT32 grade-0x4, ve çözülen hedef 0x30, yani grade- Evet .
Laboratuvar C: iki yöntemin anlaşmazlığını izle
pythontrap = load_fixture("sweep-trap.o")
text = trap.section(".text").data
[i.text for i in linear_sweep(text, 0, 0x20).instructions]
[i.text for i in recursive_descent(text, [0], 0x20).sorted_instructions()]Her listede talimatları say ve başlayanı bul 0x13Sonra son tablo baytını sweep-trap.s-0x48- ...0xe8Bu da bir daha çalıştırmak için.addlve retqbir callqBu işlev geri dönüşü görmediği gibi görünüyor.
Laboratuvar D: el ile dominanter
pythonanalysis = analyze_function(load_fixture("ledger-x86_64-O1.o"), "total")
analysis.cfg.edges
analysis.cfg.reverse_postorder()
analysis.cfg.dominators()
analysis.cfg.back_edges()Beş blok üzerinde çiz ve sonuçlara bakmadan önce dominanterleri hesapla.grade- ...-O0Nedenini açıkla.idom(0xa1)- Evet .0x30Ve hayır .0x62- Evet .
Pratik Laboratuvar
Aydınlıklara dolaylı bir çağrı sitesi ekle ve analizi genişlet.
Gereksinimler:
- Küçük bir diziden seçilen bir fonksiyon göstergesi üzerinden çağrı yapan bir C fonksiyonu yazın.
-O0ve-O1Ve nesneleri kendileriyle bağlayın.llvm-objdumpMesaj. - Bu iki nesne üzerinde de kodlayıcıyı oracle ile uyumlu hale getirir.
- Uzaklaştırma
recursive_descent()Yani indirek bir arama arama sitesi kayıtlarıcalls- Evet .NoneHedef olarak ve sonra devam eder. - Fonksiyon gösterge dizisini yer değiştirmeleri üzerinden okuyan ve olası calleleri rapor eden bir çözücü yazın.
- Fonksiyon raporuna callle ayarını olarak ekleyin.
indirectCallsHer hedef için kanıt türü ile. - Çözücü, bir işlev simgesi olmayan bir hedefi döndürdüğünde başarısız olan bir test yazın.
Başarılı olmak demektir .python3 code/main.py cfgHer olası kişiye, bunu kanıtlayan yer değiştirme ile birlikte isimler verir ve oracle karşılaştırması her nesneyi hala geçerli kılar.
Ders Artifakti
Bu ders de içerir .outputs/skill-disassembly-and-control-flow.mdBir nesne ve bir fonksiyon adını kontrol edilmiş bir liste, bloklar, yazılmış kenarlar, dominanterler ve her çözülmemiş aktarım haline getirmek için kullanın.
Kontrol et
Demo ve Deterministik Suite'i çalıştır:
bashcd phases/13-tools-and-protocols/33-disassembly-and-control-flow
python3 code/main.py
python3 code/tests/test_main.pyVerifikasyon, şunu kanıtlamalıdır:
- Dekodör kabul ediyor .
llvm-objdump -dDört sabitleme nesnesinin tüm 237 talimatında - REX.W 64 bit operandları seçer ve REX.R ve REX.B kayıt alanlarını genişletiyor
- ModRM ve SIB, ölçek, indeks ve imzalanan yer değiştirme dahil AT&T hafıza operandını gösterir
- RIP'ye göre bir yer değiştirme, talimatın içinde alan sıfırlaması ile kaydedilmiştir.
- Tekrarlanan eski öntanımlar ve bir bölüm üstü dekodör gibi oracle
- İhtiyaclı bir
66Önbellek SSE opkodunu seçer ve 32 bitlik kayıtları bırakır - 8-bit yakınları aritmetik için işaret uzanır ve saat sayımları için işaretsiz
- 15 byte üzerinde bir talimat, kısaltılmış bir talimat ve bilinmeyen bir opcode yükseltmesi
DecodeError R_X86_64_PC32Bir .leaBu konuda bir karar verildi.R_X86_64_PLT32Bir .call- Evet. - Evet.- Tarama, tuzak masasını kod olarak çözüyor ve atlama hedefi hakkında hiçbir talimat yok.
- Düşüş hiç masa veya yastık için talep etmez ve okuyor
addl $0x1, %eaxHedef llvm-objdumpTuzakta yapılan aramakla aynı hatayı yapar.- Bir talimatın içindeki bir giriş çatışma olarak kaydedilmiştir.
- çözülmemiş bir dolaylı sıçrayış düşüşü durdurur ve listelenir
total- ...-O1Beş blok, beş tiplenmiş kenar, bir arka kenar ve beklenen hemen üstteki baskıcılar- Bir döngü ile el yapımı bir elmas, algoritma tarafından tanımlanan baskıcıları verir.
total- ...-O2İki döngügrade- ...-O05 girişli bir atlama masası verir.rela.rodataHedeflerle0x91, 0x88, 0x7f, 0x76, 0x76- Masası olmadan.
grade- ...-O0Üç blok ve bir atlama. grade- ...-O1ve-O2değer tablosu[1, 2, 3, 4, 4]dolaylı atlama olmadangrade_label- ...-O25 yer değiştirme ile ilgili bir dizilme tablosunu okuyor- İ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 |
|---|---|---|
Data inside .text | A plausible listing with every byte decoded | The descent claims no data byte and the jump target starts an instruction |
| Padding after the last return | A nopw instruction at the end of the function | The bytes are unreachable from the entry |
| Indirect jump through a table | "jmp rax" and four dead blocks | Table address from the lea relocation, entry count from the bounds check, targets from .rela.rodata |
| Switch turned into a value table | "a jump table with a relocation" | A movl load from .rodata+0x14, three blocks, no indirect transfer |
| Call with a zero displacement | callq 0x113, the next instruction | R_X86_64_PLT32 grade-0x4 resolves to 0x30 |
| Decoder guesses an unknown opcode | A mnemonic that looks right | DecodeError with the address, and the sweep records the byte |
| Instruction longer than 15 bytes | A very long "instruction" | The cursor raises before reading byte 16 |
| Branch into the middle of an instruction | Two overlapping listings | A conflict record with both addresses |
| Loop reported as straight-line code | No while in the pseudocode | A back edge whose target dominates its source |
| Oracle captured from a different compiler | Tests pass on stale text | The fixture README names the exact compiler and command |
İşlem Kuralı
Bir oracle ile kodlama, bilinen girişlerden yürüyüş kontrol akışı ve her baytın iddia edilen, doldurma, veriler veya erişilemez olduğu hesaplama. Yer değiştirdiğiniz bir yerden veya gösterebileceğiniz bir tablodan her dolaylı hedefi kurtarın. Geri kalanı bilinmeyen olarak adresle kaydetin.
Daha Fazla Okumak
- Intel 64 and IA-32 Architectures Software Developer's Manual, Volume 2, Chapter 2: Instruction Format
- AMD64 Architecture Programmer's Manual Volume 3, Chapter 1: Instruction Encoding
- System V Application Binary Interface, AMD64 Architecture Processor Supplement, Table 4.9: Relocation Types
- Schwarz, Debray, Andrews, "Disassembly of Executable Code Revisited" (WCRE 2002)
- Andriesse et al., "An In-Depth Analysis of Disassembly on Full-Scale x86/x64 Binaries" (USENIX Security 2016)
- Allen, "Control Flow Analysis" (1970)
- Cooper, Harvey, Kennedy, "A Simple, Fast Dominance Algorithm" (2001)
- Lengauer, Tarjan, "A fast algorithm for finding dominators in a flowgraph" (1979)
- Capstone 6.0.0-Alpha10 release notes
- rea 6.5.0 kaynağı tag
3b79af2Bu ders şöyle anlatıyor:README.md- Evet .docs/ghidra-provider.md- Evet .docs/ghidra-dos.md- Evet .docs/reverse-engineering-tools.md- Evet .bridge/ghidra/ReaGhidraBridge.java- Evet .src/contracts/officialToolContracts.ts- Evet .src/ghidra/GhidraAnalysisProfile.ts- Evet .
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.