2012-05-21 11 views
11

Estoy escribiendo una herramienta. Una parte de esa herramienta será su capacidad para registrar los parámetros de las llamadas al sistema. Bien, puedo usar ptrace para ese propósito, pero ptrace es bastante lento. Un método más rápido que se me ocurrió fue modificar el glibc. Pero esto se está poniendo difícil, ya que gcc inserta mágicamente sus propias funciones integradas como envoltorios de llamadas al sistema que usando el código definido en glibc. Usar -fno-builtin tampoco ayuda en eso.¿Es esta una buena manera de interceptar las llamadas al sistema?

Así que se me ocurrió la idea de escribir una biblioteca compartida, que incluye cada contenedor de llamadas del sistema, como mmap y luego realizar el registro antes de llamar a la función de conteo de llamadas del sistema. Por ejemplo, el pseudo código de cómo se vería mi mmap se da a continuación.

int mmap(...) 
{ 
log_parameters(...); 
call_original_mmap(...); 
... 
} 

Luego puedo usar LD_PRELOAD para cargar esta biblioteca primero. ¿Crees que esta idea funcionará o me falta algo?

+7

Probablemente no funcionará para ejecutables vinculados estáticamente. Y no funcionará para ejecutables haciendo syscalls sin pasar por libc. –

+0

¿Desea interceptar cada vez que se realiza una llamada real del sistema (en cada 'int 0x80'), o en cada llamada a las funciones de un manejador de biblioteca? – Chris

+0

Creo que probablemente sea lento. Eso es básicamente lo que Valgrind hace por su instrumentación, y a pesar de ser una herramienta muy conocida y desarrollada, aún mata el rendimiento. Creo que si hubiera una manera más rápida, ya se habría utilizado allí. Supongo que subestimas cuánto costará la función de registro y no hay forma de evitarlo. – zebediah49

Respuesta

0

Todas las llamadas al sistema desde el espacio de usuario pasan por un controlador de interrupción para cambiar al modo núcleo, si encuentra este controlador probablemente pueda agregar algo allí.

EDIT He encontrado este http://cateee.net/lkddb/web-lkddb/AUDITSYSCALL.html. Núcleos Linux: 2.6.6-2.6.39, 3.0-3.4 tienen soporte para la auditoría de llamadas del sistema. Este es un módulo kernel que debe estar habilitado. Tal vez pueda ver la fuente de este módulo si no es confuso.

+2

LSM se puede usar también para controlar syscalls. http://en.wikipedia.org/wiki/Linux_Security_Modules – osgx

+0

Me gustaría saber por qué obtuve un voto negativo en este – Joelmob

+0

Esto no es verdad universalmente - podría ser cierto para x86 en ciertas versiones de ciertos sistemas operativos, pero hay muchos plataformas y/o sistemas operativos que no usan una interrupción como mecanismo de transición de modo de usuario a kernel. – twalberg

0

Como han mencionado otros, si el binario está enlazado estáticamente, el enlazador dinámico omitirá cualquier intento de interceptar funciones usando libdl. En su lugar, debería considerar iniciar el proceso usted mismo y desviar el punto de entrada a la función que desea interceptar.

Esto significa iniciar usted mismo el proceso, interceptar su ejecución y reescribir su memoria para colocar una instrucción de salto al comienzo de la definición de una función en la memoria de una nueva función que usted controle.

Si desea interceptar las llamadas al sistema real y no puede usar ptrace, tendrá que encontrar el sitio de ejecución para cada llamada al sistema y volver a escribirlo, o puede necesitar sobrescribir la tabla de llamadas del sistema en la memoria y filtrando todo, excepto el proceso que desea controlar.

2

Ningún método que pueda soñar en el espacio de usuario funcionará sin problemas con cualquier aplicación. Afortunadamente para ti, ya hay soporte para hacer exactamente lo que quieres hacer en el kernel. Las Kprofes y Kretprobes le permiten examinar el estado de la máquina que acaba de preceder y después de una llamada al sistema.

Documentación aquí: https://www.kernel.org/doc/Documentation/kprobes.txt

0

Si el código está desarrollando es relacionada con el proceso, a veces se puede desarrollar implementaciones alternativas sin romper el código existente. Esto es útil si está reescribiendo una llamada al sistema importante y desea un sistema completamente funcional con el que depurarlo.

Para su caso, está reescribiendo el algoritmo mmap() para aprovechar una nueva y emocionante función (o mejorarla con una nueva característica). A menos que obtenga todo correctamente en el primer intento, no sería fácil depurar el sistema: una llamada al sistema mmap() que no funciona seguramente producirá un sistema que no funciona. Como siempre, hay esperanza.

A menudo, es seguro mantener el algoritmo restante en su lugar y construir su reemplazo en el lateral.Esto se puede conseguir mediante el uso de la identificación de usuario (UID) como un condicional con la cual decidir qué algoritmo de empleo:

if (current->uid != 7777) { 
/* old algorithm .. */ 
} else { 
/* new algorithm .. */ 
} 

Todos los usuarios excepto UID 7777 utilizarán el algoritmo antiguo. Puede crear un usuario especial, con UID 7777, para probar el nuevo algoritmo. Esto hace que sea mucho más fácil probar el código crítico relacionado con el proceso.

Cuestiones relacionadas