Temel x86 Komutları
x86 komut setini bir blog yazısı ile anlatmak esasen pek mümkün değil. Ancak bu yazıdaki amaç debug ve tersine mühendislik işlemleri ile uğraşmak isteyenlerin inceledikleri assembly kod öbeklerinin büyük bir bölümünü anlayabilecek seviyeye gelmelerini sağlamakdır. Daha önce bu konu ile ilgili yapılan bir incelemede Daniel Bilar (2006), 20 farklı uygulamada gerçekleştirmiş olduğu çalışmada uygulama içinde bulunan assembly komutlarının %90 lık kısmını 14 assembly komutunun doldurduğunu belirtmiş. Bu yazıda da bu komutlar üzerinden anlatımlar gerçekleştirilecektir.
Kaynak: http://www.blackhat.com/presentations/bh-usa-06/BH-US-06-Bilar.pdf
NOP
Hiç bir işlem gerçekleştirmeyen bir makina kodudur. Programlama veya derlemede genellikle bir komutun kullanımında boşta kalan alanların doldurulmasında kullanılır. Exploit geliştirme çalışmalarında ise yazılan exploit kodunun güvenilir ve sağlam olması için bu komut kullanılmaktadır.
PUSH/POP
PUSH/POP komutları stack alanına veri eklemek yada çıkarmak için kulanılan komutlardır. PUSH Komutu 4 byte uzunluğundaki veriyi stack’e ekler ve ESP değerini 4 azaltır. Stack büyümesini, düşük adres alanına doğru gerçekleştirdiği için çıkartma işlemi yapılmaktadır.
Aynı şekilde POP işlemi sırasında da stack alanının en üst noktasından (top of the stack) 4 byte uzunluğunda bir veri alınır ve ESP değeri 4 artırılıtr.
CALL/RET
CALL assembly komutu ile program akışı uygulamanın kod alanı içinde başka bir yerde bulunan bir fonksiyon alanına gider ve fonsiyonun işlemi tamamlandıktan sonra ana program akışı içinde kaldığı yerden devam eder. CALL ile çağırılan fonksiyonun işlemleri tamamlandıktan sonra ana kod akışında kaldığı yerden devam etmesi için çağırılan fonksiyonda RET assembly komutunun çalıştırılması gerekmektedir.
CALL Komutu çalıştırıldığında aşağıdaki işlemler gerçekleştirilmektedir.
- Bir sonraki komut adresi stack üzerine eklenir. (Bu değer fonksiyon RET komutu ile ana fonksiyona geldiğinde kaldığı yerden devam edeceği adres bilgisidir.)
- EIP değerine CALL komutunda gösterilen adres değeri atanır.
MOV
MOV komutu ile iki yazmaç arasında, yazmaçlarla hazfıza arasında veri taşımak veya bahsedilen bu alanlara bir değer atamak için kullanılan bir komuttur. Bu komut, memory adresleri arasında kullanılmaz.
mov eax, ebx
mov eax, [ebx]
LEA
LEA Komutu(Load Effective Address) etkin adresi yükle anlamındadır. MOV komutundan farklı olarak MOV komutu ile register’a bir hafızadan veya register’dan yüklersiniz. Oysa LEA komutu ile register’ao yerdeki değerideğil, oranın adresini yüklersiniz. Bu sekilde iki yada üç komut ile yapılacak bir işlemi tek bir komut ile yapabilirsiniz. Böylelikle, LEA komutu, herhangi bir register’a bir bellek adresini yükleme amacı ile kullanılmış olur.
lea register, adres
lea register, [adres]
ADD/SUB
ADD ve SUB komutları isimlerinden de anlaşılacağı üzere toplama çıkartma işlemlerinde kullanılan komutlardır. İşlem sonucunda elde edilen değeri ilk operand’a yazar.
add register, register
add memory, memory
add <register/register>, <sabitdeğer>
sub register, register
sub memory, memory
sub <register/register>, <sabitdeğer>
CMP
iki farklı değerin karşılaştırılmasında kullanılan bir komuttur. Çoğunlukla şartlı işlem gerçekleştirme öncesinde kullanılırlar. Bu komut ile iki operand karşılaştırma amacı ile birbirinden çıkartılır. Bu işlem gerçekleştirilirken kullanılan operand’ların değeri değiştirmez. Ancak işlemcide bulunan bayraklar üzerinde etkisi bulunmaktadır.
CMP Operand1, Operand2
Karşılaştırma işleminde bayrak durumları aşağıdaki gibi olmaktadır.
Bayrak | CMP Durumu |
ZF (Zero Flag) | İki değer birbirine eşit ise set edilir. |
CF (Cary Flag) | Operand2 büyük ise 1, Operand2 büyük ise 0 set edilir. |
OF (Overflow Flag) | Operand2 büyük ise,SF=0 OF=1 veya SF=1 OF=0Operand1 büyük ise,SF=0 OF=0 veya SF=1 OF=1 |
SF (Sign Flag) |
AND/OR/XOR/NOT
Bu komutlar mantıksal işlemlerin gerçekleştirilmesinde kullanılan komutlardır. Bu komutların kullanımı aşağıdaki gösterimde daha iyi anlaşılacaktır.
AND Operand1, Operand2
Operand1 | 1010 |
Operand2 | 0110 |
İşlem Sonucunda Operand1 | 0010 |
OR Operand1, Operand2
Operand1 | 1010 |
Operand2 | 0110 |
İşlem Sonucunda Operand1 | 1110 |
XOR Operand1, Operand2
Operand1 | 1010 |
Operand2 | 0110 |
İşlem Sonucunda Operand1 | 1100 |
NOT Operand1
Operand1 | 1010 |
İşlem Sonucunda Operand1 | 0101 |
TEST
Test komutu, operand’lar üzerinde herhangi bir işlem yapmadan AND işlemi gerçekleştiren komuttur. İşlem sonucundan etkilenenlersadece bayraklardır. Etkilenen bayraklar CF OF PF SF ZF bayraklarıdır. İşlemde kullanılacak verilerin büyüklüklerinebağlı olarak TEST işleminde kullanılan opcode değerleri de farklılık gösterebilirler.
test operand1, operand2
Örneğin yukarıdaki işlem sonucunda eğer sonuç sıfır ise ZF değeri 1 olarak işaretlenir. Eğer operand2 değeri negatif ise SF değeri 1 olarak işaretlenir.
JMP
JMP komutu ile koşulsuz olarak, program akışı istenilen bir noktaya dönüş değeri olmaksızın yönlendirilmiş olur. Bu komutta kullanılan operand atlama yapılacak adres değerini göstermektedir.
JMP operand1
JCC
JCC komutları ile belli bir şarta bağlı olarak program kodu içinde şartlı akış gerçekleştirilir. Bu komut ile EFLAG yazmacı içindeki bayrak değerlerinin durumuna göre atlama (jmp) işlemi gerçekleştirilir. Bu komutlar ile ilgili tablo aşağıda görülmektedir.
Instruction | Description | signed-ness | Flags | short jump codes | near jump opcodes |
JO | Jump if overflow | OF = 1 | 70 | 0F 80 | |
JNO | Jump if not overflow | OF = 0 | 71 | 0F 81 | |
JS | Jump if sign | SF = 1 | 78 | 0F 88 | |
JNS | Jump if not sign | SF = 0 | 79 | 0F 89 | |
JE JZ | Jump if equalJump if zero | ZF = 1 | 74 | 0F 84 | |
JNE JNZ | Jump if not equalJump if not zero | ZF = 0 | 75 | 0F 85 | |
JB JNAE JC | Jump if belowJump if not above or equalJump if carry | unsigned | CF = 1 | 72 | 0F 82 |
JNB JAE JNC | Jump if not belowJump if above or equalJump if not carry | unsigned | CF = 0 | 73 | 0F 83 |
JBE JNA | Jump if below or equalJump if not above | unsigned | CF = 1 or ZF = 1 | 76 | 0F 86 |
JA JNBE | Jump if aboveJump if not below or equal | unsigned | CF = 0 and ZF = 0 | 77 | 0F 87 |
JL JNGE | Jump if lessJump if not greater or equal | signed | SF <> OF | 7C | 0F 8C |
JGE JNL | Jump if greater or equalJump if not less | signed | SF = OF | 7D | 0F 8D |
JLE JNG | Jump if less or equalJump if not greater | signed | ZF = 1 or SF <> OF | 7E | 0F 8E |
JG JNLE | Jump if greaterJump if not less or equal | signed | ZF = 0 and SF = OF | 7F | 0F 8F |
JP JPE | Jump if parityJump if parity even | PF = 1 | 7A | 0F 8A | |
JNP JPO | Jump if not parityJump if parity odd | PF = 0 | 7B | 0F 8B | |
JCXZ JECXZ | Jump if %CX register is 0Jump if %ECX register is 0 | %CX = 0%ECX = 0 | E3 |
Komut Gösterimi
Assembly de bilinen farklı iki kod gösterimi bulunmaktadır. Unix türevi sistemler, AT&T komut gösterimini kullanırken, Windows sistemleri ise INTEL komut gösterimini kullanmaktadırlar. Gerekli yapılandırmalar yapıldığı taktirde Unix uygulamalarında da INTEL kod gösterimi ile çalışılabilmektedir.
Örnek INTEL kod gösterimi
4004ac: 55 push rbp
4004ad: 48 89 e5 mov rbp,rsp
4004b0: 89 7d fc mov DWORD PTR [rbp-0x4],edi
4004b3: 48 89 75 f0 mov QWORD PTR [rbp-0x10],rsi
4004b7: b8 00 00 00 00 mov eax,0x0
4004bc: 5d pop rbp
4004bd: c3 ret
Örnek AT&T kod gösterimi
4004ac: 55 push %rbp
4004ad: 48 89 e5 mov %rsp,%rbp
4004b0: 89 7d fc mov %edi,-0x4(%rbp)
4004b3: 48 89 75 f0 mov %rsi,-0x10(%rbp)
4004b7: b8 00 00 00 00 mov $0x0,%eax
4004bc: 5d pop %rbp
4004bd: c3 retq
Kaynaklar:
https://www.blackhat.com/presentations/bh-usa-06/BH-US-06-Bilar.pdf