
生のカーネルからカーネルシンボルテーブル(kallsyms)を抽出することで、完全に解析可能な.ELFを復元するツール
このツールは、vmlinux/vmlinuz/bzImage/zImage カーネルイメージ(生のバイナリブロブまたは既存のストリップ済み .ELF ファイル)から、関数および変数シンボルを復元した完全に解析可能な .ELF ファイルを取得することを可能にします。
そのために、このツールはカーネルシンボルテーブル(kallsyms)をカーネル内でスキャンします。これは、ほとんどすべてのカーネルにほぼそのままの形で存在する圧縮シンボルテーブルです。
対象のシンボルテーブルは元々圧縮されているため、元のバイナリでは見えない文字列も復元できるはずです。
出力は、IDA Pro や Ghidra で解析できる .ELF ファイルです。したがって、このツールは組み込みシステムのリバースエンジニアリングに役立ちます。
使用方法:
# 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]
ANDROID! または UNCOMPRESSED_IMG マジックで始まる特定の種類の Android boot.img ファイルを展開する [OK]「kallsyms」シンボルテーブルの簡単な歴史は、「kallsyms.py」ファイルの冒頭にあります。簡単に言うと、これは約2004年に現在の形で Linux カーネルに導入され、とりわけ「Kernel oops」メッセージを表示するために使用されています。
このテーブルには「シンボル名」「シンボルアドレス」「シンボルタイプ」のタプルが含まれています(シンボルタイプは nm ユーティリティと同様に1文字で指定されます)。この情報は単純な圧縮アルゴリズムで密にパックされています。
以下のスキーマは、この情報がカーネル内にどのようにシリアライズされるかを示しています。各構造体のオフセットは 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_indexLinux 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 (最後から2番目の構造)kallsyms_token_index (最後の構造、順方向)kallsyms_markers (逆方向)kallsyms_names (再度逆方向)kallsyms_num_syms (再度逆方向)kallsyms_addresses (または kallsyms_offsets + kallsyms_relative_base) (再度逆方向)バージョン 2.6.10(2004年12月)から現在の 6.4(2023年8月時点)までのカーネルをサポートします。サポートされないのは、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 ユーティリティを使用すると、カーネルのシンボル名、アドレス、タイプのテキストのみの出力を取得することもできます。その出力形式は /proc/kallsyms procfs ファイルに似ています。
ツールが自動的に推測するパラメータ(命令セットやベースアドレスなど)の一部は、問題が発生した場合に上書きできます。それを可能にする引数の完全な仕様は以下のとおりです。
$ 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 を開いてください。
バグ報告や質問などは、現在の Github リポジトリの issues と pull requests を優先的にご利用ください。
あるいは、プロジェクトの作者に直接連絡する必要がある場合はこの 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シンボルごとに、対象シンボルへのオフセットが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 | このルックアップテーブル(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 ... |