Avvicinarsi all'asm dal C
(brevissima introduzione per capire un po' di più)


07-01-2000

by "syscalo"

 

 

UIC's Home Page

Published by Quequero

 

Qualche mio eventuale commento sul tutorial :)))

 
UIC's form
Home page se presente: http://syscalo.cjb.net 
E-mail: [email protected]
UIC's form

Difficoltà

(X)NewBies ( )Intermedio ( )Avanzato ( )Master

 

In questo brevissimo tutorial vi mostrerò la corrispondenza tra due costrutti fondamentali di tutti i linguaggi di programmazione ad alto livello (in questo caso il C), i cicli for e do/while, e la loro implementazione in assembler (ma visti da un disassemblato).


Avvicinarsi all'asm dal C
(brevissima introduzione per capire un po' di più)
Written by syscalo

Introduzione

Eccomi qua a cercare di fare qualcosa di utile; ho pensato a questo tipo di tutorial perchè molte persone conoscono, bene o male, un linguaggio di programmazione ad alto livello ma vedono il linguaggio assembler come qualcosa di altamente contorto e impossibile da capire. Bene in queste poche righe mostrerò come in realtà i due mondi siano molto vicini tra loro, sperando che voi che state leggendo, lasciandovi entusiasmare dall'assembler, inizierete a studiarlo come avete fatto con i linguaggi ad alto livello (o anche meglio ;).

Essay

Organizziamoci: prima vi presenterò il listato in C (che è mooolto facile, quindi anche chi conosce il Pascal o altro non avrà difficoltà a capirlo) e poi vedremo come viene "tradotto" in assembler da un decompilatore (avrei potuto farlo direttamente dal compilatore ma ormai ci ho preso l'abitudine ;)
 
Ecco il listato in C:
 
/*mettiamo un po' di commenti per chi non conosce il C*/
#include <stdio.h>    /*include le librerie per l'IO*/
#include <string.h>    /*include le librerie per le stringhe -serve per usare strlen-*/
int main(void)    /*no comment*/
{    /*inizia un blocco di istruzioni (come il begin in Pascal)*/
    char nome[10];    /*definisce un array di 10 caratteri -in pratica una stringa-*/
    int i,lun;    /*due varibili intere: i per i cicli e lun per la lunghezza del nome inserito*/
    int sommaTemp=7;    /*variabile intera inizializzata a 7*/
    int seriale;    /*variabile intera per contenere il seriale inserito*/
    printf("Tentativo di tute by syscalo");    /*printf scrive sullo standard output (il video) la stringa tra virgolette*/
    printf("\nInserisci nome: ");
    scanf("%s",nome);    /*scanf legge dallo standard input (la tastiera): qui legge il dato inserito come stringa*/
    printf("\nInserisci seriale: ");
    scanf("%d",&seriale);    /*qui legge il dato inserito come valore intero*/
    lun=strlen(nome);    /*lun conterrà la lunghezza del nome inserito*/
    if (lun<4)    /*se il nome è più corto di 4 lettere*/
    {
        printf("\nNome troppo corto-almeno 4 lettere!");    /*visualizza questa frase */
        return(0); /*esce dal programma*/
    }
    /*ciclo for*/
    for(i=1;i<lun;i++)    /*inizializza i a 1 (i=1) quindi si parte dal secondo carattere del nome, esegue fino a quando i è minore di lun (i<lun) ed incrementa i di 1 dopo ogni ciclo (i++)*/
    {
        sommaTemp+=nome[i];    /*somma il valore di nome[i] (il codice ascii del carattere del nome) a sommaTemp*/
    }
    nome[lun-2]='s';    /*sovrascrive il terzultimo (lun-2) carattere del nome con il carattere s*/
    /*ciclo do/while; se avessi usato il while non ci sarebbe stata molta differenza, ma così sono più alternativo ;))*/
    i=0;    /*inizializza i a 0*/
    do
    {
        sommaTemp^=nome[i];    /*il simbolo ^ sta ad indicare l'operazione xor; xora nome[i] con sommaTemp e poi sovrascrive sommaTemp*/
        i++;    /*incrementa i di 1*/
    }while(nome[i]!='s');    /*esegue il ciclo fino a quando incontra un carattere del nome uguale ad s*/
    /*controllo finale*/
    if(sommaTemp==seriale)    /*se il seriale inserito e quello calcolato (sommaTemp) sono uguali*/
        printf("\nSeriale corretto");     /*visualizza questa stringa*/
    else
        printf("\nseriale sbagliato");     /*altrimenti visualiza questa*/
    return(0);    /*esce dal programma*/
}
 
Ed ora passiamo all'assembler:
Ho omesso volutamente tutto quel codice che non fosse immediatamente ricongiungibile al listato C per non confondere le idee. Inoltre ho rinominato le label a cui fanno riferimento i salti e gli indirizzi del tipo "bp+var_C" in ciò a cui corrispondono, quindi troverete ad esempio "bp+nome".
 
Il compilatore "ha deciso" di utilizzare il registro di per contenere il valore della variabile sommaTemp, molto probabilmente perchè vi si accede spesso, e il registro si come contatore per i cicli (in pratica fa la funzione della variabile i).
[snip]
   mov   di, 7   inizializza a 7 sommaTemp
[snip]
   lea   ax, [bp+nome]   copia in ax l'offset del nome
   push   ax passa l'indirizzo alla funzione
   call   sub_0_2312   chiama strlen
   pop   cx
   mov   [bp+lun], ax   copia la lunghezza del nome in [bp+lun] - equivale a lun=valore_ritornato_da_strlen
   cmp   [bp+lun], 4   controlla se è 4
   jge   magUgu4   se è maggiore o uguale salta
[snip]
magUgu4:   mov   si, 1   i=1 - il registro si viene utilizzato per i cicli
   jmp   short avanti salta ad avanti
ciclo_for:   mov   al, [bp+si+nome]  copia i chr del nome in al dal secondo - la scritta [bp+si+nome] sta ad indicare la locazione di memoria (la variabile) il cui indirizzo si ottiene sommando i valori di bp, si e nome. (nome come detto sopra l'ho sostituito io, ma in realtà rappresenta un offset numerico.
   cbw   converte il byte in al in dword in ax
   add   di, ax   sommaTemp+=nome[i] - ricordo che il registro di rappresenta la variabile sommaTemp
   inc   si   i++
avanti:   cmp   si, [bp+lun]   i<lun - confronta si (conteggio cicli) con la lunghezza del nome, memorizzata alla locazione il cui indirizzo è dato da bp+lun.
   jl   ciclo_for   se minore salta - esegue le istruzioni precedenti (il corpo del ciclo for) fino a che è verificata la condizione d'uscita.
   lea   ax, [bp+lun]   num[lun-2]='s' - le seguenti 4 istruzioni servono a copiare il carattere s al posto del terzultimo del nome
   mov   bx, [bp+lun]
   add   bx, ax
   mov   byte ptr [bx], 73h copia 's' nel byte puntato da bx
   xor   si, si   i=0 - inizializza il registro si a 0
do_while:   mov   al, [bp+si+nome]  copia il chr del nome in al
   cbw   converte da byte in word in ax
   xor   di, ax   sommaTemp^=nome[i] - esegue lo xor tra il carattere del nome e sommaTemp, sovrascrivendo il registro di con il risultato dello xor
   inc   si   i++ - incrementa si di 1
   cmp   [bp+si+nome], 73h nome[i]!='s' - confronta il carattere del nome con il carattere s (codice ascii 73h)
   jnz   do_while   se è diverso continua il ciclo - esegue il corpo del ciclo do/while
   push   di  
prosegue con l'esecuzione del programma, quindi passerà al controllo finale e alla visualizzazione del messaggio seriale giusto o sbagliato
 
Ok abbiamo già finito; come vedete il codice assembler non è poi così terribile ;) in fondo se qualcuno di voi ha provato a leggere qualche listato in C dei programmatori di una volta (ma anche di oggi) avrà trovato sicuramente cose molto meno leggibili di questo semplice listato, che in fondo è astruso nella forma ma lineare nei contenuti -frase filosofica-
alla prossima
×bye to all×
syscalo

Disclaimer

Generalmente qui si inserisce una piccola spiegazione per farvi capire che noi non stiamo facendo tutto questo per fregare i programmatori, ma per aumentare le nostre e le vostre conoscenze. In questo tutorial non sono stati usati programmi commerciali quindi possiamo risparmiarci il disclaimer ;-)

 
UIC's page of reverse engineering, scegli dove andare:

Home   Anonimato   Assembly    ContactMe  CrackMe   Links   
NewBies   News   Forum   Lezioni  
Tools   Tutorial   Search

UIC