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.