High-level dillerde program yazarken, arka planda otomatik yapılan ama low-level dillerde (özellikle Assembly) manuel olarak yönetmemiz gereken pek çok işlem vardır. Bunların en önemlilerinden birisi de prologue ve epilogue işlemleridir.

Prologue İşleminin Stack Üzerindeki Şeması

İşlemin Assembly dilindeki görüntüsü

Bu konuya başlamadan önce birkaç temel kavrama değineceğim:

  • Register: CPU’nun en hızlı veri depolama alanıdır; işlem yapılacak veriler burada tutulur.
  • Stack: LIFO (Last In, First Out) mantığıyla çalışan, fonksiyon çağrılarında geçici veri tutan bellek alanıdır.
  • Stack Frame: Her fonksiyon çağrıldığında stack üzerinde ayrılan, argümanlar ve yerel değişkenleri barındıran bellek bloğudur.
  • ESP/RSP: Stack pointer; stack’in en üstünü gösterir. x86’da ESP, x64’te RSP olarak adlandırılır.
  • EBP/RBP: Base pointer; stack frame’in taban adresini tutar, değişkenlere erişimde referans noktasıdır.
  • Calling Convention: Fonksiyonlara argümanların nasıl aktarıldığını ve dönüş değerlerinin nasıl alındığını belirleyen kurallar bütünüdür.
  • Push: Bir değeri stack’e ekler, stack pointer’ı aşağı kaydırır.
  • Pop: Stack’in en üstündeki değeri alır, stack pointer’ı yukarı kaydırır.

Bu kavramların hepsi prologue ve epilogue işlemlerini anlamanın temelini oluşturur. Zira bu iki işlem, stack frame’i oluşturup kaldırmaktan ibarettir.

Prologue Nedir ?

Prologue, fonksiyonun başında çalışır ve stack üzerinde bir “frame” oluşturur. Bu frame, fonksiyonun yerel değişkenlerini ve kaydedilmesi gereken registerları saklamak için kullanılır. Bu işlem aşağıdaki resimde göreceğiniz gibi 3 aşamadan oluşur.

Epilogue Adımları ve EBP Register Durumu

Şimdi ise bu 3 adımı teker teker inceleyim.

1. push ebp

Önce elimizdeki eski referans noktasını kaydediyoruz. Bulunduğumuz fonksiyon bittiği zaman artık fonksiyon içerisinde kullandığımız değerlerin stackte durması gereksiz olacağından dolayı daha frame açılmadan fonksiyon bitiminde geriye dönmesi gereken kısmı not ediyoruz.

2. mov ebp,esp

Burada ise aslında kuracağımız framein üst çerçevesini hazırlıyoruz. EBP’yi ESP’nin olduğu yere taşıyoruz. Yani diyoruz ki; ‘buradan itibaren bu fonksiyonun alanı başlıyor.’ Böylece bu fonksiyonun stack içerisindeki kendi çalışma alanı net bir şekilde belirlenmiş oluyor.

3. sub esp,0x10

Son olarak, bu fonksiyon ne kadar stackte depolama kullanmak isterse o kadar yer açıyoruz ve bu şekilde de frame alanının geri kalanı tamamlanmış oluyor. ESP’yi biraz aşağı indiriyoruz; yani stack’te biraz yer açıyoruz. Bu yer, fonksiyonun yerel değişkenleri için ayrılmış özel alan oluyor.

Özetle

Bu üç adım aslında stack frame oluşturma sürecidir. İlk komut eski EBP değerini saklayarak geri dönüş yolunu güvence altına alır. İkinci komut, yeni stack frame’in tabanını belirler ve erişimleri kolaylaştırır. Son adım ise yerel değişkenlerin saklanacağı kadar alan açar. Böylece fonksiyon kendi izole çalışma ortamına sahip olur ve başka fonksiyonların verilerini bozmaz.

Epilogue Nedir?

Epilogue, bir fonksiyonun sonunda çalışan ve prologue’un yaptığı işleri tersine çeviren koddur. Temel amacı, fonksiyonun stack üzerinde açtığı alanı temizlemek, eski düzeni geri getirmek ve çağıran koda güvenli bir şekilde dönmektir. Prologue, stack frame’i kurarken; epilogue stack frame’i kaldırır. Bu ikisi birlikte fonksiyonların güvenli ve düzenli çalışmasını sağlar.

Epilogue genellikle üç temel adımda gerçekleşir:

Epilogue Adımları ve EBP Register Durumu

1. mov esp, ebp

Bu adım, fonksiyonun kullandığı stack alanını serbest bırakır. Stack pointer (ESP), fonksiyonun başında işaretlediği base pointer’a (EBP) geri getirilir. Böylece fonksiyonun geçici olarak kullandığı alan stack’e geri kazandırılmış olur.

2. pop ebp

Prologue sırasında stack’in en üstüne koyulan eski base pointer (EBP) şimdi geri yüklenir. Yani fonksiyon öncesi ortam, stack ve base pointer tam olarak eski haline gelir. Bu sayede fonksiyon sanki hiç çağrılmamış gibi bir izlenim bırakır.

3. ret

Son adımda CPU, instruction pointer’ı (EIP) stack’ten alır ve çağıran koda geri döner. Artık fonksiyonun işi tamamen bitmiştir ve program akışı kaldığı yerden devam eder.

Epilogue olmadan stack bozulur ve programın akışı karışır. Stack frame kaldırılmazsa ESP yanlış yerde kalır ve fonksiyonlar doğru şekilde geri dönemez.

Fonksiyonlar programların temel yapı taşlarıdır ve doğru çalışmaları için hem prologue hem de epilogue adımlarına ihtiyaç duyarlar. Prologue fonksiyonun ihtiyacı olan ortamı hazırlar, epilogue ise işi bitirip ortamı temizler ve eski düzene geri döner.

Epilogue’u anlamak, özellikle düşük seviyeli programlama ve Assembly ile çalışırken kritik bir noktadır. Çünkü stack’in doğru yönetilmesi, programın stabilitesi ve güvenliği için şarttır. Bu nedenle, bir fonksiyonun nasıl başladığını ve nasıl sonlandığını görmek, kodun iç işleyişini anlamak için büyük bir adımdır.

Bu metinde Tersine Mühendislik’te veya Assembly okurken kişilerin sıklıkla karşılaşacağı temel iki işlemden bahsettik. Konumuz buraya kadar. Bir sonraki metinde görüşmek üzere