Hacker'ın Olmazsa Olmazı Assembly #0 Girişten Önce Son Çıkış

Başlıktan anlaşılacağı üzere ilk yazımız, temel bilgileri tekrarlayarak mevcut bilgilerinizi kontrol etmenizi ve bundan sonraki yazılarda karşımıza çıkacak teknik konuları anlamanızı sağlamak üzerine hazırlanmıştır. Benim temel bilgim var diyenler bu yazıyı direk atlayabilirler ancak tekrar gözden geçirilmesini şiddetle tavsiye ediyorum.

Öncelikle konu başlıklarımıza bir göz atalım:

Sayı nasıl sayılır ?

61 dediğimiz sayı aslında 10lu sayma (sayı mı sayma mı?) sisteminde:1 carpı 10 üzeri 0 -> 10 üzeri 0 = 1 , 1 çarpı 1 = 1 koyduk kenara,6 çarpı 10 üzeri 1 -> 10 üzeri 1 = 10 , 6 çarpı 10 = 60 yani,60 artı 1 = 61 [çok basit geldi galiba?]

Peki 3d sayısı (evet sayısı) 16lı sayma sisteminde: 16lı sayma sistemini hatırlarsak (0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f)d çarpı 16 üzeri 0 -> 16 üzeri 0 = 1 , 1 çarpı d (yani 13) = 13 3 çarpı 16 üzeri 1 -> 16 üzeri 1 = 16 , 3 çarpı 16 = 48 48 artı 13 = 61

10lu sayma sistemindeki 61 eşittir 16lı sayma sisteminde 3d diyebilir miyiz? Matematik öyle diyor.Bu arada şu detayı da belirtmekte fayda var.Bir sayı 16lı sayma sisteminde belirtiliyor ise yani hexadecimal bir sayıdan bahsediyorsak 3dh veya 0x3d şeklinde gösterilir.

Peki bilgisayar dünyası için yeterli mi? Malesef değil!

111101 sayısı 2li sayma sisteminde:Abi istersen sen tek tek yap ama burada yapılmışı var 1 + 4 + 8 + 16 + 32 = 61 2li sayma sisteminde (binary) sayılar 0b111101 şeklinde gösterilir.

61 = 0x3d = 0b111101 diyebiliriz.

Hesaplamalar için google'da şunları yapabiliyoruz:

1453 in binary
0x1453 in decimal
0b10011101 in hex

Hesap kitap işleri tamamsa gelelim şu bit - byte mevzularına:

Bit latincede Phthiraptera denilen,eklembacaklı bir hayvandır da bize lazım olan bit bu bit değil. Binary digit kısaltması olan bit, 2li sayma sistemindeki tek bir haneye verilen isimdir ve doğal olarak alabileceği 2 değer vardır.0 veya 1 = yanlış veya doğru = kapalı veya açık gibi. 8 haneli bitlere byte diyoruz.Alabileceği en yüksek değer olarak:

1 byte (8 bit) = 0b11111111 = 0xff = 255
2 byte (16 bit) = 0b1111111111111111 = 0xffff = 65535
3 byte (24 bit) = 0b111111111111111111111111 = 0xffffff = 16,777,215
4 byte (32 bit) = 0b11111111111111111111111111111111 = 0xffffffff = 4,294,967,295
...
8 byte (64 bit) = (Herşeyi devletten beklememek lazım di mi?)

Dikkatinizi çeken birşey var mı? Sayılar büyüdükçe binary basamaklar başaçıkılmaz hale,decimal ise hatırlaması zor bir hale geliyor.Fakat hexadecimal çok daha kolay gözüküyor.Bu yüzden hafıza adresleri için hex kullanıyoruz.Devam edelim:

1024 byte = 1 Kilobyte , 1024 KB = 1 Megabyte , 1024 MB = 1 Gigabyte , ...
4 GB -> 4 * 1024 * 1024 * 1024 = 4,294,967,295 byte ederse o zaman
4 GB = 4 byte ?! Kayış kopmak üzere mi, kopartmadan açıklayalım.

