Bir Program CPU Tarafından Nasıl Çalıştırılma Durumuna Getirilir?

04:05 ,

Bir program yazılıp derlendikten sonra nasıl çalışma aşamasına getirilmektedir? Eğer çalıştırma ortamı bir işletim sisteminin yüklü olduğu bir ortam değilse bizim programı bir biçimde reset vektöründen itibaren o ortamın belleğine (Ram) yüklememiz gerekir. Tipik olarak mikrodenetleyici (microcontroller) ile çalışmalar bu biçimde yürütülmektedir. Bir mikrodenetleyicinin içerisinde bir CPU'nun yanı sıra bir RAM ve EEPROM bellekte bulunmaktadır. Bu EEPROM belleğe mikrodenetleyicinin program belleği denilmektedir. Bu ortamlarda programcı programını başka ortamlarda yazıp derler ve "programlayıcı" denilen bir devre yoluyla mikrodenetleyicinin içerisine yerleştirir. Mikrodenetleyici dünyasının dışında genellikle bir işletim sisteminin üzerinde çalıyor durumdayızdır. (Gelişmiş mikrodenetleyicilerde işletim sistemi yüklenerek kullanılabilmektedir.)

Biz bir işletim sisteminin üzerinde çalışıyorsak programı yüklemek ve onu CPU'ya işletecek hade vermek tamamen artık işletim sisteminin bir görevidir. İşletim sistemlerinin programı yükleyip çalışır hale getiren kısmına kavramsal olarak yükleyici (loader) denilmektedir. İşletim sistemi üzerinde çalışılan bir ortamda tipik olarak program bir editörle (ya da IDE ile) yazılır. Derleyici ile derlenerek amaç dosya (object file) oluşturulur. Bu amaç dosya da bağlama (link) işlemine sokularak çalıştırılabilen (executable) dosya elde edilir.

Amaç dosyaların (object file) ve çalıştırılabilen dosyaların (executable file) içerisinde ne vardır?

Çalıştırılabilen bir dosyanın içerisinde en azından derlenmiş ve ikilik sisteme dönüştürülmüş kodlar ve bu kodların kullandığı data'lar bulunmak zorundadır. Tabii bunların dışında çalıştırılabilen dosyaların içerisinde dosyanın işletim sistemi tarafından yüklenebilmesi için gereken başka bir takım metadata bilgiler de bulunmak zorundadır. Örneğin yükleyici programı RAM'e yükledikten sonra IP (ya da PC) yazmacının ilk değerini nasıl verecektir. Bilindiği gibi C'de programın akışı main gibi bir fonksiyondan başlar ya da biz dışarıdan link edilirken nerden başlayacağını ayarlayabiliriz. İşte program akışının başlatılacağı adrese "enrty point" denilmektedir. Bu adres çalıştırılabilen dosyanın içerisinde belli bir yerde bulunur. Yükleyici de programı yükledikten sonra IP (ya da PC) yazmacına bu değeri atar. Sonra da program kendi kendine çalışmaya devam eder. Tabii çalıştırılabilen dosyaların içerisinde "entry point" dışında daha gerekli olan pek çok yükleme bilgisi de bulunmaktadır. O halde  çalıştırılabilen bir dosya kabaca şu biçimde çalıştırılma durumuna getirilmektedir:

  1. Yükleyici çalıştırılabilen dosyanın başlık kısımlarına bakarak onun içindeki kod ve data bilgilerinin nerelerde olduğunu anlar. Çalıştırılabilen dosyanın kod ve data bölümlerini bellekte uygun bir yere yükler.
  2. Yükleyici çalıştırılabilen dosyanın başlık kısımlarından programın “entry point” adresini belirler ve CPU'nun IP (ya da PC) yazmacına kodun başlangıç adresini yükler. Sonra da CPU'yu serbest bırakır.
  3. CPU IP (ya da PC) adresinde gösterilen yerden itibaren talimatları işletmeye başlar.



Döndürme (Rotate) Komutları

02:18 ,

Döndürme işlemi için C/C++ dillerinde özel bir operatör bulundurulmamıştır. Döndürme işlemi ötelemeye benzemektedir. Ancak ötelemede kaybedilen bit döndürme işleminde diğer tarafı beslemede kullanılır. Örneğin döndürme işlemlerini özetle aşağıdaki resimde olduğu gibi gösterilebiliriz:


