/proc/$pid/maps
/proc/$pid/memzeigt 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 NULLZeigers /proc/$pid/memfehlerfrei 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 memPseudodatei eines anderen Prozesses zu lesen , funktioniert dies nicht: ESRCHEs 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/memmuss ptracemit dem PTRACE_ATTACHFlag an den Prozess angehängt werden. Dies tun Debugger, wenn sie mit dem Debuggen eines Prozesses beginnen. Es ist auch das, was stracemit den Systemaufrufen eines Prozesses geschieht. Sobald der Leser das Lesen beendet hat /proc/$pid/mem, sollte er sich durch Aufrufen ptracemit der PTRACE_DETACHFlagge lösen .
- Der beobachtete Prozess darf nicht laufen. Normalerweise
ptrace(PTRACE_ATTACH, …)stoppt der Aufruf den Zielprozess (es wird ein STOPSignal 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 ptraceist. 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 /procin ist fs/proc/base.c, und die Funktion zum Lesen von /proc/$pid/memist 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 memDatei 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/memin einem anderen Thread veröffentlicht .