GNU/Linux >> Tutoriales Linux >  >> Linux

Linux:¿cómo leer desde /proc/$pid/mem en Linux?

El proc(5) de Linux la página de manual me dice que /proc/$pid/mem “se puede utilizar para acceder a las páginas de la memoria de un proceso”. Pero un intento directo de usarlo solo me da

$ cat /proc/$$/mem /proc/self/mem
cat: /proc/3065/mem: No such process
cat: /proc/self/mem: Input/output error

¿Por qué no es cat capaz de imprimir su propia memoria (/proc/self/mem )? ¿Y qué es este extraño error de "no existe tal proceso" cuando intento imprimir la memoria del shell (/proc/$$/mem , obviamente el proceso existe)? ¿Cómo puedo leer desde /proc/$pid/mem , entonces?

Respuesta aceptada:

/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 pseudo-archivo 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 eliminar la referencia a un NULL el puntero falla limpiamente en lugar de acceder involuntariamente a la memoria real), leyendo el primer byte de /proc/$pid/mem siempre produce un error de E/S.

La forma de averiguar qué partes de la memoria del proceso están asignadas es leer /proc/$pid/maps . Este archivo contiene una línea por región asignada y se ve así:

08048000-08054000 r-xp 00000000 08:01 828061     /bin/cat
08c9b000-08cbc000 rw-p 00000000 00:00 0          [heap]

Los primeros dos números son los límites de la región (direcciones del primer byte y del último byte, en hexa). La siguiente columna contiene los permisos, luego hay información sobre el archivo (compensación, dispositivo, inodo y nombre) si se trata de una asignación de archivos. Ver el proc(5) man page o Comprender Linux /proc/id/maps para obtener más información.

Aquí hay una secuencia de comandos de prueba de concepto que vuelca 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", '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

[Lo siguiente es de interés histórico. No se aplica a los núcleos actuales.]

Desde la versión 3.3 del kernel, puede acceder a /proc/$pid/mem normalmente, siempre que acceda solo acceda a él en las compensaciones asignadas y tenga permiso para rastrearlo (los mismos permisos que ptrace para acceso de solo lectura). Pero en kernels más antiguos, hubo algunas complicaciones adicionales.

Si intenta leer desde el mem pseudo-archivo de otro proceso, no funciona:obtienes un ESRCH (No hay tal proceso) error.

Los permisos en /proc/$pid/mem (r-------- ) son más liberales de lo que deberían ser. 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 la está modificando podría darle al lector una vista inconsistente de la memoria y, lo que es peor, hubo condiciones de carrera que podrían rastrear versiones anteriores del kernel de Linux (según este hilo lkml, aunque yo desconozco los detalles). Por lo tanto, se necesitan comprobaciones adicionales:

  • El proceso que quiere leer desde /proc/$pid/mem debe adjuntarse al proceso usando ptrace con el 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 desde /proc/$pid/mem , debe desconectarse llamando a ptrace con el PTRACE_DETACH bandera.
  • El proceso observado no debe estar ejecutándose. Normalmente llamando a ptrace(PTRACE_ATTACH, …) detendrá el proceso de destino (envía un 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 a wait (como se documenta en ptrace(2) ).
Relacionado:¿Mantener NumLock siempre activado?

Un proceso que se ejecuta como root puede leer la memoria de cualquier proceso, sin necesidad de llamar a ptrace , pero el proceso observado debe detenerse, o la lectura aún devolverá ESRCH .

En el código fuente del kernel de Linux, el código que proporciona entradas por proceso en /proc está en fs/proc/base.c y la función para leer desde /proc/$pid/mem es mem_read . La verificación adicional la realiza check_mem_permission .

Aquí hay un ejemplo de código C para adjuntar a un proceso y leer un fragmento de mem archivo (comprobación de errores omitida):

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é una secuencia de comandos de prueba de concepto para descargar /proc/$pid/mem en otro hilo.


Linux
  1. Linux:¿cómo obtener la dirección IPv4 para una interfaz desde /proc?

  2. Linux:¿Obtener información sobre el uso de memoria de un proceso de /proc/pid/smaps?

  3. Linux – ¿Vincular /proc/mnt a /proc/mounts?

  4. /proc/[pid]/pagemaps y /proc/[pid]/maps | linux

  5. ¿Cómo obtener la cantidad de CPU/núcleos en Linux desde la línea de comandos?

Cómo matar un proceso en Linux

¿Cómo leer un mensaje a la vez desde /var/mail?

Una guía para el sistema de archivos '/proc' en Linux

Archivos /proc/cpuinfo y /proc/meminfo en Linux

¿Cómo obtengo la ruta de un proceso en Unix/Linux?

¿Cómo calcular el uso de CPU de un proceso por PID en Linux desde C?