SuperAPI AI Directory

re-mips

Security & auditing

View repository

Updated 10-16 05:06

About this skill

MIPS architecture reverse engineering (router firmware)

When to use / when not to use

  • Use: MIPS architecture ELF samples/malware (executables extracted from router and embedded device firmware)
  • Use: logic analysis of programs inside router firmware (web backends, services, utility binaries)
  • Use: MIPS big-endian (mips)/little-endian (mipsel) binaries of any endianness
  • Do not use: other architectures such as ARM / x86 (general foundation [[re-binary-core]], this skill only supplements MIPS-specific semantics)
  • Do not use: when you only need to unpack firmware and look at the contents (go to [[re-fw-extract]])
  • Do not use: when you only need to analyze the filesystem/configuration (go to [[re-fw-rootfs]])
  • Do not use: when you need to run the firmware as a whole (go to [[re-fw-emulate]])

Tool preparation

Verify all tools before use. Static analysis can be done without a sandbox; qemu-user dynamic execution defaults to sandbox + network isolation ([[re-analyze/platform-tips]] highest principle).

Decompiler (choose either Ghidra / IDA)

  • Ghidra (built-in MIPS processor modules, with complete mips / mipsel / mips64 / mips64el variants):
    • Linux: official release package (requires JDK); some distribution repositories have apt install ghidra / pacman -S ghidra
    • macOS: brew install --cask ghidra; Windows: official zip
    • Verify: analyzeHeadless -help(headless mode) or import a MIPS ELF via the GUI
  • IDA: the commercial version includes a MIPS module; the architecture support scope of the Freeware version is subject to the official page
  • When importing, confirm the processor variant and endianness are correct (mips big-endian / mipsel little-endian); choosing wrong makes everything misaligned

binwalk / unblob —— firmware and embedded file scanning

  • For installation channels and version differentiation (v3 mainline / v2 legacy), see the binwalk/unblob installation matrix in [[re-fw-extract]]
  • Verify: binwalk --version、unblob --version

readelf / file —— architecture and byte order confirmation (binutils)

  • Linux: bundled with binutils (apt install binutils , etc.); macOS: brew install binutils or LLVM-based readelf; Windows: inside WSL
  • Verify: readelf --version

qemu-user —— MIPS user-mode emulation (dynamic verification)

  • Linux: apt install qemu-user / dnf install qemu-user / pacman -S qemu-user
  • macOS: brew install qemu(including user mode); Windows/WSL: Linux version inside WSL
  • Big-endian and little-endian are separate binaries:qemu-mips(big-endian) / qemu-mipsel(little-endian), 64-bit is qemu-mips64 / qemu-mips64el
  • Verify: qemu-mips --version、qemu-mipsel --version

python3

  • Linux: apt install python3 / dnf install python3 / pacman -S python
  • macOS: built in; Windows: official installer
  • Verify: python3 --version

Operation steps

