Athena Wiki

MIPS Reversing

reversingintermediate

Reverse engineer MIPS binaries in CTF challenges - embedded systems, routers, IoT.

reversingmipsmipselqemughidraembeddediot

MIPS Reversing

MIPS is common in embedded systems, routers, and IoT devices. CTF challenges sometimes provide MIPS binaries extracted from firmware. Like ARM, Ghidra handles MIPS natively — the decompilation workflow is the same as x86. The main differences are register names, calling conventions, and the branch delay slot.


Identifying MIPS Binary

file challenge
# → ELF 32-bit MSB executable, MIPS, MIPS-I version 1
# → ELF 32-bit LSB executable, MIPS, MIPS32 rel2 version 1 (MIPSEL)
# MSB = big-endian, LSB = little-endian (MIPSEL)

Running with QEMU

sudo apt install qemu-user qemu-user-static

# Run MIPS big-endian
qemu-mips ./mips_binary

# Run MIPS little-endian
qemu-mipsel ./mipsel_binary

# With libraries
sudo apt install libc6-mips-cross libc6-mipsel-cross
qemu-mips -L /usr/mips-linux-gnu ./mips_binary

MIPS Assembly Key Concepts

; Registers
$zero / $0    = always 0
$v0, $v1      = return values
$a0-$a3       = function arguments
$t0-$t9       = temporaries (caller-saved)
$s0-$s7       = saved (callee-saved)
$sp           = stack pointer
$ra           = return address (like ARM's LR)

; Key instructions
addiu $t0, $t1, 42  ; t0 = t1 + 42
lw $t0, 0($sp)      ; t0 = memory[sp+0]
sw $t0, 4($sp)      ; memory[sp+4] = t0
xor $t0, $t1, $t2   ; t0 = t1 XOR t2
beq $t0, $t1, label ; branch if equal
jal func            ; call func (ra = return addr)
jr $ra              ; return from function

MIPS Delay Slots

MIPS has branch delay slots — the instruction after a branch always executes before the branch takes effect:

beq $t0, $t1, target   ; branch if equal
addiu $t0, $t0, 1      ; this ALWAYS executes (delay slot!)
; branch happens here

Ghidra handles this automatically in decompilation.


Ghidra for MIPS

# Import the MIPS binary into Ghidra
# Select "MIPS:BE:32:default" or "MIPS:LE:32:default"
# Auto-analyze → decompile

Typical CTF MIPS Pattern

// Decompiled MIPS check function:
int check(char *input) {
    char key[] = {0x66, 0x6c, 0x61, 0x67};  // "flag"
    for (int i = 0; i < 4; i++) {
        if (input[i] != (key[i] ^ 0x10)) return 0;
    }
    return 1;
}
// → key XOR 0x10: 0x76, 0x7c, 0x71, 0x77 = "v|qw"

Checklist

  • file → MIPS MSB or MIPSEL (little-endian)?
  • qemu-mips or qemu-mipsel to run
  • Ghidra: select correct MIPS variant when importing
  • Remember delay slots (Ghidra handles automatically)
  • Arguments: $a0-$a3, return: $v0
  • XOR: xor instruction (same as x86 but different syntax)
  • Return: jr $ra

Last updated on

On this page