eWorld Network ReSearch
Last update: 12/1999
by Alessandro Polo
Assembler Tutorial
   
   
Descrizione Questo documente spiega brevemente e sommariamente la basi dell'Assembler x86.
Puo essere un primo approccio alla programmazione di primo livello.
   
   

 

 

Sistemi binario,
decimale,
esadecimale

Noi siamo abituati a lavorare con il sistema decimale, ma i computer a livello hardware utilizzano il sistema binario (combinazioni di 0 e 1), il pc usa ovviamente anche il sistema esadecimale.
Un cracker o un programmatore di Assembler deve conoscere tutti i sistemi elencati e sapere come convertire cifre.
Il sistema più vicino a noi è quello decimale, quindi useremo quello per rendere gli altri sistemi più "amichevoli", il sistema binario utilizza infatti le cifre 0 e 1, quello il base 8 riciclerà le cifre da 0 a 7.
Per il sistema esadecimale ci sono 16 cifre e quindi siamo costretti a ricorrere alle letter dell'alfabeto:

Esadecimale: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 .
Convertito: 0 1 2 3 4 5 6 7 8 9 A  B  C  D  E  F  .


Quindi mentre il sistema decimale è "matematicamente incompatibile" col sistema esadecimale, il sistema binario è molto vicino ad entrambi, esso infatti lavora in base due e 2*2*2*2=16 (base esadecimale).

  • Conversione Binaria a Decimale
    Prendiamo ad esempio 11011b in complemento a due, a che numero decimale corrisponde?
numero binario: 1 1 0 1 1
potenze di 2: 2^4 2^3 2^2 2^1 2^0
formato decimale: 16 +8 +0 +2 +1
sommando..          
risultato: = 27        
  • Conversione Binaria a Esadecimale
    Utilizzando lo stesso esempio calcoliamo il corrispondente esadecimale.
    (dividiamo in gruppi di quattro da dx verso sx)
numero binario: 1   1 0 1 1
potenze di 2: 2^4   2^3 2^2 2^1 2^0
formato decimale: 16   +8 +0 +2 +1
             
in Hex 1   8+2+1=11 = B      
risultato:       = 1B    

 

  • Conversione Decimale a Binaria
    Come avrete tutti studiato parecchi anni fa per convertire un valore decimale in uno binario si gioca sulle divisioni e sul rimanente. DIV è il risultato della divisione mentre MOD è il resto.
    Così se scriverò 15 MOD 2 il risultato sarà DIV=7, MOD=1 (il simbolo che identifica MOD è %).
    Praticamente per trasformare un numero decimale in uno binario è sufficiente continuare a dividere per due componendo la cifra binaria da destra verso sinistra:
    15/2 =7 +1 BIN -> 1
    7 /2 =3 +1 BIN -> 11
    3 /2 =1 +1 BIN -> 111
    1 /2 =0 +1 BIN -> 1111
Hex Bin
0 0000
1 0001
2 0010
3 0011
4 0100
5 0101
6 0110
7 0111
8 1000
9 1001
A 1010
B 1011
C 1100
D 1101
E 1110
F 1111

Quindi ad esempio 1101 1011 0111 0101 0011 corrisponde a  D B 7 5 3.

  • Operazioni & Logica
    Per distinguere la base di ogni cifra l'Assembler aggiunge l'iniziale del sistema al termine del numero
Suffisso Sistema
b Binario
o Ottale
d Decimale
h Esadecimale

Per scrivere il numero 7 in base due in 8 bit di memoria: 00000111.
Se vogliamo rappresentare il numero -7 la cosa è più complessa:

   
  00000111
invertiamo i bit:     11111000
aggiungiamo 1 al numero 11111000 + 1 =
  11111001 --> -7

Infatti:

(111 + 11111001 = 0  <->     7 - 7 = 0)

Il valore 1 corrisponde a vero mentre 0 corrisponde a falso, entriamo nell'affascinante mondo della logica (sezione della matematica che studia i predicati).
Le operazioni fondamentali (porte logiche) sono AND, OR, NOT, XOR.
Ecco una tabella riassuntiva dei risutati:

c = a AND b
a b c
0 0 0
0 1 0
1 0 0
1 1 1
c = a OR b
a b c
0 0 0
0 1 1
1 0 1
1 1 1
c = NOT a
a c
0 1
1 0

(NOT funziona con una sola variabile)

c = a XOR b
a b c
0 0 0
0 1 1
1 0 1
1 1 0

 

   
   

 

 

Unità di misura
della memoria

E' necessario introdurre ancora altri concetti prima di passare alla vera e propria programmazione, un concetto che non sarà senz'altro nuovo è quello de Bit, Byte, Word..

Un Bit è una cifra binaria: 0 oppure 1.
Un Byte è una concatenazione di 8 Bit quindi 10010010, un byte può rappresentare un qualsiasi numero tra 0 e 2^8-1 (=255) o un qualsiasi esadecimale da 0 a FF.
Una Word è l'unione di 2 byte, quindi sono in totale 16bit di memoria, i numeri arrivano a 65535, in Esadecimale fino a FFFF.
Una Double Word è 2 Word ossia 4 byte, da 0 a 4294967295 e in esa da 0 a FFFFFFFF.

