GNU/Linux >> Linux Esercitazione >  >> Linux

Linux:come leggere da /proc/$pid/mem sotto Linux?

Il proc(5) di Linux la pagina man mi dice che /proc/$pid/mem “può essere utilizzato per accedere alle pagine della memoria di un processo”. Ma un semplice tentativo di usarlo mi dà solo

$ cat /proc/$$/mem /proc/self/mem
cat: /proc/3065/mem: No such process
cat: /proc/self/mem: Input/output error

Perché cat non è in grado di stampare la propria memoria (/proc/self/mem )? E qual è questo strano errore "nessun processo del genere" quando provo a stampare la memoria della shell (/proc/$$/mem , ovviamente il processo esiste)? Come posso leggere da /proc/$pid/mem , allora?

Risposta accettata:

/proc/$pid/maps

/proc/$pid/mem mostra il contenuto della memoria di $pid mappato allo stesso modo del processo, ovvero il byte all'offset x nello pseudo-file è lo stesso del byte all'indirizzo x nel processo. Se un indirizzo non è mappato nel processo, la lettura dall'offset corrispondente nel file restituisce EIO (Errore ingresso/uscita). Ad esempio, poiché la prima pagina in un processo non viene mai mappata (quindi dereferenziando un NULL il puntatore non riesce ad accedere in modo involontario alla memoria effettiva), leggendo il primo byte di /proc/$pid/mem genera sempre un errore di I/O.

Il modo per scoprire quali parti della memoria di processo sono mappate è leggere /proc/$pid/maps . Questo file contiene una riga per regione mappata, simile a questa:

08048000-08054000 r-xp 00000000 08:01 828061     /bin/cat
08c9b000-08cbc000 rw-p 00000000 00:00 0          [heap]

I primi due numeri sono i confini della regione (indirizzi del primo byte e del byte dopo l'ultimo, in esadecimale). La colonna successiva contiene le autorizzazioni, quindi ci sono alcune informazioni sul file (offset, dispositivo, inode e nome) se si tratta di una mappatura del file. Vedi il proc(5) man page o Capire Linux /proc/id/maps per maggiori informazioni.

Ecco uno script proof-of-concept che scarica il contenuto della propria memoria.

#! /usr/bin/env python
import re
maps_file = open("/proc/self/maps", 'r')
mem_file = open("/proc/self/mem", 'rb', 0)
output_file = open("self.dump", 'wb')
for line in maps_file.readlines():  # for each mapped region
    m = re.match(r'([0-9A-Fa-f]+)-([0-9A-Fa-f]+) ([-r])', line)
    if m.group(3) == 'r':  # if this is a readable region
        start = int(m.group(1), 16)
        end = int(m.group(2), 16)
        mem_file.seek(start)  # seek to region start
        chunk = mem_file.read(end - start)  # read region contents
        output_file.write(chunk)  # dump contents to standard output
maps_file.close()
mem_file.close()
output_file.close()

/proc/$pid/mem

[Quello che segue è di interesse storico. Non si applica ai kernel attuali.]

Dalla versione 3.3 del kernel, puoi accedere a /proc/$pid/mem normalmente fintanto che accedi, accedi solo agli offset mappati e hai il permesso di tracciarlo (stesse autorizzazioni di ptrace per l'accesso in sola lettura). Ma nei kernel più vecchi c'erano alcune complicazioni aggiuntive.

Se provi a leggere da mem pseudo-file di un altro processo, non funziona:ottieni un ESRCH Errore (nessun processo del genere).

I permessi su /proc/$pid/mem (r-------- ) sono più liberali di quanto dovrebbe essere. Ad esempio, non dovresti essere in grado di leggere la memoria di un processo setuidico. Inoltre, provare a leggere la memoria di un processo mentre il processo lo sta modificando potrebbe dare al lettore una visione incoerente della memoria e, peggio, c'erano condizioni di gara che potevano tracciare versioni precedenti del kernel Linux (secondo questo thread lkml, anche se ho non conosco i dettagli). Quindi sono necessari ulteriori controlli:

  • Il processo che vuole leggere da /proc/$pid/mem deve allegare al processo utilizzando ptrace con il PTRACE_ATTACH bandiera. Questo è ciò che fanno i debugger quando iniziano a eseguire il debug di un processo; è anche ciò che strace fa alle chiamate di sistema di un processo. Una volta che il lettore ha finito di leggere da /proc/$pid/mem , dovrebbe staccarsi chiamando ptrace con il PTRACE_DETACH bandiera.
  • Il processo osservato non deve essere in esecuzione. Normalmente chiamando ptrace(PTRACE_ATTACH, …) interromperà il processo di destinazione (invia un STOP signal), ma c'è una race condition (l'erogazione del segnale è asincrona), quindi il tracciante dovrebbe chiamare wait (come documentato in ptrace(2) ).
Relazionato:Mantieni NumLock sempre attivo?

Un processo in esecuzione come root può leggere la memoria di qualsiasi processo, senza dover chiamare ptrace , ma il processo osservato deve essere interrotto, altrimenti la lettura restituirà ancora ESRCH .

Nel sorgente del kernel Linux, il codice che fornisce le voci per processo in /proc è in fs/proc/base.c e la funzione per leggere da /proc/$pid/mem è mem_read . Il controllo aggiuntivo viene eseguito da check_mem_permission .

Ecco un esempio di codice C da allegare a un processo e leggere un pezzo di mem file (controllo errori omesso):

sprintf(mem_file_name, "/proc/%d/mem", pid);
mem_fd = open(mem_file_name, O_RDONLY);
ptrace(PTRACE_ATTACH, pid, NULL, NULL);
waitpid(pid, NULL, 0);
lseek(mem_fd, offset, SEEK_SET);
read(mem_fd, buf, _SC_PAGE_SIZE);
ptrace(PTRACE_DETACH, pid, NULL, NULL);

Ho già pubblicato uno script proof-of-concept per il dump di /proc/$pid/mem su un altro thread.


Linux
  1. Linux:come scoprire lo spazio dei nomi di un particolare processo?

  2. Linux:come verificare se un dispositivo a blocchi è di sola lettura da /sys o /proc?

  3. Come leggere un messaggio alla volta da /var/mail?

  4. Come ottengo il percorso di un processo in Unix/Linux

  5. Come calcolare l'utilizzo della CPU di un processo tramite PID in Linux da C?

Come uccidere un processo in Linux

Una guida al file system '/proc' in Linux

/proc/cpuinfo e /proc/meminfo in Linux

Come scoprire da quale cartella è in esecuzione un processo?

Come posso leggere da /proc/$pid/mem sotto Linux?

Come impostare lo swapiness per processo per Linux?