web-dev-qa-db-fra.com

Lire une mémoire de processus en cours sans l’interrompre (/ proc/kcore est une option)

J'aimerais explorer la mémoire d'un processus vivant, et lorsque je le fais, le processus ne doit pas être dérangé - attacher donc gdb au processus (ce qui l'arrêterait) n'est pas une option . Par conséquent, j'aimerais obtenir cette information de/proc/kcore (si vous connaissez une autre façon de le faire, merci de me le faire savoir) . Alors j’ai fait une petite expérience. J'ai créé un fichier appelé TEST avec seulement "EXTRATESTEXTRA" à l'intérieur . Puis je l'ai ouvert avec moins

$ less TEST

J'ai eu le PID de ce processus avec

$ ps aux | grep TEST
user    7785  0.0  0.0  17944   992 pts/8    S+   16:15   0:00 less TEST
user    7798  0.0  0.0  13584   904 pts/9    S+   16:16   0:00 grep TEST

Et puis j'ai utilisé ce script pour créer un dump de tous les fichiers:

#!/bin/bash
grep rw-p /proc/$1/maps | sed -n 's/^\([0-9a-f]*\)-\([0-9a-f]*\) .*$/\1 \2/p' | while read start stop; do gdb --batch --pid $1 -ex "dump memory $1-$start-$stop.dump 0x$start 0x$stop"; done

(Je l'ai trouvé sur ce site https://serverfault.com/questions/173999/dump-a-linux-processs-memory-to-file )

$ Sudo ./dump_all_pid_memory.sh 7785

Après cela, j'ai cherché "TRATESTEX" dans tous les fichiers vidés:

$ grep -a -o -e '...TRATESTEX...' ./*.dump
./7785-00624000-00628000.dump:HEXTRATESTEXTRA
./7785-00b8f000-00bb0000.dump:EXTRATESTEXTRA
./7785-00b8f000-00bb0000.dump:EXTRATESTEXTRA

J'ai donc conclu qu'il devait exister une occurrence de cette chaîne entre 0x00624000 et 0x00628000 . J'ai donc converti les décalages en nombres décimaux et utilisé dd pour récupérer la mémoire de/proc/kcore:

$ Sudo dd if="/proc/kcore" of="./y.txt" skip="0" count="1638400" bs=1

À ma grande surprise, le fichier y.txt était plein de zéros (je n'y ai pas trouvé la chaîne que je cherchais).

En prime surprise, j'ai exécuté un test simmilar en même temps avec un fichier de test différent et j'ai constaté que l'autre chaîne de test que j'utilisais (Les deux processus avec moins fonctionnaient en même temps) devrait être trouvée en même temps. emplacement (le dumping et le craquement ont donné la même compensation) ... donc il doit y avoir quelque chose que je ne comprends pas clairement.

  • Le fichier/proc/pid/maps n’est-il pas censé montrer le décalage de la mémoire (par exemple, si vous dites que "XXX" est au décalage 0x10, un autre programme ne pourrait pas utiliser le même décalage, ai-je raison? source de ma seconde surprise)

  • Comment puis-je lire/proc/kmap pour récupérer la mémoire appartenant à un processus pid que je connais?

EDIT - pour les futurs buteurs (voir la réponse ci-dessous en premier)

Pour résumer les réponses et ajouter mon propre commentaire: -/proc/pid/maps montre les parties de la mémoire AU SEIN DU PROCESSUS (différentes pour chaque processus, recherchez un mappage de mémoire sur Linux), afin que différents processus puissent semblent utiliser la même partie de la mémoire (comme il semble de leur point de vue). Vous pouvez lire les parties spécifiées ici à partir de/proc/pid/mem en tant que super-utilisateur (ou un processus parent tel que gdb le fait avec ptrace) - la mémoire dans /proc/kcore n'est pas identique à la mémoire du point de vue du processus dans /proc/pid/mem - Donc, pour rechercher la mémoire du processus dans /proc/kcore, il faudrait comprendre comment la mémoire du processus est mappée dans la mémoire du noyau (beaucoup de choses en désordre et prenant beaucoup de temps) Donc, pour obtenir la mémoire du processus, commencez par lire les régions de /proc/pid/maps il est permis de lire/écrire de/vers puis dump-copier les régions de /proc/pid/mem. Le script ci-dessous vide toutes les régions accessibles en écriture (source: https://unix.stackexchange.com/questions/6301/how-do-i-read-from-proc-pid-mem-under-linux ). EDIT: le script de travail en python révisé est déplacé vers sa propre réponse, ce qui permet de le commenter séparément de la question.

24
hmhmhmmm

Pour le processus 1234, vous pouvez obtenir son mappage de mémoire en lisant séquentiellement /proc/1234/maps (un pseudo-fichier texte) et en lisant la mémoire virtuelle, par exemple. read (2) - ing ou mmap (2) - ing des segments appropriés du pseudo-fichier /proc/1234/mem sparse.

Cependant, je pense que vous ne pouvez pas éviter une sorte de synchronisation (peut-être avec ptrace (2) , comme le fait gdb), car le processus 1234 peut (et modifie) son espace adresse à tout moment (avec mmap & syscalls associés ).

La situation est différente si le processus contrôlé 1234 n’est pas arbitraire, mais si vous pouviez l’améliorer pour communiquer d’une manière ou d’une autre avec le processus de surveillance.

Je ne suis pas sûr de comprendre pourquoi demandez-vous cela. Et gdb est capable de watch un endroit sans arrêter le processus.

15

Si vous avez un accès root et que vous êtes sur un système Linux, vous pouvez utiliser le script Linux suivant (adapté de l'excellente réponse de Gilles de Gilles et la réponse donnée à l'origine dans la question ci-dessus, mais en incluant SyntaxErrors et Pythonique):

#!/usr/bin/env python

import re
import sys

def print_memory_of_pid(pid, only_writable=True):
    """ 
    Run as root, take an integer PID and return the contents of memory to STDOUT
    """
    memory_permissions = 'rw' if only_writable else 'r-'
    sys.stderr.write("PID = %d" % pid)
    with open("/proc/%d/maps" % pid, 'r') as maps_file:
        with open("/proc/%d/mem" % pid, 'r', 0) as mem_file:
            for line in maps_file.readlines():  # for each mapped region
                m = re.match(r'([0-9A-Fa-f]+)-([0-9A-Fa-f]+) ([-r][-w])', line)
                if m.group(3) == memory_permissions: 
                    sys.stderr.write("\nOK : \n" + line+"\n")
                    start = int(m.group(1), 16)
                    if start > 0xFFFFFFFFFFFF:
                        continue
                    end = int(m.group(2), 16)
                    sys.stderr.write( "start = " + str(start) + "\n")
                    mem_file.seek(start)  # seek to region start
                    chunk = mem_file.read(end - start)  # read region contents
                    print chunk,  # dump contents to standard output
                else:
                    sys.stderr.write("\nPASS : \n" + line+"\n")

if __== '__main__': # Execute this code when run from the commandline.
    try:
        assert len(sys.argv) == 2, "Provide exactly 1 PID (process ID)"
        pid = int(sys.argv[1])
        print_memory_of_pid(pid)
    except (AssertionError, ValueError) as e:
        print "Please provide 1 PID as a commandline argument."
        print "You entered: %s" % ' '.join(sys.argv)
        raise e

Si vous enregistrez ceci sous le nom write_mem.py, vous pouvez l'exécuter (avec python2.6 ou 2.7) ou plus tôt dans python2.5 (si vous ajoutez from __future__ import with_statement) en tant que:

Sudo python write_mem.py 1234 > pid1234_memory_dump

vider la mémoire pid1234 dans le fichier pid1234_memory_dump.

23
dr jimbob

Depuis la version 3.2 du noyau. Vous pouvez utiliser l'appel process_vm_readv system pour lire la mémoire du processus sans interruption.

ssize_t process_vm_readv(pid_t pid,
                                const struct iovec *local_iov,
                                unsigned long liovcnt,
                                const struct iovec *remote_iov,
                                unsigned long riovcnt,
                                unsigned long flags);

Ces appels système transfèrent des données entre l’espace adresse du fichier appelant ("le processus local") et le processus identifié par pid ("le processus distant"). Les données se déplacent directement entre les fichiers espaces d'adressage des deux processus, sans passer par le noyau espace.

2
Calmarius

Vous devrez utiliser/proc // mem pour lire la mémoire du processus. Je ne vous recommanderais pas d'essayer de lire/proc/kcore ou l'une des fonctions de mémoire du noyau (ce qui prend du temps).

0
user1621581