Niente Prog per oggi
(Di tutto un pò)


Data

by "GuZuRa"

 

 

UIC's Home Page

Published by Quequero

Chi sa soffre...

Guzura ci spiega il funzionamento dello stack senza entrare troppo nei particolari, il tutorial è adattissimo ai NewBies per capire cosa sia e come funzioni lo stack!

Chi non sa soffre di più...
UIC's form
E-mail: [email protected]
Su #crack-it come GuZuRa
UIC's form

Difficoltà

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

 
Questo tutorial è rivolto ai totally newbies ed ai newbies che hanno voglia di di ripassare i primi concetti (quelli da tenere sempre presenti...Come : le donne dei reverser sono le più belle; il mio nick incute terrore al solo pronunciarlo e cose del genere...)
Scherzi a parte ho scritto questo tutorial proprio mentre io stesso andavo a rivedermi le prime cose basilari tra cui il concetto di stack (questo strano oggetto del desiderio)
Ultima nota introduttiva : spero che con tutte le cose che ,al momento, ho intenzione di infilarci dentro non diventi una tut pallosissimo...
 
Chissà se servirà
(Di tutto un pò)
Written by Tuo GuZuRa

Introduzione

Lo stack chi lo ama e chi lo frega...

Tools usati

Il cevello può bastare

URL o FTP del programma

Alla UIC ovvio ;))))

Notizie sul programma 

Ma che programma

Essay

 
Ora vado ad aprire una lunghissima parentesi sul significato delle push, pop e dello stack (Que dai una controllata che è meglio :)))))
Nota: le cose che cerco di spiegare non sono per nulla generali ma sono suscettibili a cambiamenti e manipolazioni diverse proprio perchè l'assembler ci permette di controllare a bassissimo livello anche memoria e affini quindi non consideratele come ASSOLUTE 
STACK
Lo stack è un' area di memoria gestita con tecnica LIFO (Last In, First Out) che viene usata "solitamente" utilizzata per contenere i record di attivazione delle procedure o (vedetela così anche se non è la stessa cosa) i parametri in ingresso alle procedure del programma attualmente in esecuzione
E' caratterizzato dal registro SS (stack segment) che definisce il segmento che punta allo stack corrente (ma questo è poco significativo nel reversing di programmi a 32bit cioè la maggior parte di quelli che reversiamo, sui  16bit c'era il seguente problema: considerate che in memoria potevano coesistere più stack (uno per ogni programma in esecuzione grossomodo) e ognuno poteva indirizzare al massimo 64kbyte di stack quindi se un programma superava tale limite si rischiava di sovrascrivere lo stack di un'altro programma con ovvi casini per questo era necessario sapere sempre il SS del programma che si voleva reversare mentre per altri motivi questo non è più un problema per i 32bit; il messaggio che deve rimanere è che, per un newbie, questo registro "non è fondamentale") e dal registro esp (dove per sp si intende stack pointer) che chiariremo tra un attimo
Vediamo di spiegare cosa intendiamo per LIFO, cioè l'ultimo elemento infilato nello stack è anche il primo ad essere estratto
 
Prima cosa lo stack cresce dagli indirizzi alti  a quelli bassi e che l'indirizzo di partenza dello stack contenuto in (SS) non è il bottom dello stack
             SS     ----->|--------------------- |
                          | Spazio nello stack   |
                      ESP | ancora disponibile   |
                          |                      |
TOP dello stack     ----->|--------------------- |ESP+00 Indirizzo più basso
                          |  Spazio occupato     |ESP+04
                          |dalla roba nello stack|ESP+08
BOTTOM dello stack  ----->|--------------------- |       Indirizzo più alto
 
Fate un piccolo sforzo e cercate di seguirmi AD OGNI BARRA VERTICALE ( | )FAREMO CORRISPONDERE 4 BYTE (cioè una double-word) quindi nel nostro modello, potremo indirizzare fino a 8 locazioni ,da 4 BYTE ognuna, senza correre il rischio di sovrascrivere gli stack di altri programmi andando oltre lo spazio ancora disponibile (questa non è nella realtà la dimensione vera dello spazio indirizzabile, ovvio).
Ho detto prima che lo stack serve per contenere le variabili in igresso alle procedure, voi dovete immaginare che in ogni locazione (che è grande 4 byte cioè 32 bit ci sia un valore, una variabile, un indirizzo che mi possa servire)
Nel nostro modello abbiamo già occupato 4 locazioni e altre 4 invece ne abbiamo disponibili
Ancora un po di notazione OGNI BARRA VERTICALE ( | ) corrisponderà a una locazione da 4 byte libera e ad OGNI BARRA VERTICALE ( | ) una locazione occupata
Chiariamo subito che cos'è ESP: è l' OFFSET (la distanza) tra SS e il TOP sello stack; noi non sappiamo esattamente quantificarlo però sappiamo che se cerchiamo un valore che sappiamo essere nella prima locazione dello stack faremo riferimento as SS : ESP (metto anche SS anche se ho già detto che per noi è meno significativo anche perchè è ovvio che un debugger ci mostra lo stack che ci interessa) ; analogamente se vogliamo leggere un valore che sappiamo trovarsi nella terza locazione occupata nello stack faremo riferimento a SS : ESP + 8 (8 byte dopo ESP cioè due locazioni da 4 byte dopo; il PC ragiona in byte e non in locazioni) guardate in figura (in blu) dove sta ESP e ESP + 8
 
