2. MIPS交叉编译环境搭建

说实话,搭建交叉编译环境是MIPS Pwn学习中最容易劝退的一步。我见过太多人卡在这里——工具链装不上、QEMU跑不起来、调试器连不上目标。但这一步绕不过去,你想想看,没有交叉编译环境,你连一个MIPS的ELF文件都生成不了,后面还怎么玩漏洞利用?

这一章我会带你一步步把环境搭好。我自己的习惯是尽量用包管理器,实在不行再手动编译。毕竟咱们的目标是学Pwn,不是学编译工具链。

2.1 安装mips-linux-gnu-gcc工具链

MIPS交叉编译工具链,说白了就是一套能在x86电脑上生成MIPS可执行文件的工具。常用的有两大流派:mips-linux-gnu- 和 mipsel-linux-gnu-。前者是大端(Big-Endian),后者是小端(Little-Endian)。CTF里小端更常见,但大端也偶尔出现。

核心区别:大端(MIPS)和小端(MIPSEL)的字节序相反。同一个漏洞利用代码,换一个端序就得重写。我建议两个都装上。

在Ubuntu/Debian上安装很简单:

# 安装大端MIPS工具链
sudo apt-get install gcc-mips-linux-gnu

# 安装小端MIPS工具链
sudo apt-get install gcc-mipsel-linux-gnu

# 安装MIPS64工具链(偶尔用到)
sudo apt-get install gcc-mips64-linux-gnuabi64

装完之后验证一下:

mips-linux-gnu-gcc --version
mipsel-linux-gnu-gcc --version

如果能看到版本号,说明装好了。我曾经在某个老版本的Ubuntu上遇到过包名不一样的情况,那时候折腾了半小时才发现是源的问题。嗯,如果你遇到404错误,先检查一下apt源。

2.2 静态编译与动态编译

交叉编译时,你需要决定用静态链接还是动态链接。这个选择直接影响你的ELF文件能不能在目标环境里跑起来。

编译方式 命令示例 优点 缺点
静态编译 mips-linux-gnu-gcc -static -o test test.c 不依赖外部库,QEMU直接跑 文件体积大(通常1MB+)
动态编译 mips-linux-gnu-gcc -o test test.c 文件小,接近真实环境 需要模拟动态链接器

我个人建议:学习阶段用静态编译。为什么?因为省心。动态编译需要你额外指定动态链接器的路径,而且不同版本的libc行为有差异,容易踩坑。等你把漏洞利用逻辑调通了,再换成动态编译去适配真实环境。

写个简单的测试程序:

// test.c
#include <stdio.h>
#include <string.h>

void vuln() {
    char buf[64];
    printf("Enter your input: ");
    gets(buf);
    printf("You entered: %s\n", buf);
}

int main() {
    vuln();
    return 0;
}

编译它:

mips-linux-gnu-gcc -static -g -o test_mips test.c

-g 参数加上调试符号,后面调试时会用到。不加也行,但调试体验会差很多。

小技巧:用 file test_mips 查看生成的ELF信息。如果输出里包含 "MIPS" 和 "statically linked",说明编译成功了。

2.3 使用QEMU用户态模拟运行MIPS ELF文件

QEMU有两种模式:系统模拟(跑整个操作系统)和用户态模拟(只跑单个ELF)。我们做Pwn调试,用用户态模式就够了。它轻量、启动快,而且可以直接和GDB配合。

安装QEMU用户态模拟器:

sudo apt-get install qemu-user

# 或者单独安装MIPS支持
sudo apt-get install qemu-user-static

运行刚才编译的静态ELF:

qemu-mips ./test_mips

如果一切正常,你会看到程序提示输入,然后正常退出。如果报错说 "Unable to reserve page",通常是QEMU的地址空间配置问题,加个 -R 0x10000000 参数就行:

qemu-mips -R 0x10000000 ./test_mips

对于小端MIPSEL,用 qemu-mipsel:

qemu-mipsel ./test_mipsel

注意:动态编译的ELF不能直接用 qemu-mips 跑,需要指定动态链接器路径。比如:qemu-mips -L /usr/mips-linux-gnu/ ./test_mips_dynamic。这个路径取决于你的工具链安装位置。

我遇到过最坑的情况是:静态编译的ELF在QEMU里跑起来正常,但一上GDB就段错误。后来发现是QEMU版本太旧,升级到5.0以上就解决了。所以,如果你遇到奇怪的问题,先检查QEMU版本:

qemu-mips --version

2.4 gdb-multiarch调试器安装

调试MIPS程序,不能用普通的x86 GDB。你需要一个支持多架构的GDB——gdb-multiarch。它可以在同一个GDB会话里切换不同架构,省去装多个GDB的麻烦。

安装:

sudo apt-get install gdb-multiarch

验证:

gdb-multiarch --configuration

输出里应该能看到 "mips" 和 "mipsel" 字样。如果没有,说明你的GDB没编译MIPS支持,需要从源码重新编译。

启动调试会话:

# 终端1:启动QEMU并等待GDB连接
qemu-mips -g 1234 ./test_mips

# 终端2:启动GDB并连接
gdb-multiarch -q ./test_mips
(gdb) target remote localhost:1234
(gdb) b main
(gdb) c

-g 1234 告诉QEMU在端口1234上等待GDB连接。连接成功后,你就可以像调试本地程序一样设置断点、单步执行、查看内存了。

调试流程总结:

  1. QEMU以GDB Server模式启动目标ELF
  2. gdb-multiarch连接QEMU的GDB Server
  3. 在GDB中设置断点、分析内存、控制执行流

我个人习惯在GDB里加一个 .gdbinit 文件,自动设置架构和加载符号:

# ~/.gdbinit
set architecture mips
set endian big
file ./test_mips
target remote localhost:1234

这样每次启动GDB就自动连上了,省得敲重复命令。

2.5 环境搭建知识图谱

下面这张图把整个环境搭建的流程和组件关系梳理了一下,方便你对照检查:

MIPS Pwn环境搭建知识图谱 MIPS Pwn 环境搭建知识图谱 MIPS Pwn 调试环境 交叉编译工具链 QEMU 用户态模拟 gdb-multiarch mips-linux-gnu-gcc mipsel-linux-gnu-gcc qemu-mips qemu-mipsel gdb-multiarch 静态编译 (-static) vs 动态编译 GDB Server 远程调试 产物:MIPS ELF 可执行文件 + GDB 调试会话 💡 核心工作流 源码 → 交叉编译 → QEMU模拟执行 → GDB远程调试 → 漏洞分析

2.6 常见问题与避坑指南

  • QEMU报错 "page size mismatch":加 -R 0x10000000 参数,或者设置环境变量 QEMU_RESERVED_VA=0x10000000。
  • GDB连不上QEMU:检查端口是否被占用,或者QEMU是否真的以 -g 模式启动。我曾经犯过低级错误——忘了加 -g 参数,然后对着GDB折腾了十分钟。
  • 动态编译的ELF跑不起来:确认 -L 参数指向了正确的sysroot路径。用 mips-linux-gnu-gcc -print-sysroot 查看默认路径。
  • GDB里看不到源码:编译时加 -g,并且GDB的工作目录要和源码路径一致。或者用 directory 命令手动指定源码路径。

我的建议:初学者先用静态编译 + QEMU用户态 + gdb-multiarch 这个组合。等你能熟练调试MIPS程序了,再尝试动态编译和系统模拟。一步一个脚印,别贪多。