/proc/$pid/maps
/proc/$pid/memmostra 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 NULLpuntatore non riesca in modo pulito anziché accedere involontariamente alla memoria effettiva), la lettura del primo byte /proc/$pid/memproduce 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 mempseudo-file di un altro processo, non funziona: viene visualizzato un ESRCHerrore (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/memdeve collegarsi al processo usando ptracecon il PTRACE_ATTACHflag. Questo è ciò che fanno i debugger quando iniziano a eseguire il debug di un processo; è anche ciò che stracefa alle chiamate di sistema di un processo. Una volta che il lettore ha finito di leggere /proc/$pid/mem, dovrebbe staccarsi chiamando ptracecon la PTRACE_DETACHbandiera.
- Il processo osservato non deve essere in esecuzione. Normalmente la chiamata
ptrace(PTRACE_ATTACH, …)interrompe il processo di destinazione (invia un STOPsegnale), 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.ce 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 memfile (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/memsu un altro thread .