COME SI MANEGGIA LO STACK (LIFO)
Si utilizzano due istruzioni PUSH che mette un valore nello stak nella locazione sopra il TOP e l'istruzione POP che toglie l'attuale TOP e trasforma in TOP i 4 byte che erano sotto (spero di chiarire con l'esempio sotto)
 
 
       Situazione prima di una push               Situazione dopo una push
 
SS    -->|--------------------- |         SS-->   |--------------------- |
         | Spazio nello stack   |                 | Spazio nello stack   |
 ESP     | ancora disponibile   |          ESP    | ancora disponibile   |
         |                      |       TOP    -->| Nuovo   TOP          |ESP+00
TOP   -->|--------------------- |ESP+00           |--------------------- |ESP+04
         |  Spazio occupato     |ESP+04           |  Spazio occupato     |ESP+08
         |dalla roba nello stack|ESP+08           |dalla roba nello stack|ESP+0C
BOTTOM-->|--------------------- |        BOTTOM-->|--------------------- |     
 
Come potete vedere è diminuito l'offset da SS, osservate bene come  si sono modificate le cose e saremo a bolla; per capire come funziona una pop basta invertire le situazioni (si parte da quella a destra e si arriva a quella a sinistra); ora dovrebbe essere chiaro che io posso fare 3 push a fila ma se ne faccio un'altra supero SS e vado a scrivere dove non mi compete (era questa la limitazione che c'era nei programmi a 16 bit che avevano stack limitato a 64Kbyte e non è molto)
A livello di teoria della programmazione potrei accedere al bottom solo dopo aver fatto tante pop quante sono le locazioni precedenti, occupate, (in realtà io posso accedere a tutto quello che voglio conoscendo ESP e la distanza in byte da ESP della locazione che mi serve; come ho detto sopra) Qui si spiega perchè si dice che lo stack è gestito LIFO   
 
Ora cerchiamo di capire perchè lo stack è così importante analizzando le relazioni che ha con una procedura assembler che più generale non si può ;))))))) Mi scusino i maghi della programmazione in ASM...Altra cosa, come al solito lo schema a cui faccio riferimento e generico e suscettibile di infinite varianti ma mi serve solo per far capire alcuni concetti
 
In un linguaggio pseudo C l'invocazione del programma chiamante sarebbe di questo tipo :
funzione(arg1,arg2,arg3,...,argN)
 
Mentre in ASM sarebbe di questo tipo
push argN                                               
push ...                            |  arg1     |ESP+00  Se supponiamo che lo stack
push arg2                           |  arg2     |ESP+04  sia "vuoto" abbiamo una
push arg1                           |  ...      |ESP+08  situazione di questo tipo
call_funzione                       |  argN     |ESP+0C  prima della call
add esp,num_byte_arg
 
Succede questo, il chiamante :
1)passa sullo stack gli argomenti alla funzione(dall'ultimo al primo)
2)trasferisce il controllo con la call_funzione
3)al ritorno dalla procedura aggiusta il puntatore allo stack di un valore pari al totale in byte degli argomenti passati, in modo da "scaricarli" virtualmente (cioè se accedete a ESP - 04 o altro li ribeccate) dallo stack
 
Adesso però dobbiamo vedere come è fatta la call_funzione al suo interno
Lo standard è quasi sempre questo
funzione proc near (o far)
Si salva sullo stack l'attuale valore del registro EBP e poi si sovrascrive EBP con il contenuto di ESP. Per tutto il tempo di vita della procedura, EBP non viene più modificato, e viene utilizzato come record di attivazione della funzione (cioè se mi serve un valore lo pesco da EBP nella stessa maniera che avevo spiegato sopra ;se mi serve il quarto valore andrò a leggere EBP + 12...)
Tutto questo per dire che si incomincia il codice della call con
Push     EBP
Mov     EBP, ESP
 
Se la funzioncella fa uso di variabili locali queste vengono allocate a seguire con l'istruzione
Sub ESP,XXX (numero di locazioni che ci servono * 4 byte)
 
Per "convenzione" dopo essere uscito dalla call i registri EBP, ESP, CS, DS, SS, EDI, ESI devono avere il vecchio valore che avevano prima della chiamata quindi se ho intenzione di utilizzarli e modificarli nella funzione dovrò fare così (la convenzione di riavere EDI e ESI, dopo le chiamate, uguali al valore che avevano prima delle chiamate stesse viene mantenuta anche se sono chiamate API di windows, per gli altri registri non sò)
Push EDI se utilizzato cioè ne salvo il valore nello stack
Push ESI se utilizzato,etc...
 
