Para realizar cuanquier accion, en un sistema operativo monolitico, como
puede ser linux, esta debe ser realizada por el kernel, pero nuestros programas
no tienen acceso al mismo. Para ello se inventaron las Syscalls, tambien
conocidas como llamadas al sistema o APIs. La importancia de las syscalls en el
mundo de la programacion en Linux es mucha, ya que sin ellas seria imposible
trabajar. Las llamadas al sistema se dividen en 6 grupos basicos;
Administracion de ficheros, Administracion de directorios y sistemas de
archivos, Proteccion y permisos, Administracion procesos, Señales y Gestion del
tiempo. Cada uno de estos grupos consta de varias llamadas al sistema.

Para indicarle al kernel que queremos usar una syscall usaremos la
interrupcion 0x80, de manera que en el registro EAX tendremos el valor
correspondiente a la syscall y en EBX, ECX.... tendremos los parametros para
dicha llamada al sistema. Si el numero de llamadas parametros que solicita la
syscall sobrepasa el numero de registros que podemos usar deberemos crear un
vector con los parametros y pasar la posicion de memoria en EBX.

un ejemplo de syscall:

open("/etc/passwd", O_RDONLY);

En este caso usamos la syscall open(), que forma parte del grupo de
Administracion de ficheros, en ASM seria una cosa similar a esta:

mov %eax,0x05 ; 0x05 es el valor de la syscall open(), si quieres
; saber el valor de cada una de las syscalls mira
; el fichero /usr/include/asm/unistd.h

mov %ebx,0x00bf34aa ; 0x00bf34aa es la posicion de memoria que contiene
; la cadena "/etc/passwd\0". El 0 del final es
; importante, pues le dice a la syscall hasta donde
; debe leer para interpretar el nombre del fichero.

mov %ecx,0x00 ; Es el valor de O_RDONLY, definido en bits/fcntl.h
int 0x80 ; Llamamos a la interrupcion 0x80, ahora le toca
; jugar al kernel.

Todas las syscalls, tras terminar, devuelven un valor, que se suele usar
para indicar si esta ha terminado con exito o se ha producido algun tipo de
error. Este valor queda almacenado en EAX.

Y para modificar las Syscalls...
Solo tenemos que definir esto:
extern void *sys_call_table[];

Y ya tenemos las dirección de todas las syscalls. Como veis es un array, y para referenciarlas nos hace falta un índice pues bien, para saber a que numero corresponde una syscall os echáis un vistazo a <asm/unistd.h> y veréis las constantes __NR_nombre_de_la_syscall.

Ejemplos:

#define __NR_exit 1 #define __NR_fork 2 #define __NR_read 3

Lo que quiere decir que la dirección de exit en el kernel será sys_call_table[1].

Si queréis modificarla, pues en init_module() guardáis el original de la syscall y ponéis la dirección de la vuestra, y en cleanup_module() la restauráis, algo así:

init_module() { . . ioctl_original = sys_call_table[__NR_ioctl]; sys_call_table[__NR_ioctl] = mi_ioctl; . . } cleanup_module() { sys_call_table[__NR_ioctl] = ioctl_original; }

Si no restauráis la syscall original en cleanup_module() podéis iros preparando a "rebootear" vuestra maquina al descargar el módulo, pero si usáis Windows seguro que ya estáis acostumbrados