Input e Output in Asm
From UIC
Input/Output da Console
Contents |
| Infos | |
|---|---|
| Author: | b0nu$ |
| Email: | bonus@numerica.it |
| Website: | |
| Date: | 01/01/2001 (dd/mm/yyyy) |
| Level: |
|
| Language: | Italian |
| Comments: | |
Essay
In questo capitolo vedremo come si possono leggere dati dalla tastiera, elaborarli e poi visualizzare i risultati. Anche stavolta vi presentero un semplice programma che andremo poi ad analizzare in profondità; il programma legge una stringa e la stampa ribaltata.
Programma
SEG_A SEGMENT
ASSUME CS:SEG_A, DS:SEG_A
ORG 100H
Ribalta PROC FAR
inizio: jmp START ;salta a START
max_len EQU 1000 ;massima lunghezza
sorgente db max_len dup(?) ;stringa da ribaltare
destinaz db max_len dup(?) ;stringa ribaltata
START:
mov si,OFFSET sorgente
prossimo_car: mov ah,01h ;legge un car dalla tastiera</comment>
int 21h
cmp al,0Dh ;è = a return?
je fine_lettura ;se si smetti di leggere
mov sorgente[si],al ;sposta il carattere in sorgente
inc si ;incrementa si
jmp prossimo_car ;leggi il prossimo car
fine_lettura:
mov cx,si
push cx ;memorizza la lunghezza nello stack
mov bx,OFFSET sorgente
mov si,OFFSET destinaz
add si,cx ;metto in si la lunghezza della stringa
dec si ;decrementa si
Ribaltamento:
mov al,[bx] ;routine di ribaltamento
mov [si],al ;parto dal fondo e la copio
inc bx ;in destinaz
dec si
loop Ribaltamento ;salto a Ribaltamento
pop si ;prelevo la lunghezza
mov destinaz[si+1],'$' ;aggiungo il terminatore
mov ah,09h
mov dx,OFFSET destinaz
int 21h ;stampo la stringa ribaltata
RETN
Ribalta ENDP
SEG_A ENDS
END inizio ; fine del programma
Analisi
Analizziamolo... All'inizio ci sono le solite direttive per il CS e il DS e per l'indirizzo iniziale (100h) che ormai conoscete; subito dopo ci sono le dichiarazioni delle variabili usate:
Questa dichiara una costante max_len che vale 1000 tramite la direttiva EQU.
Questa dichiara una specie di array di byte di lunghezza max_len non inizializzato. Conoscete già le direttive DB e DUP, questo è un ulteriore modo di combinarle. La stessa istruzione in C sarebbe:
L'istruzione successiva è la stessa cosa. Dopo queste dichiarazioni si comincia con il vero programma:
prossimo_car: mov ah,01h ;legge un car dalla tastiera
int 21h
cmp al,0Dh ;è = a return?
je fine_lettura ;se si smetti di leggere
mov sorgente[si],al ;sposta il carattere in sorgente
inc si ;incrementa si
jmp prossimo_car ;leggi il prossimo car
questo frammento di programma legge dalla tastiera una stringa carattere per carattere; si utilizza l'int 21h, 01h che ritorna il carattere letto in al. Dopo la chiamata all'int 21h si controlla il carattere letto e se è RETURN (codice ascii 13 = 0Dh) si termina il ciclo di lettura. Il controllo del carattere si effettua tramite l'istruzione cmp (compare) che confronta i due operandi, se questi sono uguali cmp setta a 1 lo Zero Flag. L'istruzione successiva è un salto condizionato: "salta se sono uguali". Questa istruzione controlla il valore dello Zero Flag se è uno salta all'etichetta indicata altrimenti prosegue con l'istruzione successiva. Esistono vari tipi di salti condizionati, ve ne presento qui di seguito i più utilizzati:
JNE - Salta se sono diversi (ZF = 0)
JZ - Salta se è zero (ZF = 1)
JNZ - Salta se non è zero (ZF = 0)
(Nota : questi due salti condizionati sono la stessa cosa hanno soilo nomi
diversi !!)
JC - Salta se c'è stato riporto (CF = 1)
JNC - Salta se non c'è stato riporto (CF = 0)
JP - Salta se il Parity flag è 1
JNP - ...
JS - Salata se il Sign flag è 1
JNS - ...
Abbiamo poi due categorie per il confronto tra numeri con segno e senza segno la
differenza è che nei numeri con segno il bit più significativo rappresenta
appunto il segno del numero (ricordate il complemento a 2)
Con segno:
JNG - ...
JGE - salta se il primo è maggiore o uguale del secondo
JNGE - ...
JL - Salta se il primo è minore del secondo
JNL - ...
JLE - Salta se il primo è minore o uguale del secondo
JNLE - ...
Senza segno:
JA - Salta se il primo è più grande del secondo
JNA - ...
JB - Salta se il primo è più piccolo del secondo
JNB - ...
JAE - ...
JBE - ...
Queste istruzioni di salto condizionato sono indispensabili per il controllo del flusso del programma per costruire i costrutti if...then e i cicli. Tornando al nostro programma l'istruzione successiva al salto è
in al c'è il carattere appena letto che deve essere copiato nella variabile sorgente, quest'ultima viene trattata a tutti gli effetti come un array di caratteri dove si rappresenta l'indice che nell'istruzione successiva viene incrementato (inc si) per prepararsi sulla prossima posizione libera. L'ultima istruzione del ciclo di lettura è un salto (incodizionato) a prossimo_car, in pratica si riprende con la lettura del carattere successivo e si prosegue in questo modo fino a che non viene premuto Enter. Anche se non mi sono soffermato spero abbiate capito come si utilizzano le etichette per i salti, basta fare
....
....
....
jmp nome_etichetta ;nota:non termina con i due punti
Il successivo frammento di programma prepara le due stringhe per il ribaltameto:
push cx ;memorizza la lunghezza nello stack
mov bx,OFFSET sorgente
mov si,OFFSET destinaz
add si,cx ;metto in si la lunghezza della stringa
dec si ;decrementa si
La prima istruzione mette in cx la lunghezza della stringa che è contenuta nell'indice dell'array (si); tale valore viene anche salvato nello stack per utilizzarlo in futuro (push cx). Con le due istruzioni successive faccio puntare bx all'offset della sorgente e si a quello della destinazione e infine sposto si alla fine della stringa sorgente per effettuare la copia al contrario (partendo dal fondo). L'istruzione add effettua la somma si = si+cx e dec decrementa l'operando (si). A questo punto viene effettuata la copia ribaltata nella stringa destinazione:
mov al,[bx] ;routine di ribaltamento
mov [si],al ; parto dal fondo e la copio
inc bx ; in destinaz
dec si
loop Ribaltamento ; salto a Ribaltamento
La situazione è questa :
-------------------------------------
destinaz : |?|?|?|?|?|?|?|?|?|?|?|?|?|?|?|?|?|
-------------------------------------
^
|
si
-------------------------------------
sorgente: |c|c|c|c|c|c|c|c|c|c|c|c|c|c|c|c|c|
-------------------------------------
^
|
bx
si punta alla posizione cx della stringa destinazione, bx punta all'inizio della stringa sorgente e in cx c'e la lunghezza della stringa da ribaltare quindi non ci resta che scrivere un ciclo di questo tipo (in C):
{
destinazione[si] = sorgente[bx];
bx++;
si--;
cx--;
}
che in assembly risulta come sopra. Notate che lo spostamento da sorgente a destinazione non può essere effettuato in un unica istruzione perché il trasferimento memoria-memoria non è consentito si deve utilizzare un registro di appoggio (nel nostro caso al). Inoltre noterete che nel programma assembly manca l'istruzione che decrementa cx, questa viene effettuata automaticamente dall'istruzione loop che salta all'etichetta indicata dopo aver decrementato cx.
L'ultima parte del programma provvede alla visualizzazione della stringa ribaltata :
mov destinaz[si+1],'$' ;aggiungo il terminatore
mov ah,09h
mov dx,OFFSET destinaz
int 21h ;stampo la stringa ribaltata
le prime due istruzioni servono per aggiungere alla fine della stringa il terminatore '$' indispensabile per stampare una stringa con la funzione 21h, 09h; senza questo verrebbero visualizzati strani caratteri (faccine, cuoricini, ecc...)
Note Finali
Spero che abbiate capito il funzionamento di questo secondo programma, non che sia molto più difficile del primo però introduce alcuni controlli di fondamentale importanza quali i salti e i cicli indispensabili per scrivere programmi che facciano qualcosa !!!
Disclaimer
I documenti qui pubblicati sono da considerarsi pubblici e liberamente distribuibili, a patto che se ne citi la fonte di provenienza. Tutti i documenti presenti su queste pagine sono stati scritti esclusivamente a scopo di ricerca, nessuna di queste analisi è stata fatta per fini commerciali, o dietro alcun tipo di compenso. I documenti pubblicati presentano delle analisi puramente teoriche della struttura di un programma, in nessun caso il software è stato realmente disassemblato o modificato; ogni corrispondenza presente tra i documenti pubblicati e le istruzioni del software oggetto dell'analisi, è da ritenersi puramente casuale. Tutti i documenti vengono inviati in forma anonima ed automaticamente pubblicati, i diritti di tali opere appartengono esclusivamente al firmatario del documento (se presente), in nessun caso il gestore di questo sito, o del server su cui risiede, può essere ritenuto responsabile dei contenuti qui presenti, oltretutto il gestore del sito non è in grado di risalire all'identità del mittente dei documenti. Tutti i documenti ed i file di questo sito non presentano alcun tipo di garanzia, pertanto ne è sconsigliata a tutti la lettura o l'esecuzione, lo staff non si assume alcuna responsabilità per quanto riguarda l'uso improprio di tali documenti e/o file, è doveroso aggiungere che ogni riferimento a fatti cose o persone è da considerarsi PURAMENTE casuale. Tutti coloro che potrebbero ritenersi moralmente offesi dai contenuti di queste pagine, sono tenuti ad uscire immediatamente da questo sito.
Vogliamo inoltre ricordare che il Reverse Engineering è uno strumento tecnologico di grande potenza ed importanza, senza di esso non sarebbe possibile creare antivirus, scoprire funzioni malevoli e non dichiarate all'interno di un programma di pubblico utilizzo. Non sarebbe possibile scoprire, in assenza di un sistema sicuro per il controllo dell'integrità, se il "tal" programma è realmente quello che l'utente ha scelto di installare ed eseguire, né sarebbe possibile continuare lo sviluppo di quei programmi (o l'utilizzo di quelle periferiche) ritenuti obsoleti e non più supportati dalle fonti ufficiali.
Categories: Assembly | B0nu$ | 2001