Desenvolvimento

8 jun, 2015

Módulo de kernel Linux carregável em Assembly

Publicidade

Olá a todos! Primeiro de tudo, me desculpem por ficar em silêncio durante os últimos dois anos. Houve algumas razões para isso. De qualquer forma, estou de volta e eu estou pronto para compartilhar uma parte do que aprendi ao longo desse período.

Antes de começar, como de costume, uma nota para os nerds: o código neste artigo é voltado apenas para fins de demonstração e não contém certas coisas, como a verificação de erros, que seriam inevitáveis.

Tenho visto recentemente vários artigos relacionadas com a escrita de módulo do kernel para kernel pré-compilado na Internet. O pessoal está fazendo um bom trabalho, mas há uma coisa que eu, pessoalmente, não gosto – todos eles encaminham o módulo para o arquivo de configuração do kernel, coisa que pode ser obtida dessa maneira ou de outras formas melhores. Bem, ter a configuração do kernel em execução não é muito diferente da construção de um módulo do kernel que foi compilado por si próprio.

Ferramentas usadas

Como a construção de um módulo do kernel escrito em C para o kernel em uso, e do qual você não possui os arquivos .config, pode se tornar uma enorme dor de cabeça, eu decidi ir tão baixo quanto possível e escolhei a linguagem Assembler (o bom Assembler plano antigo que pode ser encontrado aqui). Esse instrumento maravilhoso lhe proporciona todo o necessário quando se trata de desenvolvimento x86/x86_64 (claro que a maioria de seus projetos em potencial pode ser muito complexa para serem implementados em conjunto).

Sistema de destino

Eu fui corajoso o suficiente para realizar essa experiência em minha máquina de desenvolvimento executando Debian com kernel 3.2.0-4.Obviamente, tenho fontes do kernel apropriadas instaladas, mas não fiz qualquer uso dessas fontes neste exemplo.

Módulo do Kernel carregável

Eu não estou pensando em mergulhar nas noções básicas de estrutura do kernel Linux e na forma sobre como o LKM é implementado. Ele é simplesmente irrelevante neste momento. O que interessa é a estrutura de um módulo. Para torná-lo simples, a estrutura de um LKM pode ser descrita como:

  1. seção .init.text – contém todo o código de inicialização do módulo.
  2. seção .exit.text – contém todo o código de limpeza executado um pouco antes de o módulo ser descarregado.
  3. Informação do módulo.
  4. Todo o resto.

Embora possamos manter “todo o resto” fora dele, por agora, temos de cuidar de uma representação adequada das seções init/exit e do módulo de informações. Na verdade, essas seções não são um problema para todos, afinal esse é apenas o código incluído depois de tudo, já que as informações sobre módulos são algo um pouco problemático. Mas as coisas mais importantes devem vir primeiro.

Seção .modinfo

Esta seção contém algumas strings que permitem identificar o módulo do kernel como algo que pode ser carregado e executado com segurança.

A primeira string diz ao kernel sobre como nosso módulo é licenciado:

“licence = GPL”

Você pode usar qualquer outra licença (por exemplo, “proprietary“), mas isso faria alguns símbolos exportados pelo kernel se tornarem invisíveis para o seu módulo.

O próximo é:

“depends =”

aqui você deve listar os módulos extras dos quais o seu módulo depende. Como o nosso pequeno módulo não possui dependências extras, podemos deixar essa informação vazia.

O último e o mais importante é:

“vermagic=3.2.0-4-amd64 SMP mod_unload modversions”

essa string nos diz (e para o kernel) qual módulo do kernel foi construído e quais opções LKM de manipulação estão ativadas. No entanto, a string acima contém informações que são boas para a construção de um módulo no sistema, mas que podem (e quase certamente vão) dar errado para o seu sistema. Mas não se preocupe, há uma maneira muito simples de obter essa string – execute /sbin/modinfo em qualquer arquivo .ko * no diretório /lib/modules/`uname -r`/.

Seção __versions

Você pode tentar construir um módulo sem esta seção, e ele pode até carregar e fazer seu trabalho, mas você enfrentará algumas queixas desagradáveis ​​do kernel informando que o código está contaminado.

O objetivo desta seção é certificar-se de que seu módulo e o do kernel estão falando a mesma língua, o que significa que eles usam símbolos idênticos. A estrutura é bastante simples – uma matriz de soma de verificação através de pares de nome, onde a soma de verificação é (no meu caso, onde é um sistema x86_64) 8 bytes seguido por um nome de 56 bytes (uma vez que os nomes mais curtos são preenchidos com 0). No entanto, não é tão simples de encontrar os valores adequados se você não tiver as fontes do kernel corretamente configuradas. Seria necessário simplesmente verificar alguns módulos para a presença do símbolo específico. Eu sugeriria fazer isso em IDA Pro, mas qualquer editor hexadecimal seria suficiente também.

Seção .gnu.linkonce.this_module

Esta seção contém apenas um estrutura ou módulo. Não vou mergulhar em detalhes desta estrutura, afinal de contas, você pode baixar o código fonte do kernel para analisar o arquivo include/linux/module.h e visualizar a declaração module.h. O que é importante saber, porém, é que essa estrutura contém o nome do módulo (como ele apareceria na saída do comando lsmod) e os ponteiros para as funções module_init() e module_cleanup().

Implementação

Parece que nós cobrimos todos os aspectos mais importantes. Vamos ao que interessa, que é a própria implementação. O código a seguir pode ser compilado com assembler plano.

 format ELF64
extrn printk
section '.init.text' executable

 module_init:
  push  rax
  
  mov  rdi, str1
  xor  eax, eax
  call  printk
  
  xor  eax, eax
  pop  rdx
  ret


section '.exit.text' executable
 module_cleanup:
  xor  eax, eax
  ret
  
section '.rodata.str1.1'
 str1 db '<0> Here I am, gentlemen!', 0x0a, 0
  
section '.modinfo' align 10h
  db  'license=GPL', 0
  db  'depends=', 0
  db  'vermagic=3.2.0-4-amd64 SMP mod_unload modversions ', 0

section '.gnu.linkonce.this_module' writable
 this_module:
  rb 18h
  db 'simple_module', 0
  rb 148h - ($ - this_module)
  dq module_init
  rb 238h - ($ - this_module)
  dq  module_cleanup
  dq 0
  
section '__versions'
 dq 0x568fba06
  @@: 
 db 'module_layout', 0
 rb 56 - ($ - @b)
 
 dq 0x27e1a049
  @@:
 db 'printk', 0
 rb 56 - ($ - @b)

Espero que este artigo seja útil de alguma forma. Obrigado pela leitura e te vejo no próximo!

***

Alexey Lyashko faz parte do time de colunistas internacionais do iMasters. A tradução do artigo é feita pela redação iMasters, com autorização do autor, e você pode acompanhar o artigo em inglês no link: http://syprog.blogspot.com.br/2015/04/linux-loadable-kernel-module-in-assembly.html