Döndürme işlemi bir sayının belli kısımlarının yer değiştirilmesi için kullanılabilmektedir. Ayrıca sayıyı ötelemek yerine döndürdüğümüzde biz onu yeniden ters döndürerek eski haline de getirebiliriz. Diğer özel bazı durumlarda da döndürme işleminden faydalanılmaktadır. Intel işlemcilerinde 4 tane döndürme (rotate) komutu vardır. Komutların ikisi carry’li döndürme için diğer ikisi de carry’siz döndürme için kullanılır:

Carry’li döndürmede sanki CF bayrağı sayının en yüksek anlamlı ekstra biti gibi davranmaktadır. Dolayısıyla döndürmeye o da dahil edilir. Örneğin sağa bir kez carry’li döndürmeyi yukarıdaki ilk resimde görüleceği üzere RCR ve ROR komutlarında gösterildiği gibi carry flag'de ötelemeye dahil edilmiştir. Rotate komutlarının biçimleri de tamamen öteleme komutları gibidir. Yani bir kez döndürme için ayrı bir makine komutu vardır. Birden fazla kez döndürme sabit bir değerle ya da CL yazmacıyla yapılabilmektedir. Komut uzunlukları da yine öteleme komutlarında olduğu gibidir. Örneğin bazı geçerli döndürme komutları şöyledir:

ror eax, 6
rcl eax, 9
rcr eax, cl

CF bayrağı her zaman (carry’li döndürme ve carry’siz döndürme durumlarında da geçerlidir.) son döndürmede kaybedilen biti tutar (ötelemede olduğu gibi). Ancak döndürme döndürülecek değerin bit uzunluğunun bir eksiğinden fazla olursa bu bayrak tanımsız durumda olur. OF bayrağı yine yalnızca bir kez öteleme söz konusu olduğunda etkilenir. SF, ZF, AF, PF bayrakları ise normal biçimde etkilenmektedir.

32 Bit Intel Ailesinde çağırma biçimi (calling convention) kavramı

06:53 ,
Fonksiyonların çağrılması ve geri dönüş değerlerinin alınması konusundaki belirlemelere çağırma biçimi (calling convention) denilmektedir. Derleyicilerin bir fonksiyonu derlerken stack'e parametreleri hangi sirayla push edeceginin veya parametrelerin kullanılacak register'a hangi sıraya göre atılacağı gibi kuralların belirlendiği modeldir. Dolayisiyla farkli diller arasinda veya farklı ortamlar arasinda kullanilacak olan fonksiyonlarin bildiriminde bunu belirtmek gerekir.


Her programlama dilinde veya sistemde farklılık gösterebilir. Her dillin kendine ait çağırma biçimi olabilir hatta assembly ile istedigimiz biçimde convention oluşturabilir ve kullanabiliriz. Fakat bunların taşınabilir ve farkllı dillerle kullanılabilir olması durumunda standartlara uyma yükümlülüğü vardır. Özetle şu sorulara cevap bulmaya çalışır:

  • Çağrılan fonksiyon hangi yazmaçları bozma hakkına sahiptir, hangi yazmaçları korumak zorundadır?
  • Çağıran fonksiyon ile çağrılan fonksiyon arasında parametre aktarımı nasıl yapılacaktır?
  • Çağrılan fonksiyonun geri dönüş değeri çağrılan fonksiyona nasıl aktarılacaktır?
  • Parametre aktarımı için Stack kullanıldı ise stack'i düzenleme işi kime aittir?


Çağırma biçimi konusu C standartlarını ilgilendiren bir konu değildir. Çünkü C standartları böylesi aşağı seviyeli belirlemeleri derleyicilere bırakmıştır. Dolayısıyla çağırma biçimlerini oluşturmak için gereken anahtar sözcükler derleyicilerde bir eklenti (extension) biçiminde bulunurlar. Çağırma biçimlerine ilişkin anahtar sözcükler genel olarak tür belirten sözcük ile fonksiyon isimlerinin arasına yerleştirilmektedir. Örneğin:

  • Microsoft derleyicilerinde çağırma biçimleri yukarıdaki örnekte olduğu gibi iki alt tire ( __ ) ile başlayan anahtar sözcüklerle temsil edilmektedir. 

void __cdecl foo(int a, int b) {
   
}


  • gcc derleyicilerinde ise fonksiyon özellikleri (function attributes) biçimindeki bir sentaks ile temsil edilir. 

void __attribute__((cdecl)) foo(int a, int b) {
   
}

API ve ABI Kavramları Üzerine