Teknik konuları anlamak için sadece okumak yeterli olmuyor malesef.Öncelikle ÇOK dikkatli okumalı ve bol bol pratik yapmanız gerekiyor.Bu uyarıdan sonra sorunun açıklamasına gelirsek az yukarıdaki görüyoruz ki 4 byte yani 32 bit kullanarak belirtebileceğimiz EN YÜKSEK değer 4 GB'ın (4 byte) karşılığıdır. Buradan bir sonuç çıkartabilir miyiz? Eğer bilgisayarınızdaki RAM 4 GB'dan daha yüksek ise ve tamamını kullanmak istiyorsanız 64 bit bir işletim sistemi kullanmaya mecbursunuz.

Sayı saymayı da öğrendiğimize göre şu bilgisayar dedikleri de neymiş bir bakalım.

Bilgisayar nedir? Nasıl çalışır?

Yaklaşık 4500 sene önce hesaplama amaçlı icat edilen ilk cihaz olan "abaküs", elektriğin icat edilmesi ve takip eden araştırmalar neticesinde yerini son yüzyılda "bilgisayar, computer - hesap yapan" cihazına bırakmıştır.Günümüzdeki kullanılan şekli itibariyle (işlemci - hafıza ikilisi bazlı) bilgisayarın mucidi olarak Von Neumann gösterilmektedir.

Peki bu bilgisayarlar nasıl çalışıyor diye sorduğumuzda "düğmesine basarak" esprisini geçersek aslında bir oda lambasını açıp kapatan düğmeden hiç bir (bilgisayarda milyonlarca düğme olması haricinde) farkı olmadığını görüyoruz. Meşhur düğmemize bastığımız anda elektrik akımı sayesinde bilgisayarın bileşenleri arasında iletişim kuruluyor ve etkileşim başlıyor.Bizim için ilk etkileşim tabii ki ekrana görüntü gelmesinden başka birşey değil.

Madem ki Assembly (makine dili) öğrenicez, o zaman bu makinenin bazı kısımlarını çok iyi tanımamız gerekiyor. Bunlar İşlemci (CPU) ve Hafıza (RAM)

İşlemci ve Hafıza?

1950li yıllardan günümüze kadar hızla gelişen ve evrimleşen işlemciler, farklı görevler üstlenen birçok bileşenden oluşan silikon dioksit bazlı yarı-iletken (semi-conductor) maddedir.[Açıklama cuuk diye oturdu diyemem ama bunun gibi birşey işte :)]İşlemcinin çalışma şeklini anlayabilmek ve programlamak için bu bileşenleri bilmek çok önemlidir.

Program Counter ( Sayaç ) İşlemciler komutları hafızadan okur ve çalıştırır.Bu bileşen işlemciye okuması gereken bir sonraki komutun hafızada hangi adreste kayıtlı olduğunu söyler.

Instruction Decoder ( Komut çözücü ) Hafızadaki kayıtlı adresten komutu alan işlemci,bu komutla istenilenin ne olduğunu,ne yapması gerektiğini (toplama,çıkarma,..) ve başka bileşenleri işin içine katıp katmaması gerektiğini bu bileşen sayesinde anlar.

Data Bus (Veriyolu) İşlemci diğer donanımlar (özellikle hafıza) ile iletişimini bu bileşeni kullanarak sağlar.

