Archív

Archív pre kategóriu ‘Assembler x86’

Assembler – Linux

September 6th, 2009 admin Žiadne komentáre

I pod operačným systémom Linux bude náš program vypisovať na obrazovku Hello, World! Pripomeňme si, že existujú špeciálne číselné identifikátory súboru STDIN STDOUT a STDERR. Náš program “Hello World” bude vypisovať tento text na obrazovku (teda zapisovať na zariadenie STDOUT) a potom skončí.
Teraz si ukážeme, ako zapísať do súboru, teda na STDOUT.
Ak sa pozrieme do do súboru unistd.h, zistíme, že systémové volanie pre zápis do súboru sa volá write.

WRITE (2) Linux Programmer’s Manual WRITE (2)
NAME
write – write to a file descriptor
Synopsis

#include <unistd.h>;
ssize_t write (int fd, const void *buf, size_t count);

Prvý zádrhel, píšeme v assembleri, ale manuálová stránka nám dáva prototyp systémového volania write v jazyku C. Ak nepoznáte jazyk C, bude sa vám prototyp čítať horšie, to však neznamená, že nebudete môcť vylúštiť, čo kam uložiť. V assembleri môžeme do registra zapísať buď priamo hodnotu alebo adresu. Myslím, že môžeme prijať zjednodušenie, že všetky parametre odovzdávame hodnotou, okrem parametrov označených hviezdičkou (to je ukazovateľ).
Funkcia write vyžaduje tri parametre, podobne ako v DOSe: identifikačné číslo súboru (handle), ukazovateľ (adresa) na dala, ktoré sa majú uložiť (vidíte, je tam napísané *buf), a počet bajtov, ktoré chceme zapísať. Výstupom funkcie je počet skutočne zapísaných byte alebo chyba. Poruchový stav rozpoznáme od zapísaných bajtov tak, že vždy ide o záporné číslo.
Zdrojový program preložíme překladačom NASM do objektového súboru, ktorý zostavíme zostavovacím programom ld. Získame tak spustiteľný súbor, ktorý na obrazovku vypíše Hello, World!

Pre preklad použijeme verziu prekladače NASM pre Linux. Objektový súbor typu elf vytvoríme prepínačom -f elf.
Program Id bude potrebovať adresu prvej inštrukcie, ktorá sa bude vykonávať po zavedení programu do pamäti. V zdrojovom programe označíme miesto prvej inštrukcie špeciálnym globálnom návestím _start.
Rovnako ako v OS DOS musíme program rozložiť do programových sekcií.
Program Hello, World! by mohol vyzerať takto:

SECTION .text 
global _start; aby linker vedel, kde je začiatok nášho programu, musíme definovať globálne symbol, _start 
_start: 
mov eax, 4; prvý parameter, čislo systémového volania 
mov EBX, 1; vieme, že konštanta STDOUT má hodnotu 1 
mov ecx, hello ; prekladač sem doplní adresu reťazca hello 
mov edx, 012 ; počet znakov reťazca Hello World! vrátane odradkovania
int 0x80 ; zavoláme jadro 
; pripojíme aj kód potrebný pre ukončenie programu 
mov eax, 1; číslo volania jadra - exit 
mov EBX, 0 ; návratový kód 0 
int 0x80 ; zavoláme jadro a tak ukončíme náš proces (program) 
 
SECTION .data 
hello db "Hello, world!", 0xa; náš reťazec vrátane nového riadku 
len equ $ - hello ;  symbolu len bude priradená dĺžka reťazca

Program preložíme ľahko:

nasm -f elf hello.asm

A zostavíme linker ld.

ld -s -o hello hello.o

Parameter -o udáva meno výsledného spustiteľného súboru. Parametrom -s chceme odstrániť všetky nepotrebné (symbolické) informácie.
Výsledný program môžeme spustiť príkazom:

./ Hello
Hello World!

VN:F [1.9.3_1094]
Rating: 10.0/10 (2 votes cast)
Categories: Assembler x86 Tags:

