/proc/$pid/maps
/proc/$pid/mem
affiche le contenu de la mémoire de $ pid mappée de la même manière que dans le processus, c'est-à-dire que l'octet au décalage x dans le pseudo-fichier est le même que l'octet à l'adresse x dans le processus. Si une adresse n'est pas mappée dans le processus, la lecture de l'offset correspondant dans le fichier est renvoyée EIO
(erreur d'entrée / sortie). Par exemple, étant donné que la première page d'un processus n'est jamais mappée (de sorte que la déréférencement d'un NULL
pointeur échoue proprement plutôt que d'accéder involontairement à la mémoire réelle), la lecture du premier octet de /proc/$pid/mem
toujours génère une erreur d'entrée / sortie.
La manière de savoir quelles parties de la mémoire de processus sont mappées est de lire /proc/$pid/maps
. Ce fichier contient une ligne par région mappée, ressemblant à ceci:
08048000-08054000 r-xp 00000000 08:01 828061 /bin/cat
08c9b000-08cbc000 rw-p 00000000 00:00 0 [heap]
Les deux premiers chiffres représentent les limites de la région (adresses du premier octet et de l'octet après le dernier, en hexa). La colonne suivante contient les autorisations, puis contient des informations sur le fichier (offset, périphérique, inode et nom) s'il s'agit d'un mappage de fichier. Voir la proc(5)
page de manuel ou Comprendre Linux / proc / id / maps pour plus d'informations.
Voici un script de validation de principe qui extrait le contenu de sa propre mémoire.
#! /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
Si vous essayez de lire le mem
pseudo-fichier d'un autre processus, cela ne fonctionne pas: vous obtenez une ESRCH
erreur (Aucun processus de ce type).
Les autorisations sur /proc/$pid/mem
( r--------
) sont plus libérales que ce qui devrait être le cas. Par exemple, vous ne devriez pas pouvoir lire la mémoire d'un processus setuid. De plus, essayer de lire la mémoire d’un processus pendant que celui-ci est en train de le modifier pourrait donner au lecteur une vue incohérente de la mémoire, et pire encore, il existait des conditions de concurrence qui pouvaient retracer les anciennes versions du noyau Linux (selon ce fil de discussion lkml , bien que ne connais pas les détails). Des vérifications supplémentaires sont donc nécessaires:
- Le processus qui veut lire
/proc/$pid/mem
doit être attaché au processus en utilisant ptrace
avec le PTRACE_ATTACH
drapeau. C'est ce que font les débogueurs lorsqu'ils commencent à déboguer un processus. c'est aussi ce qui strace
fait aux appels système d'un processus. Une fois que le lecteur a fini de lire /proc/$pid/mem
, il devrait se détacher en appelant ptrace
avec le PTRACE_DETACH
drapeau.
- Le processus observé ne doit pas être en cours d'exécution. Normalement, l'appel
ptrace(PTRACE_ATTACH, …)
arrêtera le processus cible (il enverra un STOP
signal), mais il y a une condition de concurrence critique (la livraison du signal est asynchrone). Le traceur doit donc appeler wait
(comme indiqué dans ptrace(2)
).
Un processus exécuté en tant que root peut lire la mémoire de n'importe quel processus, sans avoir à appeler ptrace
, mais le processus observé doit être arrêté ou la lecture sera toujours renvoyée ESRCH
.
Dans la source du noyau Linux, le code fournit des entrées par processus en /proc
est dans fs/proc/base.c
, et la fonction de lire /proc/$pid/mem
est mem_read
. La vérification supplémentaire est effectuée par check_mem_permission
.
Voici un exemple de code C à attacher à un processus et à lire un fragment de mem
fichier (vérification d'erreur omise):
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);
J'ai déjà posté un script de validation de principe pour le dumping /proc/$pid/mem
sur un autre thread .