Home Comunidade Eventos YouTube Networks Desafios // produtos Academy News CTI | CVE Contato

01 - Carregamento e Remoção (insmod, rmmod, modprobe)

video explicativo
Video explicativoEspaco reservado para o video desta anotação — adicione video: https://youtu.be/... no frontmatter da nota.

O sistema de módulos do kernel Linux (Loadable Kernel Modules - LKM) é uma arquitetura de extensão dinâmica que permite a manipulação do código que reside no nível mais privilegiado de execução da CPU (Ring 0). A capacidade de injetar e remover código do kernel em tempo de execução, sem a necessidade de um ciclo de recompilação e reinicialização, é o que confere ao Linux sua versatilidade inigualável em hardware. O gerenciamento destes módulos não é apenas uma tarefa de carregamento de arquivos, mas uma orquestração de dependências simbólicas e de estado do kernel, mediada por utilitários que interagem com as syscalls init_module() e delete_module().

1. A Estrutura Binária e o Registro no Kernel

Os módulos do kernel são binários compilados no formato ELF (Executable and Linkable Format), especificamente desenhados para serem realocáveis. Eles possuem a extensão .ko (Kernel Object). Internamente, um módulo contém:

  • Código Executável: Funções C que serão integradas ao espaço de endereçamento do kernel.

  • Tabela de Símbolos: Uma lista de funções e variáveis que o módulo exporta para uso por outros módulos ou pelo próprio núcleo, e uma lista de símbolos que ele importa (necessita) de outros componentes.

  • Seções de Inicialização e Destruição: Marcadas geralmente pelas macros module_init() e module_exit(), que ditam os pontos de entrada quando o módulo é inserido ou removido.

2. insmod: Injeção de Baixo Nível

O insmod (insert module) representa a interface mais primitiva e direta entre o usuário e o kernel para o carregamento de código. Ao executar insmod <arquivo.ko>, o utilitário realiza as seguintes operações:

  1. Abertura do Arquivo: O binário ELF é carregado do disco para a memória do espaço do usuário.

  2. Realocação Dinâmica: O insmod solicita que o kernel aloque uma região de memória no Ring 0. O binário é realocado para endereços de memória virtual específicos e os ponteiros de símbolos importados são resolvidos contra a tabela global de símbolos exportados pelo kernel (/proc/kallsyms).

  3. Chamada de Sistema: O utilitário invoca a syscall init_module(), passando um ponteiro para o bloco de memória contendo o código realocado e o tamanho do mesmo.

  4. Execução: O kernel assume o controle, executa a função de inicialização (module_init) e, se bem-sucedida, o módulo torna-se uma parte integrante da árvore de símbolos do kernel.

A limitação crítica do insmod é sua cegueira topológica. Ele não analisa dependências. Se o módulo driver_x.ko depender de uma função exportada pelo módulo bus_y.ko, e este último não estiver carregado, o insmod falhará com um erro de "Unknown symbol". O uso do insmod em produção é desencorajado, sendo restrito a ambientes de desenvolvimento de drivers ou debugging cirúrgico.

3. rmmod: A Remoção do Código

O utilitário rmmod (remove module) é o contraparte do insmod, acionando a syscall delete_module(). O seu funcionamento é estritamente condicional:

  • Contagem de Referências: O kernel mantém um campo refcnt (reference count) na struct module de cada módulo carregado. Este contador é incrementado sempre que outro módulo depende do referido, ou sempre que um dispositivo de hardware é aberto ou utilizado por um processo.

  • Falha de Remoção: Se o refcnt > 0, o kernel nega a remoção. Isso impede estados de inconsistência, como a tentativa de descarregar um driver de disco enquanto processos de espaço de usuário estão realizando escritas síncronas.

  • Limpeza: Se o contador for zero, o kernel executa a função module_exit(), desvincula o módulo da lista global e libera a região de memória (Ring 0) anteriormente ocupada.

4. modprobe: A Orquestração Inteligente

O modprobe é o padrão industrial e a interface de alta camada para o gerenciamento de módulos. Ele não opera sobre arquivos brutos como o insmod, mas sobre uma base de conhecimento das dependências do sistema.

O Funcionamento do modprobe

Quando o comando modprobe <modulo> é disparado, o utilitário executa uma sequência lógica determinística:

  1. Consulta ao modules.dep: O modprobe lê o arquivo /lib/modules/$(uname -r)/modules.dep.bin (um banco de dados indexado gerado pelo depmod). Este arquivo mapeia cada módulo para sua lista exata de dependências.

  2. Resolução de Grafo: O utilitário reconstrói o grafo de dependências. Ele carrega, na ordem topológica correta, cada dependência necessária antes de carregar o módulo alvo.

  3. Carregamento Condicional: Diferente do insmod, o modprobe é inteligente o suficiente para verificar se o módulo já não está carregado. Se estiver, ele termina com sucesso silencioso.

  4. Parâmetros e Aliases: O modprobe consulta arquivos em /etc/modprobe.d/ para aplicar as diretivas de options (passagem de parâmetros ao módulo) e processar aliases (nomes alternativos ou identificadores de hardware).

O Papel do depmod

A eficácia do modprobe depende integralmente da integridade do arquivo modules.dep. Este é gerado pelo utilitário depmod, que varre o diretório /lib/modules/ a cada instalação de kernel ou adição de novos drivers. O depmod realiza uma análise estática dos binários ELF para verificar quais símbolos de cada módulo são importados e exportados, construindo o mapa lógico que permite que o sistema de carregamento opere sem intervenção humana na ordem de carga.

5. Considerações de Segurança e Integridade

Em sistemas modernos com Secure Boot e Kernel Lockdown ativados, a operação de carregamento de módulos é altamente restrita. O kernel verifica a assinatura digital (Authenticode/X.509) do arquivo .ko antes de permitir sua carga na memória privilegiada. Se a assinatura não bater com uma chave confiável (no keyring do kernel), a syscall init_module() é abortada, resultando no erro "Required key not available".

Além disso, o carregamento de módulos pode ser desabilitado inteiramente após o boot (usando a diretiva kernel.modules_disabled = 1 no sysctl), transformando o kernel monolítico em um sistema estático, o que é uma estratégia comum de endurecimento (hardening) em servidores de alta segurança para impedir a injeção de rootkits dinâmicos mesmo que o usuário root seja comprometido.