Registers (Kaydediciler)İşlemcinin veriyolu ile ulaştığı hafıza (RAM) haricinde kendine ait çok hızlı çalışan hafıza bölümleri yani kaydedicileri bulunmaktadır.Matematiksel olaylar (karşılaştırmalar da dahil olmak üzere, 0 1'e eşit mi?) bu kaydediciler KULLANILARAK gerçekleşmektedir.

Şimdilik fazla detaya girmiyoruz,sadece listeleyip geçiyoruz.

  • EAX
  • EBX
  • ECX
  • EDX
  • ESI
  • EDI

Special Purpose Registers (Özel amaçlı kaydediciler):

  • EBP
  • ESP
  • EIP
  • EFLAGS (bir sürü var,mesela CF PF AF gibi)

Segment Registers (Aynısından 1.5 yoortlu,yalnız bunlar 16 bit):

  • CS
  • DS
  • ES, FS, GS
  • SS

Registerlar konusuna işletim sistemleri kısmında tekrar gelicez.[Bitmedi mi? Biter mi be abii]

Arithmetic and Logic Unit (Aritmetik ve Mantıksal birim)
Komut hafızadaki adresten alındı,işlemci komutu çözdü,neleri ihtiyacı varsa hepsini hazırladı ve bu birim sayesinde işlemin sonucunu elde ediyoruz.[Ne zormuş bu sonucu elde etmek!]

CPU hakkında limitli (!)de olsa bilgi sahibi olduğumuza göre şimdi de RAM hakkında biraz detay verelim.

RAM dediğimizde aklımıza apartman dairelerinin girişlerindeki posta kutuları gelebilir.

İşlemci veriyolu vasıtasıyla (resimdeki güvercin misali) ihtiyacı olan bilgileri kayıtlı olduğu posta kutusu numarasından isteyip, işlem sonucunu yine posta kutusunun başka bir numarasına kaydedebilir.

Bilmemiz gereken en önemli detay ise bu posta kutusu numaralarını nasıl belirtebiliriz.Mesela hedef posta kutusu numaramız 26 olsun. Seçenkleri sıralıyalım:

* Direk 26 [0x26],
* Endirek 26 [10 nolu kutuda 0x26 yazılı bir mektup var diyelim, 0x10 kutunun içeriği]
* 23den 26ya [0x23 + 3],
* 20li sayılar , tek tek atla , 6 sefer [0x20 + 1 * 6]

Hafızaya erişim metodları ilk bakışta karmaşık gibi gözükse de Assembly dili için çok önemli bir mevzu. İlerleyen bölümlerdeki videoları izleyerek bu konuyu rahatlıkla çözebilirsiniz. Şimdilik bu önbilgi yeterli diyor ve işletim sistemlerini inceleyerek devam ediyoruz.

İşletim sistemleri ?

Aklınıza hemen Windows (tescilli marka felan filan) gelmiş olsa da konumuz işletim sistemlerini listelemek değil, nasıl çalıştığını anlamak. Bilgisayarınızı açtınız, anlamadığınız (veya anladığınız) birçok yazıdan sonra masaüstünüz karşınızda.Peki bu ekran gelene kadar neler oldu? Elektrik geldi,bütün donanımlara hayat verdi,birbiriyle iletişim kurdular, oOoOoOo efendim ses kartı da burdaymış, patron yeni harddisk almış,tanışma faslı derken, harddiskinizde kayıtlı olan işletim sisteminiz, burası ÇOK ÖNEMLİ kernel yani çekirdek yüklendi.İşte bu kernel donanım ile yazılım arasında boru hattı görevi gören irtibat subayıdır.

Mesela bir program yazdık ve ekrana "Merhaba Dünyalı" yazsın istiyoruz.Bütün kodları girdikten sonra Kerneli sahneye çağırmadığınız sürece ekranda hiçbirşey göremezsiniz.Kullanıcı kerneli sahneye çağırarak işlemci ile yazılım arasında köprü kurmuş oluyor.(gibi gibi) Yani programımız kullanıcı modundan kernel moduna geçiş yapıyor. (User mode -> kernel mode durumu) Peki nasıl?

Hemen örneklendirelim: int $0x80
Operasyon kodumuz (opcode) -> int yani interrupt Operasyon değerimiz 0x80 -> kernel sahneye

Kernel sahneye geldi ama ne yapacağını nereden biliyor ?İşte burada devreye registerlar giriyor ve eax registerındaki değere bakıyor.Eğer bu değer 4 ise ekrana birşeyler yazılmasını istediğimizi anlıyor.4 ne alaka diye soranlarınız için bu bir sistem çağrı (system call) numarası. [Detaylar ilerleyen bölümlerde]

Buradan ne gibi sonuçlar çıkarıyoruz?
Assembly programları yazmak için kernel sistem çağrılarını iyi bilmemiz gerekiyor.Bu arada her işletim sistemi için farklı numaralar kullanıldığını düşünürsek, Linux için yazılan bir programı Windowsta neden kullanamadığınızı buradan anlayabilirsiniz. (Tek sebep bu değil tabii ki)

İşletim sistemlerinin çalışma prensibi hakkında kısa bilgilendirmeden sonra matematik kısmına kısa bir dönüş yapıyoruz.Günümüzde kullanılan işletim sistemlerinin birçoğu (nerdeyse hepsi) size 2 seçenek sunar:

32 bit veya 64 bit. Yukarıda neden 64 bit kullanmamız gerektiği anlatılmıştı şimdi biraz daha detaya girelim.Farz-ı misal,velev ki,örnek vermek gerekirse [benim kayışın da ara sıra sıyırdığı oluyor :)]

