GNU/Linux >> Tutoriels Linux >  >> Linux

Comment lire depuis /proc/$pid/mem sous Linux ?

/proc/$pid/maps

/proc/$pid/mem affiche le contenu de la mémoire de $pid mappé de la même manière que dans le processus, c'est-à-dire l'octet à l'offset 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 à partir de l'offset correspondant dans le fichier renvoie 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 le déréférencement d'un NULL le pointeur échoue proprement plutôt que d'accéder involontairement à la mémoire réelle), en lisant le premier octet de /proc/$pid/mem produit toujours une erreur d'E/S.

La façon de savoir quelles parties de la mémoire du processus sont mappées est de lire /proc/$pid/maps . Ce fichier contient une ligne par région cartographié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 nombres sont 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 il y a des informations sur le fichier (décalage, périphérique, inode et nom) s'il s'agit d'un mappage de fichier. Voir le proc(5) page man ou Comprendre Linux /proc/id/maps pour plus d'informations.

Voici un script de preuve de concept qui vide 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", '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

[Ce qui suit est pour un intérêt historique. Cela ne s'applique pas aux noyaux actuels.]

Depuis la version 3.3 du noyau, vous pouvez accéder à /proc/$pid/mem normalement tant que vous n'y accédez qu'aux décalages cartographiés et que vous avez la permission de le tracer (mêmes permissions que ptrace pour un accès en lecture seule). Mais dans les noyaux plus anciens, il y avait quelques complications supplémentaires.

Si vous essayez de lire à partir du mem pseudo-fichier d'un autre processus, ça ne marche pas :vous obtenez un ESRCH (Aucun processus de ce type) erreur.

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 le processus le modifie pourrait donner au lecteur une vue incohérente de la mémoire, et pire, il y avait des conditions de concurrence qui pourraient retracer les anciennes versions du noyau Linux (selon ce fil lkml, bien que je ne connais pas les détails). Des vérifications supplémentaires sont donc nécessaires :

  • Le processus qui veut lire à partir de /proc/$pid/mem doit être joint 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 que strace fait aux appels système d'un processus. Une fois que le lecteur a fini de lire depuis /proc/$pid/mem , il doit se détacher en appelant ptrace avec le PTRACE_DETACH drapeau.
  • Le processus observé ne doit pas être en cours d'exécution. Appelant normalement le ptrace(PTRACE_ATTACH, …) arrêtera le processus cible (il envoie un STOP signal), mais il y a une condition de concurrence (la livraison du signal est asynchrone), donc le traceur doit appeler wait (comme documenté dans ptrace(2) ).

Un processus exécuté en tant que root peut lire la mémoire de n'importe quel processus, sans avoir besoin d'appeler ptrace , mais le processus observé doit être arrêté, sinon la lecture renverra toujours ESRCH .

Dans la source du noyau Linux, le code fournissant des entrées par processus dans /proc est en fs/proc/base.c , et la fonction à lire depuis /proc/$pid/mem est mem_read . Le contrôle supplémentaire est effectué par check_mem_permission .

Voici un exemple de code C à attacher à un processus et à lire un morceau de mem fichier (erreur de vérification 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 preuve de concept pour le dumping /proc/$pid/mem sur un autre fil.


Cette commande (de gdb) vide la mémoire de manière fiable :

gcore pid

Les vidages peuvent être volumineux, utilisez -o outfile si votre répertoire actuel n'a pas assez de place.


Lorsque vous exécutez cat /proc/$$/mem la variable $$ est évalué par bash qui insère son propre pid. Il exécute ensuite cat qui a un pid différent. Vous vous retrouvez avec cat essayant de lire la mémoire de bash , son processus parent. Étant donné que les processus non privilégiés ne peuvent lire que leur propre espace mémoire, cela est refusé par le noyau.

Voici un exemple :

$ echo $$
17823

Notez que $$ évalue à 17823. Voyons de quel processus il s'agit.

$ ps -ef | awk '{if ($2 == "17823") print}'
bahamat  17823 17822  0 13:51 pts/0    00:00:00 -bash

C'est mon shell actuel.

$ cat /proc/$$/mem
cat: /proc/17823/mem: No such process

Ici encore $$ évalue à 17823, qui est mon shell. cat ne peut pas lire l'espace mémoire de mon shell.


Linux
  1. Linux – En quoi le lien symbolique /proc//exe diffère-t-il des liens symboliques ordinaires ?

  2. Linux - Comment obtenir l'adresse IPv4 d'une interface à partir de /proc ?

  3. Linux – Lier /proc/mnt à /proc/mounts ?

  4. Linux - Comment trouver l'espace de noms d'un processus particulier ?

  5. Linux - Comment tester si un périphérique bloc est en lecture seule depuis /sys ou /proc ?

Comment tuer un processus sous Linux

Comment Linux gère-t-il plusieurs séparateurs de chemins consécutifs (/home////nom d'utilisateur///fichier) ?

Linux – Comment lire depuis /proc/$pid/mem sous Linux ?

Linux - Obtenir des informations sur l'utilisation de la mémoire d'un processus à partir de /proc/pid/smaps ?

Un guide pour le système de fichiers ‘/proc’ sous Linux

Fichiers /proc/cpuinfo et /proc/meminfo sous Linux