En informática , un módulo de núcleo cargable ( LKM ) es un archivo de objeto que contiene código para ampliar el núcleo en ejecución , o el llamado núcleo base , de un sistema operativo . Los LKM se utilizan normalmente para añadir compatibilidad con nuevo hardware (como controladores de dispositivos ) y/o sistemas de archivos , o para añadir llamadas del sistema . Cuando la funcionalidad proporcionada por un LKM ya no es necesaria, se puede descargar para liberar memoria y otros recursos.
La mayoría de los sistemas actuales similares a Unix y Microsoft Windows admiten módulos de kernel cargables con diferentes nombres, como módulo cargable de kernel ( kld ) en FreeBSD , extensión de kernel ( kext ) en macOS (aunque se está abandonando la compatibilidad con módulos de terceros [1] ), [2] módulo de extensión de kernel en AIX , módulo de kernel cargable dinámicamente en HP-UX , [3] controlador de modo kernel en Windows NT [4] y módulo de kernel descargable ( DKM ) en VxWorks . También se conocen como módulos cargables de kernel (o KLM ), y simplemente como módulos de kernel ( KMOD ).
Sin módulos de núcleo cargables, un sistema operativo tendría que incluir toda la funcionalidad anticipada posible compilada directamente en el núcleo base. Gran parte de esa funcionalidad residiría en la memoria sin ser utilizada, lo que desperdiciaría memoria [ cita requerida ] y requeriría que los usuarios reconstruyeran y reiniciaran el núcleo base cada vez que necesitaran una nueva funcionalidad.
Una pequeña crítica a la preferencia por un núcleo modular en lugar de un núcleo estático es la llamada penalización por fragmentación . El núcleo base siempre se descomprime en memoria contigua real mediante sus rutinas de configuración; por lo tanto, el código del núcleo base nunca se fragmenta. Una vez que el sistema está en un estado en el que se pueden insertar módulos, por ejemplo, una vez que se han montado los sistemas de archivos que contienen los módulos, es probable que cualquier nueva inserción de código del núcleo haga que el núcleo se fragmente, introduciendo así una pequeña penalización de rendimiento al utilizar más entradas TLB , lo que provoca más errores de TLB. [ cita requerida ]
Los módulos del kernel cargables en Linux se cargan (y descargan) mediante el modprobe
comando. Se encuentran en /lib/modules
o /usr/lib/modules
y han tenido la extensión .ko
("objeto kernel") desde la versión 2.6 (las versiones anteriores usaban la .o
extensión). [5] El lsmod
comando enumera los módulos del kernel cargados. En casos de emergencia, cuando el sistema no puede iniciarse debido, por ejemplo, a módulos dañados, se pueden habilitar o deshabilitar módulos específicos modificando la lista de parámetros de arranque del kernel (por ejemplo, si se usa GRUB , presionando 'e' en el menú de inicio de GRUB y luego editando la línea de parámetros del kernel).
En opinión de los mantenedores de Linux, los módulos LKM son obras derivadas del núcleo [ cita requerida ] . Los mantenedores de Linux toleran la distribución de módulos propietarios , [ cita requerida ] pero permiten que los símbolos se marquen como disponibles únicamente para los módulos de la Licencia Pública General GNU (GPL).
Cargar un módulo propietario o no compatible con GPL activará una bandera de 'contaminación' [6] [7] en el kernel en ejecución—lo que significa que cualquier problema o error experimentado tendrá menos probabilidades de ser investigado por los mantenedores. [8] [9] Los LKM efectivamente se convierten en parte del kernel en ejecución, por lo que pueden corromper las estructuras de datos del kernel y producir errores que pueden no poder ser investigados si el módulo es de hecho propietario.
En 2004, Linuxant, una empresa de consultoría que publica controladores de dispositivos propietarios como módulos de kernel cargables, intentó abusar de un terminador nulo en su MODULE_LICENSE
, como se ve en el siguiente extracto de código:
MODULE_LICENSE ( "GPL \0 para archivos en el directorio \" GPL \" ; para otros, solo se aplica el archivo LICENSE" );
El código de comparación de cadenas utilizado por el núcleo en ese momento intentaba determinar si el módulo tenía licencia GPL y se detenía cuando alcanzaba un carácter nulo ( \0
), por lo que fue engañado y pensó que el módulo estaba declarando que su licencia era simplemente "GPL". [10]
Los módulos del núcleo de FreeBSD se almacenan en /boot/kernel/
para módulos distribuidos con el sistema operativo o, generalmente, /boot/modules/
para módulos instalados desde puertos de FreeBSD o paquetes de FreeBSD o para módulos propietarios o de solo binarios. Los módulos del núcleo de FreeBSD generalmente tienen la extensión .ko
. Una vez que la máquina ha arrancado, se pueden cargar con el kldload
comando , descargar con kldunload
y listar con kldstat
. Los módulos también se pueden cargar desde el cargador antes de que se inicie el núcleo, ya sea automáticamente (a través de /boot/loader.conf
) o a mano.
Algunos módulos del kernel cargables en macOS se pueden cargar automáticamente. Los módulos del kernel cargables también se pueden cargar mediante el kextload
comando. Se pueden enumerar mediante el kextstat
comando. Los módulos del kernel cargables se encuentran en paquetes con la extensión .kext
. Los módulos suministrados con el sistema operativo se almacenan en el /System/Library/Extensions
directorio; los módulos suministrados por terceros se encuentran en varios otros directorios.
Un módulo de núcleo NetWare se denomina módulo cargable NetWare (NLM). Los NLM se insertan en el núcleo NetWare mediante el comando LOAD y se eliminan mediante el comando UNLOAD; el modules
comando enumera los módulos de núcleo cargados actualmente. Los NLM pueden residir en cualquier ruta de búsqueda válida asignada en el servidor NetWare y tienen .NLM
como extensión de nombre de archivo .
Se puede crear un proyecto de tipo módulo de kernel descargable (DKM) para generar un archivo ".out" que luego se puede cargar en el espacio del kernel mediante el comando "ld". Este módulo de kernel descargable se puede descargar mediante el comando "unld".
Solaris tiene una ruta de carga de módulos del núcleo configurable, que por defecto es /platform/platform-name/kernel /kernel /usr/kernel
. La mayoría de los módulos del núcleo se encuentran en subdirectorios bajo /kernel
; aquellos que no se consideran necesarios para arrancar el sistema hasta el punto en que init pueda iniciarse se encuentran a menudo (pero no siempre) en /usr/kernel
. Al ejecutar una compilación de núcleo DEBUG, el sistema intenta activamente descargar módulos.
Linux no proporciona una API o ABI estable para los módulos del núcleo. Esto significa que existen diferencias en la estructura y función internas entre las distintas versiones del núcleo, lo que puede causar problemas de compatibilidad. En un intento por combatir esos problemas, los datos de versiones de símbolos se colocan dentro de la .modinfo
sección de módulos ELF cargables . Esta información de versiones se puede comparar con la del núcleo en ejecución antes de cargar un módulo; si las versiones son incompatibles, el módulo no se cargará.
Otros sistemas operativos, como Solaris , FreeBSD , macOS y Windows mantienen la API y la ABI del kernel relativamente estables, evitando así este problema. Por ejemplo, los módulos del kernel de FreeBSD compilados con la versión 6.0 del kernel funcionarán sin recompilación en cualquier otra versión de FreeBSD 6.x, por ejemplo, la 6.4. Sin embargo, no son compatibles con otras versiones principales y deben recompilarse para su uso con FreeBSD 7.x, ya que la compatibilidad de API y ABI se mantiene solo dentro de una rama.
Si bien los módulos de kernel cargables son un método conveniente para modificar el kernel en ejecución, los atacantes pueden abusar de ellos en un sistema comprometido para evitar la detección de sus procesos o archivos , lo que les permite mantener el control sobre el sistema. Muchos rootkits hacen uso de los LKM de esta manera. Tenga en cuenta que, en la mayoría de los sistemas operativos, los módulos no ayudan a la elevación de privilegios de ninguna manera, ya que se requieren privilegios elevados para cargar un LKM; simplemente hacen que sea más fácil para el atacante ocultar la intrusión. [11]
Linux permite deshabilitar la carga de módulos mediante la opción sysctl/proc/sys/kernel/modules_disabled
. [12] [13] Un sistema initramfs puede cargar módulos específicos necesarios para una máquina en el arranque y luego deshabilitar la carga de módulos. Esto hace que la seguridad sea muy similar a la de un núcleo monolítico. Si un atacante puede cambiar el initramfs, puede cambiar el binario del núcleo.
En OS X Yosemite y versiones posteriores, una extensión del núcleo debe estar firmada con un certificado de desarrollador que tenga un "derecho" particular. Apple solo proporciona un certificado de desarrollador de este tipo a pedido y no se entrega automáticamente a los miembros desarrolladores de Apple . Esta función, llamada "firma de kext", está habilitada de manera predeterminada y le indica al núcleo que deje de iniciarse si hay extensiones de núcleo sin firmar. [14] En OS X El Capitan y versiones posteriores, es parte de la Protección de integridad del sistema .
En versiones anteriores de macOS, o si la firma de kext está deshabilitada, un módulo de kernel cargable en un paquete de extensión de kernel puede ser cargado por usuarios que no sean root si la propiedad OSBundleAllowUserLoad está establecida en True en la lista de propiedades del paquete. [15] Sin embargo, si alguno de los archivos en el paquete, incluido el archivo de código ejecutable, no son propiedad de root y el grupo wheel, o son escribibles por el grupo u "other", el intento de cargar el módulo cargable del kernel fallará. [16]
Los módulos del núcleo pueden tener opcionalmente una sección ELF de firma criptográfica que se verifica durante la carga según la configuración de la política de arranque verificado. El núcleo puede exigir que los módulos estén firmados criptográficamente por un conjunto de certificados de confianza; la lista de certificados de confianza se guarda fuera del sistema operativo en ILOM en algunas plataformas basadas en SPARC. La carga de módulos del núcleo iniciada por el espacio de usuario solo es posible desde la ruta de confianza cuando el sistema se ejecuta con la función de zona global inmutable habilitada.