x86 Yazmaçları (x86 Registers)

Merhaba, Yüksek lisansta aldığım zararlı yazılım analizi ve tersine mühendislik dersinde işime yarayacağı düşüncesiyle assembly koda çevrilmiş her kodda karşıma sıkça çıkacak yazmaçları ve detaylarını buraya not etmeye karar verdim. Öncelikle belirteyim ki MIPS’i daha çok sevmeme ve ilerleme kaydetmeme rağmen burada x86 mimarisindeki yazmaçlara değineceğim. MIPS’ten sonra bana biraz karmaşık geldi haliyle ama x86 da bir de facto tabi..

Inteldeki zamanın mühendis abileri 8O86 işlemciyi tasarlarken her register için bir iş düşünmüşlerdi. Komut setini hazırlarlarken pek çok optimizasyon yaptılar ve özel komutlar getirdiler ki bunlar da bahsedeceğimiz yazmaçlara fazlasıyla bağlılar. Yazmaçların Intel’in planladığı gibi kullanılması yapılan tüm optimizasyonlardan azami faydalanmayı da sağlayacaktır. Bunun sorumlusu siz değil de derleyiciler, yazmaçları işlevlerine göre kullanmaktansa hızlı çalıştırmaya konsantre olmuş durumdalar.

Yazmaçları uygun kullanmak sadece optimizasyonu kolaylaştırmakla kalmıyor, sizi daha okunaklı kod yazmaya itiyor ve program paketinde daha iyi sıkıştırma dahi sunabiliyor(aynı işlemiler hep aynı yolla yapılacağı için tekrarlı alan sayısı artıyor). Uygun kullanıldığında yazmaçlar okuyanlara o for döngülerindeki i  kadar anlamlı gelecek ve ve x86 assembler kendiliğinden dokümante edilmiş gibi olacaktır.

Şimdi de biz x86’nın 32 yazmacından 8 tanesini (genel işlevliler) tanıyalım.

  • EAX – Accumulator Register
  • EBX – Base Register
  • ECX – Counter Register
  • EDX – Data Register
  • ESI – Source Index
  • EDI – Destination Index
  • EBP – Base Pointer
  • ESP – Stack Pointer

EAX: Toplayıcı                        A -> ACCUMULATOR

İşlemcide 3 ana mimari vardır; yazmaçlar, yığın ve akmülatör(accumulator) Yazmaç mimarisine göre her toplama veya çıkarma işlemi herhangi iki yazmaç arasında gerçekleşebilir. Yığıt mimarisinde yığıtın en tepesindeki değer ile yığıttaki diğer değerler arasında gerçekleşebilir. Akümülatör mimarisinde ise işlemci tekbir hesaplama yazmacına sahiptir ve tüm hesaplamalar bunun üzerinde döner. Diğer yazmaçlar ise ona göre basit veri alanlarıdır.

Gel gelelim x86 akümülatör mimarisine sahip değildir. Ancak buna benzer bir iş yapacak bir yazmacı vardır: EAX! İşlemler herhangi iki yazmaç arasında olabilmesine rağmen komut seti  bu yazmaca imtiyaz tanır. Örneğin 9 basit komutun (ADD, ADC, AND, CMP, OR, SBB, SUB, TEST, and XOR)  bu EAX yazmacıyla 1-byte’lık  özel opcode(işlem kodu) vardır ve bununla bir sabit değer işlemi kısa yoldan yapılabilmektedir. Daha da özelleşmiş işlemler, çarpma, bölme, işaretleme, BCD düzeltmesi sadece EAX yazmacıyla olabilir.

