[Asm] Kurs Asemblera (NASM)

sinis

Użytkownik
Dołączył
Wrzesień 3, 2006
Posty
958
Witam
smile.gif


Jakoś tak znudzony po szkole postanowiłem, że napiszę jakiś kursik Asma dla początkujących. Kurs będzie się opierał na kompilatorze NASM (wg. mnie najwygodniejszy, działa pod Linuchem i Windą).
Zacznijmy od tego co będzie nam potrzebne:
- "świeży mózg" - można się przejść na spacer, albo coś. Ważne żeby się "luźno" myślało
<
(ciężko z tym po szkole)
- trochę czasu - jak na wszystko
- kompilator - NASM'a. (Pobierz)
- edytor - coś do pisania kodu. Ja korzystam z Notepada, albo RadASMa. Mogę polecić NasmIDE. Ładnie koloruje kod, tylko, że jest w konsoli.

Wypakowywujemy nasma do C:\Nasm. Otwieramy PPM na Mój Komputer > Właściwości > Zaaw. > Zmienne środowiskowe. Do zmiennej Path dodajemy ciąg "C:\Nasm\bin;". Bez spacji. Zatwierdzamy i gotowe.

Po co nam Asembler, zapytacie. Otóż po to, żeby pisać wielokroć szybsze i mniejsze programy, niż te pisane w HLL'ach (High-Level Language, język wysokiego poziomu). Asembler przydaje się m.in. przy optymalizacji kodu HLL'a, szukaniu bugów (debuggery), pisaniu cracków do gier
<
, reverse engineeringu (chyba tak się to pisze), no i co najważniejsze: przy kodzeniu
<


Niejeden powie, że Asm jest trudny i niewygodny. Owszem, zgodzę się z tym. Z początku może wyglądać na trudny, ale po napisaniu kilku programów i zrozumieniu ich działania jest bardzo prosty. Czy jest niewygodny ? Zależy od upodobań. Zwykle kod jest długi, ponieważ każdą instrukcję wydaje się w osobnej linii, ale na pewno się opłaca się go nauczyć.

Instrukcje zwykle przyjmują formę:
Kod:
instrukcja cel, źródło
Niektóre instrukcje, jak odkładanie parametrów na stos (Wiki), czy zdejmowanie ich. O nich dowiemy się kiedy indziej.

Teraz podam przykładowy kod nieśmiertelnego Hello World xD Niech nikt się nie przeraża i nie ucieka... Wszystko dokładnie wytłumaczę
smile.gif

Kod:
; Nasz pierwszy program
; Nieśmiertelne Hello World :)

org 100h

; Stałe. Podobne jak w C++
%define cr 13
%define lf 10
%define nwln cr, lf

section .data
HelloWorld db "Hello World!", nwln, '$'

section .text
global _start

_start:
; Wypisujemy Hello World
    mov ah, 9
    mov dx, HelloWorld
    int 21h

; Oczekujemy na wciśnięcie klawisza
    mov ah, 0
    int 16h

; Wychodzimy z programu
    mov ax, 4C00h
    int 21h

Straszne? Zaraz udowodnię, że nie
<

Komentarze rozpoczynają się znakiem ';'. Wszystko co za nimi (w danej linii) nie jest ważne dla kompilatora. Jest to tylko informacja dla programisty.

Kod:
org 100h
Ta instrukcja oznacza, że program zaczyna się pod adresem (offsetem) 100h (256 dziesiętnie). Przyrostek 'h' oznacza, że liczba jest zapisana w systemie heksadecymalnym (szesnastkowym). Przyrostek 'b' oznacza liczbę binarną, a brak przyrostka dziesiętną. Początek kodu pod tym offsetem jest charakterystyczna dla plików *.com. Są to właśnie programy pisane w Asmie. Przed tym adresem znajdują się inne informacje, tj. linia poleceń itp...

Stałych raczej nie muszę tłumaczyć.

Kod:
section .data
HelloWorld db "Hello World!", nwln, '$'
section .data oznacza początek segmentu danych programu. W pliku wykonalnym takich segmentów jest kilka: .text (kod programu), .data (jego dane), .bss(dane niezainicjalizowane), .heap (sterta), .stack (stos) itp...
W naszym programie jest tylko jedna zmienna. Jest to napis HelloWorld.
'db' oznacza Declare Byte (deklaruj bajt), tak więc HelloWorld jest tablicą bajtów. Ciągi znaków w DOS'ie są zakańczane znakami dolara, tak jak tutaj.
Jest kilka instrukcji deklarujących dane, są to:
- db - wiadomo - 1 bajt (char w C++)
- dw - Declare Word - 2 bajty (short int w C++)
- dd - Declare Double Word (DWORD) - 4 bajty (int w C++)
- dq - Declare Quadro Word (QWORD) - 8 bajtów (long i double w C++)
- dt - Declare Ten Bytes - 10 bajtów
- resb - rezerwuje 1 bajt
- resw - analogicznie do pozostałych
smile.gif

itd...

section .text - początek sekcji kodu naszego programu

global _start - oznaczenie punktu startu programu dla linkera.

_start: - etykieta mówiąca gdzie program się zaczyna

