Настольная книга компьютерного исследователя


Linux assembler для начинающих

Хе. Насколько все себя считают крутыми, что никогда не сподобятся написать простенький туториал, для тех кому хочется, но не можется по разного рода причинам. Возможно реализация этой моей наивной мысли кому-то поможет ;).

Прежде чем касаться конкретных вещей, стоит немного сказать о синтаксисе используемом в Linux (и в unix также). Это AT&T синтаксис. Плюсы AT&T синтаксиса, в четком объяснении команд. Вообще говоря на Amiga, где используются процессоры motorola 68xxx — такой вид синтаксиса (очень похожий), идет изначально. Пример тому под DevPac:

   move.l  #$20000,a1   ;   кидаем в a1 16-ричную  константу длинной в 32 бита.
        ^

Пример под *nix (AT&T syntax):

   movl $8,%eax         //  кидаем в eax 16-ричную константу длинной в 32 бита.
      ^

И в том, и в другом случае четко объяснено, какого размера данные должны быть там где они должны быть. Под Intel синтаксис команды такого типа приходиться «расжевывать» :

   mov es: byte ptr [di],30    ; mov es:[di],30 - некоторые компиляторы
           ^^^^^^^^            ; примут такое без ошибок (старые tasm) ,
                               ; новые честно выдадут ошибку ;).

Еще одну особенность скорее следует отнести к проблеме. В AT&T синтаксисе нет поддержки многосекционности. Т.е. писать надо все ОДНИМ куском кода, что довольно-таки не приятно при написании крупных программ.

Префиксы AT&T синтаксиса (SysV/386):

 // - коментарии ;     .b - байт     ;       .globl  ;     .data       ;
 %  - регистр    ;     .l - 2 слова  ;       .extern ;     .text       ;
 $  - константа  ;     .w - слово    ;       .equ    ;     .bss        ;
 .  - деректива  ;     .q - 4 слова  ;       .set    ;     .code16/32  ;

И т.д. и т.п. — все они, описаны во многих доках, одна из самых лучших которая называется: «Использование GNU ассемблера AS «.

Компилятор (по умолчанию) используемый AT&T синтаксис в Linux, как уже говорилось выше — as. (есть конечно и другие). Также существует компилятор nasm (NetWideASM) использующий Intel синтаксис. Объяснять его префексы/суффиксы я не вижу смысла, т.к. в принципе он является в большую сторону аналогом tasm компилятора. В данной статье я буду чередовать между собой эти 2 вида синтаксиса, для более полного понимания.

Linux после загрузки находится в защищенном 32/битном режиме. А это значит, что использовать прерывания нельзя, кроме одного прерывания 0x80h. Данное прерывание, не является прерыванием в том смысле в котором мы привыкли его видеть. Оно является не чем иным, как упрощенным(?) способом обращения к kernel enter point. Т.е. вместо того, чтобы ссылаться сall’ом на точку входа ядра (как во многих юникс клонах) в Linux сделано данное 80h прерывание. А ссылаться на эту точку входа нужно системным вызовам — Syscalls, которые представляют собой как бы набор «услуг» ядра.

                              ┌──────────────────┐
                              │     SysCalls     │
                              ├──────────────────┤
                              │                  │
                              │      Kernel      │
                              │                  │
                              ├──────────────────┤
                              │ Hardware control │
                              └──────────────────┘

Теперь, что касается «грамматики» написания ассемблерных программ. Схема действий такова: Заносим номер вызова в eax (кстати список этих номеров находится в /usr/include/asm/unist.h). В ebx , ecx , edx кидаем параметры (аргументы) данного Syscall.

Писать можно с использованием libc и без использования оного. Libc — это библиотека, в которой хранится множество всяких функций. Функции эти в большинстве своем содержат макросы, которые в итоге всеже ссылаются на «настоящий» Syscall.

Начать можно без участия libc . Забудем про этот страшно преследующий всех «Hello World», и напишем маленький исходник по созданию файла:

;---Great File (Intel Syntax)--------------------------------------------------
;
; nasm -f elf file.s
; ld -o file file.o
;
global main
main:

mov eax,08h ; Системный вызов 8 (creat)
mov ebx,file ; Имя файла для создания
int 80h ; Точка входа в ядро

mov eax,1 ; Системный вызов 1(exit)
int 80h ; Точка входа в ядро

file db ‘lalal’,0
;—EOF————————————————————————

#—Great File (At&T Syntax)—————————————————
#
# as -o file.o file.s
# ld -o file file.o
#
.globl _start
_start:

movl $8,%eax // Syscall 8(creat)
movl $file,%ebx // File name of creat
int $0×80 // Kernel enter point

movl $1,%eax // Syscall 1(exit)
int $0×80 // Kernel enter point

file:
.asciz «lalal» // z=zero , 0 т.е. ;)
#—EOF————————————————————————

Суть в принципе понятна (я надеюсь) . Как видно все очень просто и похоже на Дос. (под Dos наверно сложнее всеже ;) ).

Теперь напишем аналогичное, но с участием libc. Схема действий такова: занесли аргументы в стэк, вызвали нужную нам функцию по call, очистили стэк от занесенных аргументов.

;---Great File (Intel Syntax(libc))--------------------------------------------
;
; nasm -f elf file.s
; ld и далее где какие crti/crt1/etc.
; или gcc -o file file.o ;))
;
global main
extern creat
main:
push dword name ; адрес строки в стэк
call creat ; зовем функцию
pop eax ; очищаем стэк от адреса строки
ret ; good bye.
section .data
name db "lalala",0

;—EOF————————————————————————

//—Great File (At&T Syntax(libc))———————————————
//
// as -o file.o file.s
// Аналогично intel’u.
//
.global main
.extern creat
main:
pushl $msg # адрес строки в стэк
call creat # зовем функцию
popl %eax # очищаем стэк от адреса строки
ret # good bye.
.data
msg:
.string «lalala\0″

//—EOF————————————————————————

Все просто и легко. Данная статья является некоторым родом введением для незнающих. Для остальных это конечно не более чем детский лепет ;) Надеюсь в будущих номерах, будут появляться достаточно полные и интересные вещи касающиеся программирования/шелкодирования/троянизирования под unix поднобные системы, qnx, linux, etc.

И для полного смеха и показания как все это просто, небольшой список системмных вызов, номера которых всегда заносятся в eax:

  ┌─────────────────────────────────────────────────────────────────────────┐
  │   08h Creat     - Создать Файл                                          │
  │       Вход: ebx - ASCII имя файла.                                      │
  │             ecx - атрибуты файла (полномочия/права)                     │
  ├─────────────────────────────────────────────────────────────────────────┤
  │   0bh ExecVe    - Исполнить                                             │
  │       Вход: ebx - файл который будет запущен                            │
  │             ecx - параметры(ключи) запускаемой программы.               │
  │             edx - 0                                                     │
  ├─────────────────────────────────────────────────────────────────────────┤
  │   26h Rename    - Переименовать                                         │
  │       Вход: ebx - имя файла который переименовывать                     │
  │             ecx - имя файла в который переименовывать                   │
  ├─────────────────────────────────────────────────────────────────────────┤
  │   27h MkDir     - Создать директорию                                    │
  │       Вход: ebx - имя директории                                        │
  │             ecx - атрибуты директории (полномочия/права)                │
  ├─────────────────────────────────────────────────────────────────────────┤
  │   28h RmDir     - Стереть директорию                                    │
  │       Вход: ebx - имя стeраемой директории                              │
  └─────────────────────────────────────────────────────────────────────────┘

Описывать их все нет смысла, т.к. все следует из названий функций в unist.h

(c) MadCr



©2013 Журнал Хакера Entries (RSS) and Comments (RSS)