Yazılımların tüm mimarilerde çalışması ve geriye doğru uyumlu (backward compatibility) olması en çok istenen özelliklerdir. Geliştirilen yazılımın belirli bir dağıtım veya mimariye bağımlı olmayıp, taşınabilir olması büyük kolaylık sağlayacaktır. Sistem seviyesinden bakıldığında taşınabilirlikle ilgili 2 farklı özellik bulunması gerekir. Bunlar;
  • Application Programming Interface (API)
  • Application Binary Interface (ABI)
Şimdi sırasıyla bu kavramları inceleyelim;

ABI ( Application Binary Interface )

ABI, yazılım bileşenleri arasında belirli bir mimari için amaç dosyalar (object file) arasında arayüz  (interface) tanımlamaktadır. ABI demek ayrı ayrı derlenmiş modüllerin bir arada çalışabileceklerine dair alt seviyeli ve detaylı teknik kurallar bütünüdür. ABI uygulama bileşenleri arasında makine kodu seviyesinde uyumluluğu sağlar. Bu uyum korunduğu müddetçe aralarında etkileşim bulunan yazılım bileşenlerinin arka planı değişse de yeniden derlenmeye ihtiyaç duymaksızın eskisi gibi kulanmaya devam ederler.

  • Fonksiyonların nasıl çağrılacağı (calling convention), 
  • Parametrelerin nasıl geçirileceği, 
  • Yazmaçların kullanım şekilleri,
  • Sistem çağrılarının gerçekleştirilme biçimi, 
  • Amaç dosyaların bağlanması (linklenmesi), 
  • Amaç (object dosya) formatı gibi konular ABI kavramı içerisinde değerlendirilebilir. 
Yazılım geliştirme sürecinde ABI kavramı çok karşımıza çıkmaz. Kullanılan geliştirme araçları hedef platform için belirlenen ABI kurallarına uygun kod üretirler. Alt seviye işler yapanlar için bu genelleme doğru değildir. Sistem düzeyinde kod geliştirmek isteyenlerin üzerinde çalıştığı ve diğer çalışmasını istediği sistemlerin ABI standartlarını bilmesi gerekir.


API ( Application Program Interface )

API, uygulamaların kaynak kod seviyesinde birbirleriyle iletişim kurabilmelerine imkan sağlayan, önceden kararlaştırılmış arayüzler (interface) olarak tanımlanabilir. Genellikle her bir API, daha karmaşık ve alt seviye detaylar içeren bir sürecin, çeşitli arayüzlerle (fonksiyon çağrıları gibi) soyutlanmasını sağlar. Bu şekildeki bir soyutlama üzerinden kullanılan API'yi hizmet olarak veren yazılım bileşenleri güncellense ve alt tarafta yapılan işlemlerle ilgili yöntemler değiştirilmiş bile olsa, API seviyesinde aynı arayüz sağlandığı müddetçe bu API'yi kullanan uygulamalar için bir değişiklik yapılmasına gerek olmayacaktır.




Kısaca bir yazılımın, kullandığı kütüphanelerin,servislerin sonraki versiyonlarında herhangi bir değişikliğe gitmek zorunda kalmadan problemsiz çalışabilmesini sağlayan bir arayüz tanımlanması API kavramının ürünüdür.

Fonksiyon Çağrılarında Yazmaç Değerlerinin Korunması

07:09 , ,

Sembolik makine dilinde bir fonksiyonu CALL ettikten sonra akış geri döndüğünde yazmaçların durumu ne olacaktır? CALL edilen fonksiyon yazmaçların değerlerini değiştirmiş olabilir. Bu durumda CALL etmeden önce çağıran fonksiyon yazmaçlarda belli değerleri saklamışsa CALL işleminden sonra artık o değerlerin yazmaçlarda olmayabileceğini göz önüne almalıdır. İşte bu konuda da çağıran fonksiyonla (caller) çağrılan fonksiyon (callee) bir anlaşma yapabilirler. Bu anlaşmaya göre çağrılan fonksiyon bazı yazmaçları koruyabilir, bazılarını korumayabilir. Örneğin C derleyicilerinde pek çok çağırma biçiminde çağrılan fonksiyonun EAX, ECX ve EDX yazmaçlarını bozmasına izin verilmiştir. Ancak diğer yazmaçları çağrılan fonksiyon korumalıdır. Yani onların değerleri akış fonksiyona girdiğinde neyse çıktığında da aynı olmalıdır. Tabii bazı yazmaçların korunmasında bir anlaşma yapılmışsa bu durum çağrılan fonksiyonun o yazmaç değerlerini hiç değiştirmeyeceği anlamına gelmez. Çağrılan fonksiyon eğer bu yazmaçların değerlerini değiştirecekse önce onların değerlerini saklaması gerekir. Bunun için stack kullanılmaktadır. Korunması istenen register değerleri fonksiyona girmeden push eder ardından fonksiyondan çıkmadan önce de pop eder geri yükleyebilir. Ancak onları bu biçimde koruma sorumluluğu çağrılan fonksiyona aittir.

