![](../assets/images/blog/x86-assembly_3.png)
x86 Assembly - Giriş
Selamlar, bu blog yazımızda x86 assemblye ucundanda olsa değinmeye çalışacağız. Buradaki bilgiler ilerideki blog yazılarımızda bahsedeceğimiz exploit development, reverse engineering gibi konularda işimize yarayacaklardır.
Assembly Nedir ?
Assembly düşük seviyeli bir dil olan, uygulama geliştirmenin, öğrenmenin zahmetli olması nedeniyle belirli alanlar dışında çalışan kimseler dışında kullanımı pek tercih edilmez ve günümüzde bu dil ile nadiren program geliştirilir. Kullanım alanı genellike malware analizi veya geliştirilmesi, exploit geliştirmek, cracking veya donanım ile iletişime geçmek için kullanılır.
Assembly zor bir dil olmasına karşın sanıldığının aksine eğlenceli ve işlemci ile bire bir haşir neşir olduğumuz için çok hızlı ve bilgisayar üzerinde tam hakimiyet sağlayabileceğimiz bir dildir. Assembly işlemciden işlemciye bazı değişiklikler gösterebilmektedir. Örneğin intel işlemcilerle arm işlemciler arasında bazı farklar mevcuttur. Mantık değişmemektedir farklı olan registerlar ve bazı instructionlardır. Biz yazımızı intel işlemciler üzerinden devam ettireceğiz. Tabi Assembly sadece işlemciden işlemciye değişiklik göstermiyor mimari ve yazım şekline görede değişebilmektedir. Örneğin x86 Assembly ile x64 Assembly arasında da bazı farklılıklar vardır. Biz yazımıza x86 Assembly üzerinden devam edeceğiz.
Assembly bir de yazım şekillerine göre yani syntaxina göre değişebilmektedir. İki farklı syntax mevcut bunlar Intel ve AT&T. Aşağıda her iki syntaxa bir örnek göstereceğiz. Linux ortamında genellike AT&T yazım şekli kullanılmakta tabi bu Intel yazım şeklini kullanamayacağımız anlamına gelmiyor. Biz yazımıza Intel syntaxını kullanarak devam edeceğiz.
AT&T yazım şeklinde önce kaynak sonra hedef gösterilir. Kaynak $ işareti ile işaretlenirken hedef % işareti ile belirtilir.
# AT&T tarzi
movl $0x3,%ax
movl $0xff,%ebx
int $0x80
Intel tarzında ise AT&T'nin tam tersi önce hedef sonra kaynak gösterilir. AT&T'de ki gibi $ ve % işaretleri yoktur sadedir.
; Intel tarzi
mov ax,0x3
mov ebx,0xff
int 0x80
Yukarıda bulunan kodlar daha önce assembly ile çalışmamış arkadaşlar için anlamsız gelebilir. Amaç syntaxi göstermek.
Assembly yazabilmemiz için bazı şeyleri biliyor olmamız gerekiyor.
Registerlar (Kaydediciler):
Kaydediciler işlemci üzerinde bulunan hafıza alanlarıdır ve aralarında Genel Amalı Kaydediciler, Özel Amaçlı Kaydediciler, Bölüm Kaydediciler, Durum Kaydediciler, XMM ve MMX olarak ayrılırlar. Biz daha çok genel, özel amaçlı, bölüm ve durum kaydedicileriyle çalışacağız. Şu an için xmm ve mmx kaydedicileriyle bir işimiz olmayacak.
Aşağıdaki görselde genel amaçlı Kaydediciler ve iki adet özel amaçlı kaydedici bulunmakta.
![](../assets/images/blog/x86-assembly_3.png)
Yukarıdaki resimde genel amaçlı kaydedicilerin hangileri olduğu belirtilmiştir. Biz bunları daha detaylı bir şekilde ele alıp inceleyelim.
Genel Amaçlı Kaydedicilerimiz
- EAX
- EBX
- ECX
- EDX
- ESI
- EDI
Özel Amaçlı Kaydedicilerimiz
- ESP
- EBP
- EIP (Bu kaydedici bazı kaynaklarda ayrı bir segment olarak işlenir ve önemlidir.)
Bölüm Kaydedicilerimiz
- SS (Stack Segment)
- CS (Code Segment)
- DS (Data Segment)
- FS,GS,ES (Extra Segments)
Durum Kaydedicilerimiz
- CF (Carry Flag)
- PF (Parity Flag)
- AF (Adjust Flag)
- ZF (Zero Flag)*
- SF (Sign Flag)
- TF (Trap Flag)
- IF (Interruption Flag)
- DF (Direction Flag)
- OF (Overflow Flag)
- NT (Nested Task flag)
- RF (Resume Flag)
- ID (Identification Flag)
Genel Amaçlı Kaydediciler Hakkında
Yukarıda belirttiğimiz ve görselde gösterildiği üzere 6 adet genel amaçlı kaydedicimiz bulunmaktadır. Görsel üzerinden ilerlersek genel amaçlı kaydedicilerimiz aralarında ayrılmaktadır. EAX kaydedicisini ele alacak olursak. Başındaki E harfi Extended anlamına gelmektedir. Türkçe karşılığı genişletilmiş veya uzatılmış anlamına gelmektedir. EAX 32 bitlik bir kaydedici olmasına karşın biz bu 32 bitlik kaydedicinin 16 bitlik kısmına ve 8 bitlik kısımlarına erişebiliyoruz.
- EAX = 32 Bit
- AX = 16 Bit
- AH = 16 Bit (H harfi HIGH anlamına gelmektedir.)
- AL = 16 Bit (L harfi LOW anlamına gelmektedir.)
|63..32|31..16|15-8|7-0|
|AH.|AL.|
|AX.....|
|EAX............|
|RAX...................|
Genel Amaçlı Kaydedicilerin Kullanım Amaçları
EAX = Akümülatör olarak geçer aritmetik işlemlerin sonuçlarını, giriş/çıkış erişimlerini, sistem çağrı numaralarını ve fonksiyon geri dönüş değerlerlerini saklayan kaydedicimizdir.
EBX = Base register olarak geçmektedir. Bu kaydedicimiz henüz görmediğimiz ve aşağıdaki Bölüm Kaydedici lerinde göreceğimiz DS bölümündeki veriler için taban adresini aynı zamanda bazı interrupların geri dönüş adresini ve sistem çağrısı yapılacaksa çağrının ilk parametresini alır.
ECX = Counter register olarak geçer. Bir işlemin kaç kez çalıştığına ait değeri tutar aynı zamanda döngüler ve string işlemleri için kullanılır. Sistem çağrısı yapılacaksa ikinci parametreyi tutar. (__fastcall'da değişiklik gösterebilir.)
EDX = Data register diye geçmektedir. Giriş çıkış işlemlerinde kullanılır. Sistem çağrısı yapılacaksa üçüncü parametreyi alır ayrıca EAX kaydedicisinin bir nevi yardımcısıdır şöyleki EAX kaydedicisinin 64 bitlik bir rol oynamasına yardımcı olur.
ESI = Source Index register olarak geçer. DS segmentindeki stringlerin veya bellek dizelerini kopyalarken kullanılan aynı zamanda REPZ- sınıfına ait tüm instructionlarının özellikle kullandığı, string işlemlerinde verinin okunduğu adresi tutar.
EDI = Destination index register olarak geçmektedir. String işlemlerinde ESI ile çalışır ve string işlemlerinde hedefin adresini tutar.
Özel Amaçlı Kaydedicilerin Kullanım Amaçları
Özel amaçlı kaydedicilerimizde genel amaçlı kaydedicilerimiz gibi 32 bittir. Başlarındaki E harfi Extended anlamını taşır.
ESP = Stack Pointer olarak geçer. DS segmenti gibi henüz görmediğimiz SS segmentindeki en üstte bulunan verinin adresini tutar.
EBP = Base Pointer diye geçer. SS segmentindeki an altta bulunan verinin adresini tutar.
EIP = Instruction pointer olarak geçmektedir. Çalıştırılacak bir sonraki instructionun adresini saklar. Sadece okunabilir bir alandır yani doğrudan değiştiremezsiniz burayı. İleride exploit geliştirme konularında çoğu işimiz bu kaydedici ile olacaktır.
Bölüm Kaydediciler
Bölüm kaydedicilerimiz, genel amaçlı kaydediciler ve özel amaçlı kaydedicilerin aksine 16 bitlik kaydedicilerdir.
SS = Stack segment olarak geçmektedir. İki üç cümle ile özetlenmeyecek kadar önemli olduğu için alttaki #Stack başlığı altında detaylıca incelenecektir. Şimdilik es geçiyoruz.
FS, GS, ES = Bu segmentler extra segmentlerimizdir. Genellikle işletim sistemleri buraları kullanır ve son kullanıcının burayla pek bir işi yoktur.
CS = Code segment olarak bilinir. Programın çalıştırılabilir kodlarının olduğu kısımdır. Buradaki kodların değiştirilmesi programın crash olmasına neden olabilir. Patchleme işlemleri bu alanda yapılır ve .text olarakta bilinir.
DS = Data segment diye geçmektedir. Bu segment, değer atanmış global ve statik değişkenlerin bulunduğu alandır.
char cCompany[] = "Cryptex Studio";
int iDeg = 1337;
Bu segmentlerimizin dışında BSS ve Heap bulunmaktadır.
BSS = BSS'in eski bir assembler tarafından açılımı Block Started by Symbol olarak geçmekte. BSS, DS 'nin aksine henüz değer atanmamış değişkenlerin tutulduğu alandır.
int32_t iCryptex;
extern char __Studio[];
HEAP = Heap segmenti DS ve BSS segmentinin bitişinde başlar. Bu alana malloc, calloc, HeapCreate gibi fonksiyonlar yardımı ile erişilebilmektedir.
char *cHmss;
cHmss = (char *)malloc(0xa);
![](../assets/images/blog/x86-assembly_5.png)
Durum Kaydediciler (EFLAGS)
Durum kaydedicilerimiz 32 bit ve özel işlevi olan kaydedicilerimizdir. Bu kaydedicilerimiz içindeki her bit bir bayrağı temsil etsede bazı bitler anlamsızdır. Bu bayraklar programın işleyişini direk olarak etkilemektedir.
Bit Label Desciption
---------------------------
0 "CF" Carry flag
2 "PF" Parity flag
4 "AF" Auxiliary carry flag
6 "ZF" Zero flag
7 "SF" Sign flag
8 "TF" Trap flag
9 "IF" Interrupt enable flag
10 "DF" Direction flag
11 "OF" Overflow flag
12-13 "IOPL" I/O Priviledge level
14 "NT" Nested task flag
16 "RF" Resume flag
17 "VM" Virtual 8086 mode flag
18 "AC" Alignment check flag (486+)
19 "VIF" Virutal interrupt flag
20 "VIP" Virtual interrupt pending flag
21 "ID" ID flag
Flaglarımızın bir çoğunu yukarıda listeledik bazılarına yer vermedik sebebi ise bir çoğuyla işimiz olmayacak ama yine de bilmekte fayda var. Yukarıdaki flaglarımız sadece iki değer alıyor bunlar 1(SET) ve 0(RESET) dir. Şimdi bu bayrakların ne işe yaradıklarına bakalım.
0 - CF = Carry Flag olarak geçer ve elde bayrağı olarak bilinir. Matematiksel işlemler çerçevesinde sıkça kullanılan bayrağımızdır. İşaretsiz taşma olduğu zaman set(1) edilir. Overflow olmadığı zaman reset(0) durumundadır.
2 - PF = Parity flag olarak geçmektedir. Eşlik veya eşitlik bayrağı olarak bilinir. Sonucun düşük değerli baytındaki 1 lerin çift olduğunu gösterir.
4 - AF = Auxiliary Carry Flag olarak geçer ve ara elde bayrağı olarak bilinir. EAX kaydedicisinin 8 bitlik AL kısmında yapılan işlemlerde elde kalırsa bu bayrak 1(set) edilir. Aksi taktirde durumu 0(reset) dir.
6 - ZF = Zero Flag olarak geçer. Önemli bir bayraktır çoğu dallanma veya karşılaştırma bu bayrak ile yapılır. Bir işlemin sonucu sıfır ise bu bayrak 1(set) edilir. Aksi taktirde 0(reset) dir.
7 - SF = Sign Flag olarak geçer. İşaret biti olarak bilinir. Bir işlemin sonucu negatif olduğu zaman bu bayrak 1(set) edilir. Sonuç pozitifse bayrak 0(reset) değerini alır ve genellikle en önemli bitin değerini alır.
8 - TF = Trap Flag olarak geçer. Tek adım biti olarak bilinir. Bir program debugger yardımı ile açıldığı zaman bu bayrak 1(set) edilir ve instructionları tek tek işler yani program adım adım işler aksi taktirde 0(reset) değerini taşır.
9 - IF = Interrupt Flag olarak geçer. Kesme bayrağı olarak bilinir. CPU'nun interruplara yanıt verip vermeyeceğini belirler. Bu flag 1(set) edildiği zaman maskelenebilir interruplar kullanılabilir.
10 - DF = Direction Flag olarak geçer. Yön bayrağı olarak bilinir. String işlemleri yaparken kullanılır. 1(set) edildiği zaman index kaydedicisinin değeri bir azalır 0(reset) durumunda tam tersine index kaydedeicisinin değeri bir artar.
11 - OF = Overflow Flag olarak geçer. Taşma bayrağı olarak bilinir. 1(set) edildiği zaman işaret taşması olduğu anlamına gelir.
12-13 IOPL = Input/Output Priviledge Level olarak geçer. Bu bayrak geçerli sürecin ayrıcalık düzeyini tutar. Alttaki resimden ayrıcalık düzeylerini görebilirsiniz.
![](../assets/images/blog/x86-assembly_6.png)
14 - NT = Nested task flag olarak geçer. Bu flag korumalı modda kullanılır ve 1(set) edildiği zaman çalışan sürecin sonraki süreçle bağlantılı olduğu anlamına gelir aksi taktirde 0(reset) değerini alır
16 - RF = Resume flag olarak geçer.
17 - VM = Virtual 8086 mode flag olarak geçer. 8086 uyumluluk desteği olduğu zaman bu flag 1(set) edilir aksi taktirde 0(reset) durumdadır.
18 - AC = Alignment check flag olarak geçer Hizalama bayrağı olarak bilinir.
19 - VIF = Virtual Interrupt Pending flag olarak geçer. Interrupt bekleniyorsa 1(set) edilir aksi taktirde 0(reset) durumundadır.
21 - ID = Identification Flag olarak geçer.
Stack
Stack dilimizde yığın anlamına gelmektedir ve geçici değişkenlerin saklandığı bir hafıza alanıdır. Yukarıda adı geçen SS segmenti buranın adresini ve ESP stackde ki offsetleri işaret eder. Stack LIFO (Last In First Out) ilk giren son çıkar mantığıyla çalışmaktadır. Tüm yerel değişkenler, fonksiyon parametreleri ve geri dönüş değerleri burada oluşturulur.
![](../assets/images/blog/x86-assembly_7.png)
Görselde PUSH ve POP instructionları yabancı gelebilir. Assembly instructionlarını farklı bir blog yazısına sakladığımız için bu başlıkta pek değinmekdik. Kısaca özetlemek gerekirse Stack'e birşeyler saklamak için PUSH instructionunu kullanırız. Alttaki örnekleri inceleyebilirsiniz.
mov eax,0xa ; mov instructionu yardimiyla eax registerina 10 sayisini tasiyoruz.
push eax ; eax registerindaki 10 degerini stack e yolluyoruz.
push 0xdeadbeef
POP instructioni ise stackden değer çekmek için kullanılır. Alttaki örnekleri inceleyebilirsiniz.
push 0xdeadbeef ; 0xdeadbeef'i stacke yerlestiriyoruz.
pop eax ; pop instructionu ile stackdeki son degeri yani 0xdeadbeef'i eax registerine aliyoruz.
push eax ; eax registerina aldigimiz 0xdeadbeef i tekrardan stacke yolluyoruz.
Son
Assembly serisinde ilk yazımızı burada noktalıyoruz. Bir sonraki Assembly yazımızda insturctionlardan bahsedeceğiz. Yazımızda farkettiğiniz yazım hatalarını, eksik ve yanlışları bizlere e-posta yoluyla bildirerek eksiklerimizi gidermekte yardımcı olabilirsiniz.
Unutulmamalıdır ki buradaki bilgilerin tamamı eğitim amaçlıdır.