环境搭建:QEMU用户态与系统态模拟、交叉编译工具链、GDB-multiarch调试
说实话,环境搭建这关过不去,后面所有漏洞利用都是纸上谈兵。我见过太多学员卡在「QEMU 跑不起来」或者「GDB 连不上目标」这种基础问题上,一卡就是一整天。这一章咱们就把这些坑一个个填平。
2.1 为什么需要模拟环境?
做异架构 Pwn,你手头不可能有所有架构的实体开发板。MIPS、ARM、RISC-V……买齐了得多少钱?而且很多嵌入式设备你根本买不到开发板,只能拿到固件。
QEMU 就是干这个的。它能在你的 x86 笔记本上模拟出一个完整的 MIPS 路由器,或者一个 ARM 的 IoT 设备。你想想看,调试起来多方便。
核心思路: 本地编译 → QEMU 模拟运行 → GDB 远程调试。这套流程打通了,你就能在笔记本上搞定 90% 的异架构漏洞分析。
2.2 QEMU 的两种模式
QEMU 有两种工作模式,很多人一开始搞混。我简单说下区别:
| 模式 | 命令示例 | 适用场景 | 特点 |
|---|---|---|---|
| 用户态 (User-mode) | qemu-mips-static ./pwn |
单个二进制文件分析 | 轻量、启动快、不支持系统调用完整模拟 |
| 系统态 (System-mode) | qemu-system-mips -kernel vmlinux |
完整操作系统/内核漏洞 | 重量级、模拟外设、支持完整系统调用 |
我个人习惯:先用户态快速验证漏洞逻辑,再切系统态做内核提权或沙箱绕过。别一上来就跑系统态,等镜像启动就得半分钟,调试效率太低。
2.2.1 用户态模拟实战
装个 QEMU 用户态模拟器很简单:
# Ubuntu/Debian
sudo apt install qemu-user qemu-user-static
# 验证安装
qemu-mips-static --version
qemu-arm-static --version
跑一个 MIPS 的 ELF 文件:
# 直接运行
qemu-mips-static ./vuln_program
# 带参数运行
qemu-mips-static ./vuln_program arg1 arg2
# 设置 QEMU 库路径(重要!)
QEMU_LD_PREFIX=./mips-libs/ qemu-mips-static ./vuln_program
避坑指南: 我曾经遇到过 QEMU 报错 "Invalid ELF image for this architecture",折腾了半天才发现是交叉编译时没加 -static 参数。用户态模拟要求目标二进制必须是静态链接的,否则找不到动态库。用 file 命令确认一下:file vuln_program,看到 "statically linked" 才稳。
2.2.2 系统态模拟实战
系统态模拟需要完整的镜像文件。以 MIPS 为例:
# 安装系统态 QEMU
sudo apt install qemu-system-mips qemu-system-arm
# 启动一个 Debian MIPS 虚拟机
qemu-system-mips \
-M malta \
-m 256M \
-kernel vmlinux-3.2.0-4-4kc-malta \
-hda debian_wheezy_mips_standard.qcow2 \
-append "root=/dev/sda1 console=tty0" \
-net nic -net user \
-nographic
嗯,这里要注意:-M malta 指定了机器类型,不同架构有不同的机器类型。ARM 用 -M virt,RISC-V 用 -M virt。选错了机器类型,内核根本起不来。
2.3 交叉编译工具链
你需要在 x86 上编译出 MIPS/ARM 架构的二进制。这就是交叉编译的活。
我推荐用 musl-cross 或者 buildroot 自己编译工具链。但最省事的办法是用现成的:
# 安装 MIPS 交叉编译工具链
sudo apt install gcc-mips-linux-gnu g++-mips-linux-gnu
# 安装 ARM 交叉编译工具链
sudo apt install gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf
# 编译一个 MIPS 程序
mips-linux-gnu-gcc -static -o pwn_mips pwn.c
# 编译一个 ARM 程序
arm-linux-gnueabihf-gcc -static -o pwn_arm pwn.c
注意: 编译 Pwn 题目时,记得关掉栈保护、PIE 等安全机制,方便调试:
mips-linux-gnu-gcc -static -o pwn_mips pwn.c \
-fno-stack-protector -no-pie -z execstack
真实漏洞场景中这些保护是开启的,但学习阶段先关掉,理解漏洞原理后再逐步加保护。
2.4 GDB-multiarch 调试
调试异架构程序,不能用本地的 GDB。你需要 gdb-multiarch,它支持多种目标架构。
# 安装
sudo apt install gdb-multiarch
# 启动调试
gdb-multiarch -q ./pwn_mips
连接 QEMU 用户态模拟的调试端口:
# 启动 QEMU 并开启 GDB 调试端口(默认 1234)
qemu-mips-static -g 1234 ./pwn_mips
# 另一个终端,用 GDB 连接
gdb-multiarch -q ./pwn_mips
(gdb) target remote localhost:1234
(gdb) b main
(gdb) c
为什么会这样?因为 QEMU 内置了一个 GDB stub,它把模拟的 CPU 状态暴露给 GDB。你可以在 x86 主机上调试 MIPS 的汇编指令,单步、断点、查看寄存器,跟本地调试一模一样。
个人经验: 调试时我习惯先 set architecture mips 确认架构正确,然后 info registers 看看通用寄存器。MIPS 的 $ra 相当于 x86 的返回地址,$sp 是栈指针。ARM 的话看 lr 和 sp。不同架构的寄存器名不一样,别搞混了。
2.5 知识体系总览
下面这张图把本章的核心逻辑串起来了:
2.6 常见问题与避坑
- QEMU 报错 "Unable to reserve 0x... bytes":宿主机的内存不够,减少 QEMU 的内存分配,或者关掉一些后台程序。
- GDB 连不上 QEMU:检查端口是否被占用,
netstat -tlnp | grep 1234。另外确认 QEMU 启动时加了-g参数。 - 交叉编译的程序跑不起来:用
readelf -h pwn_mips查看 ELF 头,确认 Machine 字段是目标架构。我遇到过编译时忘了指定架构,结果编出来个 x86 的。 - 系统态镜像启动卡死:多半是机器类型选错了。MIPS 用
-M malta,ARM 用-M virt,RISC-V 用-M virt。拿不准的话去 QEMU 官网查文档。
重要提醒: 调试异架构程序时,gdb-multiarch 默认可能不认识目标架构的寄存器。手动设置一下:
(gdb) set architecture mips
The target architecture is set to "mips".
(gdb) set mips abi o32
(gdb) set endian little
MIPS 有 big-endian 和 little-endian 两种,ARM 也有大小端之分。搞错了反汇编出来的代码全是乱的。
2.7 快速验证清单
环境搭好之后,跑一遍这个清单确认没问题:
- 交叉编译一个简单的 Hello World,静态链接
- 用 QEMU 用户态跑起来,看到输出
- 用 QEMU 用户态 + GDB 远程调试,能下断点、单步
- 系统态镜像能正常启动,能 SSH 进去或者串口有输出
- 系统态里也能跑 GDB server,从宿主机连上去调试
这五步走通了,你的环境就稳了。后面章节的漏洞利用全在这个框架上跑。