SuperAPI AI Directory

re-fw-emulate

Security & auditing

View repository

Updated 10-15 21:34

About this skill

Firmware Emulation (QEMU)

When to Use / When Not to Use

  • Use: need to run programs inside the firmware to observe behavior/verify hypotheses (firmware web interface, server-side logic)
  • Use: rootfs has been extracted and needs to be booted as a whole (full-system emulation)
  • Don't use: only static analysis is needed (already covered by [[re-fw-rootfs]])
  • Don't use: a physical board is available and real hardware interaction is required (use [[re-hardware-io]], hardware extraction/interface debugging)
  • Don't use: the goal is only to unpack and view contents (use [[re-fw-extract]])

Tool Preparation

Verify every tool before use. Emulation = dynamic execution, sandbox + network isolation by default ([[re-analyze/platform-tips]] highest principle); user-mode emulation preferred (lightest feasible approach).

qemu-user — user-mode emulation (lightest)

  • Linux: apt install qemu-user / dnf install qemu-user / pacman -S qemu-user
  • macOS: brew install qemu(includes user-mode and system-mode)
  • Windows/WSL: Linux version inside WSL
  • Verify: qemu-arm --version、qemu-mips --version(big-endian),qemu-mipsel --version(little-endian)

qemu-system — full-system emulation

  • Linux: apt install qemu-system-arm qemu-system-mips / dnf install qemu-system-arm qemu-system-mips / pacman -S qemu-system-arm qemu-system-mips
  • macOS: brew install qemu
  • Windows/WSL: Linux version inside WSL
  • Verify: qemu-system-arm --version

binfmt_misc — execute cross-architecture programs directly

  • Linux: apt install binfmt-support qemu-user-static(on Debian/Ubuntu, installing qemu-user-static automatically registers binfmt entries for each architecture; on Fedora use systemd binfmt configuration); manual registration: update-binfmts --enable qemu-arm(or a configuration file under /etc/binfmt.d/)
  • macOS/Windows: not supported, use qemu-<arch> to invoke explicitly
  • Verify: ls /proc/sys/fs/binfmt_misc/ qemu-arm entries are visible; afterwards you can directly execute ./rootfs_out/usr/sbin/httpd

gdb-multiarch + gdbserver — cross-debugging

  • Linux: apt install gdb-multiarch / dnf install gdb-multiarch / pacman -S gdb-multiarch;gdbserver: apt install gdbserver(or provided with the gdb package)
  • macOS: brew install gdb(requires Developer Tools authorization, see the macOS branch of [[re-analyze/platform-tips]]) or the Linux version inside WSL
  • Windows/WSL: Linux version inside WSL
  • Verify: gdb-multiarch --version

firmadyne — full-system automated emulation framework (for reference on the approach, optional install)

  • Install: git clone https://github.com/firmadyne/firmadyne, depends on qemu-system-* and a precompiled kernel (downloaded under scripts/), initial setup is heavy
  • Verify: ls sources/ has scripts such as getArch.py;which qemu-system-mips
  • most single-program analysis does not need it—user-mode first ([[re-analyze/platform-tips]] gives the lightest feasible approach first)

Procedure

