Domanda Chiarimento sull'effetto dell'ASLR

Stato
Discussione chiusa ad ulteriori risposte.

DanyDollaro

Utente Electrum
14 Luglio 2018
148
41
58
138
Ultima modifica:
Salve, avevo già letto al riguardo sull'ASLR ma non ancora ho trovato una risposta abbastanza dettagliata, se ho ben capito prendendo come esempio il programma seguente:
C++:
#include <iostream>

int main() {
    int a = 1;
    std::cout << &a;
}
ogni volta che verrà riavviato si noterà che la variabile a cambierà la sua posizione nello spazio degli indirizzi virtuali, per esempio:

output prima esecuzione: 0x003BFE34
output seconda esecuzione: 0x00EFFA50
output terza esecuzione: 0x012FFA00

Ma questo è l'effetto dell'ASLR?

in più ho notato che gli indirizzi dei moduli mappati tendono a cambiare molto meno frequntemente rispetto alla variabile a, oppure è questo l'effetto dell'ASLR e la variabile a cambia per un'altra ragione? oppure è sempre l'ASLR che fa cambiare tutti e 2 gli indirizzi in questione?

tra l'altro su un'altro forum su cui stavo leggendo una discussione che parlava al riguardo del cambio di indirizzi ad ogni riavvio di un programma un utente scrive:
ASLR does not affect virtual address space, but relocation can affect the virtual address space (per module).
Il che mi lascia al quanto perplesso (ho riportato la sua risposta intera).
 
ogni volta che verrà riavviato si noterà che la variabile a cambierà la sua posizione nello spazio degli indirizzi virtuali, per esempio:

output prima esecuzione: 0x003BFE34
output seconda esecuzione: 0x00EFFA50
output terza esecuzione: 0x012FFA00

Ma questo è l'effetto dell'ASLR?
Se mi avessi dato altri numeri, ti avrei risposto con un sì secco. Questi sono visibilmente troppo bassi, quindi per me è un no... e senza più info, al momento non ho capisco di cosa stia accadendo.

Con l'ASLR disabilitato, lo stack parte (o meglio, finisce...) a 0x7fffffffffff (aka 2^47 - 1) nei sistemi a 64 bit e a 0xbfffffff (aka 3GB - 1) nei sistemi a 32 bit. Con l'ASLR abilitato stai sottraendo un numero random a quel numero lì, però lo stack deve comunque rimanere nella parte alta della memoria (e crescere verso il basso). Tu stai stampando l'indirizzo di una variabile che si trova nello stack, ma con 0x003BFE34 mi stai praticamente dicendo che il tuo stack parte a 4MB e mi sembra molto poco realistico.
Che sistema operativo e che hardware stai usando?

in più ho notato che gli indirizzi dei moduli mappati tendono a cambiare molto meno frequntemente rispetto alla variabile a, oppure è questo l'effetto dell'ASLR e la variabile a cambia per un'altra ragione? oppure è sempre l'ASLR che fa cambiare tutti e 2 gli indirizzi in questione?
Cosa intendi esattamente per modulo?

tra l'altro su un'altro forum
Link?
 
Ultima modifica:
Grazie St3ve per la risposta, ed attualmente ho finito i like giornalieri :eek:.
Che sistema operativo e che hardware stai usando?
Ho compilato il sorgente in x86 con Visual Studio 2019 su Windows 10 - build 1909, sul mio computer sono disponibili 15,9 GB di RAM e mentre facevo il test ne era disponibile più della metà.

Cosa intendi esattamente per modulo?
Per modulo intendo il file .exe o i file .dll mappati nello spazio degli indirizzi virtuali del processo
Moduli.png

Quelli soprastanti sono i moduli enumerati da X32dbg ed il programma in qestione è lo stesso con cui ho fatto l'esempio prima (il programma l'ho chiamanto appunto "test.exe")

E' questo, è una discussione sul forum di Cheat Engine ed il messaggio in questione è attualmente l'ultimo.

In più, riguardo a ciò:
Con l'ASLR disabilitato, lo stack parte (o meglio, finisce...) a 0x7fffffffffff (aka 2^47 - 1) nei sistemi a 64 bit e a 0xbfffffff (aka 3GB - 1) nei sistemi a 32 bit.
Ho fatto un secondo test con quest'altro codice:
C++:
#include <iostream>

int main()
{
    int a = 1;
    std::cout << &a << " - " << main;
    getchar();
}
output 1 = 0075FBB8 - 000F1307
output 2 = 004FF9B0 - 000F1307
output 3 = 008FF944 - 000F1307
output 4 = 006FF908 - 000F1307
output 5 = 00EFFC98 - 000F1307

