- 8 Eyl 2016
- 1,646
- 998
Merhabalar, yeni eğitim başlıklarımız anti-disassembly üzerine olacak. Bu konuda ise giriş olarak sizlere temel olarak disassemble işleminden bahsedeceğim. Elimizdeki programı disassemble edip assembly seviyesinde inceleyeceğiz. Disassemble ederek elimizdeki uygulamanın assembly karşılığını inceleyip programımızın işlevini çözebiliyoruz.
Yukarıda gördüğünüz gibi çok basit bir programımız var.
Gcc ile test.c adlı dosyamın object file yani nesne kodunu elde ediyorum. Bu da a.out olarak bana çıktı oluşturuyor. Gcc, GNU C Compiler yani C dosyamızı derleme görevini üstleniyor bu aşamada.
Daha sonra out uzantılı dosyamızı çalıştırdık ve işlevi gereği ekrana 5 tane hello World çıktısı verdi. Şimdi aklınızdan geçenleri duyar gibiyim. Out, object file, nesne kodu? Bunlar ne, yeniyor mu içiliyor mu? Burada nesne kodunun yani üretilen out dosyasının ne olduğuna değineyim. Daha önceden Alt programlarla bağlantı kurma adlı konumda linker dediğimiz bir işlemden bahsetmiştim. Orada ne demiştim, programın kaynak kodu compiler ile derlenir ve obje kodu yani makine hali üretilir daha sonra linker yani bağlayıcı vasıtasıyla çalıştırılabilir executable formatına dönüştürülür. Yani çalıştırılabilir dosya formatı da nesne dosyalarının bağlanması neticesinde elde edilir. Ben burada direkt exe formatına ihtiyaç duymadan makine halini elde ettim, yani nesne dosyasını. Bu nesne dosyası, kaynak kodun derlenmesiyle elde ediliyor. Çalıştırılabilir dosya ise nesne dosyalarının linker işlemine tabii tutulması ile elde edilir olayımız kısaca bu. Bir programın çalıştırılabilir formata gelmesi için işlemcinin anlayacağı makine halinin oluşturulması gerekiyor. Bu makine hali de nesne dosyamız, bizde kendisini inceleyip C programımızın işlevini anlayabiliriz. Olayımız zaten disassemble yapmak.
Peki bu nesne dosyasını nasıl analiz edebiliriz? Ben kali Linux sistemimi kullanacağım, içerisinde hazırda gelen object file analizi yapan objdump aracını kullanacağım.
Şekildeki komutumuzu kullanarak nesne dosyamı disassemble ediyorum.
Burada bizi epey bir instruction, assembly kodları karşılıyor. Şimdi buradaki yoğunluk arasında kaybolmadan biraz yoğunluğu azaltıp incelememizi yapalım.
Grep komutu ile kullanarak main bölgesinden itibaren 20 satırı göstermesini istiyorum.
Bakın bize main geçen noktaları belirtiyor. Şimdi bu bir kenar da dursun, ben az önceki komutuma ekleme yapacağım.
Burada nesne kodumu intel işlemci mimarisinde görüntülemek istiyorum. Biliyorsunuz ki farklı işlemciler var ve assembly dilleri birbirinden biraz farklı oluyor. Buna çok basit dikkat çekelim.
Bu satır işlemci mimarisini değiştirmeden önceki hali.
Bu da aynı memory adresinin intel işlemcisindeki assembly kodu. 1199 numaralı adresteki lea komutu ile yapılan atamalardaki syntax farkını görüyorsunuzdur. İşlem aynı, ancak söz dizimi işlemciler arasında farklılık göstermektedir. Tabii burada assembly mikroişlemci mimarileri üzerine çok durmayacağımdan bazı kavramları aktarıyorum bunları araştırabilirsiniz. Şimdi buradaki bölmeleri ayrı ayrı kısaca açıklayacağım.
Bu kısım hafıza adreslerini gösteriyor. Karşısındaki komut hafızada hangi adreste işleniyor bunu görebiliyoruz. Yani burada bir takım komutlar var bu komutlar ram üzerinde bu adreslerde işleniyor. Bunlar geçici adresler tabii, program çalışırken ram de geçici olarak tutuluyor biliyorsunuz.
Bu bölgemiz ise instructionlarımız. Yani işlenen komutlarımızın anlamları diyebiliriz. Diğer bir ifadeyle programımızın makine dilindeki halidir.
Bu kısım ise bildiğiniz üzere assembly komutlarını gösteriyor. Yani makine dilinin bir üst seviyesinde bizim anlayacağımız dilde assembly kodlarıyla programın karşılığını görebiliyoruz.
Mesela burada call komutu ile print fonksiyonuna çağrı yapılarak ekrana yazma işlemi gerçekleştiriliyor.
Bu bölgede bir takım veri aktarımları gerçekleştiriliyor. Burada ax içindeki değer yine ax içine aktarılıyor şeklinde yorumlayabiliriz assembly bilgimizle. Yani bir Exchange komutu işleniyor. Daha önce assembly veri aktarım konumuzda anlattığımız bir komuttur kendisi. Aslında burada bir no-opcode işlemi gerçekleştiriliyor. Burada 8086 işlemcimiz nop kodunu xchg ax,ax olarak yürütüyor.
Ayrıca yukarıda 66 90 opcode bölmesinde göreceğiniz 66 yani 0x66 inctruction değerimizi yer değiştirme işlemlerinde görürüz. Biraz daha inceleyelim.
Örneğin bu bellek adresimizde işlenen komutumuz jmp. Yani jmp 11d5 yaparak 11d5 bellek adresine bir atlama gerçekleştiriyor.
Burada cmp iki işlenen değeri karşılaştırıyor. Ebp-0xc aslında bizim döngümüzün başlangıç değeri olan 0 değerini tutuyor. Karşılaştırma yapılan değer ise 4 değeri. Bizim cmp komutumuz genellikle koşullu işlemlerde karşımıza çıkar.
Kaynak kodumuzda da hatırladığınız üzere 5den küçük olacaksın abicim, yani sen 4 müsün? Cmp bunu kontrol ediyor.
Cmp pası jle komutuna atıyor. Jle ise programın akışını belirli bir offsete yönlendiriyor, o offset ise gördüğünüz üzere 11bf. Yanı cmp’e göre yapıyor bu yönlendirmeyi. Yani döngünün koşuluna göre programın akışını yönlendiriyor. Ya döngüyü bitiriyor, ya da akışı devam ettiriyor.
Yani kısaca bu konumuzda elimizdeki nesne kodunun bu bir yürütülebilir dosya da olabilirdi disassemble ederek incelemesini gerçekleştirdik. Bu çok geniş bir inceleme olmadı tabii ancak disassemble olayını, işleyişini, amacını sizlere kısaca özet geçmiş oldum. Daha önceki program analizi konularıma bakabilirsiniz.
Okuduğunuz için teşekkürler.
Yukarıda gördüğünüz gibi çok basit bir programımız var.
Gcc ile test.c adlı dosyamın object file yani nesne kodunu elde ediyorum. Bu da a.out olarak bana çıktı oluşturuyor. Gcc, GNU C Compiler yani C dosyamızı derleme görevini üstleniyor bu aşamada.
Daha sonra out uzantılı dosyamızı çalıştırdık ve işlevi gereği ekrana 5 tane hello World çıktısı verdi. Şimdi aklınızdan geçenleri duyar gibiyim. Out, object file, nesne kodu? Bunlar ne, yeniyor mu içiliyor mu? Burada nesne kodunun yani üretilen out dosyasının ne olduğuna değineyim. Daha önceden Alt programlarla bağlantı kurma adlı konumda linker dediğimiz bir işlemden bahsetmiştim. Orada ne demiştim, programın kaynak kodu compiler ile derlenir ve obje kodu yani makine hali üretilir daha sonra linker yani bağlayıcı vasıtasıyla çalıştırılabilir executable formatına dönüştürülür. Yani çalıştırılabilir dosya formatı da nesne dosyalarının bağlanması neticesinde elde edilir. Ben burada direkt exe formatına ihtiyaç duymadan makine halini elde ettim, yani nesne dosyasını. Bu nesne dosyası, kaynak kodun derlenmesiyle elde ediliyor. Çalıştırılabilir dosya ise nesne dosyalarının linker işlemine tabii tutulması ile elde edilir olayımız kısaca bu. Bir programın çalıştırılabilir formata gelmesi için işlemcinin anlayacağı makine halinin oluşturulması gerekiyor. Bu makine hali de nesne dosyamız, bizde kendisini inceleyip C programımızın işlevini anlayabiliriz. Olayımız zaten disassemble yapmak.
Peki bu nesne dosyasını nasıl analiz edebiliriz? Ben kali Linux sistemimi kullanacağım, içerisinde hazırda gelen object file analizi yapan objdump aracını kullanacağım.
Şekildeki komutumuzu kullanarak nesne dosyamı disassemble ediyorum.
Burada bizi epey bir instruction, assembly kodları karşılıyor. Şimdi buradaki yoğunluk arasında kaybolmadan biraz yoğunluğu azaltıp incelememizi yapalım.
Grep komutu ile kullanarak main bölgesinden itibaren 20 satırı göstermesini istiyorum.
Bakın bize main geçen noktaları belirtiyor. Şimdi bu bir kenar da dursun, ben az önceki komutuma ekleme yapacağım.
Burada nesne kodumu intel işlemci mimarisinde görüntülemek istiyorum. Biliyorsunuz ki farklı işlemciler var ve assembly dilleri birbirinden biraz farklı oluyor. Buna çok basit dikkat çekelim.
Bu satır işlemci mimarisini değiştirmeden önceki hali.
Bu da aynı memory adresinin intel işlemcisindeki assembly kodu. 1199 numaralı adresteki lea komutu ile yapılan atamalardaki syntax farkını görüyorsunuzdur. İşlem aynı, ancak söz dizimi işlemciler arasında farklılık göstermektedir. Tabii burada assembly mikroişlemci mimarileri üzerine çok durmayacağımdan bazı kavramları aktarıyorum bunları araştırabilirsiniz. Şimdi buradaki bölmeleri ayrı ayrı kısaca açıklayacağım.
Bu kısım hafıza adreslerini gösteriyor. Karşısındaki komut hafızada hangi adreste işleniyor bunu görebiliyoruz. Yani burada bir takım komutlar var bu komutlar ram üzerinde bu adreslerde işleniyor. Bunlar geçici adresler tabii, program çalışırken ram de geçici olarak tutuluyor biliyorsunuz.
Bu bölgemiz ise instructionlarımız. Yani işlenen komutlarımızın anlamları diyebiliriz. Diğer bir ifadeyle programımızın makine dilindeki halidir.
Bu kısım ise bildiğiniz üzere assembly komutlarını gösteriyor. Yani makine dilinin bir üst seviyesinde bizim anlayacağımız dilde assembly kodlarıyla programın karşılığını görebiliyoruz.
Mesela burada call komutu ile print fonksiyonuna çağrı yapılarak ekrana yazma işlemi gerçekleştiriliyor.
Bu bölgede bir takım veri aktarımları gerçekleştiriliyor. Burada ax içindeki değer yine ax içine aktarılıyor şeklinde yorumlayabiliriz assembly bilgimizle. Yani bir Exchange komutu işleniyor. Daha önce assembly veri aktarım konumuzda anlattığımız bir komuttur kendisi. Aslında burada bir no-opcode işlemi gerçekleştiriliyor. Burada 8086 işlemcimiz nop kodunu xchg ax,ax olarak yürütüyor.
Ayrıca yukarıda 66 90 opcode bölmesinde göreceğiniz 66 yani 0x66 inctruction değerimizi yer değiştirme işlemlerinde görürüz. Biraz daha inceleyelim.
Örneğin bu bellek adresimizde işlenen komutumuz jmp. Yani jmp 11d5 yaparak 11d5 bellek adresine bir atlama gerçekleştiriyor.
Burada cmp iki işlenen değeri karşılaştırıyor. Ebp-0xc aslında bizim döngümüzün başlangıç değeri olan 0 değerini tutuyor. Karşılaştırma yapılan değer ise 4 değeri. Bizim cmp komutumuz genellikle koşullu işlemlerde karşımıza çıkar.
Kaynak kodumuzda da hatırladığınız üzere 5den küçük olacaksın abicim, yani sen 4 müsün? Cmp bunu kontrol ediyor.
Cmp pası jle komutuna atıyor. Jle ise programın akışını belirli bir offsete yönlendiriyor, o offset ise gördüğünüz üzere 11bf. Yanı cmp’e göre yapıyor bu yönlendirmeyi. Yani döngünün koşuluna göre programın akışını yönlendiriyor. Ya döngüyü bitiriyor, ya da akışı devam ettiriyor.
Yani kısaca bu konumuzda elimizdeki nesne kodunun bu bir yürütülebilir dosya da olabilirdi disassemble ederek incelemesini gerçekleştirdik. Bu çok geniş bir inceleme olmadı tabii ancak disassemble olayını, işleyişini, amacını sizlere kısaca özet geçmiş oldum. Daha önceki program analizi konularıma bakabilirsiniz.
GNU Debugger Stack Segment Analizi
4 parametre alan fonksiyonum var. Flag, buffer isminde local değişkenlerim var bunların değerlerini de girdim. Main de fonksiyonumuzu aldığı parametrelere göre değerlerini vererek çağırdım. Programı derleyip GDB de çalıştırdım. Şimdi test_function çağrılmadan önceki instructiona ve...
turkhackteam.org
Okuduğunuz için teşekkürler.