Assembler – Windows API

September 6th, 2009 admin Žiadne komentáre

MessageBox

Navrhovaný program má vytvoriť dialógové okno a skončiť. Dialógové okno vytvoríme volaním API funkcie MessageBox a o ukončenie programu sa postaráme funkciou rozhrania API nazývanou ExitProcess.
V dokumentácii k MessageBox sa dočítame:

int MessageBox ( 
IFWND hWnd, // handle of owner window 
LPCTSTR lpText, // address of text in message box 
LPCTSTR lpCaption, // address of title of message box 
UINT Utype // style of message box 
);

Prvým parametrom bude popisovač vlastného okna. Žiadny nemáme, preto predáme nulu. Druhým parametrom je ukazovateľ na text, ktorý sa v dialógovom okne zobrazí (klasický reťazec zakončený nulovým byte), tretím ukazovateľom je text titulku okna. Posledný parameter určuje typ dialógového okna (použijeme symbolickú konštantu MB_OK – z include súboru, viď ďalej).
Druhé volanie API funkcie ExitProcess, rovnako ako v DOSe, svojím jediným parametrom určuje chybový kód ukončeného programu. Po jeho zavolaní program skončí.
Aby bol život programátora v assembleri jednoduchší, bol vytvorený “include súbor” win32n.inc, v ktorom sú definované všetky typy parametrov API (napr. HWND aj LPCTSTR zodpovedajú v assembleri obyčajnému dvojslovu – dword) i hodnoty symbolických konštánt. Do programu tento súbor vložíme direktívou include:

%include "win32n.inc"; vložíme súbor win32n.inc

Použité API funkcie uložené v dynamických knižniciach nesmieme zabudnúť sprístupniť nášmu programu. Stačí použiť direktívy EXTERN a IMPORT:

EXTERN MessageBox ; symbol MessageBox je definovaný inde
IMPORT MessageBox user32.dll ; konkrétne v user32.dll
EXTERN ExitProcess ; symbol ExitProcess je definovaný inde
IMPORT ExitProcess kernel32.dll ; konkrétne v kernel32.dll

Zostáva vyriešil jediný problém: Ako odovzdať API funkciám jednotlivé parametre. Zoznámime sa tu len stručne s odovzdávacími konvenciiami parametrov rozhrania Win32 API.
Predávaciia konvencia sa volá STDCALL. Parametre sa odovzdávajú cez zásobník sprava doľava (ako v jazyku C), vymazanie zásobníka vykonáva volaný (ako v jazyku Pascal).
Na zásobník uložíme parametre inštrukciami PUSH, požadovanú funkciu zavoláme nepriamo inštrukciou CALL, o vymazanie zásobníka sa nemusíme starať. Celý program bude vyzerať nasledovne (pro prekladac NASM):

% include "win32n.inc" ; vložíme súbor win32n.inc 
EXTERN MessageBox ; symbol MessageBox je definovaný inde
IMPORT MessageBox user32.dll ; konkrétne v user32.dll 
EXTERN ExitProcess  ; symbol ExitProcess je definovaný inde 
IMPORT ExitProcess kernel32.dll ; konkrétne v kernel32.dll 
 
SECTION CODE USE32 CLASS = CODE ; začiatok kódovej sekcie 
.start: ; špeciálne návestie pre linker, ktoré označuje vstupný bod programu z (entrypointu) 
push UINT MB OK ; na zásobník uložíme posledný parameter, dialógové okno bude mať len tlačidlo OK 
push LPCTSTR titulok ; na zásobník vložíme adresu reťazca ukončeného nulovým byte, ktorý sa zobrazí ako titulok okna 
push LPCTSTR napis ; na zásobník vložíme adresu reťazca ukončeného nulovým byte, ktorý sa zobrazí ako text okna 
push HWND NULL ; na zásobník uložíme nulu, žiadne nadradené okno nemáme 
call [MessageBox] ; zavoláme API - vytvoríme dialógové okno návrat až po stlačení tlačidla OK
 
