¿Cómo encontrar la dirección física de una variable desde el espacio de usuario en Linux?

¿Quiero encontrar la dirección física de una variable definida en un proceso de espacio de usuario? ¿Hay alguna forma de hacerlo utilizando privilegios de root?

Primero, ¿por qué querrías hacer esto? El propósito de los sistemas modernos de VM es eliminar al progtwigdor de la aplicación de la complejidad del diseño de la memoria physocal. Dándoles a cada uno su propio espacio de direcciones para hacer su vida más fácil.

Si quisiera hacer esto, casi seguramente necesitaría usar un módulo kernel. Obtenga la dirección virtual de la variable de la forma habitual, utilícela para indexar en las tablas de página de procesos y lea el valor que encuentre (la dirección física del marco). A continuación, agregue el desplazamiento de página para obtener la dirección física completa. Tenga en cuenta que no podrá usar esta dirección mientras la búsqueda está habilitada.

(Si tiene suerte, puede obtener la dirección de marco de una región de VM del sistema de archivos / proc y, por lo tanto, no necesitaría escribir un módulo kernel).

Como se contestó parcialmente antes, los progtwigs normales no deberían tener que preocuparse por las direcciones físicas, ya que se ejecutan en un espacio de direcciones virtual con todas sus comodidades. Además, no todas las direcciones virtuales tienen una dirección física, pueden pertenecer a archivos mapeados o páginas intercambiadas. Sin embargo, a veces puede ser interesante ver este mapeo, incluso en el sitio de usuario.

Para este propósito, el kernel de Linux expone su mapeo a un usuario a través de un conjunto de archivos en /proc . La documentación se puede encontrar aquí . Breve resumen:

  1. /proc/$pid/maps proporciona una lista de asignaciones de direcciones virtuales junto con información adicional, como el archivo correspondiente para los archivos asignados.
  2. /proc/$pid/pagemp proporciona más información sobre cada página asignada, incluida la dirección física si existe.

Este sitio web proporciona un progtwig en C que descarga las asignaciones de todos los procesos en ejecución utilizando esta interfaz y una explicación de lo que hace.

 #include "stdio.h" #include "unistd.h" #include "inttypes.h" uintptr_t vtop(uintptr_t vaddr) { FILE *pagemp; intptr_t paddr = 0; int offset = (vaddr / sysconf(_SC_PAGESIZE)) * sizeof(uint64_t); uint64_t e; // https://www.kernel.org/doc/Documentation/vm/pagemp.txt if ((pagemp = fopen("/proc/self/pagemp", "r"))) { if (lseek(fileno(pagemp), offset, SEEK_SET) == offset) { if (fread(&e, sizeof(uint64_t), 1, pagemp)) { if (e & (1ULL < < 63)) { // page present ? paddr = e & ((1ULL << 54) - 1); // pfn mask paddr = paddr * sysconf(_SC_PAGESIZE); // add offset within page paddr = paddr | (vaddr & (sysconf(_SC_PAGESIZE) - 1)); } } } fclose(pagemap); } return paddr; } 

(Editar: si por “dirección física”, te refieres al nivel de “en qué módulo de RAM están mis bits almacenados”, entonces la siguiente respuesta es inapropiada).

No necesita privilegios de root para hacer esto. Lo que necesita en su lugar es un depurador. Y aquí vamos (usando un sistema Linux en x86_64):

Primero, necesitamos un pequeño progtwig para jugar. Éste accede a una variable global y la imprime dos veces seguidas. Tiene dos variables globales, que encontramos en la memoria más adelante.

 #include 

 int a, b = 0;

 int principal (vacío)
 {
     printf ("a:");
     if (fscanf ("% d", & a) <1)
         return 0;

     printf ("a =% d \ n", myglobal);

     printf ("b:");
     if (fscanf ("% d", & b) <1)
         return 0;

     printf ("a =% d, b =% d \ n", a, b);

     return 0;
 }

Paso 1: compile el progtwig y elimine toda la información de depuración de él, para que no obtengamos ninguna sugerencia del depurador que no obtendríamos en una situación de la vida real.

 $ gcc -s -W -Wall -Os -o ab ab.c

Paso 2: Ejecuta el progtwig e ingresa uno de los dos números.

 $ ./ab
 a: 123
 a = 123
 b: _

Paso 3: encuentra el proceso.

 $ ps aux |  grep ab
 roland 21601 0.0 0.0 3648 456 pts / 11 S + 15:17 0:00 ./ab
 roland 21665 0.0 0.0 5132 672 pts / 12 S + 15:18 0:00 grep ab

Paso 4: Adjunte un depurador al proceso (21601).

 $ gdb
 ...
 (gdb) adjuntar 21601
 ...
 (gdb) donde
 # 0 0x00007fdecfdd2970 en read () desde /lib/libc.so.6
 # 1 0x00007fdecfd80b40 en _IO_file_underflow () de /lib/libc.so.6
 # 2 0x00007fdecfd8230e en _IO_default_uflow () de /lib/libc.so.6
 # 3 0x00007fdecfd66903 en _IO_vfscanf () desde /lib/libc.so.6
 # 4 0x00007fdecfd7245c en scanf () desde /lib/libc.so.6
 # 5 0x0000000000400570 in ??  ()
 # 6 0x00007fdecfd2f1a6 en __libc_start_main () desde /lib/libc.so.6
 # 7 0x0000000000400459 en ??  ()
 # 8 0x00007fffd827da48 in ??  ()
 # 9 0x000000000000001c en ??  ()
 # 10 0x0000000000000001 en ??  ()
 # 11 0x00007fffd827f9a2 en ??  ()
 # 12 0x0000000000000000 in ??  ()

El marco interesante es el número 5, ya que es entre un código que llama a la función main y la función scanf , por lo que debe ser nuestra función main . Continuando con la sesión de depuración:

 (gdb) fotogtwig 5
 ...
 (gdb) desmonta $ pc $ pc + 50
 ...
 0x0000000000400570: prueba% eax,% eax
 0x0000000000400572: jle 0x40058c 
 0x0000000000400574: mov 0x2003fe (% rip),% edx # 0x600978 
 0x000000000040057a: mov 0x2003fc (% rip),% esi # 0x60097c 
 0x0000000000400580: mov $ 0x40068f,% edi
 0x0000000000400585: xor% eax,% eax
 0x0000000000400587: callq 0x4003f8 
 ...

Ahora sabemos que la función printf obtendrá tres parámetros, y dos de ellos están a solo cuatro bytes de distancia uno del otro. Esa es una buena señal de que estas dos son nuestras variables a y b . Entonces, la dirección de a es 0x600978 o 0x60097c. Descubrámoslo intentando:

 (gdb) x / w 0x60097c        
 0x60097c : 0x0000007b
 (gdb) x / w 0x600978
 0x600978 : 0x00000000

Entonces, a , la variable que se lee primero está en la dirección 0x60097c (porque 0x0000007b es la representación hexadecimal para 123, que ingresamos), b está en 0x600978.

Aún en el depurador, podemos modificar la variable a vez en cuando para continuar el progtwig.

 (gdb) set * (int *) 0x60097c = 1234567
 (gdb) continuar

De vuelta en el progtwig que nos pidió ingresar dos números:

 $ ./ab
 a: 123
 a = 123
 b: 5
 a = 1234567, b = 5
 ps