Phase 13: Tools & Protocols

Pseudocode'ye yükseltme: IR, SSA ve sıfırdan yapılandırma

Decompiler çıkışı gizli bir çıkışla bir iddia. Eğer çıkışın kendiniz oluşturduğunuzda, çıkışın her satırı kontrol edebileceğiniz bir şey haline gelir.

Type: Build

Languages: Python, C

Prerequisites: Phase 13 · 32 (ELF from scratch), Phase 13 · 33 (disassembly and control flow)

Time: ~150 minutes

Öğrenme Hedefleri

  • Lift, açık bayraklar, yükler, depolar, şubeler, aramalar ve geri dönüşler ile küçük bir IR'ye x86-64 talimatlarını çözmüştür.
  • Çerçeve boşluklarını yerel değişkenlere ve Sistem V argüman kayıtlarını parametrelere dönüştürün.
  • Yönetim sınırlarında phi fonksiyonlarıyla minimal SSA oluşturun, sonra ölü kodu değiştirin, basitleştirin ve kaldırın.
  • İyileşmekif- Evet .if- Evet .else- Evet .whilevedo whileKontrol akışı grafikinden, post-dominatörler ve arka kenarları ile.
  • çarpımı açıkla0x6666666710'a bölünerek kimliği kanıtlayın.
  • Aynı fonksiyon için Ghidra pseudokodu'nu satır satır okuyun ve her farkın arkasındaki talimatın veya tahminin adını verin.

Sorun

Rea ' nın pseudokodu için sordun .percent_changeİçerideledger-x86_64-O1.oGhidra 12.1.4 yaklaşık on saniye sonra cevap verdi:

cundefined1  [16] percent_change(int param_1,int param_2,ulong param_3)

{
  long lVar1;
  undefined1 auVar2 [16];
  undefined1 auVar3 [16];
  
  if (param_1 != 0) {
    lVar1 = (long)((param_2 - param_1) * 100);
    auVar3._0_8_ = lVar1 / (long)param_1 & 0xffffffff;
    auVar3._8_8_ = lVar1 % (long)param_1 & 0xffffffff;
    return auVar3;
  }
  auVar2._8_8_ = 0;
  auVar2._0_8_ = param_3;
  return auVar2 << 0x40;
}

Kaynak dört satır uzunluğunda.intdeğerler ve dönüşler 1 intGhidra, 16 baytlı bir geri dönüş değeri ve kaynakta bulunmayan üçüncü bir parametresi bildirir. Makine kodunun on altı baytı doğru ve Ghidra hepsini doğru şekilde çözmüştür. İmza bir tahmintir ve metinde hiçbir şey onu bir olarak işaret etmez.

rea bu metni olarak gönderir.procedure_pseudo_codeBu kısıtlama "Pseudocode Ghidra dekompiler çıkışı, orijinal kaynak değil ve Hopper çıkışı için metin eşdeğer değil" diyor.compare_functionsaraç metin satırları olarak pseudokod farklılık gösterir. Kendi boyut kaydı "Yüzelim açık olmayan sunucu metni" diyor ve yer değiştirme normalleşmesi kullanılamıyor. rea metin içine bakamaz ve sadece okuyan bir ajan da bunu yapamaz.

Rea'nın DX-Ball kaydı maliyetini gösteriyor.FUN_00406400(void)Aramak__ftol()Bu görüntüde bir yığın giriş ve x87 ifadesi gösterilmişti.

Bu ders, dekompilerin sakladığı derivasyonu oluşturur. Ders 33'ün çözdüğü 56 talimattan başlayarak onları IR'ye yükseltir, SSA'ya yeniden adlandırır, basitleştirir ve yapılandırır. Sonra pseudokodunu Ghidra'nın yanında yerleştirir.percent_change- Evet .gradevetotalTestler çıkışınızı clang ile bir giriş şebekesi üzerinden kaynağa karşı çalıştırır.

Çatırma ve Orakel

code/fixtures/ledger.cBu kursun ortak bir parçasıdır:

cint percent_change(int before, int after) {
    if (before == 0) return 0;
    return (after - before) * 100 / before;
}

int grade(int score) {
    switch (score / 10) {
    case 10:
    case 9: return 4;
    case 8: return 3;
    case 7: return 2;
    case 6: return 1;
    default: return 0;
    }
}

int total(const int *values, int count) {
    int sum = 0;
    for (int i = 0; i < count; i++) sum += values[i];
    return sum;
}

grade_labelBu ders, yukarıdaki üç işlevi test eder.

Nesne , clang -target x86_64-unknown-linux-gnu -O1 -fno-asynchronous-unwind-tables -c ledger.c- Ne ?-O0Nesne, yığın yuvası bölümünde yanındadır. code/fixtures/README.mdBu, her nesnenin SHA-256 ve Ghidra fixtures'i üreten rea komutlarını listeler.

Dekodörün oracüleri llvm-objdump -d -r. Her iki nesne için de metni ,ledger-x86_64-O1.objdump.txtve ledger-x86_64-O0.objdump.txt. Bir test bu dosyaları analiz eder ve her talimatı adres, byte ve AT&T metni ile karşılaştırır.-O1Ve her talimatın .-O0Ne zaman ?llvm-objdumpsabit yolda kurulur, ikinci bir test canlı olarak çalışır ve tekrar karşılaştırılır.

İşte burası .percent_changeOrakel yazdığı gibi:

text0000000000000000 <percent_change>:
       0: 85 ff                        	testl	%edi, %edi
       2: 74 09                        	je	0xd <percent_change+0xd>
       4: 29 fe                        	subl	%edi, %esi
       6: 6b c6 64                     	imull	$0x64, %esi, %eax
       9: 99                           	cltd
       a: f7 ff                        	idivl	%edi
       c: c3                           	retq
       d: 31 c0                        	xorl	%eax, %eax
       f: c3                           	retq

1. aşama: talimatlara ait baytlar

Ders 33'te dekodör oluşturuldu. bu ders küçük bir kopyayı tutar böylece kod kendiliğinden çalışır. kopyası tam olarak yayınlanan opkodları kapsar.ledger.c- ...-O0ve -O1Başka bir şey için de .DecodeErrorAdres ve opkod bayt ile.