32 bit işletim sistemiz var , eax registerında 0x20121110 (gibi bir tarih değeri olsun) değeri var.İşlemciye soruyorum eax'ta değer nedir?Cevap geliyor.Peki 4 byte olan bu değerin ilk byte değerini veya 2. byte değerini veya 1. ve 2. byte değerini birlikte isteyebiliyor muyum?Hemen görelim:

al (l harfi low yani düşük demek) 1. byte = 0x10 [8 bit, 8086 işlemciler]
ah (h harfi high yani yüksek demek) 2. byte = 0x11 [8 bit]
ax (ah + al, pair yani ikili) yani ilk 2 byte = 0x1110 [16 bit, 286 işlemciler]
eax zaten değerimiz 4 byte = 0x20121110 [32 bit, 386 işlemciler]

Not: Intel Itanium işlemcilerden başlayarak günümüzde üretilen bütün işlemciler 64 bit özelliğine sahiptir.

Peki bu anlattıklarımızın temeli nereye dayanıyor?Tabii ki eski işlemci ve bu işlemcilere uygun hazırlanan işletim sistemlerine.Eskiden 32 bit işletim sistemleri yok iken (M.Ö bilmemkaç) 16 bit işletim sistemlerinde eax registerının adı ax idi,ondan öncesini zaten öğrendiniz. Zaten eax extended acc.. x(matematikteki herhangi bir değeri ifade eden x) buradaki extended genişletilmiş manasına geliyor. Peki 64 bit işletim sistemlerinde eax ne oluyor? Karşınızda meşhur olmasına ramak kalan artistimiz rax yani larger accum... (larger = daha geniş) [baktılar 2 tane r harfi var,adını r koyalım demişler :)]

Hani şurada Little Endian konusuna girmem lazım ama bir girersem çıkamam + niyetli olan arkadaşların çoğu vazgeçebilir ondan ben vazgeçiyorum,ileride mecburen giricez.

Bu azıcık (!) detaydan sonra Assembly konusuyla devam edelim.

Assembly ve tarzları?

Assembly hakkında detaylı bilgiyi bu adresten okuyabilirsiniz.Assembly tarzlarını incelemeye başlamadan önce konuyu daha kolay anlamak için bir örnek verelim:

6 sayısı Türkçede altı olarak yazılırken İngilizcede six olarak yazılıyor.Peki ifade edilen şey aynı mı? Aynı.

Bir de kafa karıştırabilecek örnek verelim:

50 sayısı italyancada cinquante (çinkuante) olarak yazılırken fransızcada cinquante (senkant) olarak yazılıyor.Yazılış aynı olsa da telaffuz farklı ama ifade edilen şey yine aynı.

Peki konumuzla alakası nedir?

Assembly programlama için mevcut 2 tarz (syntax) var,birisi Intel diğeri AT&T;.Aradaki farkı görebilmek için bir örnek verelim:

mov %eax, %ebx (AT&T; tarzı)
eax registerındaki değeri ebx registerına taşı, önce kaynak sonra hedef
mov ebx, eax (Intel tarzı)
Aynı komut fakat önce hedef, sonra kaynak.Bu arada yüzde işareti kullanmıyoruz.

Peki aradaki fark ne ve hangisini öğrenmek daha çok işimize gelir?

Intel firması bütün kaynaklarını doğal olarak kendi tarzında yayınlamaktadır ve bu tarz Microsoft firmasına ait bütün işletim sistemlerinde standarttır.AT&T; tarzı ise Unix için geliştirildiğinden Linux işletim sistemlerinde standarttır.

Kimileri AT&T; tarzının daha kolay okunduğunu söylese de Intel tarzını öğrenmek çok daha mantıklıdır.Çünkü Linux işletim sistemi için de Intel tarzı derleyici (compiler) ve ayıklayıcı (debugger) bulunmaktadır. Böylece hem Windows hem Linux için Assembly programları yazabilir, yazılanları inceleyebilirsiniz.

Başlıyoruz !

Yazının bu kısmına kadar gelmeyi başardıysanız umut var demektir.[Vazgeçmeyi düşünmüş olsanız da :)]

Son olarak kullanacağımız donanım ve yazılımı belirtelim.

İşletim sistemi : Mint 14 (Linux)
İşletim sistemi : 32 bit (bu detaya dikkat!)
Derleyici : NASM
Ayıklayıcı : GDB (Intel tarzı)
Metin editörü : Nano