ASLR e come un programma può effettivamente chiamare le sue funzioni

4

Sto studiando tecniche di protezione e ho un dubbio su come ASLR funzioni per un programma in ambiente Windows.

Per quanto ne so ASLR funziona randomizzando parte dell'indirizzo imagebase quando carica il modulo in memoria in modo che gli exploit non possano fare affidamento su indirizzi hardcoded.

La mia domanda è: supponiamo che un programma utilizzi il modulo "kernel32.dll" e lo carichi nel suo spazio indirizzo. Ci sono probabilmente istruzioni CALL all'interno di quel programma come

...
CALL some_address_inside_kernel32.dll
...
...

come fa il programma a sapere dove saltare se gli indirizzi della funzione kernel32.dll non sono più affidabili? Il caricatore interviene in questo processo?

    
posta Marco A. 18.01.2013 - 18:17
fonte

2 risposte

6

Non chiamate le funzioni all'interno del kernel. Il kernel risiede in un altro livello di privilegio; le sue pagine di memoria non sono accessibili dal codice normale. Per passare al codice del kernel, il codice dell'applicazione esegue una chiamata di sistema che comporta l'utilizzo di una porta specifica che gestisce l'escalation dei privilegi temporanei. Su un sistema x86 a 32 bit che esegue Linux, questo viene fatto con int 0x80 : un interrupt attivato dal software. I parametri di chiamata del sistema sono forniti dal chiamante in alcuni registri CPU specifici; in particolare, il registro %eax contiene l'identificatore simbolico per la chiamata di sistema che l'applicazione desidera eseguire. Il gestore di interruzioni (all'interno del kernel) esamina i registri della CPU per sapere che cosa l'applicazione vuole che il kernel faccia (se la chiamata di sistema è concessa è un altro problema).

Non esiste ASLR per le chiamate di sistema; questo non è un concetto rilevante qui. ASLR è per DLL . Una DLL è un pezzo di codice applicazione che viene caricato nello spazio indirizzo dell'applicazione ed è accessibile all'applicazione con i suoi normali privilegi; in particolare, il codice dell'applicazione può "saltare" nel codice DLL con un normale codice operativo "salta" (una chiamata di funzione non è altro che un salto glorificato).

Poiché l'indirizzo effettivo in cui verrà caricata la DLL è noto solo quando la DLL viene effettivamente caricata, il codice dell'applicazione segue convenzioni speciali in modo che i suoi opcode di salto possano essere adattati dinamicamente in modo che puntino ovunque si carichi la DLL. Questa regolazione dinamica viene eseguita dal linker dinamico . Questo linker utilizza tabelle di rilocazione per eseguire il suo lavoro: una voce in tale tabella descrive un opcode che deve essere regolato e il nome della funzione che l'opcode tenta di raggiungere. Quando la DLL è caricata (dal linker dinamico), la collocazione in memoria di quella funzione è nota e gli opcode di salto descritti nelle tabelle di rilocazione vengono regolati.

Come vedi, l'intero concetto di DLL consente di caricare la DLL in una posizione arbitraria nella RAM; tale posizione può differire in caso di successive esecuzioni dell'applicazione. L'effettiva collocazione dipende da dove c'è un "buco" sufficientemente grande di memoria libera per eseguire il caricamento, quindi può cambiare a seconda di cosa fa l'applicazione, di quanta memoria allocata in precedenza e così via. Il caricamento della DLL "naturalmente" implica indirizzi di caricamento non fissi. ASLR è solo volontario in movimento di DLL: il linker dinamico sceglie un caricamento casuale (gratuito) indirizzo di proposito . Questo è (quasi) completamente trasparente per le applicazioni.

    
risposta data 18.01.2013 - 18:58
fonte
3

how does the program know where to jump to if kernel32.dll function addresses are no longer reliable? Does the loader intervene in this process?

L'orso ti ha dato una panoramica generale dell'ASLR. Tuttavia, ci sono un certo numero di punti specifici per Windows che devi rimuovere, tuttavia:

  • In realtà, le DLL hanno sempre dovuto far fronte al riposizionamento. Contengono un indirizzo di base preferito all'interno dello spazio degli indirizzi, al punto che un precedente passaggio di ottimizzazione nello sviluppo di app win32 come rebase le tue DLL perché il costo del trasferimento di molte DLL all'avvio dell'applicazione ha rallentato le cose, poiché il caricamento in molte DLL causava numerose collisioni.
  • Questo è in contrasto con l'approccio PIC di Unix, vedi wikipedia .
  • Quando una DLL viene caricata rimane in memoria fino a quando l'ultimo handle non viene chiuso. Dato che sarebbe inefficiente mappare una DLL in una posizione di indirizzo univoca per processo, ciò significa che una volta caricate, DLL di sistema comuni come kernel32.dll sono presenti allo stesso indirizzo in tutte le applicazioni. Puoi leggere ulteriori informazioni sugli interni di ASLR dalle eccellenti risposte qui . Pertanto, mentre potrebbe essere difficile prevedere gli indirizzi in kernel32.dll tra i riavvii, una volta trovato per un dato avvio, l'indirizzo non cambia.
  • Nell'architettura di Windows, kernel32.dll è in effetti una libreria helper in modalità utente. Come dice giustamente Thomas, sono necessarie chiamate di sistema per arrivare alla modalità kernel. Questi in effetti si verificano in ntdll.dll (Yes, un'altra DLL) che avvolge varie funzioni prefissate Nt nelle chiamate di sistema, le impacchetta e le invia al kernel.
  • I driver del kernel sono caricati in modo non deterministico - il kernel ha ASLR. Vedi questo thread osr e questa presentazione . A quanto ho capito, il managar di avvio di Windows carica ntoskrnl e hal.dll in posizioni casuali per te. Driver dal Server 2008 caricano sempre in modo non deterministico.
risposta data 19.01.2013 - 00:42
fonte

Leggi altre domande sui tag