Çağıran ve çağrılan fonksiyonların her ikisini de biz yazacaksak hangi yazmaçların çağrılma sırasında çağrılan fonksiyonlar tarafından korunucağını yine biz kendimiz belirleyebiliriz. Eğer biz yalnızca çağrılan fonksiyonu yazacaksak bu durumda çağıran fonksiyonun hangi yazmaçların korunacağı konusundaki beklentisini karşılamamız gerekebilir. Eğer biz yalnızca çağıran fonksiyonu yazacaksak çağrılan fonksiyonun hangi yazmaçları koruduğunu bilmek yine bize fayda sağlayabilir. Örneğin biz programın büyük kısmını C’de yazmış olalım ve oradan sembolik makine dilinde yazmış olduğumuz fonksiyonu çağırmak isteyelim. Bu durumda bizim C derleyicisinin yazmaç koruması konusundaki beklentilerini karşılamamız gerekir. Çünkü derleyici bazı yazmaçların fonksiyon tarafından bozulmayacağı beklentisiyle fonksiyon çağrısından sonra o yazmaçlardaki değerleri kullanıyor olabilir. 

Sonuç olarak ileride karşımıza çokça çıkacak yazmaç değerlerinin korunması ile ilgili kullanılan yöntemler bu şekilde daha anlaşılır hale gelecektir umarım.

32 Bit Windows Sistemler için "Hello World" Assembly Programı


Tamamen sembolik makine diliyle yazılmış ekrana "Hello World" yazısını çıkartan temel assembly  programı Windows'ta şöyle yazılabilir:


  • Aşağıdaki resimde görüleceği gibi ilk adım olan yazılan kodu 1 numaralı komutta olduğu gibi nasm ile derlenebilir:


Buradan ürün olarak HelloWorld.obj dosyası elde edilecektir. Komuttaki –f win32 seçeneği amaç kodun 32 bit Windows sistemleri için COFF formatında olmasını sağlar.

  • Yukarıdaki resimde görüleceği gibi 2 numaralı komutta olduğu gibi Microsoft’un link.exe programı ile link edilmelidir.

Komuttaki /entry:start seçeneği programın başlangıç noktasını belirlemek için kullanılır. HelloWorld.asm programının başlangıç noktası _start etiketinin bulunduğu yerdedir. Windows uygulamaları “GUI” ve “Console” olmak üzere ikiye ayrılmaktadır. Console uygulamalarında işletim sistemi programı yüklendiğinde bir console ekranını kendisi oluşturmaktadır. Console uygulaması için /subsystem:console seçeneği kullanılmalıdır. Link edilecek dosya HelloWorld.obj dosyasıdır. "Hello World" programında kernel32.dll içerisindeki çeşitli API fonksiyonları (sistem fonksiyonları) kullanılmıştır. Bu nedenle link aşamasına "kernel32.lib" isimli import kütüphanesinin dahil edilmesi gerekmektedir. Bu programda üç API fonksiyonu çağrılmıştır. Önce GetStdHandle API fonksiyonuyla (kodun içerisinde 1 numaralı kısım) console ekranının handle değeri elde edilmiş, sonra WriteFile API fonksiyonu (kodun içerisinde 2 numaralı kısım) ile oraya yazma yapılmıştır. Prosesin sonlanması için ExitProcess API fonksiyonuyla (kodun içerisinde 3 numaralı kısım) gerçekleştirilmiştir. C’nin standart exit fonksiyonu da zaten ExitProcess API fonksiyonu çağırır. Ve link işleminden sonra üretilen exe dosya çalıştırıldığında aşağıdaki gibi başarıyla çalıştığı görülebilir.





Assembly ile Sistem Fonksiyonu Çağırma [Linux]

06:50 , , , ,


Assembly programı içinden Linux sistem fonksiyonları çağrılırken uyulması gereken standart işlemler vardır. Linux System Call Table 'da görülebileceği gibi hangi register'ları set etmemiz gerektiği, parametreleri hangi register aracılığıyla göndermemiz gerektiği bilinmesi gerekmektedir. 80h kesmesinden sonra hangi fonksiyonun çağrılacağı bu registerlardaki değerler üzerinden belirlenmektedir.


