Archív

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

Assembler – LOOP, LOOPZ, LOOPZN, LOOPNE

September 6th, 2009 admin 1 komentár

V Assembleri bude implementácia cyklu nasledovná. Zvolíme register ECX pre riadiacu premennú I.

for_start: 
mov ecx, 0 ; naplníme register ECX nulou 
for_cyklus: ; na toto návestie sa budeme vracať 
... ; tu budeme vykonávať príkazy v FOR-cykle 
inc ecx ; ECX zvýšime o 1 
cmp ecx, 10 ; porovnáme ECX s 10 
jnz for_cyklus ; ak nie je 10, skočíme na for_cyklus 
for_skonci: ; ak už je 10, potom skončíme

Ukážme si aj variant s premennou I umiestnenou v pamäti.

for_start:
mov  dword   [i] , 0
for_cyklus: ; na toto návestie sa budeme vracať 
... ; tu budeme vykonávať príkazy vo FOR-cykle 
inc dword [i] ; ECX zvýšime o 1 
cmp dword [i], 10 ; srovnárne ECX s 10 
jnz for_cyklus ; ak nie 10, skočíme na for_cyklus 
for_skonci: ; ak už je 10, tak skončíme

Inštrukcia LOOP

Syntax: LOOP navestie_cyklu

Inštrukcia LOOP má dva operandy, podobne ako inštrukcia MUL. Prvý operand je pevne daný a je ním register CX (alebo ECX). Druhý, voliteľný operand je adresa návestia, kam sa bude odovzdávať konanie v prípade splnenej podmienky. Inštrukcia LOOP najskôr odpočíta jedničku od registra CX (ECX), a ak výsledok nie je nula, tak odovzdá riadenie programu na miesto určené svojim voliteľným operandom (cieľ skoku musí ležať v intervale + (alebo -) 128 bytov).

For-cyklus od 10 do 1 (vrátane) vytvorený inštrukciou LOOP by vyzeral takto:

for_start:
mov cx, 10  ; naplníme register CX 10 
for_cyklus:  ; na toto návestie sa budeme vracať 
...              ; tu budeme vykonávať príkazy v FOR-cykle 
loop for_cyklus ; ak CX nie je 0, skočíme na for_cyklus 
for_skonci: ; ak už je 0, potom skončíme

Inštrukcie LOOPZ a LOOPZN

Syntax:
LOOPZ navestie_cyklu
LOOPNZ navestie_cyklu

Inštrukcia LOOPZ rozširuje podmienku uskutočnenia skoku. Skok na návestie uvedené v inštrukcii sa uskutoční práve vtedy, keď hodnota v registri CX (ECX) nie je rovná nule a zároveň je nastavený príznak ZF (zero flag) na 1. Synonymum inštrukcie je LOOPE.

Inštrukciou LOOPZ ľahko vytvoríme cyklus s dodatočnou podmienkou. Rozšírme podmienku jedného priechodu cyklu z predchádzajúceho príkladu o test registra BX na číslo 3. Cyklus sa vykoná najviac desaťkrát, ale za podmienky, že hodnota v registri BX = 3. Inak sa predčasne ukončí.

for_start: 
mov cx, 10   ; naplníme register ECX 10 
for_cyklus: ; na toto návestie sa budeme vracať 
... ; tu budeme vykonávať príkazy v FOR-cykle 
... ; možná zmena registra BX 
cmp bx, 3 ; je BX = 3?
loopz for_cyklus ; ak CX neni 0 a BX = 3, skočíme na for_cyklus 
for_skonci: ; ak už je 0 alebo BX nie je 3, tak skončíme

Inštrukcia LOOPNZ funguje analogicky, ale druhá podmienka je negovaná. Skok sa uskutoční, ak nie je register CX (ECX) rovný nule a zároveň nie je nastavený príznak ZF (zero flag je nula). Jej synonymum je LOOPNE.

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

Assembler – IF/THEN ala CMP, JMP a JX

September 6th, 2009 admin Žiadne komentáre

Inštrukcie CMP a TEST

