Quantcast
Channel: Bilgi Güvenliği AKADEMİSİ
Viewing all articles
Browse latest Browse all 331

Exploit Geliştiriciler için Endianness(Big/Little Endian) Kavramı

$
0
0

İstismar kodu geliştirmeye yeni başlayanlar için endianness meselesi genelde kafa karıştırıcı olabilmektedir. Devam eden satırlarda “İşlemcinin little endian veya big endian olması ne demektir?” ve “İstismar kodu geliştiricisi için ne değiştirir?” soruları cevaplanmıştır.

Günümüzde kullandığımız bilgisayar mimarisinde bellekler her bir adreste bir birim (byte, word vb.) veri bulunacak şekilde tasarlanırlar. Tersten düşünülürse her bir birim veriye bir adres verilir. Aktif olarak kullandığımız ve yazının devamında kastedilen belleklerde bu birim byte’tır.

Bu yapı gereği bellekte erişilebilecek (okuma/yazma) en küçük birim byte olur. Yani bir byte’ın son 4 bitine erişmek gibi bir seçenek olmaz en az 8 bitlik işlemler yapılır. Bu sebepten bellekler byte-addressable, word-addressable gibi sınıflandırılabilir.

İşlemciler bellek üzerindeki veriye erişirken belleğin standardına uymak zorunda olmalarına karşın veriyi anlamlandırma biçimleri farklılık gösterebilir. İşte endianness meselesi de tam olarak bununla alakalıdır. Big endian bir işlemci bellekteki veriye erişirken ilk adresteki değeri (en küçük adres) registerdaki en anlamlı (most significant) byte ile eşleştirir. Little endian bir işlemci ise tam tersini yaparak bellekteki en küçük adresi registerdaki en anlamsız (least significant) byte ile eşleştirir.

Örneğin bellekteki 4 byte’lık bir verinin big ve little endian işlemcilerdeki değeri (yüklendiği registerdaki hali) şu şekildedir. Tabi bu 4 byte’a tek değer gibi (örneğin 32 bit unsigned integer) davranıldığında geçerlidir.

Bellek Adresi
Veri
100
AA
101
BB
102
CC
103
DD

Big Endian Değeri
AA
BB
CC
DD

Little Endian Değeri
DD
CC
BB
AA

İstismar kodu geliştirirken little ve big endian işlemcilerin bu davranışının bilinip bellek adresleri (pointer) gibi değerlerin mimariye uygun şekilde pack edilmesi gerekir. Bu sayede girdi anlamlandırıldığında byte’ların doğru sırada olması sağlanır. Çok basit bir C kodu ile uygulaması yapılabilir.


#include <stdio.h>

main(){
    char test[30];

    printf("bof: ");
    scanf("%s", test);

    printf("\n%s\n", test);
}




İlk olarak x86 mimarisi için (little endian, 32 bit) derlenip IDA ile debug edilir. Devamında msf’nin pattern_create aracı ile 50 byte’lık bir cyclic pattern oluşturulur. Uygulamaya girdi olarak bu pattern girilir ve IDA’da uygulamanın “Segmentation fault” mesajı görülür.

Hatada görünen adres EIP’in değeridir, bu değer pattern_offset aracına parametre olarak verildiğinde değerin 42. byte’a denk geldiği hesaplanır. Buradan pattern’in 42. byte’ından başlayan 4 byte’lık değer “Ab4A” iken EIP’teki değerin “A4bA” olduğu görülür. pattern_offset aracı, 0x41346241 olarak verilen değeri uygun şekilde pack edip pattern içindeki yerini bulabilmiştir.

Aynı C kodu MIPS mimarisi için (big endian, 32 bit) derlenip pattern oluşturma ve debug işlemleri tekrarlanır.



EIP’in değeri pattern_offset aracına verildiğinde (0x62324162) eşleşen bir offset bulunamamasına karşın bu değer ters çevrilerek verildiğinde (0x62413262) değerin, pattern’in 37. byte’ından başladığı görülür.



Buradan şu sonuç çıkarılabilir. Pattern_offset aracı little endian sistemler için düzgün çalışmasına karşın big endian sistemlerde aynı başarıyı gösteremiyor ancak registerda görünen değeri ters çevirip yazarak aracı kullanmak mümkün. Çünkü araç değeri pack ederken byte dizilimini ters çevirmiş oluyor.
Bu cümleden little endian bir sistemin girdi olarak aldığı verileri ters çevirdiği genellemesine gitmek yanlış bir çıkarım olacaktır. Buna karşın araca parametre olarak hex formatında verilen 4 byte’lık değerin byte diziliminin pack işlemi sonucu ters çevrildiği söylenebilir.

Son olarak istismar kodu geliştirirken çok faydalı olan python’ın struct kütüphanesinin pack, unpack fonksiyonlarının kullanımından bahsetmek faydalı olacaktır. struct.pack fonksiyonu ikinci parametre olarak girilen değeri ilk parametrede belirtilen formata pack eder, struct.unpack fonksiyonu ise tam tersini yapar.
Yani 32 bit bir pointer’ı istismar kodunda kullanılabilecek bir string (binary packed) olarak ifade etmek için pack fonksiyonu aşağıdaki gibi kullanılabilir.

Little endian bir işlemci için:
>>> struct.pack('<I', 0xAABBCCDD)
‘\xdd\xcc\xbb\xaa'

Big endian bir işlemci için:
>>> struct.pack('>I', 0xAABBCCDD)
'\xaa\xbb\xcc\xdd'

Tam tersi işlemi yapmak yani bellekteki bir stringin işlemcideki değerini görmek içinse unpack fonksiyonu şu şekilde kullanılabilir.

Little endian bir işlemci için:
>>> hex(struct.unpack('<I', '\xaa\xbb\xcc\xdd')[0])
'0xddccbbaa'

Big endian bir işlemci için:
>>> hex(struct.unpack('>I', '\xaa\xbb\xcc\xdd')[0])
'0xaabbccdd'


Kaynaklar:
https://docs.python.org/2/library/struct.html
http://www.cs.umd.edu/class/sum2003/cmsc311/Notes/Data/endian.html

Viewing all articles
Browse latest Browse all 331

Trending Articles


Mide ağrısı için


Alessandra Torre - Karanlık Yalanlar


Şekilli süslü hazır floodlar


Flatcast Güneş ve Ay Flood Şekilleri


Gone Are the Days (2018) (ENG) (1080p)


Yildiz yükseltme


yc82


!!!!!!!!!! Amın !!!!!!!!!


Celp At Nalı (Sahih Tılsım)


SCCM 2012 Client Installation issue