Der Linux-proc(5)
Manpage sagt mir, dass /proc/$pid/mem
„kann verwendet werden, um auf die Seiten des Speichers eines Prozesses zuzugreifen“. Aber ein direkter Versuch, es zu verwenden, gibt mir nur
$ cat /proc/$$/mem /proc/self/mem
cat: /proc/3065/mem: No such process
cat: /proc/self/mem: Input/output error
Warum ist nicht cat
kann seinen eigenen Speicher drucken (/proc/self/mem
)? Und was ist dieser seltsame „No such process“-Fehler, wenn ich versuche, den Speicher der Shell zu drucken (/proc/$$/mem
, offensichtlich existiert der Prozess)? Wie kann ich aus /proc/$pid/mem
lesen , dann?
Akzeptierte Antwort:
/proc/$pid/maps
/proc/$pid/mem
zeigt den Inhalt des Speichers von $pid, der auf die gleiche Weise abgebildet wird wie im Prozess, d.h. das Byte am Offset x in der Pseudodatei ist dasselbe wie das Byte an der Adresse x dabei. Wenn eine Adresse dabei nicht abgebildet wird, liefert das Lesen aus dem entsprechenden Offset in der Datei EIO
zurück (Ein-/Ausgabefehler). Da zum Beispiel die erste Seite in einem Prozess nie abgebildet wird (so dass die Dereferenzierung eines NULL
Pointer schlägt sauber fehl, anstatt unbeabsichtigt auf den tatsächlichen Speicher zuzugreifen), indem das erste Byte von /proc/$pid/mem
gelesen wird ergibt immer einen E/A-Fehler.
Um herauszufinden, welche Teile des Prozessspeichers gemappt sind, lesen Sie /proc/$pid/maps
. Diese Datei enthält eine Zeile pro zugeordneter Region und sieht so aus:
08048000-08054000 r-xp 00000000 08:01 828061 /bin/cat
08c9b000-08cbc000 rw-p 00000000 00:00 0 [heap]
Die ersten beiden Zahlen sind die Grenzen der Region (Adressen des ersten Bytes und des überletzten Bytes in Hexa). Die nächste Spalte enthält die Berechtigungen, dann gibt es einige Informationen über die Datei (Offset, Gerät, Inode und Name), wenn es sich um eine Dateizuordnung handelt. Siehe proc(5)
Manpage oder Linux verstehen /proc/id/maps für weitere Informationen.
Hier ist ein Proof-of-Concept-Skript, das den Inhalt seines eigenen Speichers ausgibt.
#! /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
[Das Folgende dient dem historischen Interesse. Es gilt nicht für aktuelle Kernel.]
Seit Version 3.3 des Kernels können Sie auf /proc/$pid/mem
zugreifen Normalerweise, solange Sie darauf zugreifen, greifen Sie nur auf gemappte Offsets zu und Sie haben die Berechtigung, es zu verfolgen (die gleichen Berechtigungen wie ptrace
für Nur-Lese-Zugriff). Aber in älteren Kerneln gab es einige zusätzliche Komplikationen.
Wenn Sie versuchen, aus dem mem
zu lesen Pseudo-Datei eines anderen Prozesses, es funktioniert nicht:Sie erhalten einen ESRCH
(Kein solcher Prozess) Fehler.
Die Berechtigungen für /proc/$pid/mem
(r--------
) liberaler sind, als es der Fall sein sollte. Beispielsweise sollten Sie nicht in der Lage sein, den Speicher eines setuid-Prozesses zu lesen. Darüber hinaus könnte der Versuch, den Speicher eines Prozesses zu lesen, während der Prozess ihn ändert, dem Leser eine inkonsistente Ansicht des Speichers geben, und schlimmer noch, es gab Race-Conditions, die ältere Versionen des Linux-Kernels verfolgen könnten (laut diesem lkml-Thread, obwohl ich kenne die Details nicht). Daher sind zusätzliche Überprüfungen erforderlich:
- Der Prozess, der von
/proc/$pid/mem
lesen möchte muss mitptrace
an den Prozess angehängt werden mit demPTRACE_ATTACH
Flagge. Dies ist, was Debugger tun, wenn sie mit dem Debuggen eines Prozesses beginnen; es ist auch wasstrace
macht mit den Systemaufrufen eines Prozesses. Sobald der Reader das Lesen von/proc/$pid/mem
beendet hat , sollte es durch Aufrufen vonptrace
getrennt werden mit demPTRACE_DETACH
Flagge. - Der beobachtete Prozess darf nicht laufen. Normalerweise Aufruf von
ptrace(PTRACE_ATTACH, …)
stoppt den Zielprozess (es sendet einSTOP
Signal), aber es gibt eine Race-Condition (Signalübermittlung ist asynchron), also sollte der Tracerwait
aufrufen (wie inptrace(2)
dokumentiert ).
Ein als root ausgeführter Prozess kann den Speicher eines beliebigen Prozesses lesen, ohne ptrace
aufrufen zu müssen , aber der beobachtete Prozess muss gestoppt werden, oder der Lesevorgang gibt immer noch ESRCH
zurück .
In der Linux-Kernel-Quelle der Code, der Einträge pro Prozess in /proc
bereitstellt befindet sich in fs/proc/base.c
, und die Funktion zum Lesen von /proc/$pid/mem
ist mem_read
. Die zusätzliche Prüfung erfolgt durch check_mem_permission
.
Hier ist ein Beispiel-C-Code, den Sie an einen Prozess anhängen und einen Teil seines mem
lesen können Datei (Fehlerprüfung entfällt):
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);
Ich habe bereits ein Proof-of-Concept-Skript zum Sichern von /proc/$pid/mem
gepostet in einem anderen Thread.