2009-06-07 25 views
6

Para mi tesis de licenciatura, quiero visualizar la remanencia de datos de la memoria y cómo persiste después de reiniciar un sistema.Cómo obtener una dirección de memoria específica usando C

Tuve la idea simple de mapear una imagen en la memoria, apagar mi computadora, esperar x segundos, iniciar la computadora y ver si la imagen aún está allí.

 
int mmap_lena(void) 
{ 
    FILE *fd = NULL; 
    size_t lena_size; 
    void *addr = NULL; 

    fd = fopen("lena.png", "r"); 

    fseek(fd, 0, SEEK_END); 
    lena_size = ftell(fd); 

    addr = mmap((void *) 0x12345678, (size_t) lena_size, (int) PROT_READ, (int) MAP_SHARED, (int) fileno(fd), (off_t) 0); 
    fprintf(stdout, "Addr = %p\n", addr); 
    munmap((void *) addr, (size_t) lena_size); 
    fclose(fd); 
    fclose(fd_log); 
    return EXIT_SUCCESS; 
} 

Omití valores de retorno de comprobación para mayor claridad.

Así que después de la mmap intenté de alguna manera obtener la dirección, pero generalmente termino con una falla de segmentación en cuanto a mi comprensión de que la memoria está protegida por mi sistema operativo.

 
int fetch_lena(void) 
{ 
    FILE *fd = NULL; 
    FILE *fd_out = NULL; 
    size_t lenna_size; 
    FILE *addr = (FILE *) 0x12346000; 

    fd = fopen("lena.png", "r"); 
    fd_out = fopen("lena_out.png", "rw"); 

    fseek(fd, 0, SEEK_END); 
    lenna_size = ftell(fd); 

    // Segfault 
    fwrite((FILE *) addr, (size_t) 1, (size_t) lenna_size, (FILE *) fd_out); 

    fclose(fd); 
    fclose(fd_out); 

    return 0; 

} 

También observe por favor que yo no modificable las direcciones en este ejemplo, por lo que cada vez que se ejecuta mmap_lena el valor que utilizo en fetch_lena podría estar equivocado como el sistema operativo toma el primer parámetro a mmap sólo como una pista (en mi sistema siempre tiene un valor predeterminado de 0x12346000 de alguna manera).

Si hay algún error de codificación trivial lo siento porque mis habilidades C no se han desarrollado completamente.

Me gustaría ahora si hay alguna forma de obtener los datos que deseo sin implementar ningún gancho malloc o hacks de asignador de memoria.

Gracias de antemano, David

+2

En primer lugar, un proceso estándar de Linux no tiene acceso a la memoria bruta (física) para acceder fácilmente a una dirección dada de la memoria física de su proceso (consulte MMU, memoria virtual). Además, el kernel de Linux pone a cero la memoria (como en memset 0) antes de darle un proceso. – ysdx

Respuesta

18

Un problema que tiene es que está recibiendo de vuelta una dirección virtual, no la dirección física donde reside la memoria. La próxima vez que arranque, la asignación probablemente no será la misma.

Esto se puede hacer definitivamente dentro de un módulo kernel en Linux, pero no creo que haya ningún tipo de API en el espacio de usuario que pueda usar.

Si tiene permiso (y supongo que podría ser root en esta máquina si lo está reiniciando), entonces puede echar un vistazo a/dev/mem para ver el diseño real de phyiscal. Tal vez deberías intentar muestrear valores, reiniciar y ver cuántos de esos valores persistieron.

+0

Gracias, no tenía en mente que el mapeo de la memoria virtual también era un problema. No estoy seguro de cómo usar/dev/mem para obtener la información que quiero, así que supongo que tendré que escribir un módulo kernel. – tr9sh

+1

Tal vez intente mmap/dev/mem y luego busque la posición que desea ver? –

+4

Estoy de acuerdo con Chris: va a necesitar usar algún tipo de sistema operativo (o ningún sistema operativo en absoluto) que le dé acceso directo e irrestricto a la memoria RAM. Si reinicia en Linux o en cualquier otro sistema operativo con memoria virtual protegida, no puede obtener ninguna garantía de que tendrá acceso a las mismas páginas de memoria. Si quieres algo fácil de usar, ¿qué tal si reinicias en una versión anterior de DOS? –

10

Hay un similar project donde se demuestra un ataque de arranque en frío. El source code está disponible, tal vez pueda obtener algo de inspiración allí.

Sin embargo, AFAIR leen la memoria sin cargar primero un sistema operativo y, por lo tanto, no tienen que meterse con la protección de la memoria del sistema operativo. Tal vez deberías probar esto también para evitar que el SO sobreescriba o borre la memoria después del arranque.

(También puedes ver el video en el sitio, que es bastante impresionante;)

+0

De hecho, conozco muy bien su trabajo ya que mi tesis es en parte sobre su trabajo :) Describen formas muy innovadoras de obtener una imagen de memoria, pero no creo que describan cómo extraer rangos de memoria específicos de un sistema . (No he buscado en el código del gestor de arranque) ¡Gracias por la pista! – tr9sh

1

no estoy familiarizado con Linux, pero es probable que tengas que escribir un controlador de dispositivo. Los controladores de dispositivo deben tener alguna forma de convertir direcciones de memoria virtual a direcciones de memoria física para propósitos de DMA (los controladores DMA solo tratan con direcciones de memoria física). Debería poder usar esas interfaces para tratar directamente con la memoria física.

2

Su código de prueba parece extraño

ARCHIVO * addr = (FILE *) 0x12346000;
fwrite ((FILE *) fd_out, (size_t) 1, (size_t) lenna_size, (FILE *) addr);

No se puede simplemente convertir un entero en un puntero ARCHIVO y esperar obtener algo sensato. ¿Cambiaste también el primer y el último argumento para fwrite? Se supone que el último argumento es el ARCHIVO * para escribir.

+0

He corregido el error fwrite, probablemente sucedió durante la copia y pegado del código. En cuanto al reparto, tienes razón, ¡creo que también tengo que encontrar una solución para eso! ¡Gracias por la solución! – tr9sh

4

En la pregunta Direct Memory Access in Linux hemos resuelto la mayoría de los fundamentos necesarios para lograr esto. Tenga en cuenta que mmap() no es la respuesta a esto exactamente por los motivos que otros dijeron ... necesita una dirección real, no virtual, que solo puede ingresar al kernel (o escribiendo un controlador para retransmitir uno al espacio de usuario))

El método más simple sería escribir un controlador de dispositivo de caracteres que se pueda leer o escribir, con un ioctl para darle una dirección válida de inicio o finalización. De nuevo, si desea punteros sobre las funciones de administración de memoria para usar en el kernel, vea la pregunta a la que me he vinculado. La mayor parte se resolvió en los comentarios en la primera respuesta (y aceptada).

2

Es probable que desee el menor sistema operativo posible para este fin; cuanto más software cargue, más posibilidades tendrá de sobrescribir algo que desee examinar.

DOS podría ser una buena apuesta; usa < 640k de memoria. Si no carga HIMEM y en su lugar escribe su propia rutina (se requiere ensamblaje) para saltar a modo, copiar un bloque de memoria alta en memoria baja, luego volver al modo real, puede escribir un programa en su mayoría de modo real que puede descargar el ram físico (menos lo que usan el BIOS, DOS y su aplicación). Podría volcarlo en un disco flash o algo así.

Por supuesto, el problema real puede ser que el BIOS borre la memoria durante la POST.

Cuestiones relacionadas