Stack Based Overflows #1
STACK
Stack yani veri yığını, programlarda kullanılan değişkenlerin ya da jmp, return gibi komutların gideceği adreslerin tutulduğu geçici bir hafızadır. Her process için işletim sistemi yeni Stack ayırır. Last In Firs Out(LIFO) mantığıyla çalışır, yani son gelen ilk çıkar. push
komutu ile Stack’e veri yazdırılıp pop
komutu ile Stack’ten veri çekilir. Stack eksiye göre büyür diyebiliriz, 4’er 4’er artar ya da azalır. Yani yeni veri eklenince ESP (stack pointer) değer olarak küçülür, veri alınınca ESP değer olarak büyür. ESP yani Stack pointer, Stack’e gönderilen en son değerin adresini gösterir. Bir de EBP vardır, bu da Stack segmentin başlangıç noktasını gösterir.
Registerlar
Registerlar değişken olarak kullanılırlar ve işlemci çekirdeğinde bulunurlar. Çekirdekte olmalarının nedeni hafızaya ulaşırken zaman kaybı yaşanmaması içindir.
EAX, 32 bitlik bir registerdır. İlk 16 bitlik bölümü AX şeklinde ifade edilir ve AX’in de ilk 8 bitinde AL, son 8 bitinde AH vardır. Yani AX, AL ve AH registerları EAX’ın alt bölümleridir.
4 byte’lık bir veri depolamak için EAX kullanılır, çünkü 4 byte=32 bittir ve 32 bitlik bir registera ihtiyaç vardır. 2 byte’lık veri için AX ve 1 byte veri için AL ya da AH tercih edilir.
ESP: Stack pointer.
EBP: Base pointer.
EIP: Program Counter yani Instruction Pointer, programda çalıştırılacak bir sonraki komutun adresini tutar. Eğer biz bu program counter’ı manipüle edersek uygulamanın normal akışı değişir, bu sayede istediğimiz komutu çalıştırabiliriz. Bunun için de hafızada taşma durumu olunca ortaya çıkan zafiyeti kullanarak exploit geliştirebiliriz, bu konuya ilerleyen yazılarda devam edeceğim.
Örnek:
ESP başlangıçta 0xb20000 adresini gösteriyor diyelim.
mov eax,AAAAAAAAh
ile eax’a AAAAAAAAh değeri atanıyor.
push eax
ile eax değeri Stack’e yazılıyor.
Yukarıdaki kodda eax’a atanan değer Stack’e yazılır. Başlangıçta 0xb20000 olan ESP, Stack’e yeni veri eklendiği için 4 azalacak. Artık ESP, 0xb1fffc adresini gösteriyor. Çıkartma işlemi şöyle yapılır:
0xb20000 = 1011 0010 0000 0000 0000 0000
4=0000 0000 0000 0000 0000 0100
Çıkartma sonucu= 1011 0001 1111 1111 1111 1100=b1fffc
Stack’ten veri çekmek için pop komutu kullanıldığında Stack’te gösterilen yer artık ESP’nin en son değerine 4 eklenmiş yeridir. ( b1fffc+4 = b200000)
Her fonksiyon çağrıldığında “function prologue” ve her fonksiyonun bitiminde “function epilogue” olayları gerçekleşir. Funciton prolougue aslında fonksiyona gitmek için hep yapılan ilk işlemlerdir.
Function Prologue
push ebp
ile EBP’deki mevcut değeri kaybetmemek için onu Stack’te yedekler.
mov ebp,esp
ile ESP’yi EBP’ye yazar.
sub esp, N
ile yerel değişkenlere yer ayırmak için Stack N bayt büyültülür. Eksiye göre büyür dediğimiz için “sub” yani çıkartma işlemi yapılır.
call function
ile fonksiyon çağırılır. Ayrıca call fonksiyonu geri dönüş adresini(EIP) Stack’e yazar.
Özetle, fonksiyon çağırmadan önce parametreler ve geri dönüş adresi Stack’e yazılmalıdır. Programın normal akışında bir fonksiyon çağırılması durumunda, program artık o fonksiyondan devam eder ve fonksiyonun bitiminde de ana akışta kaldığı yere geri dönmesi gerekir. Geri dönüş adresi dediğimiz şey fonksiyondan ana programa geri dönüş içindir.
Function Epilogue
mov esp,ebp
ile ESP EBP’ye yazılır.
pop ebp
ile EBP tekrar Stackten alınıp düzeltilir baştaki duruma göre.
ret
ile çağırılan yere geri döner.
Stack Yapısı
ESP, görselde de gözüktüğü üzere Stack’in en üst noktasını gösteriyor ve her yeni veri eklenmesi durumunda Stack aşağıya doğru büyüyor. Taşma olunca bir sonraki bölme de işgal edildiği için tehlike yaratıyor demiştik. İşte tehlikenin olması aslında EIP’i manipüle edebilmekten oluyor. EIP yani instruction pointer çalıştırılacak bir sonraki komutun adresini tuttuğu için biz bu EIP’e ne yazarsak oradan çalışacak, yani programa istediğimizi yaptırabileceğiz.
Seri Boyunca Kaynaklar: https://www.corelan.be/ https://www.youtube.com/c/MehmetDursunInce/playlists ->binary exploitation