
Инструмент для восстановления полностью анализируемого .ELF-файла из сырого ядра путем извлечения таблицы символов ядра (kallsyms)
Этот инструмент позволяет получить полностью анализируемый файл .ELF из образа ядра vmlinux/vmlinuz/bzImage/zImage (либо из «сырого» бинарного файла, либо из уже существующего, но лишённого символов файла .ELF) с восстановленными символами функций и переменных.
Для этого он сканирует ваше ядро на наличие таблицы символов ядра (kallsyms) — сжатой таблицы символов, которая присутствует почти в каждом ядре в почти неизменном виде.
Поскольку соответствующая таблица символов изначально сжата, инструмент позволяет восстановить строки, которые не видны в исходном бинарном файле.
На выходе получается файл .ELF, который можно анализировать с помощью IDA Pro и Ghidra. Поэтому этот инструмент полезен для реверс-инжиниринга встраиваемых систем.
Использование:
# 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
Установка:
# 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
Настройка локального окружения для разработки:
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, начинающиеся с магического ANDROID! или UNCOMPRESSED_IMG [OK]Краткая история таблицы символов «kallsyms» приведена в начале файла «kallsyms.py». Вкратце, она появилась в ядре Linux примерно в 2004 году в своём нынешнем виде и используется, среди прочего, для вывода сообщений «Kernel oops».
Она содержит кортежи «имя символа», «адрес символа», «тип символа» (типы символов обозначаются одной буквой, аналогично утилите nm), и эта информация плотно упакована простым алгоритмом сжатия.
На схеме ниже показано, как эта информация сериализуется в ядре; смещение каждой соответствующей структуры определяется vmlinux-to-elf с помощью эвристик:
Эти поля имеют переменное выравнивание и размер. Размеры полей также могут различаться в зависимости от архитектуры и версии ядра. По этой причине vmlinux-to-elf был протестирован на множестве различных случаев.
OpenWRT с 2013 года имеет патч, который по умолчанию отключает сжатие таблицы kallsyms (когда сборка kallsyms включена пользователем). Это делается для экономии места при повторном сжатии ядра с помощью LZMA.
Это означает, что записи kallsyms_token_table и kallsyms_token_address исчезают, а имена символов используют обычный текст ASCII. Этот случай также поддерживается.
В стандартных ядрах Linux 6.2 массивы kallsyms кодируются в следующем порядке:
kallsyms_addresses (или kallsyms_offsets + kallsyms_relative_base)kallsyms_num_symskallsyms_nameskallsyms_markerskallsyms_seqs_of_names (только 6.2+)kallsyms_token_tablekallsyms_token_indexДля ядер Linux 6.4+ эта компоновка изменена на:
kallsyms_num_symskallsyms_nameskallsyms_markerskallsyms_token_tablekallsyms_token_indexkallsyms_addresses (или kallsyms_offsets + kallsyms_relative_base)kallsyms_seqs_of_namesПри этом алгоритм разбора vmlinux-to-elf обрабатывает их в следующем порядке:
kallsyms_token_table (предпоследняя структура)kallsyms_token_index (последняя структура, в прямом направлении)kallsyms_markers (в обратном направлении)kallsyms_names (снова в обратном направлении)kallsyms_num_syms (снова в обратном направлении)kallsyms_addresses (или kallsyms_offsets + kallsyms_relative_base) (снова в обратном направлении)Он должен поддерживать ядра от версии 2.6.10 (декабрь 2004 года) до текущей версии 6.4 (по состоянию на август 2023 года). Не поддерживаются только ядра, явно собранные без CONFIG_KALLSYMS. Если эта переменная конфигурации ядра не была задана при сборке, вы получите: KallsymsNotFoundException: No embedded symbol table found in this kernel.
Для «сырых» ядер могут быть определены следующие архитектуры (с использованием сигнатур из binwalk): MIPSEL, MIPSEB, ARMEL, ARMEB, PowerPC, SPARC, x86, x86-64, ARM64, MIPS64, SuperH, ARC.
Следующие форматы сжатия ядра определяются автоматически: XZ, LZMA, GZip, BZ2, LZ4, LZO и Zstd.
Вы также можете получить текстовый вывод имён, адресов и типов символов ядра с помощью утилиты kallsyms-finder, также входящей в состав этого инструмента. Формат её вывода будет похож на формат файла procfs /proc/kallsyms.
Некоторые параметры, которые должны автоматически определяться инструментом (например, система команд или базовый адрес), могут быть переопределены в случае проблем. Полная спецификация аргументов для этого приведена ниже:
$ 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
Не стесняйтесь открыть issue с любыми предложениями по улучшению.
Пожалуйста, приоритетно используйте issues и pull requests текущего репозитория GitHub для сообщений об ошибках, вопросов и т.п.
Кроме того, вы можете использовать этот канал Matrix, если вам нужно напрямую связаться с автором проекта, но, пожалуйста, используйте его как вторичный канал, например для отправки образцов ядра; всё остальное, что сюда попадает, скорее всего, будет потеряно.
| Имя массива | Описание | Пример содержимого |
|---|
kallsyms_addresses (или kallsyms_offsets + kallsyms_relative_base) | Адреса (или смещения относительно базы в последних ядрах) каждого символа в виде массива | 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 | Общее количество символов в виде целого числа (полезно для проверки порядка байтов, выравнивания и корректности декодирования таблицы символов) | 54 D4 00 00 |
kallsyms_names | Сжатые имена самих символов, разделённые по длине. Каждый байт в сжатой строке символа ссылается на индекс в массиве «kallsyms_token_index», который, в свою очередь, ссылается на смещение символа или фрагмента строки в массиве «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 | Таблица поиска, позволяющая быстро найти приблизительное смещение сжатого имени символа в «kallsyms_names»: каждые 256 символов в эту таблицу добавляется смещение соответствующего символа в «kallsyms_names» в виде длинного целого. | 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 | Эта таблица поиска (присутствует только в ядрах 6.2+) содержит последовательность упакованных 3-байтовых целых чисел, где индексы массива соответствуют алфавитному порядку для данного имени символа, а значения массива — соответствующим индексам записей в массивах kallsyms_addresses и kallsyms_names | |
kallsyms_token_table | Завершающиеся нулём фрагменты строк или символы, которые могут встречаться в именах символов ядра. Их может быть не более 256 фрагментов строк или символов. Индексы, соответствующие кодовым точкам ASCII, которые фактически используются в каком-либо символе ядра, будут соответствовать соответствующему символу ASCII; остальные позиции будут содержать статистически выбранный фрагмент строки. Этот инструмент сначала пытается эвристически найти этот массив в переданном файле, чтобы найти таблицу символов 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 слов, каждое из которых сопоставляется со смещениями символов или фрагментов строк, обозначенных соответствующими индексами в «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 ... |