- Puan
- 28
- Yaş
- 118
╔════════════════════════════════════════════════════════════════════╗
║ ORTA SEVİYE C SERİSİ #1 ║
║ BELLEK MİMARİSİ ve STACK ║
║ (Artık Oyuncaklar Bitti, Gerçek C) ║
╚════════════════════════════════════════════════════════════════════╝
Bu noktaya kadar:
✓ Değişkenler
✓ Döngüler
✓ Fonksiyonlar
✓ Diziler
✓ Stringler
✓ Pointerlar
✓ Dinamik Bellek
öğrendik.
Fakat acı gerçek şu:
Şu ana kadar öğrendiğin şeyler C'nin belki %20'si.
Birçok kişi burada durur.
Kod yazmaya başlar.
Sonra:
Segmentation Fault
Stack Overflow
Memory Corruption
Buffer Overflow
Use After Free
gibi hatalarla karşılaşır.
Sebep?
Belleğin nasıl çalıştığını bilmemek.
Orta seviye C'nin ilk konusu:
BELLEK
çünkü C aslında bir programlama dilinden çok bellekle konuşma yöntemidir.
════════════════════════════════════════════════════════════════════
▓▓▓ C GERÇEKTE NEDİR?
Birçok dil:
Java
Python
C#
gibi diller geliştiriciyi korur.
Yanlış bir şey yaptığında:
"Dur kardeşim."
der.
C de ise:
"İstediğini yap."
der.
Sonra işletim sistemi gelir ve programı öldürür.
Bu yüzden:
C = Güç + Sorumluluk
demektir.
════════════════════════════════════════════════════════════════════
▓▓▓ PROGRAM ÇALIŞINCA NE OLUR?
Şunu yazdık:
int main()
{
return 0;
}
Derledik.
Çalıştırdık.
Peki ne oldu?
Birçok kişi:
"Program açıldı."
diye düşünür.
Ama arka planda çok daha fazla şey olur.
İşletim sistemi:
▓▓▓ BELLEK BÖLGELERİ
Bir C programının belleği genellikle şu bölümlere ayrılır:
+------------------+
| TEXT |
+------------------+
| DATA |
+------------------+
| BSS |
+------------------+
| HEAP |
| |
| |
| |
+------------------+
| |
| |
| |
| STACK |
+------------------+
Şimdilik en önemli yer:
STACK
════════════════════════════════════════════════════════════════════
▓▓▓ STACK NEDİR?
Stack:
Fonksiyonların yaşadığı yerdir.
Yerel değişkenlerin yaşadığı yerdir.
Programın geçici hafızasıdır.
Örnek:
int main()
{
int x = 10;
return 0;
}
Buradaki:
x
Stack'te tutulur.
════════════════════════════════════════════════════════════════════
▓▓▓ STACK MANTIĞI
Stack:
LIFO
mantığıyla çalışır.
Last In First Out
Yani:
Son giren ilk çıkar.
════════════════════════════════════════════════════════════════════
▓▓▓ GERÇEK HAYAT ÖRNEĞİ
Bir tabak yığını düşün.
Üst üste diziliyor.
Yeni tabak:
en üste gelir.
Bir tabak alırken:
üsttekini alırsın.
Stack tam olarak böyledir.
════════════════════════════════════════════════════════════════════
▓▓▓ FONKSİYON ÇAĞRILINCA NE OLUR?
Örnek:
void test()
{
int x = 50;
}
int main()
{
test();
}
Program:
main()
çalıştırır.
Sonra:
test()
çağrılır.
Stack görünümü:
+--------+
| test() |
+--------+
| main() |
+--------+
Fonksiyon bitince:
test()
stackten silinir.
════════════════════════════════════════════════════════════════════
▓▓▓ STACK FRAME NEDİR?
Her fonksiyon çalıştığında:
Stack Frame
oluşturulur.
İçinde:
✓ Parametreler
✓ Yerel değişkenler
✓ Dönüş adresleri
bulunur.
Örnek:
void test(int a)
{
int x = 10;
int y = 20;
}
Bu fonksiyon çağrılınca stackte yeni bir alan oluşur.
Bu alan:
Stack Frame
olarak adlandırılır.
════════════════════════════════════════════════════════════════════
▓▓▓ NEDEN ÖNEMLİ?
Çünkü:
Buffer Overflow
Stack Overflow
ROP
Exploit Development
gibi konuların tamamı Stack üzerinden çalışır.
Reverse Engineering yapan biri için Stack bilmek zorunludur.
════════════════════════════════════════════════════════════════════
▓▓▓ STACK ADRESLERİ
Örnek:
#include <stdio.h>
int main()
{
int x = 10;
printf("%p",&x);
return 0;
}
Çıktı:
0x7ffe....
benzeri bir şey olur.
Çünkü:
x
Stack üzerinde tutulmaktadır.
════════════════════════════════════════════════════════════════════
▓▓▓ STACK VE POINTER
Örnek:
int x = 10;
int *ptr = &x;
Bellek:
Adres Veri
1000 10
ptr
1000 adresini tutar.
*ptr
ise:
1000 adresindeki değeri getirir.
Yani:
10
════════════════════════════════════════════════════════════════════
▓▓▓ EN KRİTİK HATA
Birçok kişi şöyle yapar:
int* test()
{
int x = 50;
return &x;
}
Kod derlenebilir.
Ama çok büyük hata vardır.
Neden?
Çünkü:
x
Stack üzerindedir.
Fonksiyon bitince:
x yok olur.
Adres artık geçersizdir.
Bu hataya:
Dangling Pointer
denir.
════════════════════════════════════════════════════════════════════
▓▓▓ ADIM ADIM İNCELEYELİM
Fonksiyon başladı.
Stack Frame oluştu.
x oluşturuldu.
Adres:
1000
olsun.
return &x;
dedi.
Fonksiyon bitti.
Stack Frame silindi.
Artık:
1000
adresindeki veri güvenilir değildir.
Pointer hala orayı gösterir.
İşte felaket burada başlar.
════════════════════════════════════════════════════════════════════
▓▓▓ STACK OVERFLOW NEDİR?
Şu kodu düşün:
void test()
{
test();
}
int main()
{
test();
}
Ne olur?
test()
kendini çağırır.
Sonra tekrar.
Sonra tekrar.
Sonra tekrar.
Stack sürekli büyür.
Bir noktada:
RAM'deki ayrılan Stack alanı dolar.
Sonuç:
Stack Overflow
════════════════════════════════════════════════════════════════════
▓▓▓ RECURSION'A GİRİŞ
Bir fonksiyonun kendini çağırmasına:
Recursion
denir.
Örnek:
void say(int n)
{
if(n == 0)
return;
printf("%d\n",n);
say(n-1);
}
say(5);
Çıktı:
5
4
3
2
1
Burada recursion güvenlidir.
Çünkü durma şartı vardır.
════════════════════════════════════════════════════════════════════
▓▓▓ SEGMENTATION FAULT NEDİR?
Linux kullanıcılarının korkulu rüyası.
Sebep:
Programın erişmemesi gereken belleğe erişmesi.
Örnek:
int *ptr = NULL;
*ptr = 10;
Sonuç:
Segmentation Fault
Çünkü:
NULL
geçerli bir adres değildir.
════════════════════════════════════════════════════════════════════
▓▓▓ BUFFER OVERFLOW'A GİRİŞ
Örnek:
char isim[5];
scanf("%s",isim);
Kullanıcı:
Cyber3301RD
girerse ne olur?
5 karakterlik yere
11 karakter sığdırmaya çalışırız.
Sonuç:
Bellek taşması.
Buna:
Buffer Overflow
denir.
Bu konu güvenlik dünyasının temelidir.
════════════════════════════════════════════════════════════════════
▓▓▓ REVERSE ENGINEERING'DE STACK
Bir crackme açtığında
bir oyun analiz ettiğinde
bir malware incelediğinde
sürekli göreceğin şeyler:
ESP
EBP
RSP
RBP
CALL
RET
PUSH
POP
olacaktır.
Bunların tamamı Stack ile ilgilidir.
Bu yüzden bu konuyu ezber değil anlayarak öğrenmek gerekir.
════════════════════════════════════════════════════════════════════
▓▓▓ MİNİ LAB #1
Adresleri İnceleme
#include <stdio.h>
int main()
{
int a = 10;
int b = 20;
int c = 30;
printf("%p\n",&a);
printf("%p\n",&b);
printf("%p\n",&c);
return 0;
}
Adreslerin birbirine yakın olduğunu göreceksin.
════════════════════════════════════════════════════════════════════
▓▓▓ MİNİ LAB #2
Dangling Pointer
int* test()
{
int x = 100;
return &x;
}
int main()
{
int *p = test();
printf("%d",*p);
}
Davranış tanımsızdır.
Bazen çalışır.
Bazen çöker.
Bazen saçma değer verir.
En tehlikeli hata türlerinden biridir.
════════════════════════════════════════════════════════════════════
✓ BÖLÜM ÖZETİ
Bu bölümde:
✓ Program belleği
✓ Text Segment
✓ Data Segment
✓ BSS
✓ Heap
✓ Stack
✓ Stack Frame
✓ Fonksiyon çağrıları
✓ Dangling Pointer
✓ Stack Overflow
✓ Segmentation Fault
✓ Buffer Overflow'a giriş
✓ Reverse Engineering bağlantısı
öğrendik.
Bir sonraki bölümde:
HEAP MİMARİSİ
malloc'un arkasında gerçekte ne oluyor
Heap Exploitation temelleri
Fragmentation
Allocator Mantığı
konularına geçeceğiz.
║ ORTA SEVİYE C SERİSİ #1 ║
║ BELLEK MİMARİSİ ve STACK ║
║ (Artık Oyuncaklar Bitti, Gerçek C) ║
╚════════════════════════════════════════════════════════════════════╝
Bu noktaya kadar:
✓ Değişkenler
✓ Döngüler
✓ Fonksiyonlar
✓ Diziler
✓ Stringler
✓ Pointerlar
✓ Dinamik Bellek
öğrendik.
Fakat acı gerçek şu:
Şu ana kadar öğrendiğin şeyler C'nin belki %20'si.
Birçok kişi burada durur.
Kod yazmaya başlar.
Sonra:
Segmentation Fault
Stack Overflow
Memory Corruption
Buffer Overflow
Use After Free
gibi hatalarla karşılaşır.
Sebep?
Belleğin nasıl çalıştığını bilmemek.
Orta seviye C'nin ilk konusu:
BELLEK
çünkü C aslında bir programlama dilinden çok bellekle konuşma yöntemidir.
════════════════════════════════════════════════════════════════════
▓▓▓ C GERÇEKTE NEDİR?
Birçok dil:
Java
Python
C#
gibi diller geliştiriciyi korur.
Yanlış bir şey yaptığında:
"Dur kardeşim."
der.
C de ise:
"İstediğini yap."
der.
Sonra işletim sistemi gelir ve programı öldürür.
Bu yüzden:
C = Güç + Sorumluluk
demektir.
════════════════════════════════════════════════════════════════════
▓▓▓ PROGRAM ÇALIŞINCA NE OLUR?
Şunu yazdık:
int main()
{
return 0;
}
Derledik.
Çalıştırdık.
Peki ne oldu?
Birçok kişi:
"Program açıldı."
diye düşünür.
Ama arka planda çok daha fazla şey olur.
İşletim sistemi:
- Programı RAM'e yükler
- Bellek alanları oluşturur
- Stack oluşturur
- Heap oluşturur
- main fonksiyonunu çağırır
- Çalıştırmaya başlar
▓▓▓ BELLEK BÖLGELERİ
Bir C programının belleği genellikle şu bölümlere ayrılır:
+------------------+
| TEXT |
+------------------+
| DATA |
+------------------+
| BSS |
+------------------+
| HEAP |
| |
| |
| |
+------------------+
| |
| |
| |
| STACK |
+------------------+
Şimdilik en önemli yer:
STACK
════════════════════════════════════════════════════════════════════
▓▓▓ STACK NEDİR?
Stack:
Fonksiyonların yaşadığı yerdir.
Yerel değişkenlerin yaşadığı yerdir.
Programın geçici hafızasıdır.
Örnek:
int main()
{
int x = 10;
return 0;
}
Buradaki:
x
Stack'te tutulur.
════════════════════════════════════════════════════════════════════
▓▓▓ STACK MANTIĞI
Stack:
LIFO
mantığıyla çalışır.
Last In First Out
Yani:
Son giren ilk çıkar.
════════════════════════════════════════════════════════════════════
▓▓▓ GERÇEK HAYAT ÖRNEĞİ
Bir tabak yığını düşün.
Üst üste diziliyor.
Yeni tabak:
en üste gelir.
Bir tabak alırken:
üsttekini alırsın.
Stack tam olarak böyledir.
════════════════════════════════════════════════════════════════════
▓▓▓ FONKSİYON ÇAĞRILINCA NE OLUR?
Örnek:
void test()
{
int x = 50;
}
int main()
{
test();
}
Program:
main()
çalıştırır.
Sonra:
test()
çağrılır.
Stack görünümü:
+--------+
| test() |
+--------+
| main() |
+--------+
Fonksiyon bitince:
test()
stackten silinir.
════════════════════════════════════════════════════════════════════
▓▓▓ STACK FRAME NEDİR?
Her fonksiyon çalıştığında:
Stack Frame
oluşturulur.
İçinde:
✓ Parametreler
✓ Yerel değişkenler
✓ Dönüş adresleri
bulunur.
Örnek:
void test(int a)
{
int x = 10;
int y = 20;
}
Bu fonksiyon çağrılınca stackte yeni bir alan oluşur.
Bu alan:
Stack Frame
olarak adlandırılır.
════════════════════════════════════════════════════════════════════
▓▓▓ NEDEN ÖNEMLİ?
Çünkü:
Buffer Overflow
Stack Overflow
ROP
Exploit Development
gibi konuların tamamı Stack üzerinden çalışır.
Reverse Engineering yapan biri için Stack bilmek zorunludur.
════════════════════════════════════════════════════════════════════
▓▓▓ STACK ADRESLERİ
Örnek:
#include <stdio.h>
int main()
{
int x = 10;
printf("%p",&x);
return 0;
}
Çıktı:
0x7ffe....
benzeri bir şey olur.
Çünkü:
x
Stack üzerinde tutulmaktadır.
════════════════════════════════════════════════════════════════════
▓▓▓ STACK VE POINTER
Örnek:
int x = 10;
int *ptr = &x;
Bellek:
Adres Veri
1000 10
ptr
1000 adresini tutar.
*ptr
ise:
1000 adresindeki değeri getirir.
Yani:
10
════════════════════════════════════════════════════════════════════
▓▓▓ EN KRİTİK HATA
Birçok kişi şöyle yapar:
int* test()
{
int x = 50;
return &x;
}
Kod derlenebilir.
Ama çok büyük hata vardır.
Neden?
Çünkü:
x
Stack üzerindedir.
Fonksiyon bitince:
x yok olur.
Adres artık geçersizdir.
Bu hataya:
Dangling Pointer
denir.
════════════════════════════════════════════════════════════════════
▓▓▓ ADIM ADIM İNCELEYELİM
Fonksiyon başladı.
Stack Frame oluştu.
x oluşturuldu.
Adres:
1000
olsun.
return &x;
dedi.
Fonksiyon bitti.
Stack Frame silindi.
Artık:
1000
adresindeki veri güvenilir değildir.
Pointer hala orayı gösterir.
İşte felaket burada başlar.
════════════════════════════════════════════════════════════════════
▓▓▓ STACK OVERFLOW NEDİR?
Şu kodu düşün:
void test()
{
test();
}
int main()
{
test();
}
Ne olur?
test()
kendini çağırır.
Sonra tekrar.
Sonra tekrar.
Sonra tekrar.
Stack sürekli büyür.
Bir noktada:
RAM'deki ayrılan Stack alanı dolar.
Sonuç:
Stack Overflow
════════════════════════════════════════════════════════════════════
▓▓▓ RECURSION'A GİRİŞ
Bir fonksiyonun kendini çağırmasına:
Recursion
denir.
Örnek:
void say(int n)
{
if(n == 0)
return;
printf("%d\n",n);
say(n-1);
}
say(5);
Çıktı:
5
4
3
2
1
Burada recursion güvenlidir.
Çünkü durma şartı vardır.
════════════════════════════════════════════════════════════════════
▓▓▓ SEGMENTATION FAULT NEDİR?
Linux kullanıcılarının korkulu rüyası.
Sebep:
Programın erişmemesi gereken belleğe erişmesi.
Örnek:
int *ptr = NULL;
*ptr = 10;
Sonuç:
Segmentation Fault
Çünkü:
NULL
geçerli bir adres değildir.
════════════════════════════════════════════════════════════════════
▓▓▓ BUFFER OVERFLOW'A GİRİŞ
Örnek:
char isim[5];
scanf("%s",isim);
Kullanıcı:
Cyber3301RD
girerse ne olur?
5 karakterlik yere
11 karakter sığdırmaya çalışırız.
Sonuç:
Bellek taşması.
Buna:
Buffer Overflow
denir.
Bu konu güvenlik dünyasının temelidir.
════════════════════════════════════════════════════════════════════
▓▓▓ REVERSE ENGINEERING'DE STACK
Bir crackme açtığında
bir oyun analiz ettiğinde
bir malware incelediğinde
sürekli göreceğin şeyler:
ESP
EBP
RSP
RBP
CALL
RET
PUSH
POP
olacaktır.
Bunların tamamı Stack ile ilgilidir.
Bu yüzden bu konuyu ezber değil anlayarak öğrenmek gerekir.
════════════════════════════════════════════════════════════════════
▓▓▓ MİNİ LAB #1
Adresleri İnceleme
#include <stdio.h>
int main()
{
int a = 10;
int b = 20;
int c = 30;
printf("%p\n",&a);
printf("%p\n",&b);
printf("%p\n",&c);
return 0;
}
Adreslerin birbirine yakın olduğunu göreceksin.
════════════════════════════════════════════════════════════════════
▓▓▓ MİNİ LAB #2
Dangling Pointer
int* test()
{
int x = 100;
return &x;
}
int main()
{
int *p = test();
printf("%d",*p);
}
Davranış tanımsızdır.
Bazen çalışır.
Bazen çöker.
Bazen saçma değer verir.
En tehlikeli hata türlerinden biridir.
════════════════════════════════════════════════════════════════════
✓ BÖLÜM ÖZETİ
Bu bölümde:
✓ Program belleği
✓ Text Segment
✓ Data Segment
✓ BSS
✓ Heap
✓ Stack
✓ Stack Frame
✓ Fonksiyon çağrıları
✓ Dangling Pointer
✓ Stack Overflow
✓ Segmentation Fault
✓ Buffer Overflow'a giriş
✓ Reverse Engineering bağlantısı
öğrendik.
Bir sonraki bölümde:
HEAP MİMARİSİ
malloc'un arkasında gerçekte ne oluyor
Heap Exploitation temelleri
Fragmentation
Allocator Mantığı
konularına geçeceğiz.