MyTetra Share
Делитесь знаниями!
О, смотри-ка какое хорошее место. Дайте два!
Использование Ассемблера в Linux
02.01.2009
15:48
Текстовые метки: ассемблер, assembler, машинный код
Раздел: Компьютер - Linux - Ассемблер


Введение.


Эта статья призвана описать программирование на ассемблере под Linux. В этой статье мы сравним AT&T и Intel синтаксисы ассемблера и рассмотрим, как использовать системные вызовы. В некоторых частях этой статьи содержится код, полученный экспериментальным путём, и поэтому в нём могут быть допущены ошибки.

Для прочтения этой статьи требуется только базовое знание ассемблера.


Intel и AT&T синтаксисы.


Intel и AT&T синтаксисы очень разные по «внешности», и это может привести путанице при переходе, допустим, с Intel’овского на AT&T’шный, и наоборот.


Префиксы.


В Intel синтаксисе не используются приставки не перед регистрами, не перед данными. В AT&T же, однако, перед регистрами ставится приставка «%», а перед данными «$». В Intel синтаксисе после шестнадцатеричных и двоичных данных ставятся суффиксы «h» и «b», соответственно. Также если шестнадцатеричное число начинается с буквы, то добавляется приставка «0»


Например:


+++++++++++++++++++++++++++++++++++++++++++++++++

+-Intel синтаксис:----+--AT&T синтаксис:--------+

+++++++++++++++++++++++++++++++++++++++++++++++++

+-mov------eax,1------+movl------$1,%eax--------+

+-mov------ebx,0FFh---+movl------$0xff,%ebx-----+

+-int------80h--------+int-------$0x80----------+

+++++++++++++++++++++++++++++++++++++++++++++++++



Порядок операндов.


В Intel и AT&T порядок операндов противоположен. В Intel первый операнд – приемник, а второй – источник. А в AT&T первый – источник, второй – приемник. Преимущество AT&T тут очевидно. Мы читаем слева направо, мы пишем слева направо, таким образом, этот порядок привычнее и естественнее.


Например:


+++++++++++++++++++++++++++++++

+-Intel синтаксис:--------+-AT&T синтаксис:-----+

+++++++++++++++++++++++++++++++

+ instr-----dest,source---+instr------source,dest--+

+ mov-----eax,[ecx]------+movl-----(%ecx),%eax+

+++++++++++++++++++++++++++++++



Операнды памяти.


Операнды памяти, как вы, наверное, уже заметили, тоже различаются. В Intel синтаксисе регистры, содержащие указатель на некоторую область памяти, заключаются в квадратные скобки: «[» и «]». А в AT&T в круглые: «(» и «)».


Например:


+++++++++++++++++++++++++++++++++

+-Intel синтаксис:--------+-AT&T синтаксис:---------+

+++++++++++++++++++++++++++++++++

+mov------eax, [ebx]-----+movl-------(%ebx),%eax-+

+mov------eax [ebx+3]---+movl------3(%ebx),%eax+

+++++++++++++++++++++++++++++++++


AT&T’шная форма инструкций, содержащих математические действия, очень сложна по сравнению с Intel’овской. Допустим, имеется инструкция на Intel синтаксисе: «segreg:[base+index*scale+disp]». На AT&T аналогичная инструкция будет выглядеть так: «%segreg:disp(base,index,scale)».


Index/scale/disp/segreg опциональны и могут быть опущены. Если index и/или scale не определены, то будут принимать дефолтное значение – «1».Segreg зависит от того выполняется ли приложение в реальном или защищенном режиме. В реальном режиме зависит от инструкции, а в защищенном нет.


Например:


+++++++++++++++++++++++++++++++++++++++++++++++++++++

+-Intel синтаксис:-------------------------------+-AT&T синтаксис:--------------------------+

+++++++++++++++++++++++++++++++++++++++++++++++++++++

+instr-foo,segreg:[base+index*scale+disp]+instr-%segreg:disp(base,index,scale),foo+

+mov-------eax,[ebx+20h]---------------------+movl------0x20,(%ebx),%eax-------------+

+add-------eax,[ebx+ecx*2h]-----------------+addl-------(%ebx,%ecx,0x2),%eax------+

+lea--------eax,[ebx+ecx]----------------------+leal--------(%ebx,%ecx),%eax-----------+

+sub-------eax,[ebx+ecx*4h-20h]------------+subl------ -0x20(%ebx,%ecx,0x4),%eax+

+++++++++++++++++++++++++++++++++++++++++++++++++++++


Как видите AT&T не очень понятен. «segreg:[base+index*scale+disp]» гораздо легче для восприятия, чем «%segreg:disp(base,index,scale)».



Суффиксы.


Как вы, возможно, заметили в AT&T’шной мнемонике используются суффиксы. Эти суффиксы указывают на размер операндов. AT&T’шный суффикс «l» соответствует Intel’овскому «DWORD», «w» - «WORD» и «b» - «byte».


