Search     or:     and:
 LINUX 
 Language 
 Kernel 
 Package 
 Book 
 Test 
 OS 
 Forum 
iakovlev.org

AT&T Tutorial

[1]. Как сравнить 2 строковые переменные ?:

Столкнуться с необходимостью такого сравнения можно в любой программе, принимающей входные данные от пользователя, например авторизация. Для этого существует специальная инструкция сравнения строк - cmpsb, она берет данные из регистров %esi, %edi и %ecx:

-------------------------------------------------------------------------
 /* strcmp.s */
 .globl _start
 _start: movl $str1,%esi    # кладем первую строку в %esi
 movl $str2,%edi            # вторую в %edi
 movl $4,%ecx               # количество байт для сравнения
 cld                        # очистить флаг D, иначе сравнение 
                            # пойдет в обратном порядке
 rep  cmpsb                 # сравнение
 jz   yes                   # если строки равны то прыгаем в yes,
                            # если нет, то exit
 exit:
 movl $1,%eax               # выход
 int  $0x80 yes:
 movl $4,%eax               # write
 movl $1,%ebx               # stdout
 movl $msg,%ecx             # наше сообщение
 movl $msglen,%edx          # его длина
 int  $0x80 
 jmp exit 
 .data
 str1:   .string "asdf"
 str2:   .string "asdf"
 msg:    .string "The strings are equal! Quiting...\n"
 msglen = . -msg            # вычислить длину msg
 /* End */
 -------------------------------------------------------------------------
 
Компиляция:
as strstr.s -o strstr.o
ld strstr.o
Можно использовать gcc: gcc strstr.s , предварительно заменив "_start" на "main".

[2]. Как соединить строковые переменные ?:

Для этого будем использовать интсрукцию movsb, первую строку кладем в %esi и буфер, в котором получим результат, в %edi. Взгляни на пример:
-------------------------------------------------------------------------
 /* strcat.s */
 .globl main
 main: 
 movl $str1,%esi          # кладем строку "AT&T" в %esi
 movl $buf,%edi           # буфер с будущим результатом
 movl $6,%ecx             # размер стринга
 cld   
 rep  movsb               # после выполнения в $buf находится str1
 movl $str2,%esi          # далее производим выше описаные операции с str2
 movl $9,%ecx  
 cld   
 rep  movsb               # вуаля, теперь у нас в $buf имеется str1+str2,
                          # т.е "AT&T is cool\n"
 movl $4,%eax             # write
 movl $1,%ebx             # stdout
 movl $buf,%ecx           # "AT&T is cool\n"
 movl $15,%edx            # size of $buf
 int  $0x80 
 movl $1,%eax             # exit
 int  $0x80 .data
 str1: .string "AT&T "
 str2: .string "is cool\n"
 buf: .byte 15            # static buffer
 /* End */
 ------------------------------------------------------------------------- 
 

[3]. Как создать счетчик ?:

Что за программа без счетчика ? Да, счетчик часто используемая функция. Для визуальной демонстрации работы нашего счетчика будет использован вывод строки через write, поэтому регистры %eax,%ebx,%ecx,%edx будут заняты, но %esi и %edi останутся, их-то мы и будем использовать:
-------------------------------------------------------------------------
 /* counter.s */
 .globl main
 main: 
 movl $5,%esi   # число повторений , используй %esi или %edi не важно 
 loop:                # write (stdout,str,5);
 movl $4,%eax  
 movl $1,%ebx
 movl $str,%ecx
 movl $5,%edx
 int  $0x80 decl %esi # уменьшить %esi на 1
 tesl %esi,%esi       # если %esi 0
 jz   exit            # jmp exit 
 jmp  loop            # прыжок на начало 
 exit:
 movl $1,%eax
 int  $0x80
 /* End */
 ------------------------------------------------------------------------- 
 

[4]. Как динамически вычислить длину строки ?:

Для реализации этой функции мы прибегнем к небольшому трюку. Будем использовать инструкцию lodsb для проверки строки байт за байтом, попутно увеличивая счетчик для вычисления длины. Сперва необходимо поместить эфективный адрес строки в %esi, %edi будет счетчиком.

-------------------------------------------------------------------------
 /* strlen.s */
 .globl main
 main: 
 xorl %edi,%edi   
 leal str,%esi                # эфективный адрес в %esi abc:
 lodsb                        # читает байт в строке и автоматически устанавливает 
                              # следующую позицию
 cmpb $0,%al                  # если байт равен 0, значит end of string
 jz   continue    
 incl %edi                    # счетчик
 jmp  abc                     # повторить... continue:
 decl %edi ...                # в %edi готовый результат - длина (int) .data
 str: .string "CheckMylength" # если в строке присутствует \n, длина увеличивается 
                              # дополнительно на 1 байт
 /* End */
 ------------------------------------------------------------------------- 
 
