Rendered from the source repository. Headings, examples, code, tables, links, and referenced images are preserved.
Firmware Static Analysis
Perform comprehensive static analysis of ELF firmware binaries using command-line reconnaissance tools. This skill guides systematic metadata extraction and binary characterization before deeper reverse engineering.
Analysis Workflow
Follow this workflow when analyzing firmware binaries. Always start from Step 1 and proceed sequentially:
- Binary identification - Determine architecture, bitness, endianness
- String extraction - Find human-readable strings and clues
- Symbol analysis - Examine imports, exports, and functions
- Structure analysis - Analyze segments, sections, and entry points
- Security analysis - Check PIE, RELRO, stack canaries
- Metadata extraction - Find compiler info and build metadata
- Report generation - Create structured markdown report
Step 1: Binary Identification
Identify the target architecture and basic characteristics.
file <binary>Extract these details:
- CPU architecture (ARM, MIPS, x86, RISC-V, PowerPC, etc.)
- Bitness (32-bit or 64-bit)
- Endianness (LSB/little-endian or MSB/big-endian)
- Link type (dynamically linked, statically linked, or not linked)
- Binary type (executable, shared object, relocatable)
Architecture reference: If unfamiliar with the detected architecture, read references/architectures.md for architecture-specific details including calling conventions and common use cases.
Step 2: String Extraction
Extract all printable strings to understand program behavior and inputs.
strings -a <binary> > strings.txtAnalyze for:
- Usage/help text patterns (
usage|help|--) - Format strings (
%s|%d|%x) - Error messages (
error|fail|invalid) - File paths and directories (
/etc/|/tmp/|/var/) - URLs and network addresses
- Hardcoded credentials (common in firmware!)
- Library names and debug paths
- Version strings and build info
Targeted searches:
strings -a <binary> | grep -i -E 'usage|error|%d|%s'
strings -a <binary> | grep -i -E 'password|admin|root|key'
strings -a <binary> | grep -E '^/|^\.' # File pathsStep 3: Symbol Analysis
Examine what functions the binary imports and exports.
Dynamic Symbols (when .dynsym is present, including many stripped binaries)
readelf --dyn-syms --wide <binary>Look for:
- Imported functions (from libc, libssl, custom libraries)
- Exported functions (what this binary provides)
- Function names that hint at behavior (
authenticate,encrypt,parse, etc.) - Standard library usage patterns
Alternative command:
objdump -T <binary>Function Discovery
Identify key functions for later disassembly:
readelf -Ws <binary> | grep ' main$'
readelf -Ws <binary> | grep -E 'FUNC.*GLOBAL'Common firmware functions to look for: main, init, setup, parse_config, handle_request, authenticate, encrypt, decrypt
Step 4: Structure Analysis
Analyze the binary's internal structure.
ELF Header
readelf -h <binary>Key information:
- Entry point address - Where execution begins
- Type - ETEXEC (non-PIE) or ETDYN (PIE/shared object)
- Machine - Confirm architecture
- Section headers - Number and locations
Program Headers (Segments)
readelf -lW <binary>Check for:
- INTERP segment - Dynamic linker path (if dynamically linked)
- LOAD segments - Memory layout (addresses, permissions)
- DYNAMIC segment - Dynamic linking information
- GNU_STACK - Stack permissions (NX bit)
Section Headers
readelf -S <binary>Important sections:
.text- Code section (note size and address).rodata- Read-only data (strings, constants).data- Initialized data.bss- Uninitialized data.dynsym/.dynstr- Dynamic symbols and strings.plt/.got- Procedure linkage table and global offset table.comment- Compiler information
Verify symbol tables directly:
# Get .dynsym and .dynstr offsets from readelf -S
xxd -s 0x<DYNSYM_OFFSET> -l 256 <binary>
xxd -s 0x<DYNSTR_OFFSET> -l 256 <binary>Dynamic Section
readelf -d <binary>Extract:
- NEEDED - Shared library dependencies
- SONAME - Shared object name (if present)
- RPATH/RUNPATH - Library search paths
- FLAGS - Dynamic linking flags
Step 5: Security Analysis
Assess available evidence, retaining an unknown/not observed result where needed. PT_GNU_RELRO is a program header. For conventional dynamically linked ELF, that segment plus immediate binding (BIND_NOW or FLAGS/FLAGS_1 containing NOW) indicates full RELRO; the segment alone indicates partial RELRO. Absent dynamic tags in a static binary require a different assessment.
GNU_STACK without E requests a non-executable stack. A missing header is unknown and depends on the ABI/kernel. Canary or _chk symbols show some instrumentation, not complete coverage; absence does not prove absence in stripped, static or inlined code. Inspect relevant functions when reporting.
PIE/ASLR Check
readelf -h <binary> | grep TypeType: EXEC (Executable file)= Non-PIE (fixed addresses)Type: DYN (Shared object file)= PIE candidate OR shared library. CheckFLAGS_1: PIE, loader metadata and intended use.- ASLR is runtime policy, not established by ELF type. Verify on the target kernel and compare mappings across runs.
Security Features Check
readelf -dW <binary> | grep -E 'BIND_NOW|FLAGS.*NOW'
readelf -lW <binary> | grep -E 'GNU_RELRO|GNU_STACK'
readelf -Ws <binary> | grep -E '__stack_chk_fail|__(memcpy|memmove|strcpy|strncpy|sprintf|snprintf|printf)_chk'Look for:
- RELRO (RELocation Read-Only) - GOT protections
- Stack canaries (
__stack_chk_fail) - NX stack (non-executable stack via GNU_STACK)
- Fortify evidence: checked wrappers such as
__memcpy_chkor__snprintf_chk
Step 6: Metadata Extraction
Extract compiler and build information.
Compiler Info
readelf -p .comment <binary>
strings <binary> | grep -i -E 'gcc|clang|build|version'Look for:
- GCC/Clang version
- Target triplet (e.g.,
arm-linux-gnueabihf-gcc) - Optimization level hints
- Build date/time
Build Metadata
readelf -n <binary> # Build ID and ABI infoStep 7: Disassembly (with symbols)
If symbols are present, disassemble key functions.
Verify Symbol Availability
readelf -Ws <binary> | grep ' main$'
readelf -Ws <binary> | grep -E '<function_name>'Disassemble Functions
objdump -d --disassemble=main <binary>
objdump -d --disassemble=<function_name> <binary>Analysis tips:
- Look for calls to imported functions (e.g.,
printf@plt,strcmp@plt) - Identify control flow (branches, loops)
- Note register usage patterns
- Track function call arguments
Step 8: Handling Stripped Binaries
When symbols are removed, adapt the analysis approach.
Strip and Compare
cp <binary> <binary>_stripped
strip <binary>_stripped
# Compare before/after
readelf -Ws <binary>
readelf -Ws <binary>_strippedDisassemble Without Symbols
# Name-based disassembly will fail:
objdump -d --disassemble=main <binary>_stripped # FAILS
# Instead, dump entire .text section:
objdump -d -j .text <binary>_stripped | lessFunction identification strategies:
- Look for common prologues (e.g., ARM:
push {r11, lr}, x86:push rbp) - Find calls to known PLT functions (e.g.,
atoi@plt,printf@plt) - Use entry point address from readelf -h as starting point
- Look for string references (xrefs to .rodata)
Report Generation
Create a structured markdown report with all findings. Use this template:
# Firmware Static Analysis Report
**Binary:** <filename>
**Analysis Date:** <date>
## Executive Summary
[2-3 sentence overview of the binary: purpose, architecture, and key findings]
## Binary Characteristics
- **File Type:** [ELF type]
- **Architecture:** [CPU architecture, bitness, endianness]
- **Entry Point:** [address]
- **Link Type:** [static/dynamic]
- **PIE:** [Yes/No/Unknown; evidence]
- **ASLR:** [Runtime tested / Not tested; evidence]
- **Stripped:** [Yes/No]
## Architecture Details
- **CPU:** [ARM/MIPS/x86/RISC-V/etc.]
- **Bitness:** [32-bit / 64-bit]
- **Endianness:** [Little-endian / Big-endian]
- **Calling Convention:** [ABI details]
## Symbol Analysis
### Imported Functions
[List key imported functions and what they suggest about behavior]
- `printf` - Formatting and output
- `socket` - Network communication
- `strcmp` - String comparison
- etc.
### Exported Functions
[List exported functions if any]
### Key Functions Identified
[List main and other important functions found]
## String Analysis
### Interesting Strings
[List notable strings found, categorized by type]
**Configuration/Paths:**
- `/etc/config.conf`
- etc.
**Credentials/Keys:**
- `admin:default_password` [⚠️ SECURITY CONCERN]
- etc.
**Error Messages:**
- "Invalid input"
- etc.
**Network/URLs:**
- `http://update.example.com`
- etc.
## Structure Analysis
### Segments
[List key program segments with addresses and permissions]
### Sections
[List important sections with sizes]
- `.text`: [size] - Code
- `.rodata`: [size] - Read-only data
- etc.
### Dynamic Dependencies
[List required shared libraries]
- `libc.so.6`
- `libssl.so.1.1`
- etc.
## Security Analysis
### Mitigations Detected
- PIE: [Yes/No/Unknown; evidence]
- ASLR: [Runtime result/Not tested]
- RELRO: [Full/Partial/None/Unknown; evidence]
- Stack canary evidence: [Observed/Not observed/Unknown; coverage unverified]
- Stack execute request: [Non-executable/Executable/Missing; runtime unverified]
- Fortify evidence: [Observed/Not observed/Unknown]
### Security Concerns
[List any security issues found]
- Hardcoded credentials
- Weak/no ASLR
- Missing stack protection
- etc.
## Metadata
### Compiler Information
- **Compiler:** [GCC/Clang version]
- **Target:** [Toolchain target triplet]
- **Build Date:** [if available]
### Build ID
[Build ID from notes section]
## Disassembly Highlights
### main() Function
[Brief description of what main does based on disassembly]
### Other Key Functions
[Brief description of other important functions]
## Recommendations for Further Analysis
1. [Specific next steps based on findings]
2. [Tools to use next: Ghidra, IDA, Binary Ninja, etc.]
3. [Specific functions or areas to focus on]
4. [Dynamic analysis recommendations]
## Appendix
### Full Symbol Table
[Attach or reference full symbol list if relevant]
### Complete String Dump
[Reference to strings.txt file]Additional References
- Architecture details: See
references/architectures.mdfor architecture-specific calling conventions and characteristics - Toolchain commands: See
references/toolchain.mdfor detailed command syntax and options
Tips
- Always work on a copy - Never modify the original firmware binary
- Save intermediate outputs - Redirect command outputs to files for reference (e.g.,
strings -a binary > strings.txt) - Cross-reference findings - Strings/imports are candidates; verify authentication use, attacker control and reachability before reporting a vulnerability
- Architecture matters - Load the architecture reference early if unfamiliar with the target
- Document as you go - Build the report incrementally during analysis
- Look for the unusual - Hardcoded credentials, unusual network addresses, and debug paths are common in firmware
- Check toolchain - Version strings are clues; verify component identity, backports and vulnerable code before assigning a CVE
- PIE vs non-PIE - ET_EXEC binaries have fixed addresses, making analysis easier
- Stripped binaries - Don't despair, entry point and PLT calls still provide context