Execute in order, archive the result of each step; step 5 (dynamic execution) defaults to sandbox.

  1. Prerequisite: firmware extraction and architecture confirmation:

    • Whole firmware file → unpack with [[re-fw-extract]]; extracted filesystem → [[re-fw-rootfs]]; extracted ELF continues with this step
    • Architecture confirmation:
      file target.elf               # 是否 MIPS + 端序提示
      readelf -h target.elf         # Machine=MIPS;Data=big-endian/little-endian
      readelf -A target.elf | head  # 子版本/ABI 附加信息
      
    • file Enter this skill's workflow only if the output contains "MIPS"; if not MIPS, go to the [[re-binary-core]] general foundation
  2. Endianness determination (big-endian / little-endian):

    • file Output MSB → big-endian (processor variant mips); LSB → little-endian (mipsel)
    • readelf -h The Data field directly gives the endianness; e_flags contains ABI information (o32 / n32 / n64)
    • strings readability verification: big-endian data appears byte-reversed from a little-endian perspective (e.g., magic number hsqs appears in reverse order)
    • After confirming, import into the decompiler and select the correct variant, then handle $gp, jump targets, and string offsets throughout according to that endianness—if the endianness is wrong, the data, instructions, and addresses will all be misaligned
  3. Decompilation notes (MIPS-specific semantics):

    • Delay slot (hardware feature): the ordinary branch/jump in pre-R6 (beq / bne / j / jal) is followed by one instruction that executes on both the taken / not-taken paths (control transfer occurs after execution);Branch Likely(beql / bnel / blezl / bgtzl / bltzl / bgezl and FP likely types) are different—the delay slot executes only when taken, and when not-taken the delay slot is annul(skipped);beq / bne / j / jal is often followed by an assembler-filled nop or a useful instruction; when patching, the delay slot after a branch must be handled (fill with nop or an equivalent instruction); MIPS32r6 removes branch-likely and adds a compact branch form with no delay slot (beqc / bnec / bc / balc / jic / jialc), but beq / bne / jalr / jr still has a delay slot—classify by ISA revision + opcode, and do not say categorically that R6 eliminates delay slots
    • Calling convention: $a0-$a3 pass the first 4 arguments (extra arguments passed on the stack), $v0 is the return value, $ra is the return address (jal is written automatically), $s0-$s7 are callee-saved, $t0-$t9 are caller-saved; PIC indirect call pattern jalr $t9($t9 loads the address of the called function, and the function prologue often reloads $gp)
    • $gp and .got: $gp (r28) generally points to the middle of .got (commonly .got+0x7FF0, with a ±32KB offset covering the entire GOT); data access takes the form lw $t0, off($gp) / addiu $t0, $gp, off; the function prologue commonly has lui gp, %hi(...) + addiu gp, gp, %lo(...) resetting $gp
    • -mlong-calls trampoline:j / jal immediate jump only covers the same 256MB segment (26-bit address shifted left by 2 bits); cross-segment calls use compile-time generated stub trampolines (first load the address then jalr); seeing blocks of lui + addiu + jalr three-instruction sequences are mostly trampolines, do not treat them as business logic
    • Decompilers (such as Ghidra) handle delay slots and GOT references by default, but when symbols/static chains are stripped the $gp value needs manual confirmation (see pitfall 3)
  4. Router firmware specifics:

    • Locating the web backend httpd: in the rootfs find httpd-family programs (httpd / mini_httpd / boa / lighttpd and vendor-renamed versions) and web directories (www / htdocs / cgi-bin); search strings for "GET " / "HTTP/1" / response header strings to locate the main request loop
    • Authentication bypass / command injection patterns: search system( / popen( / exec call sites → trace back whether the argument source is a URL parameter or CGI field; a common authentication flaw is the backend trusting client-controllable fields (cookie, Referer, fixed password) or hardcoded password comparison
    • Identifying busybox integrated commands: busybox is an all-in-one—multiple applets share the same main and dispatch by argv[0]; how to locate: at runtime busybox --list, statically find the applet table in the symbol table (applet_names); multiple applet logics in a single binary, avoid mistakenly judging "one function handles everything"
    • Config and password hash extraction: /etc/passwd, /etc/shadow, /etc/config configuration directory (common in open-source router systems) and web backup configs; password hashes commonly $1$(MD5-crypt)、$5$ / $6$(SHA-crypt) and custom formats; backup configurations are often base64 / gzip / custom-packed (connecting to [[re-fw-rootfs]] and [[re-crypto-id]])
  5. Dynamic validation (sandbox):

    • Default sandbox principle: before running MIPS programs with qemu-user, first enter [[re-sandbox]] (network isolation + snapshot)
    • qemu-mips -L <rootfs路径> ./target           # 大端;小端用 qemu-mipsel
      qemu-mips -L <rootfs路径> -strace ./target   # 系统调用跟踪
      qemu-mips -L <rootfs路径> -E <ENV=val> ./target
      
    • -L Specify rootfs as the source of dynamic loaders/libraries (MIPS programs often depend on firmware-internal libc and configuration)
    • Dynamic is only for behavior validation (file/network/configuration reads); the main logic is primarily static

Cross-domain federation

  • [[re-firmware]]: gateway path for firmware-type samples (re-firmware → re-fw-extract → re-fw-rootfs → this skill)
  • [[re-fw-extract]] / [[re-fw-rootfs]]: prerequisites for firmware unpacking and filesystem/configuration analysis
  • [[re-binary-core]]: general MIPS ELF reconnaissance/decompilation/debugging foundation (sub-skills such as [[re-ghidra]] and [[re-gdb]] are used as usual)
  • [[re-vuln]]: vulnerability confirmation and exploitation validation for web authentication bypass/command injection
  • [[re-sandbox]]: mandatory prerequisite for all dynamic execution ([[re-analyze/platform-tips]] default sandbox principle)
  • Supporting: [[re-emulation]] (use Unicorn to emulate MIPS instructions in scenarios without QEMU), [[re-fw-emulate]] (when the entire firmware needs to be booted), [[re-crypto-keys]] (hardcoded keys in configurations/firmware), [[re-patching]] (byte patching aware of delay slots)

Common pitfalls and traps

  • Delay slot execution flow misalignment (including annul semantics of branch-likely): Symptom—the logic at a manually read branch does not line up; the instruction immediately following the branch "seemingly should not execute" but changes state (or conversely, when a likely branch is not-taken, the instruction in the slot does not execute); Cause—MIPS hardware characteristic: after pre-R6 ordinary branches/jumps (beq/bne/j/jal), the immediately following instruction executes before control transfers on either path of the jump, whereas Branch Likely (beql/bnel/blezl/bgtzl/bltzl/bgezl and FP likely types) executes the delay slot only when taken, and annuls when not-taken; Countermeasure—distinguish non-likely from likely: for the former, count the delay slot instruction into both jump paths; for the latter, count it only in the taken path; patch by adding a nop/equivalent instruction after the branch; when relying on decompiler output, verify the restored position of the delay slot instruction
  • Wrong endianness determination → everything becomes garbled: Symptom—strings, immediates, and addresses are all garbled, and decompilation is unrecognizable; Cause—MIPS has both big-endian and little-endian variants (router firmware is mostly big-endian, but little-endian also exists); after reading the wrong endianness, all 4-byte whole words are misaligned; Countermeasure—file(MSB/LSB)+ readelf -h Triple confirmation with the Data field + strings readability, and choose the correct mips / mipsel variant in the import tool
  • Without relocation information, the $gp base address can only be guessed: Symptom—lw $t0, off($gp) points to something unknown, and GOT references cannot be resolved; Cause—after symbols are stripped/static linking, relocations are lost, and the $gp value (commonly .got+0x7FF0) must be confirmed manually; Countermeasure—in _start / the crt0 prologue's lui gp + addiu gp calculate the actual gp value and annotate it in the decompiler; when there are no symbols, infer it from the .got segment base address
  • Non-standard headers/multi-layer compression in router firmware: Symptom—binwalk cannot extract it, or the extracted content is not a filesystem, and the web backend cannot be found; Cause—vendor custom headers + multi-layer compression/nested packing (same common pitfall as [[re-fw-extract]]); Countermeasure—manually inspect magic numbers with hexdump, skip the header offset and split with dd, confirm layer by layer with file, and then analyze
  • Differences in decompiler tool handling of pseudo-instructions: Symptom - the same function has large output differences across different decompilers, and the expansion of li / la / move is inconsistent; Cause - the MIPS assembler expands pseudo-instructions in various ways (li split into lui+ori, etc.), and tools restore them according to their own rules; Countermeasure - take machine-code semantics as authoritative, cross-compare tools, and verify key logic (delay slots / trampolines / GP references) back at the instruction level
  • Misjudgment of busybox single-binary multiple applets: Symptom - in one binary, the logic "does everything," and no entry point can be found; Cause - busybox dispatches applets according to argv[0], and all logic shares one main; Countermeasure -busybox --list or the symbol table applet table (applet_names) to locate the specific applet, and use the corresponding subcommand entry as the starting point for analysis