/proc/$pid/maps
/proc/$pid/mem
zeigt den Inhalt des Speichers von $ pid, der auf die gleiche Weise wie im Prozess abgebildet wurde, dh das Byte am Offset x in der Pseudodatei ist das gleiche wie das Byte an der Adresse x im Prozess. Wenn eine Adresse dabei nicht zugeordnet wird, wird das Lesen des entsprechenden Offsets in der Datei zurückgegeben EIO
(Eingabe- / Ausgabefehler). Da beispielsweise die erste Seite in einem Prozess niemals zugeordnet wird (so dass die Dereferenzierung eines NULL
Zeigers /proc/$pid/mem
fehlerfrei fehlschlägt, anstatt unbeabsichtigt auf den tatsächlichen Speicher zuzugreifen), führt das Lesen des ersten Bytes von immer zu einem E / A-Fehler.
Der Weg, um herauszufinden, welche Teile des Prozessspeichers abgebildet sind, ist zu lesen /proc/$pid/maps
. Diese Datei enthält eine Zeile pro zugeordnetem Bereich und sieht folgendermaßen 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 Bytes nach dem letzten in Hexa). Die nächste Spalte enthält die Berechtigungen. Wenn es sich um eine Dateizuordnung handelt, werden einige Informationen zur Datei (Offset, Gerät, Inode und Name) angezeigt. Weitere Informationen finden Sie in der proc(5)
Manpage oder unter Linux / proc / id / maps .
Hier ist ein Proof-of-Concept-Skript, das den Inhalt seines eigenen Speichers speichert.
#! /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
Wenn Sie versuchen, aus der mem
Pseudodatei eines anderen Prozesses zu lesen , funktioniert dies nicht: ESRCH
Es wird ein Fehler (Kein solcher Prozess) angezeigt.
Die Berechtigungen für /proc/$pid/mem
( r--------
) sind liberaler als dies der Fall sein sollte. Beispielsweise sollte es nicht möglich sein, den Speicher eines Setuid-Prozesses zu lesen. Der Versuch, den Arbeitsspeicher eines Prozesses zu lesen, während dieser geändert wird , kann dem Leser eine inkonsistente Sicht auf den Arbeitsspeicher geben. Schlimmer noch, es gab Race-Bedingungen, die ältere Versionen des Linux-Kernels nachvollziehen können (laut diesem lkml-Thread , obwohl ich Ich kenne die Details nicht. Daher sind zusätzliche Überprüfungen erforderlich:
- Der Prozess, aus dem gelesen werden soll,
/proc/$pid/mem
muss ptrace
mit dem PTRACE_ATTACH
Flag an den Prozess angehängt werden. Dies tun Debugger, wenn sie mit dem Debuggen eines Prozesses beginnen. Es ist auch das, was strace
mit den Systemaufrufen eines Prozesses geschieht. Sobald der Leser das Lesen beendet hat /proc/$pid/mem
, sollte er sich durch Aufrufen ptrace
mit der PTRACE_DETACH
Flagge lösen .
- Der beobachtete Prozess darf nicht laufen. Normalerweise
ptrace(PTRACE_ATTACH, …)
stoppt der Aufruf den Zielprozess (es wird ein STOP
Signal gesendet), aber es liegt eine Race-Bedingung vor (die Signalübermittlung ist asynchron), sodass der Tracer einen Aufruf durchführen sollte wait
(wie in dokumentiert ptrace(2)
).
Ein Prozess, der als root ausgeführt wird, kann den Speicher eines beliebigen Prozesses lesen, ohne dass ein Aufruf erforderlich ptrace
ist. Der beobachtete Prozess muss jedoch gestoppt werden, oder der Lesevorgang wird weiterhin ausgeführt ESRCH
.
In den Linux - Kernel - Quellen und bietet der Code pro Prozess Einträge in /proc
in ist 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, der an einen Prozess angehängt und einen Teil der mem
Datei gelesen werden kann (Fehlerprüfung weggelassen):
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 Speichern /proc/$pid/mem
in einem anderen Thread veröffentlicht .