Небольшой перерыв для нескольких советов. Почему бы не использовать переменные в более дружественном виде ? int $sysint - выглядит лучше чем int $0x80, объявляется это так - sysint = 0x80, все что захочешь:
int = 1
char = 97 # character "a"
(dec) char = 0x61 # character "a" (hex)
etc...

Но не забывай ! Это постоянные (статические) переменные, устанавливающиеся во время компиляции. В итоге невозможно увеличить\уменьшить их в коде. К этому методу можно прибегнуть если большой код расчитан на анализ другими людьми, в таком случае осмысленные названия будут более понятны при изучении.

[ Continue... ]

[5]. Как работать с массивами ?:

 -------------------------------------------------------------------------
 /* Small C example */
 int main()
 {
 char a[10];
 int b; for (b=0;b!=10;b++)
  {
   a[b]='a';
   printf("%c",a[b]);
  }
 }
 /* End */
 ------------------------------------------------------------------------- 
 
 
Этот пример пихает символ "a" в каждый элемент массива, вывод программы выглядит так:aaaaaaaaaa На мой взгляд это подходящий пример.
-------------------------------------------------------------------------
 /* array.s */ count  = 10  # счетчик
 a      = -12               # смещение в %ebp, место для нашего массива - 
                            # как char a[10];
 b      = -16               # int b;
 char   = 97                # символ 'a' (dec)
 sysint = 0x80 
 .globl main
 main: 
 movl $0,b(%ebp)      # for(b=0; loop:
 cmpl $count,b(%ebp)        # b != 10;
 jnz  lp                    # jump if not equal
 jmp  exit                  # jump if counter reached lp:
 leal a(%ebp),%edi          # a[
 movl b(%ebp),%esi          # b]
 movb $char,(%esi,%edi)     # ='a';
 incl b(%ebp)               # b++);
                            # write(stdout,a[b],1);
 movl $4,%eax
 movl $1,%ebx
 leal (%esi,%edi),%ecx
 movl $1,%edx
 int  $sysint
  
 jmp loop 
 exit:
 movl $1,%eax
 int  $sysint
 /* End */
 ------------------------------------------------------------------------- 
 

Как можно заметить, область массива в %edi и его элементы в %esi, готовый массив находится в (%esi,%edi).

[6]. Как сконвертировать число(integer) в его ASCII эквивалент(string)?:

Для чего это надо ? Например syscall write работает только со строками, и если мы сделаем write(stdout,numeric,length), будет выдан символ из ASCII таблицы с номером numeric. Числа в ASCII таблице начинаются с 48 (decimal 0), поэтому мы просто увеличиваем наш numeric на 48 и получаем аскишный эквивалент необходимой цифры. Перед тем как выполнить это на асме, взглянем на процедуру выполненную в С...Так как система счисления десятеричная, то соответственно и делить необходимо на 10:

-------------------------------------------------------------------------
 /* num2ascii.c */
 int  main()
  {
 char a[10];
 int x = 1234,b=0; do    // отделяем каждую цифру от целого числа
  {
    a[b]=48 + x % 10;    // кладем в массив, первый элемент содержит "4", второй "3"
                         // etc... - обратный порядок
           b++;          
  } while (x /= 10);
 do                      // перевернем 4321 в необходимое 1234
  {
    b--;
    printf("%d\n",a[b]);
  } while(b!=0);
 }
 /* End */
 -------------------------------------------------------------------------
 
Это приблизительная реализация, если перевести ее шаг за шагом на асм, мы получим большой код в пределах ~1kb, поэтому пример на асме имеет немного другую структуру.
-------------------------------------------------------------------------
 /* num2ascii.s */ 
 int = 1234 .globl main
 main: 
 xorl %esi,%esi
 movl $int,%eax 
 loop:
 movl $0,%edx
 movl $10,%ebx
 divl %ebx            # деление, результат в %eax (123) и остаток в %edx (4)
 addb $48,%dl         # +48
 pushl %edx           # на стек
 incl %esi            # счетчик цифр
 cmpb $0,%al
 jz   next 
 jmp loop 
 next:   
 popl (%ecx)          # взять со стека
 testl %esi,%esi
 jz   exit
 decl %esi
 movl $4,%eax
 movl $1,%ebx
 movl $2,%edx
 int  $0x80 
 jmp  next
 exit:
 movl $1,%eax
 int $0x80
 /* End */
 -------------------------------------------------------------------------  
 
 

[7]. Как сконвертировать числовую переменную в сетевой байтовый порядок (network byte order) ?:

Такая нужда у нас появляется при работе с сокетами, а точнее htons(port).
-------------------------------------------------------------------------
 /* htons.s */
 int = 10
 .globl main
 main: 
 movl $int,%edi
 movl %edi,%eax
 rol $8,%ax  # сдвиг, rol или ror здесь роли не играют 
 
 # сейчас %eax содержит 10 в nbo
 ... /* End */
 ------------------------------------------------------------------------- 
 
Оставьте свой комментарий !

Ваше имя:
Комментарий:
Оба поля являются обязательными

 Автор  Комментарий к данной статье