/proc/$pid/maps
/proc/$pid/mem
muestra el contenido de la memoria de $ pid mapeado de la misma manera que en el proceso, es decir, el byte en el desplazamiento x en el pseudoarchivo es el mismo que el byte en la dirección x en el proceso. Si una dirección no está asignada en el proceso, la lectura del desplazamiento correspondiente en el archivo devuelve EIO
(error de entrada / salida). Por ejemplo, dado que la primera página de un proceso nunca se asigna (por lo que la eliminación de la referencia a un NULL
puntero falla de manera limpia en lugar de acceder involuntariamente a la memoria real), leer el primer byte /proc/$pid/mem
siempre produce un error de E / S.
La manera de averiguar qué partes de la memoria de proceso están mapeadas es leer /proc/$pid/maps
. Este archivo contiene una línea por región asignada, con este aspecto:
08048000-08054000 r-xp 00000000 08:01 828061 /bin/cat
08c9b000-08cbc000 rw-p 00000000 00:00 0 [heap]
Los dos primeros números son los límites de la región (direcciones del primer byte y el byte después del último, en hexa). La siguiente columna contiene los permisos, luego hay información sobre el archivo (desplazamiento, dispositivo, inodo y nombre) si se trata de una asignación de archivo. Consulte la proc(5)
página de manual o Understanding Linux / proc / id / maps para obtener más información.
Aquí hay un script de prueba de concepto que volca el contenido de su propia memoria.
#! /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
Si intentas leer el mem
pseudoarchivo de otro proceso, no funciona: obtienes un ESRCH
error (No hay tal proceso).
Los permisos en /proc/$pid/mem
( r--------
) son más liberales de lo que debería ser el caso. Por ejemplo, no debería poder leer la memoria de un proceso setuid. Además, tratar de leer la memoria de un proceso mientras el proceso lo modifica podría darle al lector una visión inconsistente de la memoria, y lo que es peor, había condiciones de carrera que podían rastrear versiones anteriores del kernel de Linux (de acuerdo con este hilo lkml , aunque yo No sé los detalles). Por lo tanto, se necesitan verificaciones adicionales:
- El proceso que desea leer
/proc/$pid/mem
debe adjuntarse al proceso usando ptrace
con la PTRACE_ATTACH
bandera. Esto es lo que hacen los depuradores cuando comienzan a depurar un proceso; también es lo que strace
hace a las llamadas al sistema de un proceso. Una vez que el lector haya terminado de leer /proc/$pid/mem
, debe separarse llamando ptrace
con la PTRACE_DETACH
bandera.
- El proceso observado no debe estar ejecutándose. Normalmente, la llamada
ptrace(PTRACE_ATTACH, …)
detendrá el proceso de destino (envía una STOP
señal), pero hay una condición de carrera (la entrega de la señal es asíncrona), por lo que el rastreador debe llamar wait
(como se documenta en ptrace(2)
).
Un proceso que se ejecuta como root puede leer la memoria de cualquier proceso, sin necesidad de llamar ptrace
, pero el proceso observado debe detenerse o la lectura aún regresará ESRCH
.
En la fuente del kernel de Linux, el código que proporciona entradas por proceso /proc
está en fs/proc/base.c
, y la función para leer /proc/$pid/mem
es mem_read
. La verificación adicional es realizada por check_mem_permission
.
Aquí hay un código C de muestra para adjuntar a un proceso y leer un fragmento de su mem
archivo (se omite la comprobación de errores):
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);
Ya publiqué un script de prueba de concepto para descargar /proc/$pid/mem
en otro hilo .