Dekoder Intel SDM'nin bayt sırasını izler, 2. Cilt, bölüm 2.1. Önlükler önce gelir, sonra opcode, sonra ModR/M, sonra SIB, sonra yer değiştirme, sonra hemen. 2.2.1 Bölüm REX önlüğünü ekler, bir bayt 0x40- ...0x4FRex.W 64 bitli bir operand seçer, Rex.R regalanı, REX.X SIB endeksini genişletiyor ve REX.B genişletiyor r/m2.1.3 bölümünde üç ModR/M alanı tanımlanır.mod- Evet .regver/m, ve üç SIB alanı scale- Evet .indexvebase64 bit modunda.mod = 00- Evet .r/m = 101"Bit" (BIT) ifadesi, "BIT" ifadesi anlamına gelir.

Opcode bytesFormFixture example
01 29 31 39 85 89op Ev, Gv29 fe subl %edi, %esi
03 2b 3b 8b 8dop Gv, Ev03 04 97 addl (%rdi,%rdx,4), %eax
63 with REX.Wmovsxd Gv, Ev48 63 c7 movslq %edi, %rax
69 and 6bimul Gv, Ev, imm48 69 c0 67 66 66 66
83 /0 /5 /7add sub cmp Ev, imm883 c1 fa addl $-0x6, %ecx
c1 /5 /7shr sar Ev, imm848 c1 f9 22 sarq $0x22, %rcx
f7 /7idiv Evf7 ff idivl %edi
ff /0 /4inc Ev, jmp Ev48 ff c2 incq %rdx
70 to 7f, eb, e8jcc rel8, jmp rel8, call rel3274 09 je 0xd
99, c3, 90, 0f 1fcltd, retq, nop66 0f 1f 44 00 00 nopw (%rax,%rax)
  • Evet .leaq (%rip), %rcx- ...grade+0x23Bu nesne, yer değiştirme ile birlikte yer değiştirme ile birlikte.R_X86_64_PC32.rodata+0x10- Yapabilir miyim ?0x36Asansör 3. aşamada çözüyor.

İkinci aşama: Bloklar ve Yöneticiler

Temel blok, bir giriş üstte ve bir çıkış altta olan bir ifade dizisidir. Allen'ın 1970'deki tanımlamasıdır ve her dekompiler kağıdı onu tekrar kullanır. build_blocks()Bu sayede, bir blokun bir sonraki atlamasından sonra iki tane ve bir sonraki atlamasından sonra bir sonraki atlamasından sonra bir sonraki atlamasından sonra bir sonraki atlamasından sonra bir sonraki atlamasından sonra bir sonraki atlamasından sonra bir sonraki atlamasından sonra bir sonraki atlamasından sonra bir sonraki atlamasından sonra bir sonraki atlamasından sonra bir sonraki atlamasından sonra bir sonraki atlamasından sonra bir sonraki atlamasından sonra bir sonraki atlamasından sonra bir sonraki atlamasından sonra bir sonraki atlamasından sonra bir sonraki atlamasından sonra bir sonraki atlamasından sonra bir sonraki atlamasından sonra bir sonraki atlamasından sonra bir sonraki atlamasından sonra bir sonraki atlamasından sonra bir sonraki atlamasından sonra bir atlaması vardır.jmp- Bir tane var.retHiç bir tane yok ve sıradan bir talimatla biten bir blok bir sonraki liderin eline geçiyor.

total- ...-O1Beş bloklu:

BlockInstructionsSuccessors
0x40testl %esi, %esi then jle 0x5c0x5c, 0x44
0x44movl %esi, %ecx, xorl %edx, %edx, xorl %eax, %eax, nopw0x50
0x50addl (%rdi,%rdx,4), %eax, incq %rdx, cmpq %rdx, %rcx, jne 0x500x50, 0x5b
0x5bretqexit
0x5cxorl %eax, %eax then retqexit

immediate_dominators()Bu, Cooper, Harvey ve Kennedy'nin (2001) tekrarlayıcı algoritmasıdır. Bu blokları ters post-sıra ile yürür ve hiçbir şey değişene kadar işlenmiş öncekilerin baskıcılarını kesiyor.total- Öyle diyor.0x44Yöneticisi .0x50Ayrıca şöyle yazıyor:0x50Arka kenarından kendini kontrol eder, bu da 8. aşamada döngü bulma şeklini gösterir.

Aynı işlev iki kat daha fazla çalışır.dominance_frontiers()5. aşamada SSA için kullanıyor.post_dominators()8. aşamada birleştirmeler bulmak için sanal çıkış düğmesi ile ters çizilen grafikte çalışır.

3. aşama: Küçük IR'ye yükseltme

