2009-03-03 20 views
8

Quiero ser capaz de detectar cuándo se produce una escritura en la dirección de memoria, por ejemplo, configurando una devolución de llamada asociada a una interrupción. ¿Alguien sabe cómo?Posible captura de escritura en la dirección (x86 - linux)

Me gustaría poder hacer esto en tiempo de ejecución (posiblemente gdb tenga esta característica, pero mi aplicación particular hace que gdb se bloquee).

Respuesta

14

Si desea interceptar escrituras en un rango de direcciones, puede usar mprotect() para marcar la memoria en cuestión como no grabable, e instalar un manejador de señal usando sigaction() para capturar el SIGSEGV resultante, haga su registro o lo que sea y marque la página como escribible de nuevo.

7

Lo que se necesita es tener acceso a los registros de depuración X86: http://en.wikipedia.org/wiki/Debug_register

Tendrá que ajustar la dirección de punto de interrupción en una de DR0 a DR3, y entonces la condición (escribir datos) en DR7. La interrupción ocurrirá y puede ejecutar su código de depuración para leer DR6 y encontrar qué causó el punto de interrupción.

Si GDB no funciona, puede intentar con un depurador más simple/más pequeño como http://sourceforge.net/projects/minibug/ - si eso no funciona, al menos puede pasar por el código y comprender cómo usar el hardware de depuración en el procesador usted mismo .

Además, hay un gran recurso desarrollador de IBM en el dominio de las técnicas de Linux de depuración que debe proporcionar algunas opciones adicionales:

http://www.ibm.com/developerworks/linux/library/l-debug/

Un razonablemente buen artículo sobre cómo hacer esto es Windows es aquí (Sé que estás que se ejecuta en Linux, pero otros podrían venir a esta pregunta querer hacerlo en windows):

http://www.codeproject.com/KB/debug/hardwarebreakpoint.aspx

-Adam

+1

Los registros de depuración solo se pueden acceder al nivel de privilegio 0, es decir, en el núcleo. Ver http://pdos.csail.mit.edu/6.828/2008/readings/i386/s12_02.htm –

4

BGF tiene esa característica: se llama puntos de observación de hardware, y que está muy bien soportado en Linux/x86:

(gdb) watch *(int *)0x12345678 

Si la aplicación se bloquea el BGF, la construcción actual GDB de CVS Head.

Si ese GDB aún falla, presente un GDB bug.

Lo más probable es que podamos arreglar GDB más rápido de lo que puede hackear SIGSEGV handler (siempre que sea un buen caso de prueba), y las correcciones a GDB también lo ayudan en futuros problemas.

+1

+1, acabo de encontrar un nuevo uso para GDB :) –

2

mprotect tiene una desventaja: su memoria debe estar alineada con el límite de página. Tenía mi memoria problemática en la pila y no pude usar mprotect().

Como dijo Adam, lo que quiere es manipular los registros de depuración. En Windows, utilicé esto: http://www.morearty.com/code/breakpoint/ y funcionó muy bien. También lo porté a Mach-O (Mac OS X), y funcionó muy bien también. También fue fácil, porque Mach-O tiene thread_set_state(), que es equivalente a SetThreadContext().

El problema con Linux es que no tiene tales equivalentes. Encontré Ptrace, pero pensé, esto no puede ser, debe haber algo más simple. Pero no hay. Todavía. Creo que están trabajando en una API hw_breakpoint para kernel y espacio de usuario.(Consulte http://lwn.net/Articles/317153/)

Pero cuando encontré esto: http://blogs.oracle.com/nike/entry/memory_debugger_for_linux Lo intenté y no fue tan malo. El método ptrace funciona mediante un "proceso externo" que actúa como un "depurador", adjuntando a su programa, inyectando nuevos valores para los registros de depuración, y terminando con su programa continuando con un nuevo conjunto de puntos de interrupción hw. La cuestión es que puede crear este "proceso externo" usted mismo usando fork(), (no tuve éxito con una subtrama), y haciendo estos simples pasos en línea en su código.

El código addwatchpoint se debe adaptar para trabajar con 64 bit linux, pero eso solo está cambiando USER_DR7 etc. a offsetof (struct user, u_debugreg [7]). Otra cosa es que después de un PTRACE_ATTACH, tiene que esperar a que el depurador realmente se detenga. Pero en lugar de volver a intentar un POKEUSER en un bucle ocupado, lo correcto sería un waitpid() en su pid.

La única pega con el método ptrace es que su programa puede tener solo un "depurador" adjunto a la vez. Por lo tanto, una conexión ptrace fallará si su programa ya se está ejecutando bajo control gdb. Pero al igual que el código de ejemplo, puede registrar un manejador de señal para SIGTRAP, ejecutar sin gdb, y cuando capte la señal, ingrese un bucle ocupado esperando a que se adjunte gdb. Desde allí puedes ver quién intentó escribir tu memoria.

Cuestiones relacionadas