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连接。连接成功后,你就可以像调试本地程序一样设置断点、单步执行、查看内存了。
调试流程总结:
- QEMU以GDB Server模式启动目标ELF
- gdb-multiarch连接QEMU的GDB Server
- 在GDB中设置断点、分析内存、控制执行流
我个人习惯在GDB里加一个 .gdbinit 文件,自动设置架构和加载符号:
# ~/.gdbinit
set architecture mips
set endian big
file ./test_mips
target remote localhost:1234
这样每次启动GDB就自动连上了,省得敲重复命令。
2.5 环境搭建知识图谱
下面这张图把整个环境搭建的流程和组件关系梳理了一下,方便你对照检查:
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程序了,再尝试动态编译和系统模拟。一步一个脚印,别贪多。