IR altı ifade türüne sahiptir: Assign- Evet .Store- Evet .Branch- Evet .Jump- Evet .IndirectJumpveReturn. İfadeler sabitler, değişkenler, genişliğe sahip ikili işlemler, atışlar (sext- Evet .zext- Evet .trunc5 şekil daha var ve daha sonra basitleştirilmiştir:Flags- Evet .Cond- Evet .Concat- Evet .SignFillveSymbolic- Evet .

Registerlar 64 bit ailesi tarafından adlandırılan değişkenlerdir.%eax- Evet .Assign- ...rax32 genişliğinde ve bir okuma ile %eax- Bu bir okuma.rax32 bit yazma biçiminde 32 bit yazma sıfır genişlemeyi tam kayıtlara uzatır, böylece aynı değerli daha sonraki 64 bit okuma bir zext. Asansör ulaşma tanımını bilmiyor, bu yüzden okuma genişliğini kaydeder ve SSA'nın 5. aşamada döküm yerleştirmesine izin verir.

InstructionIR statements
subl %edi, %esiflags = cmp(esi, edi) then esi = esi - edi
testl %edi, %ediflags = test(edi, edi)
je 0xdif je(flags) goto 0xd else goto 0x4
xorl %eax, %eaxeax = 0
cltdedx = signfill(eax)
idivl %edidividend = edx:eax, eax = dividend / edi, edx = dividend % edi
movslq %edi, %raxrax = sext64(edi)
sarq $0x22, %rcxrcx = rcx >>s 34
shrq $0x3f, %raxrax = rax >>u 63
addl (%rdi,%rdx,4), %eaxflags = add(eax, load32(rdi + rdx * 4)) then eax = eax + load32(rdi + rdx * 4)
leaq (%rip), %rcx with its relocationrcx = &.rodata+0x14
pushq %rbp, movq %rsp, %rbp, subq $0x10, %rsp, popq %rbpnothing, frame bookkeeping
retqreturn rax

Bayraklar değişken

Şart kodlarını belirleyen her talimat bir değişkenin adı yazılır flags, ve her şartlı sıçrama onu okuyor.cmp a, bYazar cmp(a, b), a test a, bYazar test(a, b)vesubAynı şeyi yazıyor.cmpBu, bir yan etkisi olarak şekillendirildi.jccBranch(Cond(cc, flags), target, fallthrough)Şart kodu, 6 aşamada iki bölünme karşısında karşılaştırma operatörünü belirler:

CodeAfter cmp(a, b)After test(a, a)
jea == ba == 0
jnea != ba != 0
jl, jge, jle, jgsigned <, >=, <=, >signed compare with 0
jb, jae, jbe, jaunsigned <, >=, <=, >unsigned compare with 0

xorl %eax, %eaxeax = 0Bayraklar şöyle yazıyor:addBu şekilde bir atlama yapmadan bayraklar çizer.xorİdman meselesi gradeGhidra'nın kaybettiği yer.

Ctd ve IDEV

cltddolduruyor .%edx işaretli bir parça ile%eax- Evet .idivl %edi64 bit çiftini bölüyor %edx:%eax- ...%edi, oranı içeriyor %eax, ve kalanı %edxAsan çiftleri geçici bir isimle yazıyor .dividendÖnce kalan ifade, sonra eski%eaxSSA'da yüksek yarısı,signfillBasitleştiricisi çiftin aşağı yarısına düşürülmesini sağlar ve bölünme 32 bit olur./- Evet .

-O0'da yığın boşlukları

  • Evet .-O0Clang, tüm yerelleri çerçeveye sokar.percent_changemağazalar %edi- ...-0x8(%rbp)ve %esi- ...-0xc(%rbp)Sonra her kullanımı için tekrar okuyor.pushq %rbpArdından movq %rsp, %rbpO prologun ardından, bir hafıza operandı ve bir taban.%rbp, indeks yok ve bir yer değiştirme adı verilen değişken haline gelir.local_<hex>- Evet .-0x8(%rbp)- Evet .local_8- Evet .-0xc(%rbp)- Evet .local_c- Evet .

Çerçeve talimatları bile IR üretmez ve ne de subqve addq- Evet .%rspBaşka bir hafıza operandı bir Loadveya Store- Evet .

Yer değiştirme

  • Evet .leaq (%rip), %rcxİçeridegradeDosyada sıfır yer değiştirme var.0x36diyor kiR_X86_64_PC32.rodata+0x10Bağlantı yazar .S + A - PSahaya çıkın.Sadresidir .rodata- Evet .AEk olarak 0x10vePBu, alanın adresidir.next_ip + disp = (P + 4) + S + A - P = S + A + 4- Evet .

Yükselticisi aynı aritmetikleri bir bağlantı olmadan uyguluyor.0x10 + 4 = 0x14- İN diyor ki:rcx = &.rodata+0x14Ve orada masa beş kişiyi tutuyor .intdeğerler1, 2, 3, 4, 4- Evet .

4. Adım: Çağırış Anlaşmasından Parametre

Sistem V AMD64 psABI, bölüm 3.2.3, INTEGER sınıf argümanlarını %rdi- Evet .%rsi- Evet .%rdx- Evet .%rcx- Evet .%r8- Evet .%r9Sonuç şu sırada gelir.%raxVe sonra ...%rdx3.2.1 bölümünde şöyle yazıyor:%rbp- Evet .%rbxve%r12- Evet .%r15Çağırana ait ve korunmalıdır.

collect_params()Bu kuralın bir kısmı vardır: fonksiyonda herhangi bir yazmadan önce okunur bir argüman kaydı bir parametre.%rsiVe asla .%rdiHâlâ iki parametre var.percent_changeokur %edive %esi- Evet , öyle .param_1ve param_2- Evet .gradeSadece okur.%edi- Evet .totalokur %rdi64 bitlik bir taban olarak ve %esi32 bit sayımı olarak.

Şimdi Ghidra'nın param_3Bir açıklaması var.idivlYazar %rdx, ve psABI 128 bitli INTEGER geri dönüşünü sağlar %rax:%rdxBir nesne dosyasında, geri dönüş değerinin ne kadar geniş olduğunu söyleyen bir aramacı yoktur. Ghidra geri dönüş değer olarak her iki kayıtı da tutmuştur.

Zira yol üzerinde,xorl %eax, %eaxSadece yazıyor.%eaxGhidra okudu .%rdxBu işlevden bir giriş olarak, bu giriş param_3Bu imza, talimatların açık bıraktığı parçaları tahmin ediyor.

5. aşama: SSA, tanım başına bir isim

  • Yukarı kaldırıldıktan sonra.gradeYazar rax4 kez vercxÜç kez. Cytron, Ferrante, Rosen, Wegman ve Zadeck (1991) statik tek görev biçimini tanımlar. Her değişken tam olarak bir kez atfedilir ve phi fonksiyonları, onlara ulaşan tanımlar arasında seçilir. İnşaatları iki aşamalı. Bölüm 5.1'de, pi fonksiyonları egemenlik sınırlarını kullanarak yerleştirir ve bölüm 5.2'de her tanım ve kullanımı egemenlik ağacının üzerinde bir yürüyüş ile yeniden adlandırır.insert_phis()ve rename()Bu iki bölümü takip edin.

SSA'nın listesi totalİnşaatın ne ürettiğini gösterir:

textblock 0x44 -> 0x50
  ecx_1 = esi_0
  edx_1 = 0
  eax_1 = 0
block 0x50 -> 0x50, 0x5b
  eax_2 = phi(0x44: eax_1, 0x50: eax_3)
  rdx_2 = phi(0x44: edx_1, 0x50: rdx_3)
  flags_2 = phi(0x44: flags_1, 0x50: flags_5)
  flags_3 = add(eax_2, load32(rdi_0 + rdx_2 * 4))
  eax_3 = eax_2 + load32(rdi_0 + rdx_2 * 4)
  flags_4 = inc(rdx_2, 1)
  rdx_3 = rdx_2 + 1
  flags_5 = cmp(zext64(ecx_1), rdx_3)
  if jne(flags_5) goto 0x50 else goto 0x5b

Bir rejistrin 0 versiyonu, giriş sırasında değeridir. esi_0ve rdi_0- Bu da bir simge.0x50Bu da bir değişkenin bir fi olduğunu gösterir.flags_2Phi, geldiğinde ölü.flagsHer iki önceki aşamada da tanımları var. 6. aşamada da hiçbir şey okumayı nedeniyle kaldırılır.

Ad değiştirme adımı genişlikleri de düzeltir. edx_1 = 032 bit tanımlı bir rdx_3 = rdx_2 + 164 bit, bu yüzden phi rdx_2Daha geniş bir alanı alır.%rcxİçeridecmpq %rdx, %rcx64 bitken tanımlaması ecx_1 = esi_032 bit, bu yüzden kullanımı zext64(ecx_1)Son pseudokoddaki her oyuncu bu tek kuralı takip ediyor.

"Lite" kısmı SSA'dan çıkış yolu.coalesce_webs()Bu, bir kompiler tarafından bu işlevler için yayılan şekiller için çalışır. Birlikte iki phis değişim değerlerini birleştirirken paralel kopyalara bölünebilir ve ders bu durumu ele almıyor. Cytron'un 7. bölümü renkleme yoluyla genel çıkışı tanımlar.

6. Adım: Basitleştir, Değiştir, Ölü Kodları Kaldır

optimize()Basitleştiricisi ifadeleri aşağı yukarı aşağıdaki kurallarla yeniden yazar:

BeforeAfter
Cond(je, cmp(a, b))a == b, and the same for the other nine codes
Cond(jle, test(a, a))a <= 0
x + (-6)x - 6
trunc32(zext64(x)) where x is 32-bitx
trunc32(a + b) on 64-bit operandstrunc32(a) + trunc32(b)
(signfill(x): x) / dx / d at width 32
load32(base + idx * 4)base[idx]
trunc32(p >>s 34) + trunc32(p >>u 63) with p = sext64(x) * 0x66666667x / 10

Önceki değiştirme bir tanımı tek kullanımına taşıyor. Kural üç şartı vardır. Kullanım aynı blokta. Kullanım bir ifade ve bir phi değil. Defini bir yük içerdiğinde, tanım ve kullanım arasında hiçbir depo veya çağrı bulunmaz. Bir sabit veya bir bölüm adresi phi dışında her yerde değiştirilir, çünkü bir phi argümanı web için bir değişken kalmalıdır.

Ölü kod kaldırmak, bir görev veya bir phi'yi, hedefinin kullanılmaz olduğu ve ifade edilmesi çağrıda bulunmayan bir görev silmektedir.percent_changeGeri kalanı .edx_2 = dividend_1 % edi_0Ölüdür, yani önce gidiyor.edx_1 = signfill(eax_1)bir kullanım için kullanılır, böylece dividend_1 = edx_1:eax_1- Evet .

Çift kuralı sonra azaltır signfill(eax_1):eax_1- ...eax_1, ve bölünme eax_1 / edi_0- Şimdi .eax_1 = esi_1 * 100tek bir kullanım ve esi_1 = esi_0 - edi_0Bir kullanım var ve her ikisi de geri dönüş bildirimine geçiyor.flagsBu nedenle, bu kısımlar, bir dizi metin olarak kullanılır.

creturn (param_2 - param_1) * 100 / param_1;

0x66666667 çarpması

gradeNotu 10 ile bölüyor ve clang hayır yayıyor.idiv- Çıkıyor .imulq $0x66666667, %rax, %raxO zaman ...sarq $0x22, %rcxBir kopyasına, o zaman shrq $0x3f, %raxGranlund ve Montgomery, "Multiplikasyon kullanarak değişmez tam sayılarla bölme" (PLDI 1994), yöntemi tanımlar.mVe bir vardiya.sHer biri için .xAralıkta,(x * m) >> sx / d- Evet .

İşte .m = 0x66666667 = 1717986919ve s = 34- Matematik doğrular:2^34 / 10 = 1717986918.4Ve tavan 1717986919Hata şu .m * 10 - 2^34 = 6Kimlik her 32 bit üzerinde kalsın .x, hata zamanları 2^31Aşağıda kalmak zorunda 2^34Bu yüzden hata en fazla 8 olmalıdır.(x * m) >> 34eşit floor(x / 10)- Nefi olmayan her şey için .int- Evet .

Negatif içinxzemin sıfırdan uzaklaşırken C sıfıra doğru döner. shrq $0x3fürünün işaret bitini çıkarır, bu da tam olarak 1 olduğunda x- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

magic_divisor(m, s)- Bir aday çiftini test eder.d'nin tam sayı oranı olarak2^s- ...mVe dener.dve d + 1- Bir dSadecem 2^s / dve hatalı bağlama geçerli.s = 33ver .d = 5, bu doğru, çünkü 0x66666667Aynı zamanda 2^33 / 5- Kimliği kontrol eden bir test .c_div(x, 10) intAralık.

Tanınma devam eder.gradeokur ecx_1 = param_1 / 10 - 6;Tanımayı kapatırken demo çiğ şekli basıyor:

crax_1 = (long)param_1 * 0x66666667;
ecx_1 = (int)(rax_1 >> 34) + (int)((unsigned long)rax_1 >> 63) - 6;

Her ikisi de aynı değerleri oluşturur ve hesaplar. Şalter tutun. İncelemediğiniz bir kompiliörden kod okuduğunuzda, ham biçim dürüst bir biçimdir.

7. Adım: Kullanım Tipleri

Makine kodunun genişliği var ve türleri yok.infer_types()Bu, üç tür kanıtı C türüne dönüştürür.int, ve 64 bitli bir web longBir element erişimi için temel olan bir web .base[idx]4 baytlı elementler ile int *. işaretçi tipi kopyalar ve fişler boyunca yayılır, bu yüzden local_8 = param_1- ...-O0İkisini de yapar.int *. Geri dönüş tipi değerin genişliğini returnAçıklamalar.

Sonuç:total- Evet .int total(int *param_1, int param_2)Ghidra yazdı .int total(long param_1,uint param_2)İkisi de talimatlara uymuş.Lifter, ölçülü yükü işaretçi olarak kullandı.Ghidra kayıt genişliğini korudu ve girişini şöyle yazdı:*(int *)(param_1 + uVar2 * 4)- Göremiyorum .const, ve hiçbirisi bilemez .int *-unsigned *- Evet .

Lee, Avgerinos ve Brumley (TIE, NDSS 2011) çerçeve tipi kurtarma bir red üzerinde kısıtlama çözümü olarak. Sonuç bir tahmin yerine üst ve alt sınırdır. Noonan, Loginov ve Cok (Retypd, PLDI 2016) polimorfizm ve geri dönüşü ekler.constBu ders, bu iş çizgisinden en ucuz iki kural kullanıyor ve göremediği şeyleri adlandırabiliyor.

İmza edilmemiş aritmetik, değişken türlerde değil ifadelerde tutulur. jaSonra .cmpl $0x4, %ecx gibi izler(unsigned int)ecx_1 > 4veshrq gibi izler(unsigned long)rax_1 >> 63Değişkenler imzalanmış kalır, böylece bir okuyucu kayıt değerini ve talimatın imzalanmamış olarak ele aldığı tek yeri görür.

8. Adım: Grafikten Yapılandırma

Cifuentes (1994), 6. bölüm, grafikin bölgelerini yüksek düzeyde bir dil şekillerine karşı eşleştirir. İlk olarak döngüler (6.6.1), sonra iki yönlü koşullar (6.6.2), sonra n yönlü koşullar (6.6.3). Yakdan, Eschweiler, Gerhards-Padilla ve Smith (NDSS 2015) diğer yoldan geçiyorlar. Her düğümün uygulandığı ve yapıdaki yapıdaki koşulları, sabit şekiller olmadan oluşturduğu koşulları çıkarırlar. Onların motivasyonu bir ölçümdür. Bir örnek üzerinde 1571 adet Hex-Rays üretildi.goto49.514 satırdaki açıklamalar, 32 satırda bir, ve yapılandırmaları 1.530 temel fonksiyonlarda hiçbirini üretmedi.

structure()Bir blokun hemen sonrası dominantoyu, bir blokun sonuna kadar iki gerçek gerektirir.BranchBir arka kenar, kaynağını ele alan bir blokun kenarıdır. Doğal döngü, başlığı geçmeden arka kenar kaynağına ulaşan blokların bir kümesidir.

Bir .Branchkatlanmaklaj- ...

  • - EğerjAtlama hedefi, yayınif (!cond) { fallthrough region }Ve devam et .j- Evet .
  • - EğerjBu, düşüşün, yayımlamanın bir yolu.if (cond) { target region }Ve devam et .j- Evet .
  • - EğerjBu da bir işlev çıkışı, her iki kol geri döner.if (cond) {... }Ve diğer kolun arkasından, bir elseBu erken dönüş .percent_changeve total- Evet .
  • Aksi takdirde yayın .if (cond) { target } else { fallthrough }Ve devam et .j- Bu ...percent_change- ...-O0, her iki silahın da depoladığı yer .local_4Ve epilogta buluşuruz.

Bir döngü başlığı için:

  • Başlık bir ile sona erirseBranchBir halefiyle, döngü dışında, yayınwhile (cond) { body }- Evet .total- ...-O0Şöyle şekillidir: başlık karşılaştırır local_14- Evet .local_cve yapraklar devam ediyor.jge- Evet .
  • Eğer başlık aynı zamanda arka kenarlı bloksa veya kilit çıkışları ise, yayın do { body } while (cond);- Evet .total- ...-O1Bu şekli: blok0x50ekler, artır, karşılaştırır ve kendi kendine atlar.

Bir IndirectJumpBir blokta hiçbir halefi yok.grade- ...-O0Gerçek bir atlama masası.jmpq *%rax5 nispeten karşılaştırma yoluyla .rodata+0x0Bu yüzden, bu noktada, bir tane daha yazıyor.0x76- Evet .0x7f- Evet .0x88ve0x91Bu, sadece dolaylı atlama yoluyla ulaşılabilir ve kaldırılmamıştır.switch- Evet .

-O1 anahtarı ne yaptı?

  • Evet .-O1Clang için bir atlama masası tutmadı .grade- Bilgisayarı .score / 10 - 6Sonra 5 girişli bir tabloyla geri dönüş değerini yükledi.int- ....rodata+0x14İmza edilmemiş bir karşılaştırma , her ikisini de kapsayan bir talimat .score / 10 < 6ve score / 10 > 10Negatif bir fark büyük bir imzalanmamış değerdir, bu yüzden aynı kontrolden geçerli değildir.

Masada kalır .1, 2, 3, 4, 46 ila 10 oranları için switchBeş kişiyle.returnAçıklamalar bir ifAsansör tam olarak bunu yayıyor ve masanın adını veriyor.lookup_14Bölümünün ödenmesinden sonra.

Masanın uzunluğu heuristiktir.table_values()Masa başından sonraki sayfaya kadar okuruyor .rodataBu kadarını karşılaştır ..textBu nesneye göre, sonraki referanslı ofset 0x28, tablograde_label- Evet .lookup_14- Evet .(0x28 - 0x14) / 4 = 5Ghidra'nın uzunluğu da bilmiyor.*(uint *)(&DAT_001000b4 + (ulong)uVar2 * 4), bir byte adresi artı bir ofset, ve okuyucu için bakmak için beş değer bırakın.

Ghidra'yla Birlikte

Demo her çiftin baskılarını yapar.code/fixtures/ghidra/*.json... 2026-10-14'te geri döndüğünü gösteren kanıt kayıtları.npx -y rea-agents@6.5.0 decompile ledger-x86_64-O1.o <function> --provider ghidra --format jsonHer kayıtta bir tane var .confidence: observedve authority: shipped-artifactMetin gözlemlenir. metin içindeki iddialar çıkarılır ve kayıtlarda hangileri belirtilmez.

%_değişim

textlifter                                         | ghidra
-----------------------------------------------+-----------------------------------------------
int percent_change(int param_1, int param_2)   | undefined1  [16] percent_change(int param_1,int param_2,ulong param_3)
{                                              | {
  if (param_1 == 0) {                          |   long lVar1;
    return 0;                                  |   undefined1 auVar2 [16];
  }                                            |   undefined1 auVar3 [16];
  return (param_2 - param_1) * 100 / param_1;  |   if (param_1 != 0) {
}                                              |     lVar1 = (long)((param_2 - param_1) * 100);
                                               |     auVar3._0_8_ = lVar1 / (long)param_1 & 0xffffffff;
                                               |     auVar3._8_8_ = lVar1 % (long)param_1 & 0xffffffff;
                                               |     return auVar3;
                                               |   }
                                               |   auVar2._8_8_ = 0;
                                               |   auVar2._0_8_ = param_3;
                                               |   return auVar2 << 0x40;
                                               | }
DifferenceInstruction or guess
Return type undefined1 [16] against intGuess. idivl writes %rax and %rdx, and no caller fixes the return width. Both registers were kept.
param_3Guess that follows from the first. On the zero path only %eax is written, so %rdx is read as an input.
lVar1 / (long)param_1 & 0xffffffff against / param_1Instruction. cltd plus idivl is a 64-bit division of a sign-extended 32-bit value, truncated to 32 bits. Both texts describe it. Ours reduced the pair with the signfill rule.
lVar1 % (long)param_1 keptFollows from the return type. The remainder in %rdx is dead in our analysis and alive in Ghidra's.
if (param_1 != 0) with the zero case after itInstruction order. je 0xd jumps to the zero return. Ghidra put the computed arm first, we put the smaller arm first. Both are correct.
return auVar2 << 0x40Guess. A 16-byte value with param_3 in the low half, shifted into the high half, is how the decompiler expresses %rax = 0 with %rdx unknown.

Düzeltilen versiyon .code/fixtures/ghidra/ledger-x86_64-O1.ghidra-repaired.cBu test sadece bu işlevi düzenler ve test 81 giriş çiftinin kaynağı ile karşılaştırılır.

sınıfı

textlifter                                         | ghidra
-----------------------------------------------+-----------------------------------------------
static const int lookup_14[5] = {1, 2, 3, 4, 4}; | uint grade(int param_1)
int grade(int param_1)                         | {
{                                              |   uint uVar1;
  int eax_1;                                   |   uint uVar2;
  int ecx_1;                                   |   
                                               |   uVar2 = param_1 / 10 - 6;
  ecx_1 = param_1 / 10 - 6;                    |   uVar1 = -(param_1 >> 0x1f) ^ (uint)((ulong)((long)param_1 * 0x66666667) >> 0x3f);
  eax_1 = 0;                                   |   if (uVar2 < 5) {
  if ((unsigned int)ecx_1 <= 4) {              |     uVar1 = *(uint *)(&DAT_001000b4 + (ulong)uVar2 * 4);
    eax_1 = lookup_14[(unsigned long)(unsigned int)ecx_1]; |   }
  }                                            |   return uVar1;
  return eax_1;                                | }
}                                              |
DifferenceInstruction or guess
param_1 / 10 - 6 in bothInstruction. Both recognized the 0x66666667 idiom.
uVar1 = -(param_1 >> 0x1f) ^ (uint)(... >> 0x3f) against eax_1 = 0Instruction xorl %eax, %eax at grade+0x1a. The register is zero. Ghidra expressed the xor as the sign of param_1 exclusive-or the sign bit of the product, two spellings of the same bit, and did not cancel them. The value is 0 for every int, and the test proves it over 141 scores. A reader who trusts the text believes the default depends on the score.
uVar2 < 5 against (unsigned int)ecx_1 <= 4Instruction cmpl $0x4, %ecx then ja. Same condition. Ghidra typed uVar2 as uint, we cast at the use.
*(uint *)(&DAT_001000b4 + (ulong)uVar2 * 4) against lookup_14[...]Instruction plus relocation. Ghidra loaded the object with .rodata at 0x001000a0, so 0x001000b4 is .rodata + 0x14. It applied the relocation and kept a byte address. We named the table and read its five values.
uint return against intGuess on both sides. The instructions only say 32 bits.

toplam

textlifter                                         | ghidra
-----------------------------------------------+-----------------------------------------------
int total(int *param_1, int param_2)           | int total(long param_1,uint param_2)
{                                              | {
  int eax_1;                                   |   int iVar1;
  int ecx_1;                                   |   ulong uVar2;
  long rdx_1;                                  |   
                                               |   if (0 < (int)param_2) {
  if (param_2 <= 0) {                          |     uVar2 = 0;
    return 0;                                  |     iVar1 = 0;
  }                                            |     do {
  ecx_1 = param_2;                             |       iVar1 = iVar1 + *(int *)(param_1 + uVar2 * 4);
  rdx_1 = 0;                                   |       uVar2 = uVar2 + 1;
  eax_1 = 0;                                   |     } while (param_2 != uVar2);
  do {                                         |     return iVar1;
    eax_1 = eax_1 + param_1[rdx_1];            |   }
    rdx_1 = rdx_1 + 1;                         |   return 0;
  } while ((unsigned long)(unsigned int)ecx_1 != rdx_1); | }
  return eax_1;                                |
}                                              |
DifferenceInstruction or guess
int *param_1 against long param_1Inference on our side from addl (%rdi,%rdx,4), %eax. Ghidra kept the width.
uint param_2 and (int)param_2 against int param_2Ghidra chose unsigned because cmpq %rdx, %rcx compares the zero-extended count. We kept int and cast at the compare.
do { } while in bothInstruction jne 0x50 from the block to itself. The compiler rotated the loop. The -O0 object keeps the while.
ecx_1 = param_2 kept against param_2 != uVar2Our substitution rule stays inside one block. The copy movl %esi, %ecx is in the setup block and the compare is in the loop, so the variable stays. Ghidra propagated it.
(unsigned long)(unsigned int)ecx_1Instruction. movl %esi, %ecx zero-extends, and the 64-bit compare reads the full register.

Davranış testi Ghidra metnini oluşturur.gradeve totalDeğişmemiş.typedefSıkıntılı bir şekildeDAT_001000b420 byte'den .rodata+0x14Her iki fonksiyon da kaynakla eşleşir.

Araştırmalar Pseudocode Okuması Hakkında Ne Söyler

LLM4Decompile (EMNLP 2024) Ghidra'yı tek başına HumanEval-Decompile'de yüzde 20.12 yeniden uygulanabilirliğe değerlendirdi. Beş işlevden biri, referans değerinin yanında gelen iddiaları yeniden birleştirdi ve geçti. Ghidra'nın 6.7B modelleriyle üretimini iyileştirmek bu oranı yüzde 52.74'e çıkardı. DecompileBench (Findings of ACL 2025) 130 programdan 23.400 işlevi üzerinde altı endüstriyel dekompiliyer ve altı LLM yaklaşımı yürütmüştür. LLM sonuçları daha iyi okundu ve %52,2 daha düşük bir işlevsel doğruluk gösterdi.

Liu, Raff ve Micinski (arXiv 2609.05370, Eylül 2026) boşluğu açıklar. GitHub kütüphanesi fonksiyonları 300 ve CVE tabanlı fonksiyonlar 287 kullanmışlar. En güçlü rafine modelinde Ghidra'nın inşa oranı yüzde 75'ten yüzde 90'a yükseldi. Davranışlı eşleşme oranı ise yüzde 74'ten yüzde 62'ye düştü. Benchmark'daki her sınavı geçen adaylar hala karışık girişler konusunda farklılık gösterdi. Bu, sekiz sistemdeki vakaların yüzde 4,9'unda ve bir sistem için yüzde 13'e kadar oldu. Açıklanan güvenlik açığıların onundan biri kadar, daha iyi kodlardan kayboldu.

Bu tür türde yeni yapılmış alanlar, türler, kalleler ve koruyucular, yazıda dekompilerci tarafından bırakılan görünen bilinmeyen şeyleri değiştirdi.

Bu yüzden asansör izleri .(unsigned long)(unsigned int)ecx_1Daha güzel bir isim yerine bir not yazdırıyor.switchçözülmemiş atlama için velookup_14Bir model etiket yerine bir etiket icat eder. her satırın okuyabileceğiniz bir türev vardır.code/main.pyGhidra'nın.undefined1 [16]- Güzel bir model temiz.int percent_change(int, int)Aynı model bir sonraki işlevi temiz yanlış imza üretir.

Yapın

Demo çalıştır:

bashcd phases/13-tools-and-protocols/34-lifting-to-pseudocode
python3 code/main.py

Demo , -O1Oturma ve her talimatı , yapılmış olanlarla karşılaştırır .llvm-objdump- Mesaj için.percent_change- Evet .gradevetotalSSA'nın önüne IR'yi, phi fonksiyonları olan SSA listesini ve Ghidra metninin yanındaki pseudokodu yazdırır.gradeBölüm tanıma kapatıldığında, -O0percent_change- Evet .totalvegrade_label, ve bir özet:

json{
  "functions": ["grade", "grade_label", "percent_change", "total"],
  "instructions": 56,
  "blocks": {"percent_change": 3, "grade": 3, "total": 5, "grade_label": 4},
  "phis": {"percent_change": 0, "grade": 1, "total": 2, "grade_label": 1},
  "objdumpDifferences": 0
}

Oku code/main.pyBu sırada:

  1. read_object()Bölüm başlıklarını, sembol tablosunu ve .relaELF64 yer değiştirilebilir bir nesnenin bölümleri.
  2. decode_one()ve render_att()Bir talimatı çözüp , doğru şekilde yazdır .llvm-objdump- Evet.
  3. build_blocks()- Evet .immediate_dominators()vedominance_frontiers()Grafik gerçekleri oluşturun.
  4. lift_insn()Bir talimatı IR açıklamalarına dönüştürür ve yer değiştirmeleri çözür.
  5. insert_phis()ve rename()SSA'yı oluşturup genişlik dökümlerini ekle.
  6. make_simplifier()- Evet .substitute_once()veremove_dead_code()İçeri koş .optimize()- Evet .
  7. collect_params()- Evet .coalesce_webs()veinfer_types()Ağların adını ve yazısını.
  8. structure()- Evet .post_dominators()ve natural_loops()C vücudu yayıyor.
  9. lift_function()aşamaları bir araya getirir velift_all()Bir nesneyi Callee imzalarıyla kaldırır.

Kullan

Bir fonksiyonu kendi nesnenin üzerinden kaldırmak için, onu kullanın.clang -target x86_64-unknown-linux-gnu -cVe aramak .lift_all(read_object(path))- Dekodör yüklendiğinde .DecodeError, mesajdan opcode byte okuyun ve SDM opcode haritasında bul. Sonra dosyanın üstündeki tablolara bir giriş ekleyin.lift_insn()- Yapımcı bir goto, grafikleri basın IrPrinterHer eklem küçük ve her birine karşı kontrol edilir.llvm-objdumpYa da mevcut testler tarafından çığlık atmak.

Ghidra yüklendiğinde, aynı işlevi çalıştırın ve iki metni yan yana koyun. Herhangi bir metneye inanmadan önce fark tablosunu doldurun. Arkasında bir talimat olan bir fark sunum seçeneğidir. Arkasında bir talimat olmayan bir fark bir tahmindir ve tahmin her iki taraftan da olabilir.

İnteraktif Laboratuvar

Laboratuvar A: beş aşamada bir fonksiyon

  • Evet .codeDizin, açık Python:
bashcd phases/13-tools-and-protocols/34-lifting-to-pseudocode/code
python3 -q

Çık:

pythonfrom main import *
obj = read_object(FIXTURES / "ledger-x86_64-O1.o")
lifted = lift_function(obj, "percent_change")
for insn in lifted.decoded: print(f"0x{insn.address:x}: {insn.raw.hex(' ')}  {insn.att()}")
print(lifted.ir_text)
print(lifted.ssa_text)
print(lifted.pseudocode)

Takip edin .%esi-subl %edi, %esi- ...esi_1 = esi_0 - edi_0- ...(param_2 - param_1)- Bayt .fe modr/msubl, ilemod = 11- Evet .reg = ediver/m = esi- Evet .

Laboratuvar B: Bölüm dilini

pythonprint(lift_function(obj, "grade", recognize_division=False).pseudocode)
print(magic_divisor(0x66666667, 34), magic_divisor(0x66666667, 33), magic_divisor(0x66666667, 31))

İlk satır çiğ çarpımı ve değişimleri basar.10 5 NoneAynı çarpıcının neden bir şifte az olan 5 ile bölündüğünü ve neden 31 şifte hata sınırını çiğnediğini öğrenin.

Laboratuvar C: Yeni bir nesne dekodörü durdurur

-O2Ve kaldır:

bashclang -target x86_64-unknown-linux-gnu -O2 -fno-asynchronous-unwind-tables -c fixtures/ledger.c -o "$TMPDIR/ledger-O2.o"
python3 -c 'from main import *; from pathlib import Path; import os; lift_all(read_object(Path(os.environ["TMPDIR"]) / "ledger-O2.o"))'

Dekodörün durması 0x56- Evet .opcode 81 is outside the fixture subset- Opcode .8132 bitli bir anlık form olan grup 1 formudur.andl $0x7ffffff8, %edx, ve Clang onu vektörlü döngüde kullandı total- Yanına ekle .0x83İçeridedecode_one()Alt kümesi bir seferde bir giriş büyütür ve her giriş karşılaştırılır llvm-objdump- Evet .

Laboratuvar D: Ghidra'nın standart değeri derecede

pythonprint(ghidra_pseudocode("grade"))
for score in (-25, 0, 59, 60, 100, 115):
    sign = -(score >> 31)
    product_sign = ((score * 0x66666667) & ((1 << 64) - 1)) >> 63
    print(score, sign ^ product_sign)

Her satırda 0'luk izler. Ghidra'nın uVar1ifade doğru ve okunamaz ve talimat xorl %eax, %eaxİki baytta yazıyor.

Pratik Laboratuvar

Ekleswitchgrade- ...-O0- Evet .

Gereksinimler:

  • Beş yer değiştirmeyi okuyun ..rela.rodataledger-x86_64-O0.o, her biriR_X86_64_PC32.text+offset, ve 5 dava hedefini hesaplayın ..rodata+0x0- Evet .
  • Bir ekleSwitchEndeks ifadesi, hedef listesi ve öntanımlı jaMasanın yüklenmesinden önce.
  • Sıkıştırıcı blokuna hedef başına bir halefi verin böylece dominanterler, post-dominatorlar ve SSA çalışmaya devam etsin.
  • Çıkar .switch (local_10) { case 0:... }Bir taneyle.caseBirleştirilmesinden sonra hedef ve ortak epilog.
  • Masasını bağlayamayan herhangi bir indirek at için not tut.
  • Sonuçları clang ile oluşturun ve testlerde puan çizelgesinde kaynağı ile karşılaştırın.

Başarılı olmak demektir .grade- ...-O0ve grade- ...-O1Aynı çıkışın ağ üzerinde üretilmesi, bir switchVe hayır .goto- Evet .

Sanatlı

Bu ders bize çok yararlı .outputs/skill-lifting-to-pseudocode.md. Her satır için bir türev ile bir işlevi kaldırmak ve bir dekompiler metnini karşı fark tablosunu doldurmak için kullanın.

Kontrol et

Demo ve Deterministik Suite'i çalıştır:

bashcd phases/13-tools-and-protocols/34-lifting-to-pseudocode
python3 code/main.py
python3 -m unittest discover -s code/tests -v
  • Suit 25 testle oluşuyor.
  • Dekodör, sözleşmeyi kabul eder.llvm-objdumpHer iki nesneyi de bir canlı ile yazıyor.llvm-objdumpkurulduğunda
  • REX, ModR/M, SIB ve leaq (%rip), %rcxSDM'de açıklandığı gibi kodlandırılır
  • Alt kümeden dışarıda bir opkod yükseltir DecodeErrorTahmin etmek yerine
  • ELF okuyucuunda dört işlevi ve üç işlevi listelenir .textYer değiştirme
  • totalBeş bloklu bir yer.0x44Yöneticisi .0x50ve0x50Kendisiyle bir döngü başlığı olan bir vücut
  • %rdive %rsiOlmakparam_1ve param_2veparam_1İçeridetotal- Evet .int *
  • testve cmp- Evet .je- Evet .jle- Evet .javejgeOlmak==- Evet .<=İmza edilmemiş .>ve<
  • SSA iki canlı phi fonksiyonunu ın döngü başlığına yerleştirir .totalVe hiçbiri .percent_change
  • percent_change- Evet .gradevetotalbeklenen metin ile eşittir
  • gradeokur param_1 / 10 - 6Tanınmış ve çiğ çarpı tanınmamış olarak
  • 0x6666666734'ün 10'a bölünmesi, 33'ün 5'e bölünmesi ve kimlik ağda kalması
  • -O0Olmaklocal_4- Evet .local_8- Evet .local_c, ve çerçeve işaretçisi asla görünmez
  • .rodata+0x14okur 1, 2, 3, 4, 4Yer değiştirme yoluyla
  • indirekt sıçrayış .grade- ...-O04 taşınmamış blokları ile rapor edilir.
  • Ghidra cihazları 16 byet geri dönüşü içerir.param_3, basitleştirilmemiş .xor, ve long param_1
  • 6 emisyon birimi ile oluşturulur.clang -std=c11 -Wall -Werroriçinx86_64-unknown-linux-gnu
  • kaldırılan fonksiyonlar, onarılmış Ghidra metni ve kaynak, giriş şebekesi üzerinde aynı 232 satır yazdırıyor

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

FailureWhat the weak reading reportsWhat the derivation must show
Decompiler widens the return value"returns a 16-byte struct"idivl writes %rdx, and no caller fixes the width
Decompiler adds a parameter"takes three arguments"The register is read on one path only because the return width was guessed
Idiom goes unrecognized"multiplies by a magic constant"The multiplier is the ceiling of 2^s / d and the error bound holds
Idiom is recognized and a xor is lost"default depends on the input"xorl %eax, %eax writes zero, two spellings of one bit cancel
Pointer typed as integer"takes a long"A scaled 4-byte load from the register is pointer evidence
Table length unknown"reads past the table"The next referenced offset or the section end bounds it, and the range check agrees
Loop shape misread"while loop"The header has no exit, the latch jumps back, so it is do while
Indirect jump guessed"switch with five cases"The targets come from .rela.rodata, or the jump is reported as unresolved
Substitution across a store"one expression"A loaded value moved past a store or a call changes meaning
Refinement model edits the text"compiles and looks right"Build rate up, behaviour match down, invented guards replaced visible unknowns
Decoder guesses an unknown opcode"disassembly looks fine"DecodeError with the address and the byte, then one table entry checked against llvm-objdump

İşlem Kuralı

Bu nedenle, bu metinleri birbiriyle birlikte yerleştirin ve her farklı satırın arkasında talimat ya da tahmin yazın. Her ikisini de birleştirin ve kaynağa karşı çalıştı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.