Archív

Archív pre kategóriu ‘Assembler a cracking’

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:

Assembler – Inštrukcie CALL a RET

September 6th, 2009 admin Žiadne komentáre

Syntax:
CALL typ_volania operandy
RET

Inštrukcie CALL a RET používame na implementáciu podprogramov. Inštrukcia CALL má jeden, zvyčajne priamy operand (ako u inštrukcii skoku sa pripúšťa operand v registri alebo v pamäti), ktorým je adresa miesta v pamäti, kam sa odovzdá riadenie. Na rozdiel od inštrukcie JMP sa do zásobníka uloží hodnota registra IP (EIP) nasledujúcej inštrukcie po inštrukcii CALL.
Parameter typ volania je podobný ako u inštrukcie JMP, ak nie je uvedený, bude sa predpokladať typ volania near, a preto sa bude ukladať a meniť iba register IP (EIP). Pri typu volania far sa na zásobník okrem registra IP (EIP) uloží aj hodnota segmentového registra CS (adresa volania sa lak bude pozostávať z hodnoty pre register IP (EIP) a hodnoty pre register CS).
Návrat z podprogramu zabezpečuje inštrukcia RET, ktorá zo zásobníka vyberie hodnotu z vrcholu a tu uloží do registra IP (EIP), čím sa začnú vykonávať inštrukcie uložené za volajúcou inštrukciou CALL. Pre návrat z podprogramu, ktorý bol privolaný inštrukciou CALL far musíme obnoviť aj pôvodnú hodnotu registra CS, čo môžeme zabezpečiť použitím inštrukcie RETF, ktorá zo zásobníka najprv vyberie hodnotu a uloží ju do registra IP, a potom vyberie ešte jednu, ktorú uloží do registra CS.
Situáciu môžeme ešte trochu skomplikovať.
Inštrukcie RET alebo RETF môžu mať ako priamy operand počet položiek, ktoré sa majú zo zásobníka vybrať po naplnení registra IP (EIP) (alebo aj SK). Tento špeciálny tvar inštrukcie RET využijeme až neskôr, kde si povieme o možnostiach prepojenia vyšších programovacích jazykov a assembleru.

Ukážme si použitie inštrukcií CALL a RET na príklade:
Pomocou podprogramu sčítame dve čísla uložené v registroch EAX a EBX, výsledok súčtu uložíme v registri ECX (ignoruje pretečenie). Hodnoty uložené v registroch EAX a EBX musia byť zachované aj po návrate z podprogramu.
Vytvoríme podprogram sčítaj. Najskôr musíme vyriešiť odovzdávanie parametrov podprogramu. Vo vyšších programovacích jazykoch sa pre odovzdanie parametra používa zásobník, my si zatiaľ vystačíme s dohodnutými registrami. Akonáhle vykonáme inštrukciu pre sčítanie ADD, bude jedna hodnota operanda prepísaná výsledkom. Využijeme zásobník a hodnotu uloženú v danom registri si uložíme a neskôr opäť obnovíme.

scitaj:
push eax; uložíme hodnotu registra EAX na zásobník 
add eax, EBX; sčítame EAX a EBX, výsledok bude v EAX 
mov ecx, eax; výsledok z EAX uložíme do ECX 
pop eax; obnovíme pôvodnú hodnotu v registri EAX 
ret ; návrat z podprogramu.

Podprogram scitaj vyskúšame s hodnotami 4 a 8.

mov eax, 4; do EAX uložíme 4 
mov EBX, 8; EBX = 8 
call scitaj ; zavoláme podprogram scitaj 
;Výsledok bude uložený v registri ECX a bude JIRRA. 
; hodnota ECX = OxOOOOOOOC

Čo by sa stalo keby sme vynechali inštrukciu POP eax? Inštrukcia RET by tak predala riadenie na adresu, ktorá bola pôvodne uložená v registri EAX, čo by viedlo k pádu programu a v horšom prípade pádu systému. Podobne opomenutie inštrukcie RET by viedlo k vykonávanie inštrukcií dávno nepatria nášmu podprogramu, čo tiež ľahko (a celkom určite) vedie k pádu programu (to sa veľmi často využíva crackarmi a hackermi).
Podprogram scitaj možno v našom jednoduchom prípade zjednodušiť tak, že sa zaobídeme bez inštrukcií PUSH a POP:

