Risposte:
In realtà, sulla maggior parte delle piattaforme, non riesce con un errore, ma ciò dipende dall'architettura hardware. Non c'è assolutamente alcuna garanzia che ciò sia innocuo a meno che non si esegua il comando come utente senza privilegi. Con un utente senza privilegi, il comando è perfettamente innocuo perché non è possibile aprirlo /dev/mem
.
Quando esegui un comando come root, dovresti sapere cosa stai facendo. Il kernel a volte ti impedisce di fare qualcosa di pericoloso, ma non sempre. /dev/mem
è una di quelle cose potenzialmente pericolose in cui dovresti davvero sapere cosa stai facendo.
Vedremo come funziona una scrittura /dev/mem
su Linux. Il principio generale sarebbe lo stesso su altri Unices, ma cose come le opzioni del kernel sono completamente diverse.
Cosa succede quando un processo legge o scrive su un file del dispositivo dipende dal kernel. Un accesso a un file di dispositivo esegue un codice nel driver che gestisce questo file di dispositivo. Ad esempio, scrivendo per /dev/mem
invocare la funzione write_mem
indrivers/char/mem.c
. Questa funzione accetta 4 argomenti: una struttura di dati che rappresenta il file aperto, un puntatore ai dati da scrivere, il numero di byte da scrivere e la posizione corrente nel file.
Si noti che si arriva così lontano solo se il chiamante era autorizzato ad aprire il file in primo luogo. I file del dispositivo obbediscono normalmente alle autorizzazioni dei file. Le normali autorizzazioni di /dev/mem
sono di crw-r-----
proprietà di root:kmem
, quindi se si tenta di aprirlo per la scrittura senza essere root, si ottiene semplicemente "permesso negato" (EACCESS). Ma se sei root (o se root ha cambiato i permessi di questo file), l'apertura passa e puoi provare a scrivere.
Il codice nella write_mem
funzione effettua alcuni controlli di integrità, ma questi controlli non sono sufficienti per proteggere da tutto ciò che è male. La prima cosa che fa è convertire la posizione corrente del file *ppos
in un indirizzo fisico. Se ciò fallisce (in pratica, perché sei su una piattaforma con indirizzi fisici a 32 bit ma offset dei file a 64 bit e l'offset del file è maggiore di 2 ^ 32), la scrittura non riesce con EFBIG (file troppo grande). Il controllo successivo è se l'intervallo di indirizzi fisici da scrivere è valido su questa particolare architettura del processore e in caso di errore si verifica EFAULT (indirizzo errato).
Successivamente, su Sparc e m68k, qualsiasi parte della scrittura nella prima pagina fisica viene silenziosamente ignorata.
Ora abbiamo raggiunto il ciclo principale che scorre sui dati in blocchi che possono rientrare in una pagina MMU .
/dev/mem
accede alla memoria fisica, non alla memoria virtuale, ma le istruzioni del processore per caricare e archiviare i dati in memoria utilizzano indirizzi virtuali, quindi il codice deve disporre per mappare la memoria fisica su un indirizzo virtuale. Su Linux, a seconda dell'architettura del processore e della configurazione del kernel, questa mappatura esiste o permantentemente o deve essere fatta al volo; questo è il lavoro di xlate_dev_mem_ptr
(e unxlate_dev_mem_ptr
annulla qualunque cosa xlate_dev_mem_ptr
faccia). Quindi la funzione copy_from_user
legge dal buffer che è stato passato alwrite
chiamata di sistema e scrive solo all'indirizzo virtuale in cui è attualmente mappata la memoria fisica. Il codice emette le normali istruzioni dell'archivio di memoria e ciò significa che dipende dall'hardware.
Prima di discutere che una scrittura su un indirizzo fisico fa, discuterò un controllo che accade prima di questa scrittura. All'interno del ciclo, i page_is_allowed
blocchi funzione accedono a determinati indirizzi se l'opzione di configurazione del kernel CONFIG_STRICT_DEVMEM
è abilitata (che è il caso di default): solo gli indirizzi consentiti da devmem_is_allowed
possono essere raggiunti /dev/mem
, per altri la scrittura fallisce con EPERM (operazione non consentita). La descrizione di questa opzione indica:
Se questa opzione è attivata e IO_STRICT_DEVMEM = n, il file / dev / mem consente solo l'accesso dello spazio utente allo spazio PCI, al codice BIOS e alle aree dati. Questo è sufficiente per dosemu e X e tutti gli utenti comuni di / dev / mem.
Questa è una descrizione molto x86-centrica. In effetti, più genericamente, CONFIG_STRICT_DEVMEM
blocca l'accesso agli indirizzi di memoria fisica associati alla RAM, ma consente l'accesso agli indirizzi che non mappano alla RAM. I dettagli di quali intervalli di indirizzi fisici sono consentiti dipendono dall'architettura del processore, ma tutti escludono la RAM in cui sono memorizzati i dati del kernel e dei processi di terra dell'utente. L'opzione aggiuntiva CONFIG_IO_STRICT_DEVMEM
(disabilitata da Ubuntu 18.04) blocca gli accessi agli indirizzi fisici rivendicati da un driver.
Indirizzi di memoria fisica associati a RAM . Quindi ci sono indirizzi di memoria fisica che non si associano alla RAM? Sì. Questa è la discussione che ho promesso sopra su cosa significhi scrivere a un indirizzo.
Un'istruzione di memoria non scrive necessariamente su RAM. Il processore decompone l'indirizzo e decide a quale periferica spedire il negozio. (Quando dico "il processore", includo i controller delle periferiche che potrebbero non provenire dallo stesso produttore.) La RAM è solo una di quelle periferiche. Il modo in cui viene effettuato l'invio dipende molto dall'architettura del processore, ma i fondamenti sono più o meno gli stessi su tutte le architetture. Il processore sostanzialmente decompone i bit più alti dell'indirizzo e li cerca in alcune tabelle popolate in base a informazioni codificate, informazioni ottenute sondando alcuni bus e informazioni configurate dal software. Molta cache e buffering possono essere coinvolti, ma in poche parole, dopo questa decomposizione,bus e poi tocca alla periferia occuparsene. (O il risultato della ricerca della tabella potrebbe essere che non ci sono periferiche a questo indirizzo, nel qual caso il processore entra in uno stato trap dove esegue del codice nel kernel che normalmente risulta in un SIGBUS per il processo chiamante.)
Un archivio a un indirizzo mappato alla RAM non "fa" altro che sovrascrivere il valore precedentemente memorizzato a questo indirizzo, con la promessa che un successivo caricamento nello stesso indirizzo restituirà l'ultimo valore memorizzato. Ma anche la RAM ha alcuni indirizzi che non si comportano in questo modo: ha alcuni registri che possono controllare cose come la frequenza di aggiornamento e la tensione.
In generale, una lettura o scrittura su un registro hardware fa qualunque cosa l'hardware sia programmato per fare. La maggior parte degli accessi all'hardware funzionano in questo modo: il software (normalmente il codice del kernel) accede a un determinato indirizzo fisico, questo raggiunge il bus che collega il processore alla periferica e la periferica fa la sua cosa. Alcuni processori (in particolare x86) hanno anche istruzioni CPU separate che causano letture / scritture su periferiche che sono distinte dal caricamento e dall'archiviazione della memoria, ma anche su x86, molte periferiche sono raggiunte attraverso il caricamento / archiviazione.
Il comando dd if=/dev/urandom of=/dev/mem
scrive dati casuali su qualsiasi periferica mappata all'indirizzo 0 (e indirizzi successivi, a condizione che la scrittura abbia esito positivo). In pratica, mi aspetto che su molte architetture, l'indirizzo fisico 0 non abbia alcuna periferica mappata ad esso, o abbia RAM, e quindi il primo tentativo di scrittura fallisce. Ma se esiste una periferica mappata all'indirizzo 0, o se si modifica il comando per scrivere su un indirizzo diverso, si innescherà qualcosa di imprevedibile nella periferica. Con dati casuali a indirizzi crescenti, è improbabile che faccia qualcosa di interessante, ma in linea di principio potrebbe spegnere il computer (probabilmente c'è un indirizzo che lo fa in realtà), sovrascrivere alcune impostazioni del BIOS che rendono impossibile l'avvio, o addirittura colpire alcuni Buggy periferico in un modo che lo danneggia.
alias Russian_roulette='dd if=/dev/urandom of=/dev/mem seek=$((4096*RANDOM+4096*32768*RANDOM))'
CONFIG_STRICT_DEVMEM
è abilitato.
Per pagina manuale mem (4) :
/ dev / mem è un file di dispositivo di carattere che è un'immagine della memoria principale del computer. Può essere usato, ad esempio, per esaminare (e persino patchare) il sistema.
Quindi, in teoria, dd if=/dev/urandom of=/dev/mem
dovrebbe sovrascrivere l'intero spazio di indirizzamento della memoria fisica che hai installato, e poiché il kernel e altri programmi eseguiti dalla memoria, questo dovrebbe effettivamente arrestare il sistema. In pratica, c'è un limite. Dalla stessa pagina man:
A partire da Linux 2.6.26, e in base all'architettura, l'opzione di configurazione del kernel CONFIG_STRICT_DEVMEM limita le aree a cui è possibile accedere tramite questo file.
Provando questo sulla macchina virtuale Ubuntu 18.04, restituisce un errore dd: writing to '/dev/mem': Operation not permitted
anche con sudo
e nonostante le autorizzazioni per il root crw-r-----
. Da Ubuntu Wiki :
/ dev / mem protezione
Alcune applicazioni (Xorg) richiedono l'accesso diretto alla memoria fisica dallo spazio utente. Il file speciale / dev / mem esiste per fornire questo accesso. In passato, era possibile visualizzare e modificare la memoria del kernel da questo file se un utente malintenzionato avesse accesso come root. L'opzione del kernel CONFIG_STRICT_DEVMEM è stata introdotta per bloccare l'accesso alla memoria non del dispositivo (originariamente chiamato CONFIG_NONPROMISC_DEVMEM).
Quindi tecnicamente, no, non è sicuro (poiché si bloccherebbe il sistema) e se l'opzione del kernel CONFIG_STRICT_DEVMEM
è disabilitata è una falla di sicurezza, ma da quello che vedo finora il comando non verrebbe eseguito se tale opzione è abilitata. Secondo il duplicato tra siti , un riavvio risolverà eventuali problemi con esso, ma ovviamente i dati nella RAM in quel momento verrebbero persi e non scaricati su disco (se necessario).
Esiste un metodo suggerito sul duplicato precedentemente collegato, busybox devmem
quindi se sei determinato a scherzare con la RAM, potrebbe esserci un modo dopo tutto.
CONFIG_STRICT_DEVMEM
, è possibile accedere alle aree di memoria in cui è mappata una periferica, che è il punto fondamentale di avere /dev/mem
. Se scrivi cose casuali sulle periferiche, potrebbe succedere di tutto. Si ottiene "operazione non consentita" se si tenta di accedere a un indirizzo non mappato e il comando inizia all'indirizzo 0. Se l'indirizzo 0 è associato a qualcosa di brutto dipende dall'architettura hardware. Per quanto ne so, potrebbe non essere mai mappato a nulla su un PC, ma non è sicuro in generale.
head -c 1024 </dev/mem | od -tx1
), ma non so se questi vengano utilizzati quando il processore non è in modalità reale (modalità 8088). Non credo che possano essere usati in modalità 64 bit: dopo tutto, i vettori di interruzione 8088 hanno solo 32 bit per l'indirizzo. E comunque questo è accessibile con CONFIG_STRICT_DEVMEM
set, quindi immagino che Linux non lo usi.