Invece di darti una risposta diretta, perché sarà per lo più "dipende", ho pensato che potrebbe essere più interessante portarti attraverso una mia recente scoperta che descrive il comportamento dell'allocazione di memoria su Windows, in modo che hai qualche contesto dietro tutto questo. Non ne so abbastanza del modello di gestione della memoria di Linux, quindi non farò commenti su quel lato delle cose, anche se mi è stato detto che è abbastanza simile.
Recentemente stavo pensando alle funzionalità di protezione della memoria W ^ X offerte dal kernel di Windows, noto come " criterio del codice dinamico ". L'idea è che una volta che una pagina mappata in un'applicazione è stata contrassegnata come scrivibile, non può mai essere contrassegnata come eseguibile. Si tratta di una potente strategia di mitigazione degli exploit perché impedisce quasi ogni classe di exploit che implica il rilascio di shellcode. Non si può semplicemente fare un overflow del buffer, ottenere il controllo del puntatore dell'istruzione, ROP su VirtualProtect, contrassegnare il payload come RWX ed eseguirlo - la chiamata VirtualProtect fallisce perché la memoria non può essere scrivibile ed eseguibile.
Il mio pensiero per un bypass generico qui era che potevi VirtualAlloc un po 'di memoria di lettura + scrittura, VirtualFree, quindi VirtualAlloc di nuovo come read + execute, sperando di ottenere re-allocate le stesse pagine. A quanto pare, questo non funziona. Il motivo per cui è pertinente alla tua domanda.
Ci sono tre casi in cui la gestione della memoria di Windows scriverà le pagine a zero:
- Quando una pagina è impegnata in un processo
- Quando una pagina è stata contrassegnata come
MEM_RESET
- Quando una pagina è stata liberata e contrassegnata come sporca
Il primo ostacola il mio piano. Ai processi può essere assegnato spazio di indirizzamento virtuale senza impegnarli, ad es. tramite chiamata VirtualAlloc con MEM_RESERVE
. Lo spazio indirizzo riservato non viene mappato direttamente su alcuna memoria fisica o swap, ma viene conservato per essere utilizzato dal processo nel caso sia necessario. Per utilizzare effettivamente la memoria, le pagine devono essere impegnate, ad es. chiamando VirtualAlloc con MEM_RESERVE | MEM_COMMIT
. Dopo essere stato impegnato, una pagina verrà azzerata se non è già stata utilizzata con altri mezzi. In quanto tale, il mio trucco di allocare dati scrivibili, liberarlo, quindi allocarlo come eseguibile è interrotto perché il gestore della memoria azzera la pagina.
Anche il secondo è interessante. Se hai finito di utilizzare un blocco di memoria per ora, ma lo utilizzerai nuovamente in seguito, puoi reimpostare la pagina utilizzando MEM_RESET
. Ciò indica al gestore della memoria che non dovrebbe disimpegnare la pagina dal processo, ma non dovrebbe preoccuparsi di scambiarli su disco poiché i dati contenuti all'interno non sono più interessanti. Il sistema azzererà queste pagine in background quando c'è tempo libero. Tuttavia, l'applicazione può richiedere di annullare il flag di ripristino se il sistema non ha ancora azzerato la pagina, utilizzando il flag MEM_RESET_UNDO
.
La terza opzione è che si disattiva e si rilascia una pagina, ed è completamente gratuito per essere riutilizzato dal gestore della memoria con qualsiasi altro processo. Se la pagina contiene dati, è contrassegnata come sporca. Il sistema lo scrosterà in background o lo azzererà attivamente quando verrà nuovamente eseguito.
Questi sono i casi per la gestione della memoria livello di sistema . La gestione a livello di processo è molto diversa, ad es. utilizzando HeapAlloc, gli allocatori di libreria (malloc) o un allocatore completamente personalizzato. Questi progetti possono allocare e impegnare una pagina di memoria ed eseguire la propria gestione dell'uso della memoria in aggiunta a quel blocco impegnato. Di conseguenza, una chiamata a libc free () potrebbe non disattivare effettivamente le pagine di memoria sottostanti. È anche totalmente possibile che una chiamata malloc () restituisca pagine che non sono state ripristinate: ecco perché calloc () esiste.
La prossima parte della domanda è se i browser hanno difese attive contro questo. Non so se lo fanno. È certamente possibile che i loro allocatori di memoria esplicitamente azzerino tutte le nuove pagine allocate (o le impostino su qualche valore magico, ad esempio 0xDEADBEEF) per limitare il potenziale e identificare meglio i casi di letture non inizializzate della memoria. Inoltre, ad esempio, l'allocatore di memoria di Microsoft Edge è stato appositamente progettato per offrire maggiore sicurezza contro il danneggiamento della memoria, pertanto l'azzeramento delle allocazioni è parte di quello.
In sintesi, dipende!