scitaj: 
mov ecx, eax ; hodnotu registra EAX (prvý parameter) prekopírujete do registra ECX 
add ecx, EBX ; a sčítame s druhým parametrom v registri EBX, 
; výsledok bude v požadovanom registri ECX 
ret ; návrat z podprogramu.
VN:F [1.9.3_1094]
Rating: 0.0/10 (0 votes cast)
Categories: Assembler x86 Tags:

Assembler – inštrukcie PUSH/POP a ich varianty

September 6th, 2009 admin Žiadne komentáre

Syntax: PUSH ol

Pomocou inštrukcie PUSH môžeme na zásobník uložiť ľubovoľný 16-bitový alebo 32-bitový register alebo obsah pamäťového miesta (nepoužíva sa príliš často).

push eax; na zásobník uložíme register EAX

Inštrukciu push môžeme rozpísať inštrukciami:

sub esp, 4; zníž ESP o 4 
mov [ss: esp], eax; zapíš do zásobníka hodnotu registra EAX

alebo všeobecnejšie (pomocou operátora sizeof, požičaného z vyšších prog. jazykov):

push ol

môžeme zapísať ako

(E) SP = (E) SP-sizeof (ol)
ol -> SS: [(E) SP]

Inštrukcia POP presunie do operanda hodnotu (obsah) z vrcholu zásobníka a vrcholom zásobníka sa stane hodnota uložená na vyššej adrese. Inštrukcia POP môže mať rovnaký typ operandov ako inštrukcia PUSH.

Opäť možno inštrukciu POP prepísať pomocou inštrukcií MOV a ADD:

mov eax, [ss: esp]; načítaj z vrcholu zásobníka do registra EAX 
add esp, 4; "smaž" najvyššie dvojslovo zo zásobníka

Uveďme si niekoľko príkladov.

push eax; na zásobník ulož hodnotu uloženú v registri EAX 
push esi; na zásobník ulož hodnotu registra ESI 
pop eax; zo zásobníka vyber hodnotu a ulož ju v registri EAX 
pop esi; zo zásobníka vyber hodnotu a ulož ju v registri ESI

Pripomeňme si, že na zásobník nie je možné pomocou inštrukcie PUSH uložiť 8-bitové registre a ani nemožno priamo uložiť obsah registra IP (EIP) (instruction pointer, ukazovateľ na ďalšiu inštrukciu). Inštrukcie PUSH / POP ip alebo PUSH / POP EIP neexistujú (možno ich ale nahradiť inak, viď ďalej).

Instrukcie PUSHA/POPA a PUSHAD/POPAD

Syntax:
PUSHA
POPA

Niekedy potrebujeme uložiť všetky univerzálne registre na zásobník. Na tento účel je mikroprocesor vybavený inštrukciami PUSH a POPA, ktoré na zásobník uloží (PUSHA) alebo zo zásobníka obnoví (POPA) hodnoty všetkých univerzálnych 16-bitových registrov. Inštrukcie PUSH a POPA nemajú žiadny voliteľný operand.

Inštrukcie PUSH / POPA boli zavedené v predchodcovi procesoru 80386, a preto neukladá 32-bitové registre (nemôže, pretože neexistovali). Ak ich cheme uložiť alebo vybrať, tak musíme použiť “nové” inštrukcie PUSHAD a POP AD.

Poradie ukladaných registrov na zásobníka od vrcholu nadol je:
(E)AX, (E)CX, (E)DX, (E)BX, pôvodné (E)SP, (E)BP, (E)SI, (E)DI

pusha; uložíme na zásobník obsah všetkých univerzálnych registrov
; urobíme veľké zmeny (napríklad komplikovaný výpočet,; ktorý zmení väčšinu hodnôt univerzálnych registrov) 
popa; zase všetky registre obnovíme
VN:F [1.9.3_1094]
Rating: 9.0/10 (1 vote cast)
Categories: Assembler x86 Tags: