Tersine Mühendislik Serisi #1: Kodun Anatomisi, CPU Mimarisi ve İlk Statik Analiz

Vesper

Editör
Katılım
12 Eylül 2026
Mesajlar
74
Tepkime puanı
103
Puan
59
Konum
root
  • Kurucu Üye
UyI4TWn.png

Selamlar Sevgili Siberatay Mensupları,

Tersine mühendislik (Reverse Engineering), bir bütünü alıp parçalarına ayırarak nasıl çalıştığını anlama sanatıdır. Siber güvenlikte bu sanat; zararlı yazılımları (Malware) analiz etmek, yazılım zafiyetlerini (Exploit) bulmak veya lisans/şifre mekanizmalarını atlatmak (Cracking) için kullanılır.

Yazılımcılar kodlarını C, C++, Rust veya Go gibi dillerde yazarlar. Ancak işlemci (CPU) İngilizce kelimelerden veya if/else bloklarından anlamaz. İşlemcinin anladığı tek şey voltaj seviyeleridir (1 ve 0'lar). Bu rehberde, "insan dilinden" "makine diline" geçişi ve bu makine dilini (Assembly) okuyarak asıl mantığı nasıl geri çıkaracağımızı (Reverse) inceleyeceğiz.


QtpdWUD.png

1. BÖLÜM: İllüzyonu Yıkmak (Derleme Süreci)

Bir kodu analiz etmeden önce, o kodun nasıl o hale geldiğini bilmek zorundasınız. Yüksek seviyeli bir C/C++ kodunun çalıştırılabilir bir dosyaya (.exe veya .elf) dönüşmesi 4 kritik aşamadan geçer:

Preprocessing (Önişlem): #include ve #define gibi makrolar koda dahil edilir. Koddaki yorum satırları silinir.
Compilation (Derleme): Temizlenmiş C kodu, işlemci mimarisine (x86, ARM) uygun Assembly koduna dönüştürülür.
Assembly (Çevirme): Assembler devreye girer ve Assembly komutlarını işlemcinin anlayacağı saf Makine Koduna (Opcode / Binary) çevirir. Object (.o) dosyaları oluşur.
​
  1. Linking (Bağlama): Yazılımın kullandığı dış kütüphaneler (Örn: Windows'ta kernel32.dll, Linux'ta libc.so) object dosyalarıyla birleştirilir ve nihai Executable (Çalıştırılabilir) dosya üretilir.​
Tersine mühendis, bu sürecin 4. adımındaki "Executable" dosyayı alıp, 2. adımdaki (Assembly) ve hatta 1. adımdaki (C) kodlarına geri döndürmeye çalışan kişidir.


jI5tbBX.png

2. BÖLÜM: x86_64 CPU Mimarisi ve Register (Yazmaç) Cehennemi

İşlemcinin içinde, verileri çok hızlı işleyebilmek için kullandığı ultra hızlı küçük hafıza birimleri vardır. Bunlara Register (Yazmaç) denir. Bir değişkeni RAM'den okumak yavaştır, bu yüzden işlemci işlemleri Register'lar üzerinde yapar. 64-bit (x64) mimarisindeki en kritik Register'lar şunlardır:
​
  • RAX (Accumulator): Matematiksel işlemlerin merkezidir. Bir fonksiyon işlem bitiminde geriye bir değer döndürüyorsa (Örn: return 1;), bu değer her zaman RAX register'ına yazılır. (Crack yaparken en çok manipüle edeceğimiz yazmaç budur).​
  • RBX (Base): Genel amaçlı veri saklama.​
  • RCX (Counter): Döngülerde (Loop) sayaç olarak kullanılır.​
  • RDX (Data): Giriş/Çıkış (I/O) ve matematiksel işlemlerde RAX'a destek olur.​
  • RSI (Source Index) & RDI (Destination Index): Veri kopyalama işlemlerinde (Kaynak ve Hedef) ve fonksiyonlara parametre gönderilirken kullanılırlar.​
  • RSP (Stack Pointer): Yığının (Stack) en üst noktasını (Tepesini) işaret eder.​
  • RBP (Base Pointer): O an çalışan fonksiyonun hafızadaki taban (Başlangıç) noktasını işaret eder.​
  • RIP (Instruction Pointer): CPU'nun o milisaniyede çalıştırdığı kodun hafıza adresini tutar. Yazılım hacklemede asıl amaç, RIP'in işaret ettiği adresi kendi zararlı kodumuzun adresine yönlendirmektir (Buffer Overflow).​
(Not: Bu register'ların 32-bit halleri EAX, EBX, ECX, 16-bit halleri AX, BX, 8-bit halleri AL, BL şeklinde isimlendirilir).


zx1KHPr.png

3. BÖLÜM: Hafıza Hiyerarşisi – Yığın (Stack) Mimarisi
Bir program çalıştığında RAM'de kendine bir alan (Process Memory) ayırır. Bizi ilgilendiren en önemli bölge Stack (Yığın) bölgesidir. Stack, LIFO (Last In, First Out - Son giren ilk çıkar) mantığıyla çalışan, aşağıya doğru büyüyen geçici bir hafızadır.

Lokal değişkenler, fonksiyona giden parametreler ve fonksiyon bittiğinde kodun nereye döneceği (Return Address) Stack üzerinde tutulur.

Fonksiyon Prologue (Giriş) ve Epilogue (Çıkış) Ritüeli: Assembly okurken bir fonksiyonun nerede başlayıp nerede bittiğini anlamak çok önemlidir. Derleyiciler her fonksiyonun başına ve sonuna standart bir kalıp koyar.

Giriş (Prologue) - Yeni fonksiyon çağrıldığında ortam hazırlanır:

​
Kod:
push rbp        ; Eski taban adresini Stack'e yedekle (Kaybetmemek için)
mov rbp, rsp    ; Taban adresini güncel Stack tepesine eşitle (Yeni çerçeve)
sub rsp, 0x20   ; Stack'i 32 byte (0x20) aşağı çekerek lokal değişkenlere yer aç

Çıkış (Epilogue) - Fonksiyon bittiğinde ortam temizlenir:
​
Kod:
mov rsp, rbp    ; Stack işaretçisini eski yerine getir (Değişkenleri sil)
pop rbp         ; Eski taban adresini geri yükle
ret             ; Çağrılan yere geri dön (Return)


sT2UFrW.png

4. BÖLÜM: Assembly Dilinde Hayatta Kalma Kiti
Ghidra veya IDA Pro ile bir dosyayı açtığınızda karşınızda binlerce satır komut (Instruction) çıkacaktır. Temel komutları bilmezseniz o ekrana boş boş bakarsınız.
​
  • MOV hedef, kaynak : Kaynaktaki veriyi hedefe kopyalar. (Örn: mov eax, 1 -> eax'in içine 1 koy).​
  • LEA hedef, kaynak : (Load Effective Address) Veriyi değil, verinin hafızadaki adresini hedefe kopyalar. C'deki Pointer mantığıdır.​
  • ADD / SUB : Toplama ve çıkarma yapar.​
  • CMP arg1, arg2 : (Compare) İki değeri birbirinden çıkararak karşılaştırır, ancak sonucu kaydetmez. Sadece CPU'nun bayraklarını (Flag - Zero Flag vs.) günceller. Karar mekanizmasının kalbidir.​
  • JMP adres : (Jump) Belirtilen adrese koşulsuz şartsız atlar (C'deki goto).​
    • JE / JZ : (Jump if Equal / Zero) Eğer bir önceki CMP işleminde iki değer eşitse atla.​
    • JNE / JNZ : (Jump if Not Equal / Not Zero) Eşit değilse atla.​
    • JG / JL : (Jump if Greater / Less) Büyükse / Küçükse atla.​


uM4E7xk.png

5. BÖLÜM: Uygulamalı Statik Analiz (CrackMe Laboratuvarı)
Teoriyi pratiğe dökelim. Klasik bir lisans kontrolü yapan küçük bir C programı (CrackMe) yazalım, derleyelim ve Assembly çıktısını IDA Pro mantığıyla analiz edelim.

1. Orijinal C Kodumuz (Bunu biz biliyoruz, kurban bilmiyor):

​
C:
#include <stdio.h>
#include <string.h>

int check_license(int pin) {
    // Lisans kodumuz 1337 olsun
    if (pin == 1337) {
        return 1; // Başarılı
    } else {
        return 0; // Başarısız
    }
}

int main() {
    int user_input;
    printf("Lisans PIN kodunu girin: ");
    scanf("%d", &user_input);

    if (check_license(user_input) == 1) {
        printf("Lisans Gecerli. Sisteme Giris Yapildi!\n");
    } else {
        printf("HATA: Yanlis PIN!\n");
    }
    return 0;
}

Linux üzerinde GCC ile bu kodu derleyelim ve tersine mühendisliğe karşı zorlaştırmak için sembollerini silelim (strip):
​
Bash:
gcc crackme.c -o crackme -O0
strip crackme

2. Statik Analiz: Assembly Kodunu Parçalamak objdump veya Ghidra/IDA kullanarak dosyayı Decompile (Statik analiz) ettiğimizde, o masum C kodunun check_license fonksiyonu şu korkunç Assembly bloğuna dönüşmüş olarak karşımıza çıkar. Adım adım parçalayalım:
​
Kod:
0000000000001149 <check_license>:
 --- PROLOGUE (Hazırlık) ---
  1149: push   rbp                   ; Eski base pointer'ı sakla
  114a: mov    rbp, rsp              ; Yeni stack frame oluştur
  114d: mov    DWORD PTR [rbp-0x4], edi ; Fonksiyona gelen PIN değerini (edi) hafızaya (rbp-4) kaydet
  
; --- MANTIK (Karşılaştırma) ---
  1150: cmp    DWORD PTR [rbp-0x4], 0x539 ; Kullanıcının girdiği pini, Hexadecimal 0x539 (Decimal 1337) ile karşılaştır! (CMP)
  1157: jne    1160 <check_license+0x17>  ; Eğer EŞİT DEĞİLSE (JNE), başarısızlık bloğuna (1160) atla!
  
; --- BAŞARILI BLOĞU (If) ---
  1159: mov    eax, 0x1              ; Eşitse atlamadı. RAX (EAX) register'ına 1 (Başarılı) değerini koy.
  115e: jmp    1165 <check_license+0x1c>  ; Çıkış bloğuna atla.
  
; --- BAŞARISIZ BLOĞU (Else) ---
  1160: mov    eax, 0x0              ; RAX (EAX) register'ına 0 (Başarısız) değerini koy.
  
; --- EPILOGUE (Çıkış) ---
  1165: pop    rbp                   ; Stack'i temizle
  1166: ret                          ; Main fonksiyona geri dön (EAX içindeki değerle)

Analiz Sonucu ve Crack (Yama) Mantığı: Bir Reverser olarak cmp komutundaki 0x539 değerini gördüğümüzde, bunun Decimal karşılığının 1337 olduğunu anlar ve PIN'i bulmuş oluruz. (Yazılımcılar sabit değerleri kodun içine (Hardcoded) yazmamalıdır!).

Diyelim ki program bir lisans sunucusuna bağlanıyor ve doğru lisansı asla bulamayız. O zaman Patching (Yama) devreye girer. Yukarıdaki kodda yer alan:1157: jne 1160 (Eşit değilse başarısızlığa atla) komutundaki jne (Jump Not Equal) byte'ını (Örn: 0x75), Hex Editor ile je (Jump Equal - 0x74) olarak değiştiririz. Sonuç? Siz yanlış bir şifre girseniz bile program şifreyi doğru kabul edip sisteme giriş yapacaktır. İllüzyon kırıldı!


Sonuç
Bu bölümde CPU mimarisini, Stack hiyerarşisini ve x86_64 Assembly'nin temel mantığını anlayıp ilk statik analizimizi gerçekleştirdik. Kodun arka planda nasıl çalıştığını kavramadan yapılacak her analiz ezbere dayanır ve karmaşık malware/koruma sistemlerinde (Packers, Obfuscators) tıkanmanıza sebep olur.

Serinin 2. partında işin "Dinamik Analiz" (Dynamic Analysis) kısmına gireceğiz. x64dbg ve GDB debugger'ları ile programı çalışırken durduracak (Breakpoint), hafıza adreslerine canlı olarak müdahale edecek ve yazılımın kalbine enjekte olacağız.

​
⚠️ Konu içerisinde paylaşılan tüm görseller ve kodlar vs. şahsıma aittir.​

Register'larınız boş, Stack'iniz sağlam olsun.
İyi Forumlar Dilerim.
:coffee:


yo10ni9c.png
ivaz1qns.png
yo10ni9c.png
 
Uzun bir seri olacak gibi :D
eline sağlık
 
UyI4TWn.png

Selamlar Sevgili Siberatay Mensupları,

Tersine mühendislik (Reverse Engineering), bir bütünü alıp parçalarına ayırarak nasıl çalıştığını anlama sanatıdır. Siber güvenlikte bu sanat; zararlı yazılımları (Malware) analiz etmek, yazılım zafiyetlerini (Exploit) bulmak veya lisans/şifre mekanizmalarını atlatmak (Cracking) için kullanılır.

Yazılımcılar kodlarını C, C++, Rust veya Go gibi dillerde yazarlar. Ancak işlemci (CPU) İngilizce kelimelerden veya if/else bloklarından anlamaz. İşlemcinin anladığı tek şey voltaj seviyeleridir (1 ve 0'lar). Bu rehberde, "insan dilinden" "makine diline" geçişi ve bu makine dilini (Assembly) okuyarak asıl mantığı nasıl geri çıkaracağımızı (Reverse) inceleyeceğiz.


QtpdWUD.png

1. BÖLÜM: İllüzyonu Yıkmak (Derleme Süreci)

Bir kodu analiz etmeden önce, o kodun nasıl o hale geldiğini bilmek zorundasınız. Yüksek seviyeli bir C/C++ kodunun çalıştırılabilir bir dosyaya (.exe veya .elf) dönüşmesi 4 kritik aşamadan geçer:

Preprocessing (Önişlem): #include ve #define gibi makrolar koda dahil edilir. Koddaki yorum satırları silinir.
Compilation (Derleme): Temizlenmiş C kodu, işlemci mimarisine (x86, ARM) uygun Assembly koduna dönüştürülür.
Assembly (Çevirme): Assembler devreye girer ve Assembly komutlarını işlemcinin anlayacağı saf Makine Koduna (Opcode / Binary) çevirir. Object (.o) dosyaları oluşur.
​
  1. Linking (Bağlama): Yazılımın kullandığı dış kütüphaneler (Örn: Windows'ta kernel32.dll, Linux'ta libc.so) object dosyalarıyla birleştirilir ve nihai Executable (Çalıştırılabilir) dosya üretilir.​
Tersine mühendis, bu sürecin 4. adımındaki "Executable" dosyayı alıp, 2. adımdaki (Assembly) ve hatta 1. adımdaki (C) kodlarına geri döndürmeye çalışan kişidir.


jI5tbBX.png

2. BÖLÜM: x86_64 CPU Mimarisi ve Register (Yazmaç) Cehennemi

İşlemcinin içinde, verileri çok hızlı işleyebilmek için kullandığı ultra hızlı küçük hafıza birimleri vardır. Bunlara Register (Yazmaç) denir. Bir değişkeni RAM'den okumak yavaştır, bu yüzden işlemci işlemleri Register'lar üzerinde yapar. 64-bit (x64) mimarisindeki en kritik Register'lar şunlardır:
​
  • RAX (Accumulator): Matematiksel işlemlerin merkezidir. Bir fonksiyon işlem bitiminde geriye bir değer döndürüyorsa (Örn: return 1;), bu değer her zaman RAX register'ına yazılır. (Crack yaparken en çok manipüle edeceğimiz yazmaç budur).​
  • RBX (Base): Genel amaçlı veri saklama.​
  • RCX (Counter): Döngülerde (Loop) sayaç olarak kullanılır.​
  • RDX (Data): Giriş/Çıkış (I/O) ve matematiksel işlemlerde RAX'a destek olur.​
  • RSI (Source Index) & RDI (Destination Index): Veri kopyalama işlemlerinde (Kaynak ve Hedef) ve fonksiyonlara parametre gönderilirken kullanılırlar.​
  • RSP (Stack Pointer): Yığının (Stack) en üst noktasını (Tepesini) işaret eder.​
  • RBP (Base Pointer): O an çalışan fonksiyonun hafızadaki taban (Başlangıç) noktasını işaret eder.​
  • RIP (Instruction Pointer): CPU'nun o milisaniyede çalıştırdığı kodun hafıza adresini tutar. Yazılım hacklemede asıl amaç, RIP'in işaret ettiği adresi kendi zararlı kodumuzun adresine yönlendirmektir (Buffer Overflow).​
(Not: Bu register'ların 32-bit halleri EAX, EBX, ECX, 16-bit halleri AX, BX, 8-bit halleri AL, BL şeklinde isimlendirilir).


zx1KHPr.png

3. BÖLÜM: Hafıza Hiyerarşisi – Yığın (Stack) Mimarisi
Bir program çalıştığında RAM'de kendine bir alan (Process Memory) ayırır. Bizi ilgilendiren en önemli bölge Stack (Yığın) bölgesidir. Stack, LIFO (Last In, First Out - Son giren ilk çıkar) mantığıyla çalışan, aşağıya doğru büyüyen geçici bir hafızadır.

Lokal değişkenler, fonksiyona giden parametreler ve fonksiyon bittiğinde kodun nereye döneceği (Return Address) Stack üzerinde tutulur.

Fonksiyon Prologue (Giriş) ve Epilogue (Çıkış) Ritüeli: Assembly okurken bir fonksiyonun nerede başlayıp nerede bittiğini anlamak çok önemlidir. Derleyiciler her fonksiyonun başına ve sonuna standart bir kalıp koyar.

Giriş (Prologue) - Yeni fonksiyon çağrıldığında ortam hazırlanır:

​
Kod:
push rbp        ; Eski taban adresini Stack'e yedekle (Kaybetmemek için)
mov rbp, rsp    ; Taban adresini güncel Stack tepesine eşitle (Yeni çerçeve)
sub rsp, 0x20   ; Stack'i 32 byte (0x20) aşağı çekerek lokal değişkenlere yer aç

Çıkış (Epilogue) - Fonksiyon bittiğinde ortam temizlenir:
​
Kod:
mov rsp, rbp    ; Stack işaretçisini eski yerine getir (Değişkenleri sil)
pop rbp         ; Eski taban adresini geri yükle
ret             ; Çağrılan yere geri dön (Return)


sT2UFrW.png

4. BÖLÜM: Assembly Dilinde Hayatta Kalma Kiti
Ghidra veya IDA Pro ile bir dosyayı açtığınızda karşınızda binlerce satır komut (Instruction) çıkacaktır. Temel komutları bilmezseniz o ekrana boş boş bakarsınız.
​
  • MOV hedef, kaynak : Kaynaktaki veriyi hedefe kopyalar. (Örn: mov eax, 1 -> eax'in içine 1 koy).​
  • LEA hedef, kaynak : (Load Effective Address) Veriyi değil, verinin hafızadaki adresini hedefe kopyalar. C'deki Pointer mantığıdır.​
  • ADD / SUB : Toplama ve çıkarma yapar.​
  • CMP arg1, arg2 : (Compare) İki değeri birbirinden çıkararak karşılaştırır, ancak sonucu kaydetmez. Sadece CPU'nun bayraklarını (Flag - Zero Flag vs.) günceller. Karar mekanizmasının kalbidir.​
  • JMP adres : (Jump) Belirtilen adrese koşulsuz şartsız atlar (C'deki goto).​
    • JE / JZ : (Jump if Equal / Zero) Eğer bir önceki CMP işleminde iki değer eşitse atla.​
    • JNE / JNZ : (Jump if Not Equal / Not Zero) Eşit değilse atla.​
    • JG / JL : (Jump if Greater / Less) Büyükse / Küçükse atla.​


uM4E7xk.png

5. BÖLÜM: Uygulamalı Statik Analiz (CrackMe Laboratuvarı)
Teoriyi pratiğe dökelim. Klasik bir lisans kontrolü yapan küçük bir C programı (CrackMe) yazalım, derleyelim ve Assembly çıktısını IDA Pro mantığıyla analiz edelim.

1. Orijinal C Kodumuz (Bunu biz biliyoruz, kurban bilmiyor):

​
C:
#include <stdio.h>
#include <string.h>

int check_license(int pin) {
    // Lisans kodumuz 1337 olsun
    if (pin == 1337) {
        return 1; // Başarılı
    } else {
        return 0; // Başarısız
    }
}

int main() {
    int user_input;
    printf("Lisans PIN kodunu girin: ");
    scanf("%d", &user_input);

    if (check_license(user_input) == 1) {
        printf("Lisans Gecerli. Sisteme Giris Yapildi!\n");
    } else {
        printf("HATA: Yanlis PIN!\n");
    }
    return 0;
}

Linux üzerinde GCC ile bu kodu derleyelim ve tersine mühendisliğe karşı zorlaştırmak için sembollerini silelim (strip):
​
Bash:
gcc crackme.c -o crackme -O0
strip crackme

2. Statik Analiz: Assembly Kodunu Parçalamak objdump veya Ghidra/IDA kullanarak dosyayı Decompile (Statik analiz) ettiğimizde, o masum C kodunun check_license fonksiyonu şu korkunç Assembly bloğuna dönüşmüş olarak karşımıza çıkar. Adım adım parçalayalım:
​
Kod:
0000000000001149 <check_license>:
 --- PROLOGUE (Hazırlık) ---
  1149: push   rbp                   ; Eski base pointer'ı sakla
  114a: mov    rbp, rsp              ; Yeni stack frame oluştur
  114d: mov    DWORD PTR [rbp-0x4], edi ; Fonksiyona gelen PIN değerini (edi) hafızaya (rbp-4) kaydet
  
; --- MANTIK (Karşılaştırma) ---
  1150: cmp    DWORD PTR [rbp-0x4], 0x539 ; Kullanıcının girdiği pini, Hexadecimal 0x539 (Decimal 1337) ile karşılaştır! (CMP)
  1157: jne    1160 <check_license+0x17>  ; Eğer EŞİT DEĞİLSE (JNE), başarısızlık bloğuna (1160) atla!
  
; --- BAŞARILI BLOĞU (If) ---
  1159: mov    eax, 0x1              ; Eşitse atlamadı. RAX (EAX) register'ına 1 (Başarılı) değerini koy.
  115e: jmp    1165 <check_license+0x1c>  ; Çıkış bloğuna atla.
  
; --- BAŞARISIZ BLOĞU (Else) ---
  1160: mov    eax, 0x0              ; RAX (EAX) register'ına 0 (Başarısız) değerini koy.
  
; --- EPILOGUE (Çıkış) ---
  1165: pop    rbp                   ; Stack'i temizle
  1166: ret                          ; Main fonksiyona geri dön (EAX içindeki değerle)

Analiz Sonucu ve Crack (Yama) Mantığı: Bir Reverser olarak cmp komutundaki 0x539 değerini gördüğümüzde, bunun Decimal karşılığının 1337 olduğunu anlar ve PIN'i bulmuş oluruz. (Yazılımcılar sabit değerleri kodun içine (Hardcoded) yazmamalıdır!).

Diyelim ki program bir lisans sunucusuna bağlanıyor ve doğru lisansı asla bulamayız. O zaman Patching (Yama) devreye girer. Yukarıdaki kodda yer alan:1157: jne 1160 (Eşit değilse başarısızlığa atla) komutundaki jne (Jump Not Equal) byte'ını (Örn: 0x75), Hex Editor ile je (Jump Equal - 0x74) olarak değiştiririz. Sonuç? Siz yanlış bir şifre girseniz bile program şifreyi doğru kabul edip sisteme giriş yapacaktır. İllüzyon kırıldı!


Sonuç
Bu bölümde CPU mimarisini, Stack hiyerarşisini ve x86_64 Assembly'nin temel mantığını anlayıp ilk statik analizimizi gerçekleştirdik. Kodun arka planda nasıl çalıştığını kavramadan yapılacak her analiz ezbere dayanır ve karmaşık malware/koruma sistemlerinde (Packers, Obfuscators) tıkanmanıza sebep olur.

Serinin 2. partında işin "Dinamik Analiz" (Dynamic Analysis) kısmına gireceğiz. x64dbg ve GDB debugger'ları ile programı çalışırken durduracak (Breakpoint), hafıza adreslerine canlı olarak müdahale edecek ve yazılımın kalbine enjekte olacağız.

​
⚠️ Konu içerisinde paylaşılan tüm görseller ve kodlar vs. şahsıma aittir.​

Register'larınız boş, Stack'iniz sağlam olsun.
İyi Forumlar Dilerim.
:coffee:


yo10ni9c.png
ivaz1qns.png
yo10ni9c.png
Baya güzel ve uzunn detaylı bir konu olmuş eline sağlık ASM iyice korkutuyor insanı😂
 
Geri
Üst