
Uma ferramenta para recuperar um .ELF totalmente analisável de um kernel bruto, através da extração da tabela de símbolos do kernel (kallsyms)
Esta ferramenta permite obter um arquivo .ELF totalmente analisável a partir de uma imagem de kernel vmlinux/vmlinuz/bzImage/zImage (seja um blob binário bruto ou um arquivo .ELF pré-existente, porém sem símbolos), com símbolos de funções e variáveis recuperados.
Para isso, ela verifica o seu kernel em busca de uma tabela de símbolos do kernel (kallsyms), uma tabela de símbolos compactada que está presente em quase todos os kernels, quase sem alterações.
Como a tabela de símbolos em questão é originalmente compactada, ela deve recuperar strings que não são visíveis no binário original.
Ela produz um arquivo .ELF que você pode analisar usando IDA Pro e Ghidra. Portanto, esta ferramenta é útil para engenharia reversa de sistemas embarcados.
Uso:
# Command line:
vmlinux-to-elf <input_kernel.bin> <output_kernel.elf>
# Command line, list symbol addresses only:
kallsyms-finder <input_kernel.bin> # If installed with uv
vmlinux-to-elf.kallsyms-finder # If installed with snap
# Command line, just decompress the kernel:
vmlinuz-decompressor <input_kernel.bin> <output_kernel.bin> # If installed with uv
vmlinux-to-elf.vmlinuz-decompressor # If installed with snap
# Graphical:
vmlinux-to-elf-gui # If installed with uv
vmlinux-to-elf.gui # If installed with snap
flatpak run re.fossplant.vmlinux-to-elf # If installed with flatpak
Instalação:
# Install CLI+GUI with Snap (recommended on Ubuntu)
sudo snap install vmlinux-to-elf
# Install CLI+GUI with yay (recommended on Arch, Manjaro)
yay -S vmlinux-to-elf libadwaita python-gobject
# Install CLI+GUI with uv (example with Fedora)
sudo dnf install -y uv glib2-devel libadwaita-devel gtk4-devel \
gobject-introspection-devel python3-gobject-devel \
python3-devel cairo-devel @development-tools
uv tool install vmlinux-to-elf[gui]
vmlinux-to-elf-gui --install-metadata # Install .desktop file
# Install CLI with uv and GUI with Flatpak (recommended on
# distributions with libadwaita < 1.6)
sudo dnf install -y uv flatpak @development-tools
uv tool install vmlinux-to-elf
flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo
flatpak install re.fossplant.vmlinux-to-elf
Configuração do ambiente de desenvolvimento local:
sudo snap install --classic astral-uv
sudo apt install git
git clone [email protected]:marin-m/vmlinux-to-elf.git
# Dependencies for the GTK-4 GUI
sudo apt install libgirepository-2.0-dev libadwaita-1-dev \
gir1.2-adw-1 gir1.2-gtk-4.0 python3-dev blueprint-compiler
cd vmlinux-to-elf
cp -a .github/hooks/* .git/hooks/
# Download Python modules and initialize virtualenv (creates ".venv",
# call "source .venv/bin/activate" to set up)
uv sync --extra gui
# Add vmlinux-to-elf to $PATH, so that the commands are callable
# system-wide (creates a symlink to the source in "~/.local/bin")
uv tool install -e .[gui]
boot.img do Android, começando com uma magia ANDROID! ou UNCOMPRESSED_IMG [OK]Uma breve história da tabela de símbolos "kallsyms" pode ser encontrada no topo do arquivo "kallsyms.py". Resumidamente, ela foi introduzida por volta de 2004 no kernel Linux em sua forma atual e é usada para imprimir as mensagens de "Kernel oops", entre outras coisas.
Ela contém tuplas de "nome do símbolo", "endereço do símbolo", "tipo do símbolo" (os tipos de símbolo são designados com uma única letra, de maneira semelhante ao utilitário nm), sendo essas informações compactadas de forma densa com um algoritmo de compressão simples.
O esquema abaixo mostra como essas informações são serializadas no kernel, sendo o offset de cada estrutura respectiva detectado pelo vmlinux-to-elf por meio de heurísticas:
Esses campos têm alinhamento e tamanho de campo variáveis. Os tamanhos dos campos também podem variar conforme a arquitetura e a versão do kernel. Por esse motivo, o vmlinux-to-elf foi testado em uma variedade de casos.
O OpenWRT desde 2013 possui um patch que remove a compressão da tabela kallsyms por padrão (quando a compilação de kallsyms foi habilitada pelo usuário). Eles fazem isso para economizar espaço ao recompactar o kernel usando LZMA.
Isso significa que as entradas kallsyms_token_table e kallsyms_token_address desaparecem, e que os nomes dos símbolos usam ASCII em texto simples. Esse caso também é suportado.
Em kernels Linux 6.2 padrão, os arrays kallsyms são codificados na seguinte ordem:
kallsyms_addresses (ou kallsyms_offsets + kallsyms_relative_base)kallsyms_num_symskallsyms_nameskallsyms_markerskallsyms_seqs_of_names (apenas 6.2+)kallsyms_token_tablekallsyms_token_indexPara kernels Linux 6.4+, este layout é alterado para:
kallsyms_num_symskallsyms_nameskallsyms_markerskallsyms_token_tablekallsyms_token_indexkallsyms_addresses (ou kallsyms_offsets + kallsyms_relative_base)kallsyms_seqs_of_namesEmbora estes sejam analisados na seguinte ordem pelo algoritmo de análise do vmlinux-to-elf:
kallsyms_token_table (estrutura penúltima)kallsyms_token_index (última estrutura, para frente)kallsyms_markers (para trás)kallsyms_names (para trás novamente)kallsyms_num_syms (para trás novamente)kallsyms_addresses (ou kallsyms_offsets + kallsyms_relative_base) (para trás novamente)Deve suportar kernels desde a versão 2.6.10 (dezembro de 2004), até a atual 6.4 (em agosto de 2023). Apenas kernels explicitamente configurados sem CONFIG_KALLSYMS não devem ser suportados. Se esta variável de configuração do kernel não foi definida na compilação, então você obterá: KallsymsNotFoundException: No embedded symbol table found in this kernel.
Para kernels brutos, as seguintes arquiteturas podem ser detectadas (usando magias do binwalk): MIPSEL, MIPSEB, ARMEL, ARMEB, PowerPC, SPARC, x86, x86-64, ARM64, MIPS64, SuperH, ARC.
Os seguintes formatos de compressão de kernel podem ser detectados automaticamente: XZ, LZMA, GZip, BZ2, LZ4, LZO e Zstd.
Você também pode obter uma saída somente em texto dos nomes, endereços e tipos de símbolos do kernel usando o utilitário kallsyms-finder, também incluído nesta ferramenta. O formato da saída será semelhante ao arquivo procfs /proc/kallsyms.
Alguns parâmetros que devem ser inferidos automaticamente pela ferramenta (como o conjunto de instruções ou o endereço base) podem ser sobrescritos em caso de problema. A especificação completa dos argumentos que permitem fazer isso é apresentada abaixo:
$ vmlinux-to-elf -h
usage: vmlinux-to-elf [-h] [--e-machine DECIMAL_NUMBER] [--bit-size BIT_SIZE]
[--file-offset HEX_NUMBER] [--base-address HEX_NUMBER]
[--bss-size BSS_SIZE] [--use-absolute]
input_file output_file
Turn a raw or compressed kernel binary, or a kernel ELF without symbols, into a fully analyzable ELF whose symbols were extracted from the kernel symbol table
positional arguments:
input_file Path to the vmlinux/vmlinuz/zImage/bzImage/kernel.bin/kernel.elf file to make into an analyzable .ELF
output_file Path to the analyzable .ELF to output
options:
-h, --help show this help message and exit
--e-machine DECIMAL_NUMBER
Force overriding the output ELF "e_machine" field with this integer value (rather than auto-detect)
--bit-size BIT_SIZE Force overriding the input kernel bit size, providing 32 or 64 bit (rather than auto-detect)
--file-offset HEX_NUMBER
Consider that the raw kernel starts at this offset of the provided raw file or compressed stream (rather than 0, or the beginning of the ELF
sections if an ELF header was present in the input)
--base-address HEX_NUMBER
Force overriding the output ELF base address field with this integer value (rather than auto-detect)
--bss-size BSS_SIZE Size in megabytes of the .bss section in the binary
--use-absolute Assume kallsyms offsets are absolute addresses
Não hesite em abrir uma issue para qualquer sugestão de melhoria.
Por favor, priorize as issues e pull requests do repositório atual do Github para relatar bugs, fazer perguntas, etc.
Alternativamente, você pode usar este canal matrix se precisar entrar em contato diretamente com o autor do projeto, mas reserve-o como um canal secundário, por exemplo, para envio de amostras de kernel; caso contrário, o que for postado aqui provavelmente se perderá.
| Nome do array | Descrição | Exemplo de conteúdo |
|---|
kallsyms_addresses (ou kallsyms_offsets + kallsyms_relative_base) | Os endereços (ou offsets relativos a uma base, em kernels recentes) de cada símbolo, como um array | 80 82 00 C0 80 82 00 C0 80 82 00 C0 0C 84 00 C0 B4 84 00 C0 5C 85 00 C0 60 85 00 C0 60 85 00 C0 ... |
kallsyms_num_syms | O número total de símbolos, como um inteiro (útil para verificar endianness, alinhamento, decodificação correta da tabela de símbolos) | 54 D4 00 00 |
kallsyms_names | Os próprios nomes de símbolos compactados, separados por comprimento. Cada byte nas strings de símbolos compactados faz referência a um índice no array "kallsyms_token_index", que por sua vez faz referência ao offset de um caractere ou fragmento de string no array "kallsyms_token_table". | 09 54 64 6F 5F E1 F1 66 F5 25 05 54 F3 74 AB 74 0E 54 FF AB ... |
kallsyms_markers | Uma tabela de consulta (lookup table) que serve para encontrar rapidamente o offset aproximado de um nome de símbolo compactado em "kallsyms_names": a cada 256 símbolos, um offset para o símbolo em questão em "kallsyms_names" é adicionado como um long a esta tabela. | 00 00 00 00 03 0C 00 00 0C 18 00 00 1B 24 00 00 0F 31 00 00 DA 3D 00 00 CF 4A 00 00 ... |
kallsyms_seqs_of_names | Esta tabela de consulta (presente apenas em kernels 6.2+) contém uma sequência de array de inteiros compactados de 3 bytes, onde os índices do array correspondem à ordem alfanumérica de um determinado nome de símbolo, e os valores do array correspondem aos índices de entrada correspondentes nos arrays kallsyms_addresses e kallsyms_names | |
kallsyms_token_table | Fragmentos de string ou caracteres terminados em nulo que podem estar contidos em nomes de símbolos do kernel. Isso pode conter no máximo 256 fragmentos de string ou caracteres. Índices correspondentes a pontos de código ASCII que são realmente usados em qualquer símbolo do kernel corresponderão ao caractere ASCII em questão; outras posições conterão um fragmento de string estatisticamente escolhido. Esta ferramenta tenta encontrar heuristicamente este array no arquivo passado primeiro, a fim de encontrar a tabela de símbolos kallsyms. | 73 69 00 67 70 00 74 74 00 79 6E 00 69 6E 74 5F 00 66 72 00 ... |
kallsyms_token_index | 256 palavras, cada uma mapeando para os offsets dos caracteres ou fragmentos de string designados por seus respectivos índices em "kallsyms_token_table". | 00 00 03 00 06 00 09 00 0C 00 11 00 14 00 1B 00 1E 00 22 00 2C 00 30 00 35 00 38 00 ... |