Execute in order, record the result of each step.

  1. User-mode emulation (lightest, preferred):

    file rootfs_out/usr/sbin/httpd                    # 确认架构:ARM 32/64、MIPS(BE/EL)、RISC-V
    qemu-arm -L rootfs_out rootfs_out/usr/sbin/httpd  # -L 把 rootfs 当 sysroot(动态库/链接器从 rootfs 加载)
    # 大端 MIPS: qemu-mips -L rootfs_out ...
    # 小端 MIPS: qemu-mipsel -L rootfs_out ...
    

    Missing library errors → cross ldd / readelf -d to see dependencies, supplement libraries from rootfs; for web service programs you can add -E to pass environment variables.

    • -strace record the guest program's system calls (equivalent to strace inside emulation—use it directly to troubleshoot mmap/ioctl crash points)
    • -0 <argv0> forge argv[0] (used when a program branches on the invocation name);-cpu <型号> specify CPU (e.g. qemu-arm -cpu cortex-a9)
    • can't run but is statically analyzable → go back to [[re-fw-rootfs]] / [[re-binary-core]], don't keep fighting with emulation.
  2. Full-system emulation:

    # ARM(vexpress 平台)+ 内核与 initramfs:
    qemu-system-arm -M vexpress-a9 -kernel vmlinuz -initrd initramfs.img -nographic \
      -append "console=ttyAMA0 root=/dev/ram rdinit=/sbin/init"
    # MIPS(malta 平台,大端示例):
    qemu-system-mips -M malta -kernel vmlinuz -initrd initramfs.img -nographic \
      -append "console=ttyS0 rdinit=/sbin/init"
    

    Make the rootfs into a disk image (ext2 mounted as root) or initramfs (package rootfs_out with cpio); firmadyne's scripts automate this whole process. When the rootfs is already a disk image, mount the disk directly:

    qemu-system-arm -M vexpress-a9 -kernel vmlinuz -drive file=rootfs.ext2,format=raw \
      -nographic -append "console=ttyAMA0 root=/dev/mmcblk0 rdinit=/sbin/init"
    

    root device names are determined by the platform disk controller (vexpress=mmcblk0, malta's IDE=/dev/sda, etc.; refer to the -M platform documentation).

  3. Missing peripherals: use stub/loopback:

    • User mode: program crashes when mmap'ing a fixed address (GPIO/UART registers) → strace locate the access point,LD_PRELOAD provide a stub library that returns fake register values
    • Full system:-device attach virtual peripherals (e1000 / virtio, etc.); real chip peripherals (wifi/baseband) cannot be emulated by QEMU → patch to skip initialization or stub that ioctl
    • First determine up to which step of program initialization it crashes (serial output/logs), then decide which part to stub
  4. Cross debugging (qemu -g + gdb-multiarch):

    qemu-arm -g 1234 -L rootfs_out rootfs_out/usr/sbin/httpd &   # -g 起 gdbstub
    gdb-multiarch rootfs_out/usr/sbin/httpd
    (gdb) target remote :1234
    

    Inside the full system: put gdbserver into the rootfs,gdbserver :1234 /usr/sbin/httpd, host target remote <qemu_ip>:1234; debugging techniques follow [[re-gdb]].

  5. Emulation under network isolation:

    • User mode: it is not a network sandbox (socket/connect are forwarded to the host kernel via syscall); when a controlled network is needed, switch to the full-system approach
    • Full system: if QEMU is not given network parameters, it creates a NIC (e1000) + user backend by default—first explicitly -nic none confirm the behavior, then add a controlled network with -nic user,restrict=on(user-mode NAT, emulates outbound only)
    • Isolate before analyzing callbacks/protocols ([[re-analyze/platform-tips]] highest principle); traffic capture and protocol reconstruction go to [[re-protocol]]; firmadyne also comes with a NIC by default and must be isolated first per this principle

Cross-domain integration

  • [[re-firmware]]: step 4 of the workflow always invokes this skill
  • Architecture identification and instruction-level deep dive: ARM (vector table/Thumb/MMIO peripheral cross-referencing) → [[re-arm]]; RISC-V (RV32/RV64/ecall) → [[re-riscv]] (cross-check before choosing the right qemu- prefix)
  • Observe dynamic behavior inside emulation → [[re-tracing]] + [[re-gdb]] (by default inside the sandbox, [[re-analyze/platform-tips]] highest principle)
  • Firmware execution produces communication → [[re-protocol]]; deep dive into ELF inside emulation → [[re-binary-core]]
  • Programs that cannot be emulated fall back to static → [[re-fw-rootfs]]

Common pitfalls and traps

  • Crash on peripheral register access: symptom—segmentation fault when reading GPIO/UART registers after the program mmap's a fixed address; cause—QEMU user mode does not emulate peripherals, so the address is unmapped; countermeasure—use strace to locate the access point, LD_PRELOAD a stub that returns simulated values (step 3)
  • Wrong architecture choice causes an immediate segfault: symptom—qemu-arm running a MIPS program crashes instantly; cause—did not first file/readelf confirm the architecture and endianness (big-endian mips ≠ mipsel); countermeasure—confirm first in step 1, choose the right qemu-
  • NIC model mismatch → initialization hangs: symptom—the program hangs at NIC initialization and does not exit; cause—the firmware initializes for a specific chip, the default-attached e1000 does not match, and the ioctl never returns; countermeasure—add -device <型号> and other virtual NICs as the firmware expects, or first -nic none observe whether it skips (step 5)
  • Timestamp/clock function traps: symptom—the program reads strange times (1970/going backwards) and behaves differently from the real device; cause—QEMU's virtual clock is not synchronized with wall-clock time; countermeasure—-rtc base=utc fix it, or stub out clock_gettime-related calls
  • Emulating without network isolation: Phenomenon — the firmware actually connects back to the external network (C2/update server); Cause — QEMU creates a NIC + user backend by default (restrict=off), so skipping explicit isolation means it goes online; Countermeasure — for full-system emulation, explicitly -nic none, and user mode is not a network sandbox either (step 5); before callback analysis, isolate according to [[re-analyze/platform-tips]]
  • For a quick reference of command families and operation sequences, see [[commands]]; for tool-specific pitfalls and version differences, see [[gotchas]]