mov ah, 9 - Wrzucamy do rejestru AH wartość 9. Jest to numer funkcji DOS'a wypisującej na ekranie dany w rejestrze DX ciąg znaków.
Rejestry to takie specjalne "zmienne" procesora. Jego własna pamięć. Rejestrów jest trochę:
- EAX - rejestr akumulatora, dzieli się na: 32 starsze bity + AX (rejestry: AH + AL). Wykorzystuje się go głównie do wartości zwracanych przez funkcję (procedurę) i mówienia systemowi, z której funkcji chcemy skorzystać.
- EBX - rejestr bazowy, dzieli się na: 32 starsze bity + BX (BH + BL).
- ECX - rejestr licznika, dzieli się analogicznie do poprzednich. Jest wykorzystywany przy pętlach, w nim zawarta jest ilość skoków do etykiety pętli.
- EDX - rejestr danych, wiadomo jak się dzieli. W nim zwykle ląduje adres do ciągu znaków od wypisania itp.
- ESI - rejestr źródłowy. Wykorzystywany przy przetwarzaniu ciągów znakowych.
- EDI - rejest docelowy. Jak wyżej.
- ESP - wskaźnik stosu. Wskazuje na koniec stosu.
- EBP - wskaźnik bazowy. Zwykle używa się go do uzyskiwania dostępu do zmiennych lokalnych.
- CS - rejestr segmentu kodu
- DS - rejestr segmentu danych
- SS - rejestr stosu
- flagi: jest ich trochę. Omówię kilka głównych:
- CF - flaga przeniesienia
- ZF - flaga zera (równości)
- OF - flaga przepełnienia
- PF - flaga parzystości
- DF - flaga kierunku
- SF - flaga znaku.
Nie musicie znać tych wszystkich rejestrów na pamięć (nawet ja nie wszystkie pamiętam, tylko te ważniejsze
<
) i tak same wchodzą do głowy.

int 21h - wywołanie przerwania DOS'a o numerze 21h, które to wywołuje funkcję o numerze zawartym w rejestrze AX. Int oznacza wywołanie przerwania, dalej jest jego numer. Przerwania są różne: sprzętowe, systemowe itp... My korzystamy głównie z systemowych.

int 16h - wywołuje przerwanie BIOS'u i analogicznie do poprzedniego odpowiednią funkcję.

mov ax, 4C00h - do rejestru AX, leci numer funkcji wyjścia z programu
int 21h - przerwanie DOS'a

Kompilacja prorgamu:
Start > Uruchom > cmd > przemieszczamy się do folderu gdzie jest nasz plik źródłowy z kodem helloworlda i wydajemy polecenie:
Kod:
nasm helloworld.asm -o helloworld.com
Odpalamy i naszym oczom ukazuje się napis, który każdy widział kiedy zaczynał swoją zabawę z programowaniem
<

W tym momencie warto spojrzeć na rozmiar pliku wykonalnego. Jego rozmiar wynosi 31 BAJTÓW!! Cudnie, nieprawdaż? To tysiące razy krócej niż tasiemce produkowane przez HLL'e (nie żebym miał coś przeciwko, sam w kodzę w C++ xD).

Myślę, że wszystko związane z tym programem powinno być jasne. W celu nabycia trochę większej praktyki zaprezentuję jeszcze jeden program. Pyta on użytkownika o imię i grzecznie się z nim wita. Poza funkcjami DOS'a wypisującymi dane na ekranie będziemy musieli użyć funkcji, która odczyta te dane, bo skąd będziemy znać imię użytkownika?
Ta funkcja to 0Ah przerwania int 21h (gdy szykamy jakiejś funkcji warto skorzystać z Listy Przerwań Ralfa Browna - link niżej). Funkcja ta przyjmuje jako parametr ciąg bajtów, pierwszy bajt to liczba określająca pojemność bufora, drugi zwróci nam sama funkcja, tam będzie ilość odczytanych bajtów. Reszta to bufor na imię.
Kod:
; Witajka;)

org 100h; Program com

; Stałe
%define cr 13
%define lf 10
%define nwln 13, 10

; Dane naszego programu
section .data
logo db "Witajka", nwln, nwln, '$'
entername db "Podaj swoje imie: $"
name db 15
     db 0
     times 15 db '$'
; Tutaj małe wytłumaczonko. Operator "times" rozmnaża daną wartość ileś razy.
; times <ile razy> db <co>
hello db nwln,"Witaj $"
bye db nwln,"Aby zakonczyc nacisnij dowolny klawisz...$"

; Kod programu
section .text
global _start

_start:
; Wyświetlamy logo
    mov ah, 9
    mov dx, logo
    int 21h
    
; Wyśeiwlamy prośbę o imię
    mov ah, 9
    mov dx, entername
    int 21h
    
; Pobieramy imię
    mov ah, 0Ah; Czemu 0Ah ? Ponieważ liczby nie mogą się zaczynać od znaku. Muszą od cyfry.
    mov dx, name
    int 21h
    
; Wyświetlamy powitanie i imię usera
    mov ah, 9
    mov dx, hello
    int 21h
    
    mov ah, 9
    mov dx, name+2; Imię usera zaczyna się dopiero od drugiego bajtu bufora.
    int 21h
    
; Wyświetlamy pożegnanie
    mov ah, 9
    mov dx, bye
    int 21h
    
; Oczekujemy na klawisz i wychodzimy z programu.
    mov ah, 0
    int 16h
    
    mov ax, 4C00h; Trzeba wrzucić do AX, bo do AH się nie zmieści
    int 21h
Kompilacja odbywa się tak jak poprzednio przez wydanie komendy:
Kod:
nasm witajka.asm -o witajka.com

Mam nadzieję, że zachęciłem chociaż jedną osobę do głębszego poznania Asemblera
<

W razie problemów proszę pisać - od tego jest forum.

Jeśli ktoś jest głodny wiedzy mogę polecić tutorial BogDro (klik).
Listę przerwań Ralfa Browna można znaleźć tutaj.
Kody źródłowe i skompilowane pliki wykonalne można znaleźć na http://www.sinis.yoyo.pl/pub/asmik.rar

Pozdrawiam
Sinis
 
Do góry Bottom