Çoğu hesaplama bu yazmaçta yapıldığı için(en azından inteldeki mühendisler öyle varsaymış) x86 mimarisi bu yazmaca veri taşıma veya veri almak için per çok iyileştirilmiş komuta sahip. En basitinden işlemci EAX ile diğer yazmaçlar arasında veri değiş tokuşu gerçekleştirilmek üzere 16 byte büyüklüğünde XCHG (Exchange = değiş tokuş)işlem koduna (opcode) sahiptir. Intel mühendisleri sanırım bunu EAX dışındaki bir yazmaçta bulunan bir veriyle işlem yapmak için onu hızlıca EAX yazmacına taşımaya teşvik etmek için yapmışlar.

EAX yazmacı aşağıdaki gibi bir yapıya sahip. Burada değinmediğimiz AL/AH gibi yazmaç kısımlarına ilerideki yazmaçlarda değineceğimiz için bu şekli her yazmaç için incelemekte fayda var:

Screen Shot 2016-01-10 at 12.55.30

EBX: Taban – Adresleyici                B -> BASE

16 bit işlemci dünyasında EBX yazmacı genel işlevli işaretçi gibi davranmaktadır. ESI, EDI, ve EBP yazmaçlarını bu bağlamdan ayrı tutarsak EBX köşeli parentez notasyonuyla (Ör. MOV [BX], AX) address erişimi yapan yegane yazmaçtır diyebilirdik; ancak 32 bitlik dünyaya girdiğimizde herhangi yazmaç hafıza ofseti görevi ifa edebilmektedir.

Taban yazmacı adını XLAT(Look-Up Translation Table) komutundan almaktadır. EBX, XLAT address tablosundan AL değerini indeks olarak, kendi değerini de baz alarak kullanarak address değerine ulaşır. XLAT komutu  MOV AL, [BX+AL] işleminin kısaltılmış hali gibi düşünebilirsiniz.

Tüm genel işlevli yazmaçlar arasında EBX kendisine önemli bir işlev atanmamış tek yazmaçtır. Bu nendenle fazladan bir işaretçi saklamak ya da işlem adımı oluşturmak için kullanılabilir ancak daha fazlası için değil.

ECX: Sayaç Yazmacı              C -> COUNTER

ECX yüksek programlama dillerindeki döngülerde i,j gibi değişkenlerin x86 versiyonu gibidir diyebiliriz. Sayma bazlı her komut ECX yazmacını kullanır. Örneğin LOOP, LOOPZ, LOOPNZ ve ECX “O”(sıfır) olduğunda jump işlemi yapan JCXZ. ECX ayrıca bit kaydırma işlemlerinde kaydırma sayısını da tutar. Son olarak ECX , REP, REPE, and REPNE gibi string komutlarının kontrolünde kullanılır. Bu durumda ECX işlemin azami  tekrarlanacağı sayıyı tutar.

Her döngü işleminde ECX etrafında tasarlanmış dallanma devreleri ile işinin hakkını vererek yazılımcının işini kolaylaştırır. Ancak dikkat edilmelidir ki ECX geriye doğru sayım yapar.

EDX: Veri Yazmacı           D -> DATA

Diğer yazmaçlar arasında EAX yazmacına en yakın ve onunla en çok haşır neşir olan yazmaç budur diyebiliriz zira aralarından su sızmaz. Çarpmada bölmede, kayıt etmede aşırı büyüklükteki verilerin de kullanılabilmesine olanak sağlar. Komutlar “most significant bits” dediğimiz, verinin en büyük bit kısımlarını üzerine yükler, geri kalanını da EAX üzerine yükler.  EDX için bir nevi EAX üzerine 64-bit genişleticidir denilebilir. EDX ayrıca IO işlemlerinde de rol oynar. Bu durumda EAX bir porta yazılacak ya da porttan okunacak değeri tutarken EDX port adresini tutar.

EAX ile yapılan işlemlerde elde edilen sonuçları saklamak için en ideal yer EDX dir. Eğer güzel assemble dili nasıl anlaşılır derseniz eğer veri kaydetme işlemleri için sadece EAX ve EDX kullanılmışsa yazan ne yaptığını biliyor diyebiliriz.