Например:


+++++++++++++++++++++++++++++++++

+-Intel синтаксис:----------+-AT&T синтаксис:-------+

+++++++++++++++++++++++++++++++++

+mov------al,bl---------------+movb-----%bl,%al-------+

+mov------ax,bx-------------+movw-----%bx,%ax----+

+mov------eax,ebx----------+movl------%ebx,%eax--+

+mov------eax,dword [ebx]+movl------(%ebx),%eax+

+++++++++++++++++++++++++++++++++



Системные вызовы.


В этой части мы рассмотрим системные вызовы. Системные вызовы составляют все функции из второй части мануала расположенного в /usr/man/man2. Также их можно найти в /usr/include/sys/syscall.h. А также большой список расположен здесь: http://www.linuxassembly.org/syscall.html. Все эти функции могут быть вызваны прерыванием int $0x80.



Системные вызовы с < 6 аргументами.


Для всех системных вызовов номер функции ложится в %eax. Для функций количество аргументов, которых не превышает пяти аргументы ложатся в %ebx, %ecx, %edx, %esi, %edi соответственно. Результат выполнения функции возвращается в %eax.

Номера функций вы можете найти в /usr/include/sys/syscall.h. Выглядят она так: «SYS_имя функции». Например: SYS_exit, SYS_close, и т.д.


Например:


(Напишем Hello World, куда же без него)

Согласно описанию из man страницы, функция «write» объявлена так: «ssize_t write(int fd, const void *buf, size_t count);».


Следовательно «fd» ложем в %ebx, «buf» в %ecx, «count» в %edx и «SYS_write» в %eax. Далее вызываем прерывание «int $0x80», которое выполнит нашу функцию и вернет в %eax результат.


$ cat write.s

.include "defines.h"

.data

hello:

.string "hello worldn"


.globl main

main:

movl $SYS_write,%eax

movl $STDOUT,%ebx

movl $hello,%ecx

movl $12,%edx

int $0x80


ret

$


Аналогично вызываются все функции с количеством аргументов не превышающем пяти.



Системные вызовы с > 5 аргументами.


В этих функциях номер функции по-прежнему ложится в %eax, но аргументы уже записываются в структуру, и указатель на неё ложится в %ebx. Также можно положить аргументы в стек в обратном порядке и в %ebx поместить указатель на вершину стека.


Например:


$ cat mmap.s

.include "defines.h"


.data

file:

.string "mmap.s"

fd:

.long 0

filelen:

.long 0

mappedptr:

.long 0


.globl main

main:

push %ebp

movl %esp,%ebp

subl $24,%esp


// open($file, $O_RDONLY);


movl $fd,%ebx // save fd

movl %eax,(%ebx)


// lseek($fd,0,$SEEK_END);


movl $filelen,%ebx // save file length

movl %eax,(%ebx)


xorl %edx,%edx


// mmap(NULL,$filelen,PROT_READ,MAP_SHARED,$fd,0);

movl %edx,(%esp)

movl %eax,4(%esp) // file length still in %eax

movl $PROT_READ,8(%esp)

movl $MAP_SHARED,12(%esp)

movl $fd,%ebx // load file descriptor

movl (%ebx),%eax

movl %eax,16(%esp)

movl %edx,20(%esp)

movl $SYS_mmap,%eax

movl %esp,%ebx

int $0x80


movl $mappedptr,%ebx // save ptr

movl %eax,(%ebx)


// write($stdout, $mappedptr, $filelen);

// munmap($mappedptr, $filelen);

// close($fd);


movl %ebp,%esp

popl %ebp


ret

$


**Внимание: Этот исходник обрезан, для того чтобы показать как вызывать функции с кол-вом аргументов превышающим 5.



Работа с сокетами.


Функции сокетов вызываются несколько иначе, чем другие, в %eax всегда ложится одно число: «SYS_socketcall». А идентификатор самой функции ложится в %ebx. Список идентификаторов можно найти в /usr/include/linux/net.h. Аргументы ложатся в структурустек, и указатель на структурустек ложится в %ecx/


Например:


$ cat socket.s

.include "defines.h"


.globl _start

_start:

pushl %ebp

movl %esp,%ebp

sub $12,%esp


// socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);

movl $AF_INET,(%esp)

movl $SOCK_STREAM,4(%esp)

movl $IPPROTO_TCP,8(%esp)


movl $SYS_socketcall,%eax

movl $SYS_socketcall_socket,%ebx

movl %esp,%ecx

int $0x80


movl $SYS_exit,%eax

xorl %ebx,%ebx

int $0x80


movl %ebp,%esp

popl %ebp

ret

$



Оригинал: http://asm.sourceforge.net/articles/linasm.html

Автор: Phillip

Перевод Xteen [Hackzona.Ru], 2007


← Содержание ...
 
MyTetra Share v.0.35
Яндекс индекс цитирования