最近在进行iot方面的学习,这里出一篇环境搭建教程以及32位指令集详解
本篇中使用的是截止到目前为止最新的ubuntu版本Ubuntu 24.04 LTS, kernel版本为6.8.0-51

指令直接输入即可
# 1. 更新源
sudo apt update
# 2. 安装 QEMU 核心组件
sudo apt install -y qemu-system qemu-user-static binfmt-support
# 3. 安装 ARM 交叉编译工具链
sudo apt install -y libncurses5-dev gcc-arm-linux-gnueabi build-essential gcc-aarch64-linux-gnu
# 4. 安装 MIPS 交叉编译工具链
sudo apt install -y gcc-mips-linux-gnu gcc-mipsel-linux-gnu gcc-mips64-linux-gnuabi64 gcc-mips64el-linux-gnuabi64
# 5. 安装 GDB Multiarch (用于调试异构架构程序)
sudo apt install -y gdb-multiarch使用的gdb为pwndbg和pwntools
测试一下环境,测试代码:
#include <stdio.h>
#include <unistd.h>
int main(){
char buffer[0x6];
printf("kiki1e\n");
read(0,buffer,0x6);
printf("%s",buffer);
return 0;
}指令
❯ mipsel-linux-gnu-gcc -g test.c -o test_mipsel_32
❯ readelf -h test_mipsel_32
测试qemu
qemu-mipsel-static -L /usr/mipsel-linux-gnu/ ./test_mipsel_32
MIPS 32位指令集详解
1. 寄存器命名约定 (Registers)
MIPS拥有32个通用寄存器,在汇编中既可以用编号($0-$31)表示,也可以用别名表示。在逆向分析中,熟记别名至关重要。
2. 数据传输指令 (Data Transfer)
MIPS是加载/存储(Load/Store)架构,只有 load 和 store 指令可以访问内存,其他指令只能操作寄存器。
加载指令 (Load)
存储指令 (Store)
3. 算术与逻辑运算 (Arithmetic & Logical)
操作数通常是三个:目标寄存器,源寄存器1,源寄存器2(或立即数)。
算术指令
逻辑指令
4. 分支跳转指令 (Branch & Jump)
控制程序流向的核心指令。
条件分支 (Branch)
无条件跳转 (Jump)
5. 特殊机制:延迟槽 (Delay Slot)
MIPS 架构的一个重要特性。 由于流水线设计,跳转指令(j, jal, beq 等)后面的那条指令(即延迟槽指令)会先于跳转发生前执行。
汇编示例:
jal printf ; 调用 printf 函数
move $a0, $s0 ; 【延迟槽】这条指令会在跳转生效前执行!
; 实际上参数 $a0 是在 printf 被调用前准备好的
MIPS32 栈溢出 + ROP 链构造全流程(以 IoT 固件栈溢出漏洞为例)
(1)漏洞环境与调试指令
环境:某 MIPS32 Little-Endian 固件(Linux 3.10,glibc 2.23),漏洞函数为 “设备名称设置”(未限制输入长度,strcpy 导致栈溢出);
调试工具:
qemu-mipsel-static + gdb-multiarch + peda-mips;核心调试指令:
启动模拟:
qemu-mipsel-static -g 1234 -L ./glibc-mipsel ./firmware-bin(-L 指定交叉编译 glibc 路径,避免库依赖错误);连接调试:
gdb-multiarch→set architecture mipsel→target remote localhost:1234;定位漏洞:
b *0x40081560(漏洞函数中 strcpy 调用地址)→c→ 输入测试 payload(如python -c "print('A'*200)")→ 观察崩溃时寄存器状态;计算偏移:
info registers查看 ra 寄存器值(若为 0x41414141,说明已覆盖),通过逐步调整 “A” 的长度,确定缓冲区到 ra 的偏移为 144 字节(前 128 字节覆盖缓冲区 + 寄存器保存区,后 16 字节覆盖 ra 及后续栈空间);查找 gadget:
ROPgadget --binary ./firmware-bin --only "lw|sw|move|jr|syscall"→ 筛选可用 gadget(如读取 libc 地址的 lw gadget、触发 syscall 的 gadget)。
(2)ROP 链构造逻辑(应对 NX 防护,栈不可执行)
核心目标:构造 ROP 链泄露 libc 基址 → 计算 system 和 “/bin/sh” 地址 → 触发 system ("/bin/sh")。
第一步:泄露 libc 基址(利用 puts 函数输出 puts@GOT 地址)
gadget1:
lw $t9, 0x8($sp)+jr $t9+nop(地址 0x40050230)→ 从栈中加载 puts 地址到 t9,跳转执行 puts;gadget2:
move $a0, $s0+jr $t9+nop(地址 0x40060110)→ 将 s0 寄存器的值(puts@GOT 地址)传入 a0(puts 函数参数);ROP 片段:
偏移填充(144字节) + gadget2地址 + puts@GOT地址 + gadget1地址 + puts@PLT地址;原理:执行时先通过 gadget2 将 puts@GOT 地址传入 a0,再通过 gadget1 调用 puts@PLT,输出 puts 的真实地址(libc 中的地址),结合 glibc 2.23 MIPS 版本的 puts 与 system 偏移(如 0x25800),计算 system 地址。
第二步:构造 system ("/bin/sh") 调用
构造 “/bin/sh” 字符串:在 payload 中嵌入 “/bin/sh\x00”(Little-Endian 存储为 0x6e69622f、0x68732f00),地址为栈中固定偏移(如 0x7fffe500);
gadget3:
move $a0, $s1+jr $t9+nop(地址 0x40070340)→ 将 s1 寄存器的值(“/bin/sh” 地址)传入 a0;ROP 片段:
system地址 + 0xdeadbeef(填充ra,无实际作用) + gadget3地址 + "/bin/sh"地址;原理:跳转至 system 地址时,t9 已加载 system 地址,延迟槽执行 nop,a0 通过 gadget3 传入 “/bin/sh” 地址,满足 MIPS 传参约定,触发 shell。