ver todos sbin nucleo módulos modulos modules modificar mediante lsmod los listar equipo ejecución ejecucion descargar consola como cli cargar cargados actual c linux linux-kernel kernel-module

todos - modulos cargados linux



¿Cómo cargar módulos de kernel de Linux desde código C? (5)

No estoy seguro de que haya una forma más limpia que el system .

Pero seguro, si desea cargar / descargar los módulos de su demonio de espacio de usuario, entonces se obliga a ejecutar el demonio como root *, lo que no puede considerarse como seguro.

*: o puede agregar los comandos explícitos en el archivo sudoers, pero esto será una pesadilla para administrar al implementar su aplicación.

Tengo una aplicación que tiene dos módulos de kernel externos y un demonio de espacio de usuario. Quiero cargar los módulos desde el código del daemon, escrito en C, al inicio, y descargarlos en la salida limpia. ¿Puedo cargarlos de una forma más limpia que haciendo el system("modprobe module"); ¿Y descargarlos usando el rmmod correspondiente?


Puede realizar las mismas tareas que modprobe y Co., pero dudo que se pueda caracterizar como más limpio .


Recomiendo el uso de system() en cualquier código de demonio que se ejecute con permisos de raíz, ya que es relativamente fácil de explotar desde un punto de vista de seguridad. modprobe y rmmod son, de hecho, las herramientas adecuadas para el trabajo. Sin embargo, sería un poco más limpio y mucho más seguro utilizar un fork() explícito fork() + exec() para invocarlos.


insmod / rmmod usa las funciones init_module y delete_module para hacer esto, que también tienen una página de manual disponible. Ambos declaran las funciones como extern lugar de incluir un encabezado, pero la página de manual dice que deberían estar en <linux/module.h> .


Ejemplo ejecutable mínimo

Probado en una máquina virtual QEMU + Buildroot VM y Ubuntu 16.04 con este sencillo módulo de impresora de parámetros .

Utilizamos las llamadas al sistema init_module / finit_module y remove_module Linux .

El kernel de Linux ofrece dos llamadas al sistema para la inserción del módulo:

  • init_module
  • finit_module

y:

man init_module

documentos que:

La llamada del sistema finit_module () es como init_module (), pero lee el módulo que se va a cargar desde el descriptor de archivos fd. Es útil cuando la autenticidad de un módulo del kernel se puede determinar a partir de su ubicación en el sistema de archivos; en los casos en que sea posible, se puede evitar la sobrecarga de usar módulos firmados criptográficamente para determinar la autenticidad de un módulo. El argumento param_values ​​es como para init_module ().

finit es más nuevo y se agregó solo en v3.8. Más razones: https://lwn.net/Articles/519010/

glibc no parece proporcionar un envoltorio de C para ellos, así que solo creamos el nuestro con syscall .

insmod.c

#define _GNU_SOURCE #include <fcntl.h> #include <stdio.h> #include <sys/stat.h> #include <sys/syscall.h> #include <sys/types.h> #include <unistd.h> #include <stdlib.h> #define init_module(module_image, len, param_values) syscall(__NR_init_module, module_image, len, param_values) #define finit_module(fd, param_values, flags) syscall(__NR_finit_module, fd, param_values, flags) int main(int argc, char **argv) { const char *params; int fd, use_finit; size_t image_size; struct stat st; void *image; /* CLI handling. */ if (argc < 2) { puts("Usage ./prog mymodule.ko [args="" [use_finit=0]"); return EXIT_FAILURE; } if (argc < 3) { params = ""; } else { params = argv[2]; } if (argc < 4) { use_finit = 0; } else { use_finit = (argv[3][0] != ''0''); } /* Action. */ fd = open(argv[1], O_RDONLY); if (use_finit) { puts("finit"); if (finit_module(fd, params, 0) != 0) { perror("finit_module"); return EXIT_FAILURE; } close(fd); } else { puts("init"); fstat(fd, &st); image_size = st.st_size; image = malloc(image_size); read(fd, image, image_size); close(fd); if (init_module(image, image_size, params) != 0) { perror("init_module"); return EXIT_FAILURE; } free(image); } return EXIT_SUCCESS; }

GitHub aguas arriba .

rmmod.c

#define _GNU_SOURCE #include <fcntl.h> #include <stdio.h> #include <sys/stat.h> #include <sys/syscall.h> #include <sys/types.h> #include <unistd.h> #include <stdlib.h> #define delete_module(name, flags) syscall(__NR_delete_module, name, flags) int main(int argc, char **argv) { if (argc != 2) { puts("Usage ./prog mymodule"); return EXIT_FAILURE; } if (delete_module(argv[1], O_NONBLOCK) != 0) { perror("delete_module"); return EXIT_FAILURE; } return EXIT_SUCCESS; }

GitHub aguas arriba .

Interpretación de la fuente de Busybox

Busybox proporciona insmod , y ya que está diseñado para el minimalismo, podemos tratar de deducir cómo se hace desde allí.

En la versión 1.24.2, el punto de entrada está en modutils/insmod.c función insmod_main .

IF_FEATURE_2_4_MODULES es un soporte opcional para los módulos más antiguos del kernel 2.4 de Linux, por lo que podemos ignorarlo por ahora.

Eso solo reenvía a la función bb_init_module .

bb_init_module intenta dos cosas:

  • mmap el archivo a la memoria a través de try_to_mmap_module .

    Esto siempre establece image_size al tamaño del archivo .ko como efecto secundario.

  • si eso falla, malloc el archivo a la memoria con xmalloc_open_zipped_read_close .

    Esta función, opcionalmente, descomprime el archivo primero si es un archivo zip y, de lo contrario, lo hace mallocs.

    No entiendo por qué se hace este negocio de compresión, ya que ni siquiera podemos confiar en él porque el try_to_mmap_module no parece descomprimir las cosas.

Finalmente llega la llamada:

init_module(image, image_size, options);

donde la image es el ejecutable que se guardó en la memoria, y las opciones son solo "" si llamamos insmod file.elf sin más argumentos.

Se proporciona init_module arriba por:

#ifdef __UCLIBC__ extern int init_module(void *module, unsigned long len, const char *options); extern int delete_module(const char *module, unsigned int flags); #else # include <sys/syscall.h> # define init_module(mod, len, opts) syscall(__NR_init_module, mod, len, opts) # define delete_module(mod, flags) syscall(__NR_delete_module, mod, flags) #endif

ulibc es una implementación de libc incrustada, y parece proporcionar init_module .

Si no está presente, creo que se supone glibc, pero como man init_module dice:

Glibc no admite la llamada al sistema init_module (). No se proporciona ninguna declaración en los encabezados de glibc, pero, a través de una peculiaridad de la historia, glibc exporta un ABI para esta llamada de sistema. Por lo tanto, para emplear esta llamada al sistema, es suficiente declarar manualmente la interfaz en su código; como alternativa, puede invocar la llamada del sistema utilizando syscall (2).

BusyBox sigue sabiamente ese consejo y usa syscall , que proporciona glibc, y que ofrece una API de C para llamadas al sistema.