
Una herramienta para recuperar un .ELF totalmente analizable desde un kernel en bruto, mediante la extracción de la tabla de símbolos del kernel (kallsyms)
Esta herramienta permite obtener un archivo .ELF completamente analizable a partir de una imagen de kernel vmlinux/vmlinuz/bzImage/zImage (ya sea un blob binario sin procesar o un archivo .ELF existente pero sin símbolos), con los símbolos de funciones y variables recuperados.
Para ello, escanea el kernel en busca de una tabla de símbolos del kernel (kallsyms), una tabla de símbolos comprimida que está presente en casi todos los kernels, prácticamente sin alterar.
Debido a que la tabla de símbolos en cuestión está originalmente comprimida, debería recuperar cadenas que no son visibles en el binario original.
Produce un archivo .ELF que puedes analizar con IDA Pro y Ghidra. Por lo tanto, esta herramienta es útil para la ingeniería inversa de sistemas embebidos.
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
Instalación:
# 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
Configuración del entorno de desarrollo 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 de Android, que comienzan con la firma mágica ANDROID! o UNCOMPRESSED_IMG [OK]Puede encontrarse una breve historia de la tabla de símbolos "kallsyms" al principio del archivo "kallsyms.py". Brevemente, esta se introdujo en el kernel de Linux hacia 2004 en su forma actual y se utiliza, entre otras cosas, para imprimir los mensajes de "Kernel oops".
Contiene tuplas de "nombre del símbolo", "dirección del símbolo", "tipo del símbolo" (los tipos de símbolo se designan con una sola letra, de forma similar a la utilidad nm), y esta información está empaquetada de forma compacta con un sencillo algoritmo de compresión.
El esquema siguiente muestra cómo se serializa esta información en el kernel; el desplazamiento de cada estructura respectiva es detectado por vmlinux-to-elf mediante heurísticas:
Estos campos tienen una alineación y un tamaño de campo variables. Los tamaños de campo también pueden variar según la arquitectura y la versión del kernel. Por esta razón, vmlinux-to-elf ha sido probado en una variedad de casos.
OpenWRT desde 2013 tiene un parche que elimina la compresión de la tabla kallsyms por defecto (cuando el usuario ha habilitado la compilación de kallsyms). Lo hacen para ahorrar espacio al re-comprimir el kernel con LZMA.
Esto significa que las entradas kallsyms_token_table y kallsyms_token_address desaparecen, y que los nombres de símbolo usan texto ASCII plano en su lugar. Este caso también está soportado.
En los kernels Linux 6.2 estándar, los arrays kallsyms se codifican en el siguiente orden:
kallsyms_addresses (o kallsyms_offsets + kallsyms_relative_base)kallsyms_num_symskallsyms_nameskallsyms_markerskallsyms_seqs_of_names (solo 6.2+)kallsyms_token_tablekallsyms_token_indexPara kernels Linux 6.4+, esta distribución cambia a:
kallsyms_num_symskallsyms_nameskallsyms_markerskallsyms_token_tablekallsyms_token_indexkallsyms_addresses (o kallsyms_offsets + kallsyms_relative_base)kallsyms_seqs_of_namesMientras que estos se analizan en el siguiente orden por el algoritmo de análisis de vmlinux-to-elf:
kallsyms_token_table (estructura penúltima)kallsyms_token_index (última estructura, hacia adelante)kallsyms_markers (hacia atrás)kallsyms_names (hacia atrás de nuevo)kallsyms_num_syms (hacia atrás de nuevo)kallsyms_addresses (o kallsyms_offsets + kallsyms_relative_base) (hacia atrás de nuevo)Debería soportar kernels desde la versión 2.6.10 (diciembre de 2004) hasta la actual 6.4 (agosto de 2023). Solo los kernels configurados explícitamente sin CONFIG_KALLSYMS no deberían estar soportados. Si esta variable de configuración del kernel no se estableció en la compilación, entonces obtendrás: KallsymsNotFoundException: No embedded symbol table found in this kernel.
Para kernels sin procesar, se pueden detectar las siguientes arquitecturas (utilizando las firmas mágicas de binwalk): MIPSEL, MIPSEB, ARMEL, ARMEB, PowerPC, SPARC, x86, x86-64, ARM64, MIPS64, SuperH, ARC.
Los siguientes formatos de compresión del kernel se pueden detectar automáticamente: XZ, LZMA, GZip, BZ2, LZ4, LZO y Zstd.
También puedes obtener una salida de solo texto con los nombres, direcciones y tipos de símbolos del kernel mediante la utilidad kallsyms-finder, incluida también en esta herramienta. El formato de su salida será similar al archivo procfs /proc/kallsyms.
Algunos parámetros que deberían inferirse automáticamente mediante la herramienta (como el conjunto de instrucciones o la dirección base) pueden sobreescribirse en caso de problemas. La especificación completa de los argumentos que permiten hacerlo se presenta a continuación:
$ 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
No dudes en abrir un issue para cualquier sugerencia de mejora.
Por favor, prioriza los issues y pull requests del repositorio actual de Github para reportar errores, hacer preguntas, etc.
Alternativamente, puedes usar este canal de Matrix si necesitas contactar directamente con el autor del proyecto, pero reserva esto como un canal secundario, p. ej. para enviar muestras de kernels; lo que se publique aquí de otro modo es más probable que se pierda.
| Nombre del array | Descripción | Contenido de ejemplo |
|---|
kallsyms_addresses (o kallsyms_offsets + kallsyms_relative_base) | Las direcciones (o desplazamientos relativos a una base, en kernels recientes) de cada símbolo, como un 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 | El número total de símbolos, como un entero (útil para verificar el endianness, la alineación y la decodificación correcta de la tabla de símbolos) | 54 D4 00 00 |
kallsyms_names | Los nombres de símbolo comprimidos, separados por longitud. Cada byte en las cadenas de símbolo comprimidas hace referencia a un índice en el array "kallsyms_token_index", que a su vez hace referencia al desplazamiento de un carácter o fragmento de cadena en el 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 | Una tabla de búsqueda que sirve para encontrar rápidamente el desplazamiento aproximado de un nombre de símbolo comprimido en "kallsyms_names": cada 256 símbolos, se añade a esta tabla un desplazamiento al símbolo correspondiente en "kallsyms_names" como un long. | 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 tabla de búsqueda (presente solo en kernels 6.2+) contiene una secuencia de enteros empaquetados de 3 bytes, donde los índices del array coinciden con el orden alfanumérico de un nombre de símbolo dado, y los valores del array coinciden con los índices de entrada correspondientes en los arrays kallsyms_addresses y kallsyms_names | |
kallsyms_token_table | Fragmentos de cadena o caracteres terminados en nulo que pueden estar contenidos en los nombres de símbolo del kernel. Puede contener como máximo 256 fragmentos de cadena o caracteres. Los índices correspondientes a los puntos de código ASCII que realmente se utilizan en cualquier símbolo del kernel corresponderán al carácter ASCII en cuestión; otras posiciones contendrán un fragmento de cadena elegido estadísticamente. Esta herramienta intenta encontrar este array de forma heurística a lo largo del archivo proporcionado primero, con el fin de encontrar la tabla 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 palabras, cada una de las cuales mapea a los desplazamientos de los caracteres o fragmentos de cadena designados por sus respectivos índices en "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 ... |