Extract and apply function names, data structures, and symbols between firmware binaries using Ghidra. Designed for reverse engineering modified firmware by porting symbols from reference builds.
My current use is extracting the symbols, functions, etc. from the real Unleashed Flipper Zero firmware's firmware.elf and importing them into the bin I extracted from the "Unleashed V2" firmware using my bin extraction tool https://github.com/RocketGod-git/DFU-Binary-Extractor and further investigations can be done with https://github.com/RocketGod-git/RocketGods-SubGHz-Toolkit
Extract and apply function names, data structures, and symbols between firmware binaries using Ghidra. Designed for reverse engineering modified firmware by porting symbols from reference builds.
- Extract functions, strings, pointers, and data symbols from ELF files
- Apply extracted symbols to stripped binary files based on instruction sequence matching
- Name interrupt vectors using vendor startup files
- Import/export known function databases for iterative analysis
- Support for base address relocation and Thumb mode detection
Ideal for analyzing custom firmware variants, OEM modifications, or production builds that lack debug symbols. Originally developed for Flipper Zero firmware analysis but works with any compatible binary format. Might have to tweak a bit for other applications.
This example shows how to restore symbols from Unleashed firmware to analyze a custom/modified firmware.bin.
Clone and build Unleashed firmware: bash git clone https://github.com/DarkFlippers/unleashed-firmware cd unleashed-firmware ./fbt.cmd firmware_all # Windows ./fbt firmware_all # Linux/Mac
Open Ghidra Import the Unleashed ELF file: unleashed-firmware/build/f7-firmware-D/firmware.elf Let Ghidra auto-analyze it (just click OK on the analysis dialog) Open Script Manager (Window → Script Manager) Create new script, paste the porter script, save as firmware_symbol_porter.py Run the script and choose "Extract from ELF (Step 1)" Wait for extraction to complete (will show statistics)
Import your custom firmware.bin into Ghidra Important: Set base address to 0x08000000 during import Language: ARM:LE:32:Cortex (or let auto-detect) Click OK and let it analyze Run the script again and choose "Apply to BIN (Step 2)" The script will: Apply map file labels Name interrupt vectors Match functions by instruction hash Apply any known symbols
After manual analysis adds more symbols: Run the script and choose "Export known functions (Step 3)" This creates a database of your named functions Use this database on similar firmware variants
Edit these paths at the top of the script:
BIN_PATH = r"C:\path\firmware.bin"
UNLEASHED_ROOT = r"C:\path\unleashed-firmware"
ELF_DATA_PATH = r"C:\path\ULV2-TEST\elf_function_data.json"
KNOWN_FUNCTIONS_PATH = r"C:\path\ULV2-TEST\known_functions.json"
Adapting for Other Firmware STM32 Projects For other STM32 firmware, modify:
Base Address: Change ROM_BASE if your MCU maps flash elsewhere python ROM_BASE = 0x08000000 # STM32 default, change if needed Startup File: Update path to your startup assembly file python STARTUP_PATH = os.path.join(PROJECT_ROOT, r"path\to\your\startup_stm32xxx.s") Build Paths: Adjust ELF and map file locations
ELF_CANDIDATES = [
r"build\output\firmware.elf",
r"Debug\project.elf"
]
MAP_CANDIDATES = [
r"build\output\firmware.map",
r"Debug\project.map"
]
For non-ARM processors:
Remove Thumb Mode: Comment out _ensure_thumb() calls Adjust Vector Parsing: Modify _parse_startup_names() for your assembly syntax Update Validation: Change the vector table validation in _get_actual_base() ESP32 Example python ROM_BASE = 0x40000000 # ESP32 IROM base VEC_MAX = 256 # ESP32 has more vectors
vcount = _name_vectors(api, STARTUP_PATH, base_to_use)
For unknown architectures:
Set MIN_FUNC_LENGTH = 10 (increase for better matching accuracy) Disable vector naming Rely primarily on function hash matching
Symbol Extraction: Reads ELF debug symbols and creates instruction hashes for each function Hash Matching: Compares instruction sequences between binaries to identify matching functions Offset Normalization: Stores symbols as offsets from base address for portability Multi-Source Application: Combines map files, vector tables, and hash matching for comprehensive restoration
"No functions matched"
The binary may be compiled with different optimization settings Try reducing MIN_FUNC_LENGTH to 3 for more aggressive matching Check if symbols are already named (obfuscated vs stripped) "Context register change conflicts"
This is normal for Thumb mode setting, the script handles it gracefully Wrong base address
Check Window → Memory Map in Ghidra Ensure your binary is loaded at the correct address (0x08000000 for STM32) Missing startup file
Use PowerShell to find it: Get-ChildItem -Path "firmware" -Recurse -Filter "startup.s" Or disable vector naming if not needed
