/proc/$pid/maps
/proc/$pid/mem
mostra il contenuto della memoria di $ pid mappato nello stesso modo del processo, cioè il byte all'offset x nello pseudo-file è uguale al byte all'indirizzo x nel processo. Se un indirizzo non è mappato nel processo, viene restituita la lettura dall'offset corrispondente nel file EIO
(errore di input / output). Ad esempio, poiché la prima pagina di un processo non viene mai mappata (in modo che il dereferenziamento di un NULL
puntatore non riesca in modo pulito anziché accedere involontariamente alla memoria effettiva), la lettura del primo byte /proc/$pid/mem
produce sempre un errore I / O.
Il modo per scoprire quali parti della memoria di processo sono mappate è leggere /proc/$pid/maps
. Questo file contiene una riga per area mappata, simile al seguente:
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 hexa). La colonna successiva contiene le autorizzazioni, quindi ci sono alcune informazioni sul file (offset, dispositivo, inode e nome) se si tratta di un mapping di file. Vedi la proc(5)
pagina man o Comprensione di Linux / proc / id / maps per maggiori informazioni.
Ecco uno script di prova di concetto 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", 'r', 0)
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
print chunk, # dump contents to standard output
maps_file.close()
mem_file.close()
/proc/$pid/mem
Se provi a leggere dallo mem
pseudo-file di un altro processo, non funziona: viene visualizzato un ESRCH
errore (Nessun processo di questo tipo).
Le autorizzazioni su /proc/$pid/mem
( r--------
) sono più liberali di quanto dovrebbe essere il caso. Ad esempio, non dovresti essere in grado di leggere la memoria di un processo setuid. 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 ancora, c'erano condizioni di razza che potevano rintracciare versioni precedenti del kernel Linux (secondo questo thread lkml , sebbene io non conosco i dettagli). Quindi sono necessari ulteriori controlli:
- Il processo da cui si vuole leggere
/proc/$pid/mem
deve collegarsi al processo usando ptrace
con il PTRACE_ATTACH
flag. 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 /proc/$pid/mem
, dovrebbe staccarsi chiamando ptrace
con la PTRACE_DETACH
bandiera.
- Il processo osservato non deve essere in esecuzione. Normalmente la chiamata
ptrace(PTRACE_ATTACH, …)
interrompe il processo di destinazione (invia un STOP
segnale), ma esiste una condizione di competizione (la consegna del segnale è asincrona), quindi il tracciante dovrebbe chiamare wait
(come documentato in ptrace(2)
).
Un processo in esecuzione come root può leggere la memoria di qualsiasi processo, senza la necessità di chiamare ptrace
, ma il processo osservato deve essere arrestato o la lettura tornerà comunque ESRCH
.
Nel sorgente del kernel Linux, il codice che fornisce le voci per processo in /proc
è attivo fs/proc/base.c
e la funzione da cui leggere /proc/$pid/mem
è mem_read
. Il controllo aggiuntivo viene eseguito da check_mem_permission
.
Ecco un codice C di esempio da allegare a un processo e leggere un blocco del suo mem
file (controllo degli 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 di prova di concetto per il dumping /proc/$pid/mem
su un altro thread .