Şimdiye kadar anlattığım yazmaçların büyüklükleri ve özel alanlarını aşağıdaki tablodan inceleyebilirsiniz.

Screen Shot 2016-01-10 at 12.59.58

EDI: The Destination Index            D -> DESTINATION

Bir veri üreten her döngü ya da işlem okadar zaman(!) didinip uğraştığı sonucu biryerlerde saklamak isteyecektir. Bilgisayarda bunu için hafıza alanları vardır ve buraya öylesine erişmek istanbulda parasız kalmaya benzer. Elinizde bir adres, referans vs. yoksa ne olacağını düşünün. İşte EDI de bunun gibi durumlarla karşılaşmayın diye size yol gösterir. Yani bir işaretçidir aslında. Tüm string işlemlerinin yazılacağı adresi gösterir. Örneğin en kullanışlı string işlemlerinden biri STOS EAX’den aldığı veriyi EDI değerine bakarak hafızaya kopyalayacağı alanı bulur ve kopyaladıktan sonra EDI değerini sonraki hafıza alanını gösterecek şekilde artırır.

EDI yazmacını fazladan bir değer tutucu olarak kullanmak Mac pro alıp sadece internet gazette okumaya benzer. Tüm işlemler sonuçta veri saklamaya ihtiyaç duyarlar ve bir yazmaç en azından yazma alanını göstermelidir. EDI bu iş için dizayn edildiğinden başka bir yöntemi denemek kaynak israfı olur.

ESI: The Source Index              S -> SOURCE

Kaynak indeks yazmacı, hedef indeks yazmacıyla hemen hemen aynı özelliklere sahiptir. Tek farkı hedef indeks gibi yazma değil okuma işlemi yürütür. Tüm veri işleyen rutinler çok büyük ağırlıkla yazma üzerine işlediğini düşünürsek bu yazmaç genel manada EDI kadar kullanışlı sayılmaz ancak kullanılacaksa da onun kadar güçlüdür. Eğer kodunuz herhangi bir veri okuma işlemi yapmıyorsa yine de yazmaçların kısıtlı dünyasında ESI yazmacını değer tutmak için kullanabilirsiniz.

ESP and EBP: The Stack Pointer and the Base Pointer

Tüm yazmaçlar amaçları dışında kullanılmalarına rağmen sadece tasarlandıkları iş için kullanılan ve kullanılması gereken iki yazmaç ESP ve EBP.. Bu iki yazmaç x86’nın fonksiyon (Javacılar için metod) çağırma mekanizmasının kalbidir.

Bir fonksiyon çağırıldığında parametreleri ve dönüş adresi yığında üst üste dizilir. Fonksiyona girildiğinde EBP ESP ile eşitlernir ve kendi kullanacağı değişkenleri yığına dizmeye başlar. Bu noktadan sonra fonksiyon parametrelerine ve değişkenlerine ESP serine EBP’yi referans alarak erişmeye başlar. Çünkü ESP kötü bir adresleme alışkanlığına sahip. 16 bit sistemde örneğin [] notasyonu ile bir adresleme yapamaz, 32 bit sistemde ise [] ile adresleme yapmak için opcode değerine ekstra yük getirecek SIB byte eklenmesini gerektirmekte.

Yazacağınız herhangi bir kod için ESP yi yığın işlemlerinden başka bir nedenle kullanmayın. Eğer işlemleriniz, olması gerektiği gibi , parametreleri yığın yerine yazmaçlar ile iletiyorsa ESP değerini EBP’ye atamanıza dahi gerek kalmayacaktır.

Aşağıdaki şekilde adresler aşağı doğru artmaktadır. Yığın ise yukarı doğru büyümektedir (StackOverFlowException hatası aldınız mı hiç?). PUSH işlemi ESP değerini azaltırken POP işlemi ESP değerini artırır. Yukarıda anlattıklaım ışığında aşağıdaki şekli incelemenizde fayda var.

