调试环境搭建:pwndbg、gef、peda安装与配置、QEMU用户态模拟、不同架构交叉编译工具链
工欲善其事,必先利其器。做堆利用调试,调试器就是你的眼睛。我见过太多新手花大量时间在分析漏洞上,结果连断点都打不对位置——说白了,环境没搭好,后面全是白费功夫。
这一章我们就把调试环境彻底搞定。包括三个主流GDB插件的安装、QEMU用户态模拟的配置,以及不同架构的交叉编译工具链。嗯,一步到位。
2.1 GDB插件三件套:pwndbg、gef、peda
GDB本身功能强大,但原生界面实在太简陋了。堆信息、寄存器状态、反汇编结果全挤在一起,看着就头疼。这三个插件就是来解决这个问题的。
| 插件 | 特点 | 适用场景 |
|---|---|---|
| pwndbg | 堆信息展示最详细,支持glibc各版本 | 堆利用分析、chunk状态检查 |
| gef | 功能全面,命令丰富,架构支持广 | 多架构调试、ROP链构造 |
| peda | 轻量简洁,上手快,适合初学者 | 基础漏洞分析、简单栈溢出 |
我的建议:三个插件可以共存,但默认只启用一个。我个人习惯用pwndbg做主力,gef做备选。peda嘛,偶尔用来对比查看堆布局。
2.1.1 安装pwndbg
pwndbg是目前堆利用调试的首选。它对glibc堆管理器的可视化做得最好,能直接显示chunk的fd、bk指针,甚至tcache的结构。
# 克隆仓库
git clone https://github.com/pwndbg/pwndbg
cd pwndbg
# 执行安装脚本
./setup.sh
# 验证安装
gdb -q -ex "pwndbg" -ex "quit"
安装完成后,启动GDB就能看到pwndbg的彩色界面了。我记得第一次用的时候,看到堆信息被清晰分块展示,心里就一个想法:以前用原生GDB看堆的日子真是白过了。
2.1.2 安装gef
gef的强项在于架构支持。x86、ARM、MIPS、RISC-V它都能搞定。如果你要做跨架构的堆利用分析,gef是绕不开的。
# 直接下载脚本
wget -O ~/.gdbinit-gef.py https://github.com/hugsy/gef/raw/master/gef.py
# 在.gdbinit中配置
echo "source ~/.gdbinit-gef.py" >> ~/.gdbinit
注意:gef和pwndbg的某些命令会冲突。比如heap命令,两个插件都有。我建议在.gdbinit里只保留一个插件的source,另一个用的时候手动加载。
2.1.3 安装peda
peda虽然功能不如前两个强大,但它有个好处——命令短。比如pattern create、pattern offset这些,写起来特别快。适合快速验证一些想法。
git clone https://github.com/longld/peda.git ~/peda
echo "source ~/peda/peda.py" >> ~/.gdbinit
2.2 QEMU用户态模拟
做异架构Pwn,你不可能每换一个架构就买一块开发板。QEMU的用户态模拟模式就是干这个的——它能在你的x86机器上直接运行ARM、MIPS、RISC-V的二进制文件。
为什么会需要这个?你想想看,很多IoT设备的漏洞都是ARM架构的。没有QEMU,你连跑都跑不起来,更别说调试了。
2.2.1 安装QEMU用户态模拟器
# Ubuntu/Debian
sudo apt-get install qemu-user qemu-user-static
# 验证安装
qemu-arm --version
qemu-mips --version
qemu-riscv64 --version
安装完成后,你可以直接用qemu-arm来运行ARM的二进制文件:
qemu-arm -L /usr/arm-linux-gnueabihf/ ./vuln_program
-L参数指定了动态链接库的路径。这个路径需要和交叉编译工具链配套。
避坑指南:我曾经在调试一个MIPS的堆溢出时,发现程序在QEMU下跑得好好的,但堆布局和真实设备上完全不一样。后来查了半天,发现是QEMU的glibc版本和设备的对不上。所以,尽量用和目标设备一致的glibc版本。
2.2.2 QEMU+GDB远程调试
这才是QEMU的杀手锏。你可以让QEMU启动一个调试服务器,然后用GDB远程连接上去,像调试本地程序一样调试异架构二进制。
# 启动QEMU调试服务器,端口1234
qemu-arm -g 1234 -L /usr/arm-linux-gnueabihf/ ./vuln_program
# 另一个终端,启动GDB并连接
gdb-multiarch -q
(gdb) target remote localhost:1234
(gdb) b main
(gdb) c
这里用gdb-multiarch而不是普通的gdb,因为它支持多架构的反汇编和调试。
2.3 交叉编译工具链
做异架构Pwn,你需要在x86机器上编译出ARM、MIPS等架构的二进制文件。这就是交叉编译工具链的活。
2.3.1 常用交叉编译工具链安装
| 目标架构 | 工具链包名 | 示例编译器 |
|---|---|---|
| ARM (32位) | gcc-arm-linux-gnueabihf |
arm-linux-gnueabihf-gcc |
| ARM (64位) | gcc-aarch64-linux-gnu |
aarch64-linux-gnu-gcc |
| MIPS (32位) | gcc-mips-linux-gnu |
mips-linux-gnu-gcc |
| RISC-V (64位) | gcc-riscv64-linux-gnu |
riscv64-linux-gnu-gcc |
# 安装ARM 32位工具链
sudo apt-get install gcc-arm-linux-gnueabihf
# 编译一个测试程序
arm-linux-gnueabihf-gcc -o test_arm test.c -static
# 用QEMU运行
qemu-arm ./test_arm
注意:如果编译时不加-static,运行QEMU时必须用-L指定动态库路径。否则会报找不到动态链接器的错误。
2.3.2 编译选项与堆布局的关系
这一点很多人会忽略。不同的编译选项会直接影响堆的布局和行为。我在项目中遇到过好几次,本地编译的二进制和远程目标机的行为不一致,最后发现是编译选项的问题。
- -O0 vs -O2:优化级别会影响变量的生命周期,进而影响堆的分配和释放时机。
- -fstack-protector:开启栈保护后,某些堆指针可能会被重新排列。
- -pie vs -no-pie:PIE会影响堆的基址随机化行为。
建议:做堆利用分析时,尽量用-O0 -g -no-pie来编译,方便调试。等分析清楚了,再考虑目标机的实际编译选项。
2.4 整体环境结构图
下面这张图展示了我们搭建的调试环境整体架构。从宿主机到目标二进制,每一层的作用都标清楚了。
2.5 环境验证清单
环境搭完了,怎么知道对不对?我列了一个验证清单,每项都过一遍,基本就不会有问题了。
- GDB插件加载:启动GDB,看是否有pwndbg/gef/peda的欢迎信息。
- 堆命令可用:在GDB中执行
heap或bins,看是否能正常输出。 - QEMU运行:用qemu-arm运行一个简单的ARM hello world程序。
- 远程调试:启动QEMU的-g模式,用GDB remote连接,能正常下断点、单步。
- 交叉编译:用交叉编译器编译一个带堆操作的程序,在QEMU下运行无误。
核心要点:调试环境的核心就是GDB + QEMU + 交叉工具链这三者的配合。任何一个环节出问题,后面的堆利用分析都无从谈起。花半小时把环境彻底验证一遍,比后面花两小时排查环境问题要划算得多。