In uscita possono avere valori diversi per EAX, EBX, ECX, EDX, ES, e i FLAG (e noi come reverser siamo interessati soprattutto a queste modifiche che avvengono dentro le call)
 
Anche l'epilogo della procedura è standard
Pop ESI
Pop EDI  etc...
 
Pop EBP ripristino di EBP al valore che avevo prima della chiamata
 
Adesso ci occupiamo della parte fondamentale e anche chiarificatrice dei deliri di qualche riga sopra
ACCESSO AI PARAMETRI PASSATI SULLO STACK
Supponiamo che il chiamante abbia passato 3 parametri sullo stack (le variabili L e K e il valore 22) e abbia invocato la procedura, lo stack che osserviamo è questo
 
             SS     ----->|--------------------- |
                          | Spazio nello stack   |
                      ESP | ancora disponibile   |
                          |                      |
TOP dello stack     ----->|  Offset di ritorno   |ESP+00
                          |         L            |ESP+04
                          |         K            |ESP+08
BOTTOM dello stack  ----->|         22           |ESP+0C
 
L'offset di ritorno definisce in pratica a quale punto del codice si ritorna una volta eseguita la call (generalmente è l'istruzione successiva alla call stessa)
Siamo ora nella nostra bella funzioncina e supponiamo di voler riservare due locazioni (8 byte in tutto) per due variabili locali, la nostra funzione sarà così
funzione PROC NEAR
Push EBP
Mov EBP, ESP
 
SUB ESP, 08
 
Lo stack a questo punto risulta questo
 
 
             SS     ----->        |     DISP0NIBILE      |
TOP dello stack     -----> ESP+00 | 2° variabile locale  |EBP-08
                           ESP+04 | 1° variabile locale  |EBP+04
                           ESP+00 |Vecchio valore di EBP |EBP+0C
                           ESP+0C |  Offset di ritorno   |EBP+04
                           ESP+10 |         L            |EBP+08
                           ESP+14 |         K            |EBP+0C
BOTTOM dello stack  -----> ESP+18 |         22           |EBP+10
 
Adesso pensiamo di voler fare in modo che il nostro programmino arrivi ad avere in EAX il valore L + K - 22 come facciamo??
Il codice che dovremo inserire è questo (vi farà capire come si accede ai vari valori nello stack)
Mov EAX, [EBP+08] muovo in eax L    (Notate che uso EBP)
Add EAX, [EBP+0C] sommo ad L il valore K
Sub EAX, [EBP+10]  e ho in pratica realizzato quello che volevo
 
Supponiamo di voler inizializzare a zero la seconda variabile locale: semplice
Mov DWORD PTR [EBP-08], 0
 
Prima di uscire devo rimettere a posto lo stack quindi farò
Mov ESP, EBP
Devo togliere il vecchio valore di EBP da ESP (guardate la figura sopra e vi sara chiaro come tutto si modifica)
Pop EBP
Ret   Perchè in questa call non ho usato nessono dei registri EDI, ESI ... (se li avessi usati avrei dovuto fare prima delle push e poi delle pop)

Add ESP,0C questa mi serve per togliere dallo stack le tre locazioni che facevano riferimento a L, K, 22 e tornare effettivamente alla situazione di stack iniziale

Spero che alla luce di questo tut lo stack non vi si più tanto oscuro...

ALLA PROSSIMA Byez GuZuRa

 

Note finali

Ringrazio Que che con la UIC mi ha fatto imparare cose di cui non conoscevo nemmeno l'esistenza, e il mio PROf di info2 che mi ha spiegato così bene lo stack (chissa se anche lui è membro????) a si anche tutti quelli che mi conoscono ;)))))))))

Disclaimer

Vorrei ricordare che il software va comprato e  non rubato, dovete registrare il vostro prodotto dopo il periodo di valutazione. Non mi ritengo responsabile per eventuali danni causati al vostro computer determinati dall'uso improprio di questo tutorial. Questo documento è stato scritto per invogliare il consumatore a registrare legalmente i propri programmi, e non a fargli fare uso dei tantissimi file crack presenti in rete, infatti tale documento aiuta a comprendere lo sforzo immane che ogni singolo programmatore ha dovuto portare avanti per fornire ai rispettivi consumatori i migliori prodotti possibili.
Noi reversiamo al solo scopo informativo e di miglioramento del linguaggio Assembly.
Capitoooooooo????? Bhè credo di si ;)))) 

UIC's page of reverse engineering, scegli dove andare:
Home   Anonimato   Assembly    CrackMe   ContactMe   Forum   Iscrizione
Lezioni    Links   Linux   NewBies   News   Playstation
Tools   Tutorial   Search   UIC Faq
UIC