Programando "Hi, Hello Cyber-mundo":
Un módulo es un trozo de código que se inserta en el kernel y por tanto se ejecuta con privilegios Ring0.
¿Que no sabes que es eso? El modo protegido del 386 permite 4 modos de protección, del Ring0 al Ring3. El Ring0 es el modo administrador, y el Ring3 es el modo usuario. El Ring1 es también modo administrador pero con menos privilegios que el Ring0, y el Ring2 idem, pero con menos privilegios que el Ring1.
Linux solo usa el Ring0 y el Ring3. El kernel corre en Ring0 y todos los procesos de usuario corren en Ring3. Los privilegios de un modo se podrían definir como las áreas de memoria a las que es capaz de acceder y modificar.
En código Ring0, los accesos a memoria se hacen directamente, y además se puede leer y escribir en cualquier lugar de la memoria, por eso si el kernel tiene algún bug o está mal programado el sistema se vuelve inestable (os podéis imaginar como debe estar programado el kernel de windows XD).
En código Ring3, los accesos a memoria se hacen según unas tablas que gestiona el kernel para cada proceso. No voy a entrar en detalle explicando esto ya que no viene al caso, pero que sepáis que la dirección de las tablas se guarda en el registro de control 3, en el procesador. Si un programa intenta acceder a una dirección de memoria a la que no tiene derecho o no esta asignado en procesador genera una excepción al S.O., y éste se encarga de matar al proceso, y tu verías el clásico segmention fault de toda la vida :).
Y si los programas de usuario no pueden escribir fuera de su rango de direcciones, ¿como pueden crear un socket o abrir un fichero?: con las llamadas al sistema (syscalls en adelante). En Linux se usa la int 0x80 para generar una syscall.
En los registros ebx, ecx y edx se ponen los parámetros, y en eax se pone el numero de syscall. Después de todo este rollo vamos a empezar con los módulos :).
Para compilar un módulo hay que definir los simbolos __KERNEL__ y MODULE. Tambien hay que includir <linux/kernel.h>, <linux/module.h> y <linux/version.h>. Hay que definir obligatoriamente dos rutinas:
- init_module() Es llamada justo después de que el módulo sea insertado en el kernel, y normalmente se usa para inicializar las estructuras de datos del kernel y para instalar "handlers" en el sistema.
Un handler puede ser, por ejemplo, hacer que una llamada al sistema apunte a una rutina definida en nuestro módulo, asi que cualquier programa que use ioctl(), por poner un ejemplo, estará llamando a nuestro programa. Con esto, hacer un módulo que oculte el flag promiscuo es un juego de niños. init_module() devuelve un entero que indica si el módulo se puede cargar, si devuelve un error el módulo no se carga.
- cleanup_module() Es llamado justa antes de descargar el módulo de la memoria. Se usa para deshacer lo que hizo init_module(). Necesitas que de mas detalles?
Como con un módulo no se pueden escribir caracteres en una terminal así como así, se usa printk(), que es como printf(), pero escribe en el fichero /var/log/messages. Un módulo sencillito seria este:
<++> modulos/holamundo.c #define __KERNEL__ #define MODULE
#include <linux/kernel.h> #include <linux/module.h>
int init_module(void) { printk(" Cargando el modulo: Hola mundo\n"); return 0; /* Ningun error */ }
void cleanup_module() { printk(" Descargando el modulo: Adios mundo :)\n"); } <-->
Para compilar un módulo hay que pasar al gcc estos parametros:
-Idirectorio_con_las_fuentes_del_kernel En mi makina es -I/usr/src/linux-2.2.10/include/linux -c porque queremos crear un fichero objeto, no un ejecutable
-O2 Si no especificas optimizacion, el kernel no carga el módulo
Compilamos el módulo:
gcc -I/usr/src/linux-2.2.10/include/linux -c holamundo.c -O2
Lo insertamos y despues lo borramos:
insmod holamundo.o rmmod holamundo
Si haceis un tail de /var/log/messages vereis algo como esto:
# tail -n 2 /var/log/messages Jan 16 13:27:46 localhost kernel: Cargando el modulo: Hola mundo Jan 16 13:27:59 localhost kernel: Descargando el modulo: Adios mundo :)
Facil, no?
(c) 2000 KIKO81