push  UINT NULL                                     ;vrátime  rodičovi  nulový  chybový  kód
call   [ExitProcess]                                  ;ukončíme proces
 
SECTION  DATA  USE32   CLASS=DATA
 
napis  db   'Hello  world',OxD,OxA,0 ;reťazec Hello,   World! vrátane odriadkovania 
titulek  db   'Hello", 0                                  ;titulok okna  bude  hello
VN:F [1.9.3_1094]
Rating: 0.0/10 (0 votes cast)
Categories: Assembler x86 Tags:

Assembler – príklady

September 6th, 2009 admin Žiadne komentáre

Súčet dvoch čísel uložených premenných

Sečtěte dve 32bitová čísla uložené v premenných cislo1 a číslo2, výsledok uložíme do premennej vysledok (tiež 32-bitová premenná).

Riešenie príkladu musíme rozložiť do troch častí. V prvej naplníme vybrané registre požadovanými hodnotami, v druhej je sčítame, a v tretej časti zapíšeme výsledok. Výber registrov riadime veľkosťou použitých dát.

mov eax, [cislo1] ; pamätáte si? hranaté zátvorky znamenajú prístup do pamäte 
mov ebx, [cislo2]; do registra EBX načítame obsah druhej "premennej" 
add eax, ebx; EAX = EAX + EBX 
mov [vysledok], eax ; výsledok uložíme do "premennej" vysledok 
...
cislo1 dd 08; tu sme definovali "premennú" cislo1 a zároveň sme do nej vložili čislo 8 
cislo2 dd 2; do premennej "cislo2" sme vložili čislo 2 
vysledok dd 0; vysledok bude 8 + 2 = 010

Môžeme ušetriť register EBX, ak program prepíšeme takto:

mov eax, [cislo1]; nahraj hodnotu "premennej" cislo1 do registra EAX 
add eax, [cislo2] ; sčítaj register EAX a "premennu" číslo2 
mov [vysledok], eax ; výsledok ulož na adresu určenú symbolom vysledok
 
cislo1 dd 08 ; tu sme definovali "premennú" cislo1 a zároveň sme do nej vložili číslo 8
cislo2 dd 2  ; do premennej "cislo2" sme vložili číslo 2
vysledok dd 0 ; výsledok bude 8 + 2 = 010

Určenie či je číslo párne alebo nepárne

Príklad: Určite, či je v registri AX uložené číslo párne alebo nepárne!
Každé nepárne číslo má najnižší bit nastavený na 1. Inštrukciou SHR môžeme tento bit presunúť do príznaku prenosu CF (carry) a vykonať podmienený skok podľa príznaku prenosu inštrukciou JC.

push ax; nechceme stratiť hodnotu v registri AX, uložíme ju na zásobník 
shr ax, 1    ; najnižší bit sa presunul do príznaku CF 
pop ax      ; do registra AX vrátime pôvodnú hodnotu, register príznakov inštrukcie POP nemení. 
jc neparne ; ak jepríznak nastavený, číslo je nepárne 
sude:      ; tu môže nasledovať nejaká akcia, ak je párne 
neparne:  ; tu bude program pokračovať, ak je v AX číslo nepárne.

Ako už býva zvykom, program môžeme zapísať aj oveľa jednoduchšie:

test al, 1 ; najnižší bit bitovej masky bude jedna, vykonáme inštrukciu TEST 
jz je_parne ; príznak nuly ZF (zero flag) bude nastavený, ak bude najnižší bit nula, číslo bude párne 
neparne: 
....
je_parne: ; program bude pokračovať tu, ak bude číslo v AX párne.

Všimnime si, že sme testovali len register AL, nie celý AX. Vyššie bity registra AX sú pre nás nezaujímavé, a preto sme mohli testovať len register AL.

VN:F [1.9.3_1094]
Rating: 10.0/10 (2 votes cast)
Categories: Assembler x86 Tags: