
원시 커널에서 커널 심볼 테이블(kallsyms)을 추출하여 완전히 분석 가능한 .ELF를 복구하는 도구
이 도구는 vmlinux/vmlinuz/bzImage/zImage 커널 이미지(원시 바이너리 블롭 또는 이미 존재하지만 심볼이 제거된 .ELF 파일)에서 함수 및 변수 심볼을 복구하여 완전히 분석 가능한 .ELF 파일을 얻을 수 있게 해 줍니다.
이를 위해 이 도구는 커널에서 커널 심볼 테이블(kallsyms)을 스캔합니다. 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 유틸리티와 유사하게 단일 문자로 지정됩니다). 이 정보는 간단한 압축 알고리즘으로 빽빽하게 패킹되어 있습니다.
아래 스키마는 이 정보가 커널에 직렬화되는 방식을 보여줍니다. 각 구조체의 오프셋은 vmlinux-to-elf가 휴리스틱을 통해 감지합니다.
이 필드들은 정렬과 필드 크기가 가변적입니다. 필드 크기는 아키텍처와 커널 버전에 따라 달라질 수도 있습니다. 이러한 이유로 vmlinux-to-elf는 다양한 사례에 대해 테스트되었습니다.
OpenWRT는 2013년부터 기본적으로 kallsyms 테이블의 압축을 제거하는 패치를 보유하고 있습니다(사용자가 빌드 시 kallsyms를 활성화한 경우). 이는 LZMA를 사용하여 커널을 다시 압축할 때 공간을 절약하기 위한 것입니다.
즉, kallsyms_token_table 및 kallsyms_token_address 엔트리가 사라지고, 심볼 이름이 일반 텍스트 ASCII를 대신 사용하게 됩니다. 이 경우도 지원됩니다.
표준 Linux 6.2 커널에서 kallsyms 배열은 다음 순서로 인코딩됩니다:
kallsyms_addresses (or 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 (끝에서 두 번째 구조)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
개선 제안이 있으면 주저하지 말고 이슈를 열어 주세요.
버그 신고, 질문 등은 현재 Github 저장소의 이슈와 풀 리퀘스트를 우선적으로 이용해 주시기 바랍니다.
또는 프로젝트 작성자에게 직접 연락이 필요하다면 이 matrix 채널을 사용할 수 있지만, 커널 샘플 전송 등과 같은 보조 채널로만 사용해 주시기 바랍니다. 그 외의 용도로 여기에 올린 내용은 유실될 가능성이 더 높습니다.
| 배열 이름 | 설명 | 샘플 내용 |
|---|
kallsyms_addresses (or 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"의 해당 심볼에 대한 오프셋이 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 ... |