Update. Ho riavviato il pc ed ecco altri output:
output 1 = 00D0FE38 - 00D71307
output 2 = 010FFC44 - 00D71307
output 3 = 00CFFA34 - 00D71307
output 4 = 007BF760 - 00D71307
output 5 = 0075FAC0 - 00D71307
 
  • Mi piace
Reazioni: Zeta 3.14
Questo post descrive alcune pecularità dell'implementazione ASLR di Windows che dimostrano il comportamento che vedi sopra, rispettivamente nei punti 2, 4, e 6 del post.

In pratica, la prima volta che esegui un programma X da quando hai acceso il computer Windows genererà un indirizzo di memoria casuale nel quale mappare contiguamente il file. Se tu chiudi e riapri X, troverai che X sarà caricato sempre allo stesso indirizzo a meno che un tot. di tempo sia passato dall'ultimo avvio o dopo aver rinominato il file, l'unico modo che hai di essere sicuro che l'indirizzo base cambi al prossimo avvio del programma è riavviare il computer, come hai visto sopra l'indirizzo di main è cambiato solo dopo il riavvio perchè il codice di main si trova nel segmento di memoria .text, impacchettato nell'eseguibile.

Poichè lo stack non è incluso all'interno dell'eseguibile ma è creato e poplato a runtime, non deve essere adiacente al blocco di memoria nel quale l'eseguibile è stato mappato e l'indirizzo di base dello stack è quindi randomizzato ad ogni caricamento del file, piuttosto che solo la prima volta.
 
  • Mi piace
Reazioni: DanyDollaro
E' questo, è una discussione sul forum di Cheat Engine ed il messaggio in questione è attualmente l'ultimo.
La "puntualizzazione" di quel tizio è abbastanza scriteriata (o stava semplicemente facendo un indefinibile giro di parole) in quanto la prassi di casualizzazione dello spazio degli indirizzi (ASLR), è specificamente eletta per influire nello spazio virtuale della memoria (o meglio gli address "virtuali").

Morale della favola: bisogna sempre accertarsi che ciò che si legge sia affidabile prima di titubare sull'argomento.
 
  • Mi piace
Reazioni: DanyDollaro
Ho compilato il sorgente in x86 con Visual Studio 2019 su Windows 10
Quindi lo spazio di indirizzamento è su 3GB, okay... ma ancora non mi spiego come possano essere così bassi quei numeri.

Il layout classico di un processo a 32 bit in memoria è questo
program_in_memory2.png

