>_ DevTrendspt

Idioma

Início

Linguagens

Seções

Frontend Backend Mobile DevOps AI / ML GameDev Blockchain Embarcados Segurança
C

Como injetar código no kernel Linux sem código-fonte e símbolos de depuração

Imagine este cenário: você tem um binário de kernel Linux pronto para um dispositivo móvel ou placa ARM64 embarcada. Não há código-fonte disponível, as informações de depuração foram removidas durante o build, e você precisa interceptar uma chamada de sistema ou adicionar sua própria lógica diretamente no nível do kernel.

A abordagem usual nesses casos é longa e tediosa. Você precisa fazer engenharia reversa do binário no IDA Pro ou Ghidra, encontrar manualmente os offsets das funções e escrever patches frágeis. Um desenvolvedor com o apelido bmax121 decidiu simplificar esse processo e lançou o projeto KernelPatch.

Por que o KernelPatch é necessário

KernelPatch é um framework para aplicar patches e fazer hooks no kernel Linux quando tudo o que você tem para trabalhar é uma imagem de kernel stripped como vmlinux ou boot.img.

Quando um kernel é compilado sem informações de depuração, ferramentas tradicionais de depuração como kprobes se recusam a funcionar sem workarounds adicionais. O KernelPatch resolve esse problema por conta própria. Ele recupera a tabela de símbolos diretamente do binário do kernel e fornece uma API conveniente para modificar código executável.

O projeto é útil para engenheiros reversos, pesquisadores de segurança Android e desenvolvedores de software de baixo nível para sistemas embarcados.

O que o framework pode fazer

O desenvolvedor construiu várias capacidades importantes no projeto para trabalhar com o kernel:

  • Busca de símbolos sem código-fonte. A ferramenta analisa estruturas internas de kallsyms dentro da imagem pronta. Não precisa de arquivos externos de System.map ou código-fonte. Uma abordagem semelhante é usada no utilitário vmlinux-to-elf, do qual o autor emprestou algumas ideias.
  • Dois modos de injeção de código. Você pode fazer um patch estático na imagem (por exemplo, sobrescrever boot.img em disco) ou carregar código dinamicamente em tempo de execução.
  • Interceptação de funções via inline hook. Para a arquitetura ARM64, a interceptação de chamadas é baseada no trabalho de android-inline-hook. O framework manipula corretamente instruções de endereçamento relativo (b, bl, adrp) para não quebrar a execução do código original.
  • Hooks de chamadas de sistema. Substituir endereços na tabela sys_call_table leva apenas algumas linhas de comando.
  • Módulo SU integrado. Para dispositivos Android, o projeto inclui sua própria implementação de superusuário.

Como funciona internamente

Para carregar módulos de usuário (KPM — Kernel Patch Modules) em tempo real, o framework precisa de memória dentro do kernel. Para isso, o KernelPatch inclui um alocador TLSF compacto. Ele ajuda a alocar páginas de memória executável (ROX) diretamente no espaço de endereçamento do kernel.

Ao realizar um inline hook, o framework lê as primeiras instruções da função alvo, forma um chamado trampoline a partir delas e escreve um jump para o seu handler. Quando seu código termina de executar, o controle retorna para a função original através do trampoline.

+-------------------+      +---------------------+      +-------------------+
|  Целевая функция  | ---> |  Ваш обработчик KPM | ---> | Трамплин (Orig)   |
|  (инструкция JMP) |      |  (пользовательский) |      |  (продолжение)    |
+-------------------+      +---------------------+      +-------------------+

Requisitos do sistema e limitações

O projeto tem limites claros de aplicabilidade que são importantes conhecer antecipadamente:

  • Apenas a arquitetura ARM64 é suportada. Ainda não há versões para x86_64 ou RISC-V.
  • Faixa de versão do kernel — de Linux 3.18 a 6.6.
  • O requisito principal é que o kernel deve ser compilado com a flag CONFIG_KALLSYMS=y.

Se o fornecedor do firmware removeu completamente CONFIG_KALLSYMS na fase de build, o KernelPatch não conseguirá analisar automaticamente os endereços das funções. Felizmente, essa flag permanece habilitada na maioria dos firmwares de dispositivos Android e ARM.

Exemplo prático de uso

Digamos que você precise rastrear todos os lançamentos de processos via chamada sys_execve em um dispositivo Android de teste, mas não quer recompilar o kernel a partir do código-fonte do fornecedor.

Anteriormente, você teria que construir um módulo de kernel carregável completo (LKM), lidar com problemas de verificação de assinatura digital e a ausência de um carregador de módulos no kernel.

Com o KernelPatch, o processo é diferente:

  1. Você escreve um pequeno código em C com a lógica de interceptação.
  2. Compila no formato KPM.
  3. Fornece ao utilitário KernelPatch sua boot.img e o módulo KPM compilado.
  4. Obtém uma boot.img modificada e faz o flash via fastboot.

Após o dispositivo inicializar, cada chamada a execve passa primeiro pelo seu handler.

O README do projeto é bastante lacônico, e toda a documentação detalhada terá que ser estudada a partir do código-fonte e arquivos na pasta doc/. Mesmo assim, o projeto está em desenvolvimento ativo e já reuniu mais de 14.000 estrelas no GitHub.

Aliás, se você não precisa do framework em si para experimentos, mas sim de um root pronto para Android baseado nele, o autor recomenda olhar seu projeto adjacente — APatch. Esta é uma alternativa de alto nível ao Magisk e KernelSU, usando o KernelPatch nos bastidores.

Para pesquisadores de segurança e desenvolvedores ARM64, o KernelPatch é uma ótima descoberta, economizando dezenas de horas ao analisar kernels de código fechado.

Projetos relacionados