Screen Shot 2016-01-10 at 13.39.13

Özetle…

Görüldüğü gib her biri farklı bir amaç için tasarlanmış bu yazmaçlar donanım düzeyinde de yapacakları işi en etkili şekilde yapmak üzere yerleştirilmişlerdir. Dolayısıyla uygulama performansı da bunların şık bir şekilde kullanılmasıyla ilişkili olabiliyor. Zaten derleyiciler günümüzde yeterince iyi optimizasyon yapabiliyorlar ancak tersine mühendislikle yine bir çok açık (buffer overflow gibi)bulunabiliyor. Kısaca özetlemek gerekirse,

  • EAX – Tüm asıl hesaplamalar bu yazmaç aracılığı ile yapılmakta.
  • EDX – EAX işlemleride elde edilen veriler bu yazmaç ile saklanır EAX’in sağ koludur diyebiliriz.
  • ECX – Döngü işlemlerinde nekadar sayacağınızın kontrolünü yapar.
  • EDI – Her döngü sonucunu biyerlerde saklamak zorundadır. EDI use onlara yol ve yer gösterir. Bu yazmaç büyük veri operasyonlarında sisteme büyük kolaylık sağlar.
  • ESI – Döngülerde gelen verilerin address bilgilerini tutar. Hafızadan veya başka bir werden gelen veriyi EAX’e yazmak için 1 byte’lık bir komut bulundurur.
  • ESP – Yığın işlemleri için yığının bekçiliğini yapar diyebiliriz. Tok sık göreceğiniz PUSH, POP, CAL ve RET işlemleri bu yazmacın değerine ihtiyaç duyarlar. Kendi işi dışında başka bir iş için kullanmamaya özen gösterin.
  • EBP – Yığında parametre ve değişken tutma ihtiyacı olan fonksiyonlarda  çalışılacak yığın alanını belirler. Bunun dışında, boşta kaldığında, ek bir veri alanı olarak kullnılabilir.
  • EBX – 16 bit sistemlerde işaretçi olarak faydalıdır. Şimdiki sistemlerde ise dilenildiği gibi kullanılabilir.

EIP: Instruction Pointer

Bir de burada bahsetmediğim sadece sistemin kendisinin kullandığı ancak çok kolay bir yazmaçtan bahsedeyim EIP. Bu yazmaca siz müdahale edemezsiniz program akışına göre bunu kendisi düzenler bu yüzden yukarıdaki yazmaçlarla aynı kategoride ele almadım ancak yine de yazmakta fayda var. EIP (Instruction Pointer) çalıştırılacak bir sonraki kod bloğunun adresini tutar, eğer buraya bir değer yazmayı başarabilirseniz istediğiniz her programda kendi kod bloğunuza uygulamayı yönlendirebilirsiniz. Buffer overflow açığının varoluş sebebidir. Program akışını düzenleyen jump, loop, interrupt gibi işlemler ancak bu değeri değiştirebilmektedir.

Flag Register

EFlags ve RFlags gibi 32bit ve 64 türevleri vardır. Diğer yazmaçların yaptıkları işlemler sonucu üzerlerindeki bazı bitler 1 ya da 0 olur.

Screen Shot 2016-01-10 at 14.29.30

Yazımı yazarken wordpress nedense her kelimemi ısrarla ingilizceye çevirmeye kalktı rastgeldiğim yerlerde düzenledim ancak bazı alanlarda kalmış olabilir….

 

Reklamlar

Bir Cevap Yazın

Aşağıya bilgilerinizi girin veya oturum açmak için bir simgeye tıklayın:

WordPress.com Logosu

WordPress.com hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap / Değiştir )

Twitter resmi

Twitter hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap / Değiştir )

Facebook fotoğrafı

Facebook hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap / Değiştir )

Google+ fotoğrafı

Google+ hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap / Değiştir )

Connecting to %s