Firmware and embedded reversing

Red team engagements increasingly include routers, IoT devices, and industrial control systems. Firmware reversing involves stripped binaries, uncommon architectures, and minimal tooling support compared to x86 PE or ELF work.

Extracting firmware

Firmware images arrive in several ways: downloaded from a vendor update server, extracted from a device via JTAG or UART, or dumped from flash storage directly.

binwalk is the standard first pass. It identifies file systems, compressed archives, and embedded binaries within a firmware image:

binwalk firmware.bin

To extract everything it recognises:

binwalk -e firmware.bin

Common findings: SquashFS or JFFS2 file systems containing the root filesystem, uBoot headers, kernel images, and configuration blobs. The extracted root filesystem often contains the application binaries you want to analyse.

If binwalk does not identify structure, the image may be encrypted. Look for a decryption stub in an earlier boot stage, or use entropy analysis to find regions of compressed or encrypted content:

binwalk -E firmware.bin

High uniform entropy across the entire image suggests encryption. Regions of lower entropy followed by high entropy suggest a compressed or encrypted payload with a plaintext header.

Identifying the architecture

Before loading into a disassembler, identify the target architecture:

file rootfs/usr/sbin/httpd
readelf -h rootfs/usr/sbin/httpd

Common embedded architectures: MIPS (big and little endian), ARM (32-bit, various calling conventions), PowerPC, and increasingly RISC-V for newer devices. The endianness matters; getting it wrong produces nonsense disassembly.

rabin2 -I works for ELF binaries:

rabin2 -I rootfs/usr/sbin/httpd

Emulation with QEMU

Full-system emulation with QEMU lets you run the firmware or individual binaries without the physical device. This enables dynamic analysis with Frida, GDB, or strace.

For a MIPS little-endian userspace binary:

qemu-mipsel-static -L rootfs/ rootfs/usr/sbin/httpd

The -L flag sets the sysroot so that dynamic linker lookups resolve against the extracted filesystem rather than the host.

For full-system emulation of a Linux-based router firmware, QEMU emulates the entire CPU and peripherals. The setup depends on the specific board; firmae and QEMU-based firmware analysis frameworks automate much of this for common devices.

GDB for remote debugging

Attach GDB to a process running under QEMU user-mode emulation:

qemu-mipsel-static -L rootfs/ -g 1234 rootfs/usr/sbin/httpd &
gdb-multiarch rootfs/usr/sbin/httpd
(gdb) target remote :1234
(gdb) set sysroot rootfs/
(gdb) b main
(gdb) c

gdb-multiarch supports cross-architecture debugging. Set the sysroot so that GDB can resolve shared libraries from the firmware’s filesystem.

Ghidra for non-x86 targets

Ghidra supports MIPS, ARM, PowerPC, and most other embedded architectures through its processor definitions. Load the binary and set the language to the correct architecture and endianness.

For stripped firmware binaries with no symbol information, the function list will be empty after initial auto-analysis. Start from known entry points: the reset vector, interrupt handlers, and any function addresses embedded in configuration structures.

String references are often the most productive starting point. Look for error messages, web server path strings, or configuration keys; these anchor analysis to specific functions and provide context for naming.

Practical targets

The typical red team objective in firmware reversing is one of:

Finding authentication bypasses: look for hardcoded credentials, default passwords embedded in the binary, or authentication logic that can be bypassed by specific input patterns.

Finding command injection: web interfaces on embedded devices frequently pass user-supplied input to shell commands. Follow system(), popen(), and execve() call sites; trace parameters back to HTTP input handling.

Finding memory corruption: stack and heap overflows in network-facing services. strings will often reveal format strings; rabin2 -i shows whether the binary has stack canaries and NX enabled.

Ghidra’s Vulnerable Functions script and community extensions for embedded targets surface likely vulnerable call sites as a starting point.