Porovnanie dvoch operandov vykonávajú inštrukcie CMP a TEST. Ako operandy môžeme použiť registre alebo operand uložený v pamäti v známych veľkostiach 8, 16 a 32 bitov.
Názov inštrukcie CMP je odvodený z anglického slova compare (porovnaj). Inštrukcia je implementovaná podobne ako inštrukcia SUB. Operand o2 sa odpočíta od operanda ol. Výsledok sa nikam neuloží, zmenia sa len príznaky v registri príznakov. Inštrukciu CMP môžeme použiť na porovnanie celých čísel bez znamienka aj so znamienkom.
Inštrukcia TEST pracuje podobne, miesto rozdielu operandov sa vykoná ich bitový súčin (AND). Výsledkom sú novo nastavené príznaky v registri príznakov. Inštrukciu TEST používame pre testovanie hodnôt jednotlivých bitov v bitovom poli.

Ukážme si niekoľko príkladov:

cmp ax, 4       ; porovná register AX s hodnotou 4
cmp dl, ah             ; porovná register DL s registrom AH
cmp [prumerl], ax   ; porovná obsah "premennej" prumer s registrom AX
cmp ax, [prumerl]   ; porovná register AX s obsahom "premennej" prumer
cmp eax, ecx         ; porovná registre EAX a ECX
test ax, 00000100b ; testujeme bit 2 (tretia) bit, ak je nastavený

Inštrukcie nepodmieného skoku JMP

Prvým spôsobom, ako zmeniť sekvenčné vykonávanie inštrukcií v programe, je použitie inštrukcie nepodmienečného skoku. Inštrukcia prinúti procesor spracovávať inštrukcie z iného miesta v pamäti (prepíše hodnoty ukazovateľa na ďalšiu inštrukciu, register IP alebo i CS).

Syntax: JMP typ_skoku operand

Nepodmienený skok známy z vyšších programovacích jazykov pod názvom GOTO v Assembleru predstavuje inštrukcia JMP. Názov vznikol z anglického slova jump (skoč). Inštrukcia vyžaduje jediný priamy operand, ktorým je adresa miesta v pamäti, kam sa má skočiť (ako operand možno použiť aj univerzálny register naplnený adresou skoku, čo by som začiatočníkom neodporúčal). V Assembleri sú rovnako ako vo vyšších programovacích jazykoch adresy skokov reprezentované návestiami (label).
Podľa “vzdialenosti” skoku v programe rozlišujeme tri druhy skokov: short, near a far. (Krátky, blízky a vzdialený).
Maximálna “dĺžka” skoku (maximálny absolútny rozdiel adries, ktorý možeme skokom zmeniť) typu short je obmedzená. V druhom byte inštrukcie je uložená len 8-bitová hodnota sa znamienkom, preto adresa cieľa skoku smie ležať iba v rozmedzí -128 až 127 byte. Skok sa vykoná tak, že sa 8-bitová hodnota znamienkovo rozšíri a pripočíta sa k súčasnej hodnote registra (E)IP.
Inštrukcia skoku typu near už obsahuje vo svojej strojovej reprezentácii novú hodnotu registra (E)IP, dĺžka skoku je obmedzená iba módom procesora. V reálnom režime procesora meníme inštrukciou jmp near register IP, preto sa smieme pohybovať len v rozmedzí jedného segmentu (64 KB), v chránenom režime používame register EIP a tak cieľ skoku môže ležať kdekoľvek v 4 GB adresového priestoru.
Skok far mení aj hodnotu segmentového registra CS, ktorý sa podieľa na výpočte adresy inštrukcie v pamäti. Súčasťou adresy skoku musí byť aj nová hodnota pre register CS.
Ak nie je typ skoku uvedený, predpokladá sa typ near.
Teraz trochu vo výklade “preskočíme” a povieme si, ako vyzerá návestie v Assembleri. Ide v podstate o identifikátor zakončený dvojbodkou, ktorému bola pri preklade pridelená adresa podľa miesta výskytu v programe. Ukážme si časť programu, kde je použité návestie:

mov ax, 4       ; do AX vlož hodnotu 4 
novy_cyklus:   ; návestie novy_cyklus 
mov bx, ax      ; do BX okopírujte AX

Ak chceme vykonať nepodmienený skok na návestie novy_cyklus, napíšeme inštrukciu:

jmp novy_cyklus; skok na novy_cyklus

Po jej prevedení sa program začne vykonávať od návestia novy_cyklus.
Nič nám nebráni najprv vykonať skok a neskôr v programe uviesť návestie. Prekladač pracuje “na viacerých priechodoch”, a preto aj také skoky dopredu vie spracovať. Časť programu používajúca dopredné skoky by mohla vyzerať nasledovne:

jmp start; skok na štart 
ciel:        ; návestie ciel 
...
...         ; ďalšie inštrukcie 
start:    ; návestie štart 
jmp ciel   ; skok na ciel

Vráťme sa ešte krátko k typom skokov, ktoré budeme síce ako začiatočníci využívať veľmi zriedka. Skok typu short použijeme všade tam, kde návestie “leží” do 128 B. Tak ušetríme 1B programového kódu, pretože preložená inštrukcia jmp short zaberá len dva byte (inštrukcia JMP alebo JMP near 3 alebo 5 byte). Možno vás desí predstava, ako odhadnúť vzdialenosť návestí v nepreloženom programe. Nemusíte sa báť, jednoducho to vyskúšajte, prekladač v prípade neúspechu zahlási chybu.

blizke_navestie:              ; návestie blizke_navestie 
....                              ; Nejaké dalšie inštrukcie 
jmp short blizke_navestie  ; urobíme skok na blizke_navestie

Inštrukcie podmienených skokov Jx

Syntax: Jx návestie_ciela_skoku

Ďalším spôsobom, ako zmeniť sekvenčné vykonávanie inštrukcií v programe, je použitie inštrukcie podmieneného skoku.
Inšturkcií podmieneného skoku existuje celá rada. Navzájom sa odlišujú rozhodovaciu podmienkou, ktorá riadi vykonanie skoku. V závislosti od vyhodnotenia podmienky sa vykonávanie programu buď presunie na iné miesto, alebo program pokračuje na nasledujúcu adresu za inštrukciou skoku. Rozhodovaciou podmienkou sú stavy príznakov (niekoľkých alebo len jedného) príznakového registra procesora.
Ukážme si najpoužívanejšie inštrukcie, ktoré skočia na návestie splnené, ak bude podmienka splnená.

jz splnene   ; skočí, ak je príznak ZF (zero flag, príznak nuly) nastavený na 1 
jc splnene  ; skočí, ak je príznak CF (carry flag, príznak prenosu) nastavený na 1 
js splnene  ; skočí, ak je nastavený príznak znamienka SF (signum flag) 
jo splnene  ; skočí, ak je nastavený príznak pretečenia

Všetky podmienky môžeme obrátiť (negovať).

jnz splnene; skočí, ak nie je príznak ZF (zero flag, príznak nuly) nastavený (je nula)

Podobne aj ostatné JNC, JNS a JNO.

Teraz môžeme s našimi znalosťami príslušných inštrukcií Assembleru podmienku IF naprogramovať. Ukážme si jednoduchý príklad, v ktorom budeme chcieť odovzdať riadenie programu (skočiť) na návestie je_tri, ak je hodnota v registri AX rovná trom.
V prvom kroku porovnáme hodnotu v registri AX s číslom 3, čo urobíme inštrukciou CMP.

cmp ax, 3; register AX porovnaj s hodnotou 3

Pri porovnaní na rovnosť použijeme inštrukciu JZ.

jz je_tri; skočí na návestie je_tri, ak bude hodnota AX = 3

Všimnime si, že pri porovnávaní rovnosti čísiel nezáleží na tom, či práve číslo chápeme ako číslo so znamienkom alebo bez.
Inštrukcia JZ vykoná skok na návestie je_tri, ak bude hodnota v registri AX rovná trom, inak bude program pokračovať inštrukciou nasledujúcou po JZ.