Con l'ASLR io mi aspetto che ci siano degli offset su tutte le arie di memoria, ma ho sempre pensato che il layout non cambiasse. Tu hai a, una variabile che sta nello stack, in posti del tipo 0x0075FBB8. Lo stack è tipicamente (di default, poi lo puoi cambiare) limitato a 1MB, quindi ci sta... ma guardando i numeri che hai postato tra lo stack e l'inizio della memoria (0x00000000) ci sono veramente un pugno di megabyte (~6-7 MB nell'esempio che ho fatto). Questo vuol dire che se fai un'allocazione dinamica (heap memory) un po' grossa deve necessariamente andare sopra lo stack... rompendo il layout in figura. Io mi sarei aspettato che anche con l'ASLR abilitato avremmo letto numeri nell'ordine di 0xbXXXXXX o comunque un numero significativamente più grande di quelli che hai riportati.

Toglimi sta curiosità, prova a compilare questo codice e ad eseguirlo 2-3 volte e riportami gli output.
C++:
#include <iostream>

int main()
{
  int *a = new int[20*1024*1024]; // ~80MB
  std::cout << &a << " is an address in the stack\n";
  std::cout << a << " is an address in the heap\n";
  return 0;
}
Anche se non l'hai detto, sono convinto (visti i 16GB di RAM) che la tua versione di Windows sia x64. Non stai girando nativamente a 32 bit e quindi ci sta che il layout sopra sia un po' diverso, ma prima di tirare a indovinare o indagare ulteriormente sarei curioso di vedere l'output.

in più ho notato che gli indirizzi dei moduli mappati tendono a cambiare molto meno frequntemente rispetto alla variabile a
Ha perfettamente senso anche perché l'idea delle shared libraries, come dice il nome, è che possano essere condivise da diversi programmi consumando meno memoria (fisica). Non che tu possa prenderti alcuna garanzia sul comportamento del sistema operativo, ma mi sembra un comportamento ragionevole e il link di Baud conferma. Random non vuol dire che debba cambiare ad ogni esecuzione.

La "puntualizzazione" di quel tizio è abbastanza scriteriata (o stava semplicemente facendo un indefinibile giro di parole) in quanto la prassi di casualizzazione dello spazio degli indirizzi (ASLR), è specificamente eletta per influire nello spazio virtuale della memoria (o meglio gli address "virtuali").

Morale della favola: bisogna sempre accertarsi che ciò che si legge sia affidabile prima di titubare sull'argomento.
Io onestamente non so nemmeno cosa intende con relocation. Comunque prima di bollarlo come deficiente magari si possono chiedere spiegazioni :asd:
EDIT: oh, gliel'ha chiesto.
 
  • Mi piace
Reazioni: DanyDollaro
Ultima modifica:
Questo post descrive alcune pecularità dell'implementazione ASLR di Windows che dimostrano il comportamento che vedi sopra, rispettivamente nei punti 2, 4, e 6 del post.
Grazie! proprio quello che cercavo :D.

Quindi lo spazio di indirizzamento è su 3GB
Non è detto che siano 3 GB, un programma a 32 Bit può usare uno spazio di indirizzamento di 3 GB solo se il 4GT (4 Gigabyte Tuning) è attivo, quindi a meno che windows 10 non abbia questa opzione 'spuntata' di default usa uno spazio di indirizzamento di 3 GB, altrimenti sono 2 GB, e non conosco modo per vedere se sia abilitato o no :\ (a meno che io non vada a cambiarlo dal promt dei comandi).

Anche se non l'hai detto, sono convinto (visti i 16GB di RAM) che la tua versione di Windows sia x64.
E' corretto, lo si notava anche dalla directory dei moduli nell'immagine riguardante X32dbg, dato che nella loro path c'è "SysWOW64".

Toglimi sta curiosità, prova a compilare questo codice e ad eseguirlo 2-3 volte e riportami gli output.
Ecco a te i tuoi output:

Output 1:
008FF768 is an address in the stack 00F2C040 is an address in the heap

Output 2:
008FFB3C is an address in the stack 00F2A040 is an address in the heap

Output 3:
0093F7B4 is an address in the stack 00F80040 is an address in the heap

Output 4:
00CFFCB8 is an address in the stack 0130E040 is an address in the heap

La "puntualizzazione" di quel tizio è abbastanza scriteriata (o stava semplicemente facendo un indefinibile giro di parole) in quanto la prassi di casualizzazione dello spazio degli indirizzi (ASLR), è specificamente eletta per influire nello spazio virtuale della memoria (o meglio gli address "virtuali").

Morale della favola: bisogna sempre accertarsi che ciò che si legge sia affidabile prima di titubare sull'argomento.
Io onestamente non so nemmeno cosa intende con relocation. Comunque prima di bollarlo come deficiente magari si possono chiedere spiegazioni
Gli chiesi come potesse affermare che l'ASLR non influenzasse la mappatura degli indirizzi virtuali, e rispose:
in context of relative address and finding static pointer, as we are talking about static addresses that points to other stuff.
as for base address, it can be found using multiple methods.
Quindi penso che si riferisse al fatto che l'ASLR non alterasse i dati statici contenuti nei moduli.

Detto ciò vi ringrazio per il grande aiuto che mi avete dato :rulzz:.


EDIT: Se a qualcuno interessasse lascio delle altre informazioni riguardante il 4GT:
 
  • Mi piace
Reazioni: St3ve
Non vorrei sbagliarmi ma non è detto che siano 3 GB, a quanto io ne sappia un programma a 32 Bit può usare uno spazio di indirizzamento di 3 GB solo se il 4GT (4 Gigabyte Tuning) è attivo, quindi a meno che windows 10 non abbia questa opzione 'spuntata' di default usa uno spazio di indirizzamento di 2 GB, e non conosco modo per vedere se sia abilitato o no :\ (a meno che non io vada a cambiarlo dal promt dei comandi).
Sì hai ragione, mi confondevo con Linux, Windows x32 splitta 2GB kernel space e 2GB user space. Almeno di default... poi c'è quel 4GT d cui io non ero a conoscenza e ci sono i vari meccanismi che hanno ideato (principalmente per i server) per poter usare più di 4GB di RAM mantenendo una CPU e un OS a 32 bit (prima di switchare a 64 bit). Comunque, ripeto, è possibile che il tuo spazio di indirizzamento sia molto diverso anche perché non sono 32 bit nativi ma è un'applicazione a 32 bit su un sistema operativo a 64 bit.

Ecco a te i tuoi output:

Output 1:
008FF768 is an address in the stack 00F2C040 is an address in the heap

Output 2:
008FFB3C is an address in the stack 00F2A040 is an address in the heap

Output 3:
0093F7B4 is an address in the stack 00F80040 is an address in the heap

Output 4:
00CFFCB8 is an address in the stack 0130E040 is an address in the heap
Okay, quindi c'è l'heap sopra allo stack... tutti indirizzi bassissimi tra l'altro. That's weird, non me l'aspettavo.
Può essere che funzioni veramente così, ma non mi sembra normale e al momento sospetto che i valori siano così bassi per qualche altra cosa che non abbiamo considerato (quella cosa del programma a 32 bit su un sistema a 64 bit, ad esempio). Al momento non ne ho voglia, ma magari un giorno di questi proverò a documentarmi un po'.

Grazie per aver testato al posto mio.
 
  • Mi piace
Reazioni: DanyDollaro
Gli chiesi come potesse affermare che l'ASLR non influenzasse la mappatura degli indirizzi virtuali, e rispose:
in context of relative address and finding static pointer, as we are talking about static addresses that points to other stuff.
as for base address, it can be found using multiple methods.
Quindi penso che si riferisse al fatto che l'ASLR non 'randomizzasse' i dati statici contenuti nei moduli.
Ancora non torna ciò che stesse cercando di dire: sta parlando di metodi per abbindolare l'ASLR (trovare il nuovo indirizzo di memoria al riavvio, e per quello ci sono tanti metodi per farlo), ciò non contende che questa non influisca nello spazio virtuale della memoria randomizzandone gli indirizzi.
 
  • Mi piace
Reazioni: DanyDollaro
Ultima modifica:
Non vorrei andare off topic aggiungendo il tema "Perchè il mio stack si trova sotto l'heap?", ma vorrei aggiungere quest'ultimo commento:
per poter usare più di 4GB di RAM mantenendo una CPU e un OS a 32 bit
Si chiama PAE (Physical Address Extension), tra l'altro ho compilando il tuo codice in x64, la situazione non cambia:
000000F93ECFFBF8 is an address in the stack 00000266E8219070 is an address in the heap

0000002C978FF778 is an address in the stack 0000020081C8A070 is an address in the heap

0000007D616FF768 is an address in the stack 000001935031D070 is an address in the heap

Però sono lieto di annunciarti che la risposta al problema è su questa discussione di Stack Overflow, in sintesi dice che dipende dalla piattaforma e che In alcune architetture lo stack viene allocato dalla parte inferiore dello spazio degli indirizzi e cresce verso l'alto.

Ancora non torna ciò che stesse cercando di dire: sta parlando di metodi per abbindolare l'ASLR (trovare il nuovo indirizzo di memoria al riavvio, e per quello ci sono tanti metodi per farlo), ciò non contende che questa non influisca nello spazio virtuale della memoria randomizzandone gli indirizzi.
Io lascierei stare, in fondo ho appurato in funzionamento dell'ASLR grazie a voi :).
 
Io lascierei stare, in fondo ho appurato in funzionamento dell'ASLR.
Riguardo a questo, messaggio rivolto a @St3ve e tutti gli altri che non avessero capito ciò che avesse detto in modo molto pacchiano quel tizio: (esempio) si può trovare un nuovo indirizzo dell'oggetto foo (al riavvio) trovando un puntatore statico ad una istanza dell'oggetto foo.
Se si dà un'occhiata alla struttura di questo oggetto si possono ritrovare i vari gruppi di indirizzi di memoria tra cui quello voluto. Basterà ricalcolarlo aggiungendo l'offset all'indirizzo di memoria, è solo un po' di matematica.
Ma ripeto, questo non contrasta il design dell'ASLR.
 
  • Mi piace
Reazioni: DanyDollaro
Però sono lieto di annunciarti che la risposta al problema è su questa discussione di Stack Overflow, in sintesi dice che dipende dalla piattaforma e che In alcune architetture lo stack viene allocato dalla parte inferiore dello spazio degli indirizzi e cresce verso l'alto.
Sì, ma dipende dall'architettura e in x86 e in x86_64 lo stack cresce verso il basso. Ci sono delle facilities (e.g. le istruzioni assembly push e pop) che ti permettono di usarlo in questo modo. Il tuo stack cresce verso il basso, ne sono sicuro anche senza chiederti di provare, ma pare che parta ad un indirizzo basso di memoria.... and that's okay. Non vedo una ragione precisa per cui non possa essere così, però io mi aspettavo un comportamento diverso.

tra l'altro ho compilando il tuo codice in x64 la situazione non cambia:
000000F93ECFFBF8 is an address in the stack 00000266E8219070 is an address in the heap

0000002C978FF778 is an address in the stack 0000020081C8A070 is an address in the heap

0000007D616FF768 is an address in the stack 000001935031D070 is an address in the heap
Si vede che il layout dei programmi in memoria su Windows è diverso da quello di Linux. Non lo sapevo.
 
  • Mi piace
Reazioni: DanyDollaro
Stato
Discussione chiusa ad ulteriori risposte.