En Twitter me topé con este interesante truco que permite recuperar, mediante el filesystem /proc, un archivo binario ejecutable que ha sido borrado del sistema de archivos, pero su proceso aún se encuentra en memoria.

El truco consiste en aprovechar el archivo "exe" dentro del directorio del proceso en el /proc, el cual es un link simbólico que apunta al binario ejecutable. Ya lo sé, si es un link simbólico y borramos el archivo fuente, supuestamente se rompe. Veamos...

Antes de comenzar vamos a crear un directorio de trabajo temporal:

[emi@hal9000 ~]$ mkdir /tmp/test

Luego, cambiar al nuevo directorio:

[emi@hal9000 ~]$ cd /tmp/test

A modo de prueba vamos a escribir un programa en lenguaje C:

[emi@hal9000 test]$ nano test.c

Este es el código fuente del programa:

[emi@hal9000 test]$ cat test.c 
#include <unistd.h>
#include <stdio.h>

int main() {
  while (1) {
    // Imprimir "1"
    printf("1");
    fflush(stdout);
    // Dormir 3 segundos
    sleep(3);
  }
}

El programa imprime indefinidamente un "1" por pantalla, cada 3 segundos.

Ahora es necesario compilar el programa para generar el ejecutable utilizando el compilador GCC (GNU C Compiler):

[emi@hal9000 test]$ gcc -o test test.c

Luego de compilar tenemos el fuente test.c y el ejecutable test:

[emi@hal9000 test]$ ll
total 16
-rwxrwxr-x 1 emi emi 8660 Oct  1 09:17 test
-rw-rw-r-- 1 emi emi  169 Oct  1 09:17 test.c

Si vemos el tipo de archivo del ejecutable, podemos comprobar que se trata de un binario ELF de 64 bit.

[emi@hal9000 test]$ file test
test: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=0x974b1e5bae021deae2370bb9ba149ecb3f3fe699, not stripped

Probemos que funcione:

[emi@hal9000 test]$ ./test 
111^C

Luego de detenerlo utilizando la combinación de teclas Ctrl+C, lo iniciamos nuevamente, pero esta vez en segundo plano y enviando su salida al dispositivo /dev/null (a la nada misma):

[emi@hal9000 test]$ ./test > /dev/null &
[1] 4537

Se ha iniciado el proceso con PID 4537:

[emi@hal9000 test]$ ps
  PID TTY          TIME CMD
 2089 pts/0    00:00:00 bash
 4537 pts/0    00:00:00 test
 4538 pts/0    00:00:00 ps

Ok, recordemos que hasta el momento tenemos el archivo de código fuente escrito en C, y el ejecutable test, el cual ocupa 8660 bytes:

[emi@hal9000 test]$ ll
total 16
-rwxrwxr-x 1 emi emi 8660 Oct  1 09:17 test
-rw-rw-r-- 1 emi emi  169 Oct  1 09:17 test.c

Mientras el proceso sigue en ejecución en segundo plano, vamos a borrar el archivo binario ejecutable:

[emi@hal9000 test]$ rm test
rm: remove regular file ‘test’? y

El archivo desapareció del filesystem:

[emi@hal9000 test]$ ll
total 4
-rw-rw-r-- 1 emi emi 169 Oct  1 09:17 test.c

Pero, por supuesto, el proceso sigue en ejecución con PID 4537:

[emi@hal9000 test]$ ps
  PID TTY          TIME CMD
 2089 pts/0    00:00:00 bash
 4537 pts/0    00:00:00 test
 4544 pts/0    00:00:00 ps

Veamos qué información del proceso 4537 hay en las estructuras de datos del kernel Linux:

[emi@hal9000 test]$ ls /proc/4537
attr             cpuset   limits      ns             root       syscall
autogroup        cwd      loginuid    numa_maps      sched      task
auxv             environ  maps        oom_adj        sessionid  uid_map
cgroup           exe      mem         oom_score      smaps      wchan
clear_refs       fd       mountinfo   oom_score_adj  stack
cmdline          fdinfo   mounts      pagemap        stat
comm             gid_map  mountstats  personality    statm
coredump_filter  io       net         projid_map     status

Entre toda la información disponible, se encuentra un archivo llamado exe (man proc) que apunta al archivo binario ejecutable. O apuntaba, porque lo acabamos de borrar:

[emi@hal9000 test]$ file /proc/4537/exe 
/proc/4537/exe: broken symbolic link to `/tmp/test/test (deleted)'

Según la herramienta file, se trata de un enlace simbólico al archivo binario ejecutable que hemos borrado anteriormente. Pero, ¿qué pasa si tratamos de acceder de todas formas a tal enlace simbólico?

Recordemos que en Linux (como en todo sistema *NIX) los enlaces duros (hard links) contienen una copia del i-nodo a nivel filesystem. Entonces si creamos un link duro y luego borramos el archivo original (por ende borrando el i-nodo original), es posible seguir accediendo a los datos desde el link duro (copia del i-nodo), ya que el mismo contiene la información completa respecto a la ubicación de los bloques de dato en disco. Lógicamente la operación de borrado tradicional sólo borra la entrada en el directorio y el i-nodo, y marca los bloques de dato como disponibles (si ningún otro i-nodo los está utilizando). Es decir, no se hace un borrado a nivel físico de los bloques de dato.

En cambio, los enlaces simbólicos no contienen una copia del i-nodo, sino que indican cual es la ruta al archivo original en el sistema de archivos. Sólo se trata de un entrada de directorio, sin i-nodo, con una ruta a otro archivo. La única información que contiene un link simbólico es un nombre de archivo. Por lo tanto, si se borra (o apenas renombra) el archivo original, el enlace simbólico se rompe. Queda apuntando a un archivo inexistente.

Volvamos al cauce del artículo. Según el filesystem /proc, el archivo /proc/[PID]/exe es un link simbólico que apunta al ejecutable, por ende sólo debería contener la ruta al archivo binario original. Acá es donde se produce la magia, ¿qué sucede si hacemos esto?:

[emi@hal9000 test]$ cat /proc/4537/exe > test.recuperado

Hicimos un volcado desde el archivo /proc/4537/exe (supuestamente un link simbólico a un archivo que no existe) hacia el nuevo archivo test.recuperado. Veamos qué pasó:

[emi@hal9000 test]$ ll
total 16
-rw-rw-r-- 1 emi emi  169 Oct  1 09:17 test.c
-rw-rw-r-- 1 emi emi 8660 Oct  1 09:19 test.recuperado

Se creó el archivo test.recuperado de 8660 bytes (el mismo tamaño del archivo binario que habíamos borrado).

¿Qué dice file?

[emi@hal9000 test]$ file test.recuperado 
test.recuperado: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=0x974b1e5bae021deae2370bb9ba149ecb3f3fe699, not stripped

Que se trata exactamente del mismo archivo binario ejecutable que habíamos borrado del filesystem (O_o).

Vamos a darle permiso de ejecución:

[emi@hal9000 test]$ chmod +x test.recuperado

Y si no me creen lo ejecutamos:

[emi@hal9000 test]$ ./test.recuperado 
111^C

Ok, acá alguien miente. Si es un link simbólico a un archivo inexistente, ¿cómo puede haber funcionado el comando cat? Y si no es un link simbólico, ¿file y ls mienten?

Hay que tener en cuenta que proc es un pseudo-filesystem que funciona como interfaz a las estructuras de datos del kernel. Entonces puede estar engañándonos, y en vez de guardar sólo un enlace simbólico, en realidad guarde tanto un enlace simbólico como una copia del binario en espacio de memoria del kernel. Esto es una suposición mía, porque en realidad no sé con exactitud qué pasa, y la página del manual (man proc) no lo aclara.

Les dejo un ejemplo más de lo que pasa en un filesystem ext4, como para generar más confusión:

[emi@hal9000 test]$ rm -fr *
[emi@hal9000 test]$ ll
total 0
[emi@hal9000 test]$ echo "soy pepe" > pepe.txt
[emi@hal9000 test]$ cat pepe.txt 
soy pepe
[emi@hal9000 test]$ ln pepe.txt pepe2.txt
[emi@hal9000 test]$ ll
total 8
-rw-rw-r-- 2 emi emi 9 Oct  1 10:47 pepe2.txt
-rw-rw-r-- 2 emi emi 9 Oct  1 10:47 pepe.txt
[emi@hal9000 test]$ rm pepe.txt 
rm: remove regular file ‘pepe.txt’? y
[emi@hal9000 test]$ cat pepe2.txt 
soy pepe
[emi@hal9000 test]$ ln -s pepe2.txt pepe3.txt
[emi@hal9000 test]$ ll
total 4
-rw-rw-r-- 1 emi emi 9 Oct  1 10:47 pepe2.txt
lrwxrwxrwx 1 emi emi 9 Oct  1 10:48 pepe3.txt -> pepe2.txt
[emi@hal9000 test]$ mv pepe2.txt pepe4.txt
[emi@hal9000 test]$ ll
total 4
lrwxrwxrwx 1 emi emi 9 Oct  1 10:48 pepe3.txt -> pepe2.txt
-rw-rw-r-- 1 emi emi 9 Oct  1 10:47 pepe4.txt
[emi@hal9000 test]$ cat pepe3.txt 
cat: pepe3.txt: No such file or directory

Prueben y vean que funciona.

Espero que les haya gustado, ya que esta clase de experimentos son los que nos hacen comprender más en profundidad cómo funciona un sistema operativo.


Tal vez pueda interesarte


Compartí este artículo