Poi: un Kilobyte è 1024 Byte, un Mega 1024 Kilobyte, Un Gigabyte 1024 Megabyte.

   
   

 

 

I Registri

I registri sono delle locazioni di memoria dove il nostro programma può salvare dati, puntatori.. I Registri in questione sono AX, BX, CX, DX.

  • AX - Registro a 16bit (divisibile in AH e AL da 8bit ciascuno), è detto "accumulatore" poichè è spesso usato per memorizzare calcoli matematici.
  • BX - Registro a 16bit (divisibile in BH e BL da 8bit ciascuno), usato spesso per memorizzare OFFSET.
  • CX - Registro a 16bit (divisibile in CH e CL da 8bit ciascuno), impiegato nella maggior parte dei casi come contatore di cicli.
  • DX - Registro a 16bit (divisibile in DH e DL da 8bit ciascuno), usato per memorizzare grosse cifre matematiche e come puntatore nell'I/O.
  • BP - Registro a 16bit (Base Pointer), Puntatore di base dello stack.
  • CS - (Code Segment), Contiene l'istruzione di programma da eseguire.
  • DS - (Data Segment), Contiene dati dell'applicazione.
  • ES - (Extra Segment), Registro aggiuntivo.
  • SS - (Stack Segment), Contiene lo Stack.

Dalle architetture del 386 sono stati aggiunti i registri 32bit: EAX, EBX, ECX, EDX, ESI, EDI, EBP.

Ci sono altri due registri (spesso utili nel cracking) creati appositamente per la manipolazione delle stringhe:

  • DI - Registro di Destinazione
  • SI - Registro di Partenza

 

Il Registro FLAG

Questo registro viene utilizzato come memoria per l'esecuzione delle istruzioni, ad esempio se si esegue una sottrazzione che da come risultato zero, il FLAG verrà impostato a zero, quindi il 6° bit sarà 1.

Bit Descrizione
0 CF Carry Flag
1 1
2 PF Parity Flag
3 0
4 AF Auxiliary Flag
5 0
6 ZF Zero Flag
7 SF Sign Flag
8 TF Trap Flag (Single Step)
9 IF Interrupt Flag
A DF Direction Flag
B OF Overflow flag
C 0
D IOPL I/O Privil. Level(286+ only)
E NT Nested Task Flag (286+ only)
F 0
10 RF Resume Flag (386+ only)
11 VM Virtual Mode Flag (386+ only)

 

   
   

 

 

Segmenti e OffSets

Le vecchie CPU dovevano gestire solo 1 mega di memoria, ma per indirizzare 1Mb di memoria sono necessari 20bit (2^20=1Mb) e noi abbiamo a disposizione solo 16bit; quindi i progettisti Intel hanno introdotto un nuovo tipo di indirizzo costituito da due parti: un SEGMENTO e un OFFSET (16bit ciascuno). Un indirizzo valido è nella forma SEGMENT:OFFSET, gli indirizzi sono in base esadecimale del tipo: 35B2:C02E.
Il problema è che questo non è un indirizzo a 20 bit, ma a 32bit; per trovare il vero indirizzo è necessare effettuare due operazioni: moltiplicare il segmento per 10h (16) e sommarlo all'OFFSET.
I registri relativi al segmento sono : CS, DS, ES, SS, quindi un segmento è 64K.

   
   

 

 

Il primo programma "Hello World!

Come da tradizione partiremo con il programma "Hello World!", tanto per famigliarizzare con l'ambiente di sviluppo e con i primi elementari comandi ASM. Non preoccupatevi se non capite, non è ancora questo lo scopo.

;ASM01.ASM

[bY_ Alessandro Polo 1999]

.MODEL small

[modello di memoria]

.STACK 100h

[dimensione dello Stack]

.DATA

[inizio del segmento dati]

MyString DB "Ciauz WoRld",13,10,'$'

[dichiarazione del Stringa]

.CODE

[inizio del segmento di codice]

mov ax,SEG MyString

[ax = indirizzo del Segmento Dati]

mov ds,ax

[ds = ax]

mov dx,OFFSET MyString

[ds = offset del Segmento Dati]

mov ah,09h

[ ah = 09h ]

int 21h

[call all'interrupt DOS]

mov ah,4Ch

[ ah = 4Ch ]

int 21h

[call all'interrupt DOS]

END

[fine del programma]

Questo programmino scriverà sullo schermo il messaggio "Ciauz WoRld", ora analizzaremo più in dettaglio i comandi usati e la struttura del codice:

.Model
E' il modello di memoria da utilizzare nel programma, può essere: Large (codice e dati superano i 64Kb), Compact (a differenza dello Small, i puntatori Far possono superare i 64Kb), Medium (il codice può superare i 64Kb), Small (non oltre i 64kb), Tiny (non oltre i 64kb, file .COM).

.Stack
Spazio riservato per lo Stack (predefinito = 200h =1kb).

.Data
Sezione dei dati del programma, qui sono assegnati i valori alle costanti.
MyString DB "Ciauz WoRld",13,10,'$'
Viene assegnato il testo "Ciauz World" alla variabile Message, DB assegna il numero di byte necessari, in tutto 14: 11 per la stringa, 1 per l'invio (#13), 1 per #10 e uno per "$" che deve essere al termine di ogni stringa dichiarata.

Nota: Mentre DB assegna un numero di byte, esistono altre direttive come:
DW (Define Word), DD (Define Double Word), DQ (Define Quadword), DF (Define 48bit), DT (Define TenByte).

.Code
Questa sezione è il cuore del programma: il codice sorgente.

   
   

 

 

Gli Interrupt

Analizziamo il codice del programma proposto:

Istruzione MOV ds,ax

Quest'istruzione sposta il contenuto di ax nel data segment.

Istruzione MOV dx,OFFSET MyString

Con quest'istruzione si copia l'offset della variabile (MyString) in dx. (N.B. OFFSET e SEG sono parole riservate.)

Istruzione MOV ah,09h

Modifichiamo il registro ah inserendo il valore esadecimale 09.

Istruzione INT 21h

Chiama l'interrupt 21h per stampare sullo schermo il messaggio.

Istruzione MOV ah,4Ch

Copiamo il valore 4C in ah, spiegherò il motivo di quest'istruzione nella sezione Interrupt.

Istruzione INT 21h

Chiama l'interrupt 21h per terminare il programma. (l'alternativa è resettare il pc)

Gli interrupt sono delle funzioni paragonabili alle API di Windows (anche se meno avanzate e comode) del DOS o del BIOS. L'interrupt usato precedentemente è il 21h (fa parte del DOS), per capire il funzionamento è necessario sapere il valore di AH quando l'interrupt viene chiamato (AH è paragonabile alle variabili date alle API). Consultando il manuale in relazione all'interrupt 21h si cerca il valore 09h e si scopre che la funzione è: stampa una stringa nell'output del monitor, la stringa stampata è in DS:DX (infatti c'è il nostro messaggio).

Le funzioni Interrupt sono salvate nel primo Kbyte di RAM, cioè dall'indirizzo 000h a 3FFh, ogni funzione è di 4 byte, quindi è possibile calcolare dove si trovi ogni interrupt in memoria: 4(byte) * 21h (interrupt) = 84h (indirizzo in memoria).

   
   

 

 

Secondo Esempio

Questo programma è molto simile a quello precedente nel senso che ha lo stesso fine: stampare qualcosa sullo schermo.

;ASM02.ASM [bY_ Alessandro Polo 1999]
SEG_A SEGMENT  
ASSUME CS:SEG_A, DS:SEG_A  
ORG 100H  
Ciauz PROC FAR  
PARTENZA: JMP START [salta a START]
MyString DB "Ciauz WoRld",13,10,'$' [dichiarazione del Stringa]
START:  
mov dx,OFFSET MyString [ds = offset del Segmento Dati]
mov ah,09h [ah = 09h]
int 21h [chiamata l'interrupt]
RETN  
Ciauz ENDP  
SEG_A ENDS  
END PARTENZA  
   

Rispetto all'esempio precedente qui manca il Data Segment e lo Stack Segment. D'altra parte questo non è un EXE ma un COM ed infatti è necessario specificarlo al momento del"linking".

   
   

 

 

Le Funzioni

La call:

message db 'Ciauz WoRld','$'

mov dx, offset message

call DisplayString

La Routine:

DisplayString:

mov ax,cs

mov ds,ax

mov ah,9 ; DOS FUNCTION: display message

int 21h ; Call the DOS interruptmov ah,

ret

 

   
   

 

 

Linker, OBJ
e file EXE
Una volta terminato di scrivere il programma ASM e salvato il file xxx.ASM, è necessario prima compilare il programma e poi linkarlo; compilando il programma verrà creato un file oggetto (.OBJ) che deve essere linkato per creare il vero e proprio eseguibile. Il file risultante è apparentemente un ammasso di caratteri ASCII senza senso, ma in realtà esiste una relazione tra il file EXE e il codice ASM, infatti è anche possibile tornare dall'eseguibile al sorgente tramite un Disassemblatore oppure un Debugger (questo permette anche di modificare le istruzioni in Runtime cioè mentre il programma stà funzionando).
Ad esempio l'istruzione JNE X8(Jump if Not Equal) ha come valore 75cb, quando si vuole patchare un programma, cioè modificare l'eseguibile per aggirare uan protezione (ad esempio) è necessario aprire il file e cambiare alcuni valori di questi caratteri ASCII (potremmo semplicemente modificare 75 a 74 per esempio). Comunque tratterò questi argomenti in altra sede.
   
   
   
 

All trademarks are property of their respective owners.
Any tools listed and available in zip package is free and was downloaded from the Internet.
You are authorized to copy, redistribute and print this paper, keeping Author's credits visible and without changing the document's content.
Author is not responsible for any consequences or damages related in any way to this material.

Last update: 12/1999

     
     
     
     
ReSearch Home open source 1999 | by Alessandro Polo eWorld Network