Aşağıdaki örnekte ekrana "Hello world" yazan programı yazacağız:


Yukarıda görüldüğü gibi ;

  • eax'e hangi sistem fonksiyonu yazılacaksa onun numarası 
  • ebx'e birinci parametre olan file descripter değeri -stdout için 1
  • ecx'e yazılacak string'in adresi
  • edx'e ise  kaç karakter yazılacak ise onun uzunluğu girilmelidir.



Gerekli çağırma parametreleri girildikten sonra 80h kesmesiyle kernel mod'a geçiş yapar ve sistem fonksiyonları çağrılır.



Yukarıdaki kod derlenip,link edilip çalıştırıldığında ekrana Hello World yazısı çıkacaktır. Ekrana yazma işini sys_write sistem fonksiyonu ile stdout'a yazarak gerçekleştirilmiştir.

Assembly içinden C Fonksiyonu Çağırma [Linux]

03:23 , , , ,


Linux'te assembly içinden C fonksiyonu çağrılacak ise yapılması gereken temel işlemler aşağıdaki örnekte anlatıldığı gibidir:

  • Çağrılacak fonksiyon .text alanında extern bildirimi yapılması gerekmektedir. 
  • Fonksiyon çağrılmadan önce ise parametreler soldan sağa stack'e push edilmesi gerekmektedir.



sample.asm isimli dosya içine yukarıdaki gibi kod yazıldıktan sonra :
  • nasm -f elf32 sample.asm ile object dosya oluşturulur
  • gcc -m32 -o sample sample.o ardından gcc ile çalıştırılabilir dosya oluşturulur.



  • gcc -m32 veya -m64 flaglerini kullandığımızda hata veriyorsa gcc için kurulum yapılması gerekmektedir.




  • apt-get install gcc-multilib kurulumu ile 32 - 64 bit derleme işlemlerini gerçekleştirebiliriz.




sample isimli çalıştırılabilir dosyamız oluşturulmuş oldu. İleride gcc'yi de devre dışı bırakacağız ve doğrudan linker ile elf dosyamızı oluşturacağız.

Assembly' de yazılıp derlenen bir fonksiyonu Windows'ta çağırma

Windows sistemlerde assembly' de yazdığımız bir fonksiyon C içerisinden aşağıdaki gibi çağrılır;


Derleme işlemi yapıldıktan sonra oluşan .obj dosyasını Visual Studio'da açılan C projesine dahil edelim.


 Aşağıdaki gibi örnek bir kod derleme&link işleminin ardından çalıştırıldığında hatasız bir şekilde çalıştığı gözükecektir.


Kısaca fonksiyonun ne yaptığını anlatırsak:
  1. Parametreler stack'e sağdan sola doğru aktarılmış yani fonksiyonla gönderilen iki parametre stack'in üstüne sırasıyla konulmuş.
  2. EBP değeri stack'e konulmuş /*geri dönüş yapıldığında akışın sorunsuz devam etmesi için*/ 
  3. ESP değeri EBP değerine atanmış
  4. EDX'e sağdan ilk parametrenin adres değeri atanmış
  5. EDX'in içinde bir adres bulunmakta bu adresin gösterdiği değer EAX'e atanmış. ( Yani ilk parametrenin değeri EAX'e atanmış )
  6. EAX içindeki değer EBP-4'e konulmuş. ( birinci parametre'nin gösterdiği adresteki değer artık burada )
  7. İkinci parametrenin adresi ECX'e atanmış
  8. İkinci parametrenin değeri EAX'e atanmış
  9. İkinci parametre' nin değeri EDX (edx'in içinde 1. parametrenin adresi var) aracılığıyla 1. parametrenin gösterdiği yere atanmış. (ilk değişim gerçekleşti)
  10. Stack'te saklanan birinci parametre'nin değeri de [ EBP-4 ]  EAX yazmacına atandı. 
  11. ECX yazmacında ikinci parametrenin adresi olduğuna göre şimdi de EAX (birinci parametrenin değeri bunun içindeydi) yazmacındaki değer ikinci parametrenin gösterdiği yere aktarılmış oldu.
  12. EBP'nin değeri de ESP'ye aktarıldı. 
  13. Ardından stack'te tutulan eski ebp değeri de pop ile geri alınmış oldu.  /*geri dönüş yaptığında akışın sorunsuz devam etmesi için*/


Not1: Yukarıdaki işlemlerden anlayacağınız üzere stack'i düzenleme işi çağrılan (callee) fonksiyon tarafından gerçekleştirilmiştir. Fonksiyon çağrılarında stack düzenlemesi önemli bir konudur ve ileride daha detaylı anlatılacaktır.

Not2: Bu swap işlemi daha basit şekilde yazılabilmektedir. Bu yazıda en kısa biçimde kod yazılmaya çalışılmamıştır. 

32 Bit Linux Sistemler için "Hello World" Assembly Programı


Linux sistemlerinde assembly diliyle "Hello World" programının kodu aşağıdaki gibidir.


1 numaralı kod parçası ekrana yazı yazan,
2 numaralı kod parçası ise programdan çıkış işleminin yapıldığı kısımdır.

Linux’taki merhaba dünya programı Windows’takine göre daha sadedir. Bunun nedeni Linux’ta sistem fonksiyonlarının dinamik kütüphaneden değil kesme (interrupt) yoluyla çağrılıyor olmasıdır. Bu nedenle ekrana yazı yazdırmak için "sys_write", programı sonlandırmak için de "sys_exit" isimli sistem fonksiyonu çağrılmıştır. Sistem fonksiyonları çağrılmadan önce onların parametreleri yazmaçlara yerleştirilmektedir. Her sistem fonksiyonunun bir numarası vardır. Çağrılacak sistem fonksiyonunun numaraları 32 bit sistemde EAX yazmacına yerleştirilir. Sonra sırasıyla EBX, ECX, EDX yazmaçlarına da fonksiyonun parametreleri yerleştirilmektedir. Örneğin void sys_exit(int exitcode) fonksiyonu şöyle çağrılmıştır; Yukarıdaki iki numaralı kod parçasında görüleceği üzere eax yazmacına 1 atanması sys_exit fonksiyonunu temsil etmekte, ebx yazmacına 0 atanması ise exitcode parametresine 0 değerinin gönderildiğini ve int 80h kesmesi ile de fonksiyonun çağrıldığını (call) temsil eder. Şimdilik bu kadar bilinmesi yeterlidir. Linux’taki sistem fonksiyonlarının çağrılma biçimi daha sonra detaylı biçimde anlatılacaktır.


1. kısım derleme işlemidir. Derleme işlemi için nasm programı kullanılmıştır.
2. kısım link işlemidir. Linker ise GNU projesi kapsamında geliştirilmiş olan Linux’un temel linker programı "ld" isimli programdır. ld programı kullanılırken –o seçeneği ile çalıştırılabilen dosyaya isim verilmiştir. Eğer link sırasında çalıştırılabilen dosyaya isim verilmezse default olarak a.out ismi kullanılır. Ayrıca Linux sistemlerinde "ld" programı ile link işlemi yapılırken "entry point" verilmediğine dikkat ediniz. "ld" linker’ı default olarak _start adresini "entry point" olarak almaktadır. Link işlemi bittikten sonra yukarıda görüleceği üzere programımız başarıyla çalışmış ve ekrana Hello World yazısı yazdırılmıştır.

Assembly ile program yazma

03:22 , ,

Assembly?


Assembly dili işlemcilerin instruction setlerinde yer alan komutları  ve registerları kullandıklarından dolayı donanım bağımlı bir dildir. Intel işlemciler için yazılan bir assembly programının ARM işlemcilerde çalışma şansı yoktur. Bu nedenle PC'lerde en çok kullanılan işlemci olan [şimdilik] x86 mimarisine üzerinden yazılar yazılacaktır. Aşağıda görüldüğü gibi ilk başlarda donanımlar ile iş yapan sistemler tasarlamış ve programlama dediğimiz iş hardware seviyesinde olmuştur. Ardından donanımlarda çalışabilecek makina kodlar üzerinden programlama işlemi gerçekleştirilmiş ardından 1 0' larla işlem yapmak yerine bunları etiketleyip assembly dili oluşturulmuş ve bu dil üzerinden insanların anlayacağı seviyeye ilk adım olan assembly dili ile programlama yapılmıştır. Ardından hardware'dan daha uzak ama insanın anlayabileceği High-Level diller ile programlama yapılmaya başlanmıştır.


Biz 1 0'larla program yazmanın bir üstü olan Assembly ile kod yazarak donanıma yanı bilgisayar mimarisine biraz daha yaklaşacağız. Böylece bilgisayar dünyasının arkasında duran o muazzam mimariyi daha iyi anlamış olacağız.

Neden NASM?

NASM hem Windows hem Linux tarafında olan bir assembler'dır. Bu sebeple her iki sistemde de rahatca çalışabilmek için nasm tercih edilmektedir. Her assembler'ın kendine özgü bir dili olduğu için kullanacağımız assembler'a özgü farklılıklar yaşanmaktadır. Fakat bir assembly dilini iyi öğrenmemiz diğer mimariye veya ortama geçip orada assembly yazmada da kolaylıklar sağlayacaktır. Kısacası bir assembler'da yazmayı ve bir işlemci mimarisini iyi öğrenin gerisi daha kolay olacaktır.

Yazılar ne şekilde olacak?


Daha çok how-to şeklinde yazmayı düşünüyorum. Her yazı içinde yazılan kod içinde öğrenilmesi gereken bilgileri açık bir şekilde anlatmaya çalışacağım.

Ön Hazırlık olarak nelerin kurulması gerekir?


Linux ve Windows ta NASM programının kurulması gerekmektedir.
Linux: sudo apt-get install nasm, Windows: exe dosya indirilip kurulum yapılmalı.)

Derleme işlemini nasm tarafında yapıldıktan sonra link işlemini linux'te gcc'ye veya ld linkerına havale edilebilir. Ya da doğrudan object dosyayı başka bir program içinde kütüphane şeklinde kullanabiliriz. Windows tarafında ise nasm ve Microsoft C/C++ linkerının kurulu olması gerekmektedir.

Derleme ile ilgili ipuçları?

32 - 64 bit cross platform derleme yapabilmek için linux için : apt-get install gcc-multilib

Ardından X.asm uzantılı dosyayı içine derlemek için:


  • nasm -f elf32 X.asm -o main.o "derlediğimiz object dosyayı linux'ta kullanacaksak"
  • nasm -f coff main.asm -o main.obj "derlediğimiz object dosyayı windows'ta kullanacaksak"

direktifleriyle oluşturabiliriz.

Ardından linux'te linker ile aşağıdaki gibi elf uzantılı çalıştırılabilir dosya oluşturulur.

  • ld -m elf_i386 --entry=main -o x.elf x.o



Çok Kullanılan Intel x86 Debuggerlar


Bir programı çalışırken incelemek için kullanılan yazılımlara debugger denilmektedir. Debugger'lar genellikle hata bulma amacıyla kullanılırlar. (Etimolojik olarak debug "hata ayıklama" anlamına gelmektedir. "Bug" sözcüğünden türetilmiştir.) Debugger'lar dinamik analiz araçlarındandır. Bir programı çalıştırmadan analiz eden araçlara "statik analiz araçları", çalıştırarak analiz eden araçlara "dinamik analiz araçları" denilmektedir. Debugger'lar çeşitli bakımlardan sınıflandırılabilmektedir. Örneğin; 
  • Bazı debugger'lar yüksek seviyeli dillerdeki kodları o dillere göre debug (bunlara "source level debugger" da denilmektedir) etmektedirler.
  • Bazıları makine kodu düzeyinde debug (bunlara "machine level debugger" da denilebilmektedir) etmektedirler.
  • Bazı debugger'lar yalnızca "user mode" programları debug ederken bazıları "kernel mode" programları da debug edebilmektedir (bunlara “kernel mode debugger”lar da denilmektedir).
  • Bazıları uzaktaki makinelerdeki programları debug (bunlara "remote debugger" da denilmektedir) edebilirler. 
  • Bazıları yalnızca o makinedeki programları debug ( bunlara "local debugger" lar da denilebilmektedir.) edebilmektedir 
Bazı debugger'lar debug faaliyeti sırasında kod üzerinde değişiklik yapılabilmektedir. Bazı debugger'lar komut satırından yönetilirler, bazıları GUI arayüzüne sahiptir. Intel x86 işlemcileri için en yaygın kullanılan debugger'lar şunlardır:

Microsost Visual Studio Debugger

Visual Studio IDE'sinin içerisinde hem kaynak kod düzeyinde hem de makine kodu düzeyinde debug işlemi yapan bir debugger vardır. Visual Studio Debugger'ı "user level" bir debugger'dır.

GNU Debugger (GDB)

GNU projesi kapsamında gcc ile birlikte geliştirilmiş en önemli debugger'lardan biridir. GDB UNIX/Linux sistemlerindeki temel debugger'dır. Windows ve MAC OS X sistemleri için de port edilmiştir. Aslında pek çok GUI araç arka planda GDB kullanmaktadır. (Bunlara GDB'nin frontend'leri de denilmektedir.) Örneğin Netbeans, Eclipse, Qt-Creator, SASM arka planda gdb debugger'ını kullanmaktadır. GDB'de "user level" bir debugger'dır. Hem kaynak kod düzeyinde hem de makine kodu düzeyinde debug yapabilmektedir.

IDAPRO Debugger

IDAPRO profesyonel hem "user level" hem de "kernel level" debugger'dır. Pek çok hacking işleminde birincil debugger olarak tercih edilmektedir. Kaynak düzeyinde ve makine kodu düzeyinde debug yapabilmektedir. IDAPRO arka planda çeşitli debugger'lardan da faydalanmaktadır. IDAPRO eski SoftIce Debugger'ının geliştirilmiş biçimidir. Çok fazla özelliğe sahiptir. Bu nedenle kullanımı biraz zordur.



Microsoft WinDbg

Windows'un "kernel level debugger" ıdır. DDK (ya da yeni ismi ile WDK) paketi içerisinde onun bir parçası olarak gelmektedir. WinDbg ile programların kernel moddaki çalışması hakkında analizler yapılabilmektedir.

KDB ve KGDB

Bu debugger’lar temelde Linux için düşünülmüş "kernel level debugger" lardır. KDB lokal (local) bir kernel debugger iken KGDB uzak (remote) makine için debugger'dır.

Çok Kullanılan Intel x86 Assembly Derleyicileri

07:01

Sembolik makine dillerinin bir standartı olmadığı için her derleyicinin kendine has bir kural seti bulunmaktadır. Bu nedenle sembolik makine dili derleyicileri arasında önemli farklılıklar bulunabilmektedir. Örneğin tüm 8086 işlemci ailesi aynı komut yapısına sahip olsa da bu komutların yazılış biçimleri ve çeşitli direktiflerin sentaksları ve anlamları sembolik makine dili derleyicisinden derleyicisine farklılık gösterebilmektedir. Çok kullanılan derleyiciler aşağıda listelenmiştir.

Microsoft MASM (Macro Assembler)

Microsoft’un assembly derleyicisine MASM denilmektedir. Bu derleyici Visual Studio IDE’sinin bir parçası olarak Windows sistemlerine yüklenebilmektedir. Microsoft’un 32 bit assembly komut satırı derleyicisi “ml.exe”, 64 bit komut satırı derleyicisi ise “ml64.exe” isimli programlardır. MASM DOS zamanlarında çok kullanılan bir derleyiciydi. Fakat son zamanlarda popülaritesi oldukça düşmüştür. Ancak hala x86 sistemleri için ana derleyicilerden biri olarak kabul edilmektedir.


Netwide Assembler (NASM)

NASM özellikle son yıllarda çok popüler olmuştur. Bunun en büyük nedenlerinden biri NASM’nin "cross platform" olmasıdır. NASM’nin hem Windows, hem Linux, hem BSD hem de MAC OS X sistemleri için derleyicisi vardır. Bu da nasm'yi popüler hale getirmiştir.


Borland Turbo Assembler (TASM)

Borland DOS zamanlarında efsane firmasıydı. Firmanın C derleyicileri çok yaygın kullanılıyordu. TASM de Borland’ın assembly derleyicisi olarak MASM ile rekabet halindeydi. Ancak TASM artık programcılar tarafından tercih edilmemektedir. Zaten Borland TASM’yi artık başka bir ürün paketi içerisinde paralı olarak dağıtmaktadır. TASM sentaks bakımından neredeyse MASM’ye çok benzemektedir. Meraklıları veya underground işler için tercih edilebilir.


Flat Assembler (FASM)

FASM de yazım biçimi olarak daha çok NASM’ye benzemektedir. Bu derleyici de "cross platform" özelliğe sahiptir. Ancak NASM kadar yaygın kullanılmamaktadır.


GNU Assembler (GASM)

GASM, GNU projesi kapsamında geliştirilmiş olan sembolik makine dili derleyicisidir. Bu nedenle UNIX/Linux tabanlı sistemlerin ana assembly derleyicisi durumundadır. Ancak gerek sentaks yapısı bakımından gerekse özellik bakımından GASM pek çok kesim tarafından eleştirilmektedir.




msg db 'Hello World!..' , 10

00:42


Anadolu'da Köyün Delisi Olmak



Sevdiği işe tutkuyla sarılan AHMET ULUÇAY (kendisi memleketinde köyün delisi olarak görülürmüş ta ki ünlü olana kadar ) kadar yaptığım işlere bağlı olamasam da yazılım geliştirme alanında iyi olmaya çalışıyorum. Bu kapsamda assembly öğrenmeye başladım. Öğrendiklerimi burada paylaşmayı düşünüyorum. İlk yazım da Ahmet Uluçay' ın hatırasına olsun.