V ďalšom príklade budeme chcieť porovnať bez znamienka hodnoty v registri CL a AI .. Do registra BL zapísať jedničku, keď sú si hodnoty rovnaké, dvojku, ak je AL väčší ako CL, a trojku, ak je v registri AL menšie ako v CL

Porovnáme registre AL a CL, výsledok porovnania sa uloží do príznakového registra procesora a použitím rôznych inštrukcií skoku zabezpečíme výber správnej alternatívy a tým aj riešenie problému.

cmp al, cl  ; porovnaj hodnoty uložené v AL a CL 
jz zapis_l   ; skoč na návestie zapis_l, ak AL = CL 
cmp al, cl  ; porovnaj hodnoty uložené v AL a CL 
ja zapis_2 ; skoč na návestie zapis_2, ak AL> CL 
mov bl, 3  ; do BL zapíš 3 
koniec_if:  ; tu je koniec nášho programu 
zapis_1:    ; návestie zapis_1 
mov bl, 1   ; do BL zapíš 1 
jmp koniec_if; skok na koniec nášho programu 
zapis_2:    ; návestie zapis_2 
mov bl, 2   ; do BL zapíš 2 
jmp koniec_if; skok na koniec nášho programu

Museli sme dokonca použiť nepodmienený skok, aby sme vrátili riadenie programu na pôvodné miesto. Riešenie možno ešte výrazne vylepšiť:

mov bl, 1     ; do BL zapíš 1 
cmp al, cl    ; porovnaj AL a CL 
je koniec_if  ; skoč na koniec programu, ak AL = CL 
mov bl, 2    ; do BL zapíš 2 
cmp al, cl   ; porovnaj AL a CL 
ja koniec_if ; skoč na koniec programu, ak AL > CL 
mov bl, 3   ; do BL zapíš 3 
koniec_if:   ; koniec programu
VN:F [1.9.3_1094]
Rating: 9.0/10 (1 vote cast)
Categories: Assembler x86 Tags:

Assembler – inštrukcie DIV a IDIV

September 6th, 2009 admin Žiadne komentáre

Podobne ako inštrukcie MUL má aj inštrukcie DIV tri možné tvary líšiace sa veľkosťou použitých voliteľných operanda.
Syntax:
DIVr/m8; osembitový voliteľný operand v pamäti alebo v registri
DIVr/ml6
DIVr/m32

Voliteľným operand je deliteľ, pevným operand je delenec.
V 8-bitovej variante je voliteľný operand ľubovoľným 8-bitovým registrom alebo miestom v pamäti. Pevný operand sa predpokladá v registri AX, výsledok delenia (podiel) sa uloží do registra AL a zvyšok po delení sa uloží v registri AH.
AX / (r/m8) -> AL, zvyšok v AH
V 16-bitovej variante je voliteľný operand ľubovoľným 16-bitovým registrom alebo miestom v pamäti. Pevný operand sa predpokladá v registrovej dvojici DX: AX, výsledok delenia sa uloží do registra AX, zvyšok po delení sa uloží do registra DX.

DX: AX / (r/ml6) -> AX, zvyšok v DX

V 32-bitovej variante je voliteľný operand ľubovoľným 32-bitovým registrom alebo miestom v pamäti, pevný operand sa predpokladá v registrovej dvojici EDX: EAX, výsledok delenia sa ukladá do registra EAX, zvyšok po delení do registra EDX.

EDX:EAX / (r/m32) -> EAX, zvyšok v EDX

Inštrukciu IDIV použijeme pri delení čísel so znamienkom, operandy inštrukcie sú rovnaké ako u inštrukcie DIV.
Uveďme si opäť niekoľko príkladov.

Príklad: Celočíselne vydeľte číslo 13 dvoma, výsledok uložte do registra BL a zvyšok do registra BH.

mov ax, 13  ; do AX vložíme 13 
mov cl, 2 ; do CL 2
div cl ; vydelíme CL 
mov bx, ax ; výsledok očakávame v BX, stačí prekopírovať
VN:F [1.9.3_1094]
Rating: 7.3/10 (4 votes cast)
Categories: Assembler x86 Tags: