第一章 漏洞挖掘环境搭建:QEMU模拟环境、GDB调试器配置、IDA Pro与Ghidra使用
做思科固件漏洞挖掘,第一件事不是翻代码,而是先把战场铺好。我见过太多人,拿到固件就往IDA里拖,结果跑不起来,调试器连不上,最后折腾半天连入口点都找不到。说白了,环境没搭好,后面全是白费功夫。
这一章,我就带你把这套环境从头到尾捋一遍。QEMU模拟、GDB调试、IDA Pro和Ghidra逆向,一个都不能少。嗯,咱们开始吧。
1.1 QEMU模拟环境搭建
思科的路由器固件,大部分跑在MIPS或ARM架构上。你手头没有真机?没关系,QEMU就是你的虚拟路由器。我个人习惯用qemu-system-mips和qemu-system-arm,这两个用得最多。
1.1.1 安装QEMU
Ubuntu上装起来很简单:
sudo apt-get install qemu-system-mips qemu-system-arm qemu-user-static
为什么要装qemu-user-static?因为有些固件里的二进制文件是静态编译的,用user模式跑起来更快。我在项目中遇到过,有些固件里的Web服务进程,用system模式模拟网络栈太麻烦,直接user模式挂载根文件系统,省事多了。
1.1.2 提取固件并挂载
拿到一个思科固件,比如cisco_firmware.bin,第一步是解包。常用工具是binwalk:
binwalk -Me cisco_firmware.bin
解出来之后,你会看到一个squashfs-root目录。这就是路由器的根文件系统。接下来,我们需要把它挂载到QEMU里。
我写了一个小脚本,专门用来启动QEMU并挂载固件:
#!/bin/bash
# 启动QEMU模拟MIPS架构思科固件
qemu-system-mips \
-M malta \
-kernel vmlinux-2.6.32-5-4kc-malta \
-hda debian_squeeze_mips_standard.qcow2 \
-append "root=/dev/sda1 console=tty0" \
-netdev user,id=net0,hostfwd=tcp::2222-:22 \
-device e1000,netdev=net0 \
-nographic
vmlinux内核和qcow2镜像需要提前准备。我一般用Debian MIPS的预编译镜像,省去编译内核的麻烦。你可以在https://people.debian.org/~aurel32/qemu/mips/找到现成的。
1.1.3 挂载根文件系统
启动QEMU后,把解压出来的squashfs-root目录通过scp传进去:
scp -P 2222 -r squashfs-root root@localhost:/root/
然后在QEMU里用chroot切换进去:
chroot /root/squashfs-root /bin/sh
这时候,你就相当于在思科路由器里了。可以跑它的Web服务、命令行工具,甚至调试它的进程。
/bin/sh是BusyBox,记得先检查它有没有符号链接。我曾经遇到一个固件,/bin/sh指向/bin/ash,结果chroot进去直接报错,折腾了半小时才发现。
1.2 GDB调试器配置
环境跑起来了,接下来要能调试。GDB是必须的,但这里有个坑——你主机是x86,目标机是MIPS/ARM,得用交叉编译的GDB。
1.2.1 安装交叉GDB
以MIPS为例:
sudo apt-get install gdb-multiarch
gdb-multiarch支持多种架构,省得你装一堆不同版本的GDB。我个人习惯用这个,一个命令搞定所有架构。
1.2.2 远程调试配置
在QEMU里启动一个待调试的进程,比如httpd:
gdbserver :1234 /usr/sbin/httpd
然后在主机上连接:
gdb-multiarch -q
(gdb) set architecture mips
(gdb) target remote 192.168.1.100:1234
这里192.168.1.100是QEMU虚拟机的IP。如果你用hostfwd映射了端口,也可以连localhost:1234。
gdb的hbreak命令设置硬件断点,或者修改内核参数允许软件断点。
1.2.3 调试技巧
调试嵌入式固件,最常用的命令就这几个:
info registers— 查看寄存器状态,尤其关注$pc和$spdisassemble $pc-20,$pc+20— 反汇编当前指令附近区域monitor system_reset— 在QEMU里重置虚拟机,省得手动重启
嗯,这里要注意,MIPS架构的GDB有时候会显示错误的反汇编结果。我遇到过disassemble出来的指令跟实际执行的不一样,原因是GDB的符号表没加载对。解决办法是手动指定固件的基地址:
(gdb) add-symbol-file httpd 0x00400000
1.3 IDA Pro与Ghidra使用
调试器负责动态分析,反汇编器负责静态分析。这两者缺一不可。IDA Pro和Ghidra,我两个都用。IDA Pro快,Ghidra免费且支持批量分析。
1.3.1 IDA Pro配置
IDA Pro加载思科固件时,有几个关键步骤:
- 选择正确的处理器类型。MIPS选
MIPSEL或MIPSB,ARM选ARM。 - 设置加载地址。思科固件通常加载到
0x80000000或0x00400000。怎么知道?看固件头里的入口点。 - 运行
Findcrypt插件,搜索硬编码的密钥和常量。思科喜欢用0xDEADBEEF之类的魔数做校验。
我个人习惯在IDA里先跑一遍auto-analysis,然后手动修正函数边界。思科的固件很多是GCC编译的,但有些函数没有正确的prologue/epilogue,IDA会识别失败。这时候就得靠经验了——看$ra寄存器的使用模式,手动标记函数。
1.3.2 Ghidra批量分析
Ghidra的优势在于脚本化和批量处理。我写过一个Python脚本,自动加载固件、分析、导出函数列表:
# Ghidra脚本示例:批量分析固件
from ghidra.app.script import GhidraScript
from ghidra.program.model.listing import Function
class BatchAnalyzer(GhidraScript):
def run(self):
# 获取当前程序
program = getCurrentProgram()
listing = program.getListing()
# 遍历所有函数
functions = listing.getFunctions(True)
for func in functions:
name = func.getName()
entry = func.getEntryPoint()
print(f"Function: {name} @ {entry}")
# 检查是否有格式化字符串漏洞
# 这里可以加自定义规则
用Ghidra的好处是,你可以一次性分析几十个固件,然后对比它们的函数差异。我在做思科漏洞挖掘时,经常用这个方法来发现新版本里新增的代码——那些往往就是漏洞点。
1.3.3 两者结合使用
我的工作流是这样的:
- 先用Ghidra批量扫描固件,找出可疑函数(比如
strcpy、sprintf的调用点) - 然后用IDA Pro打开具体的目标二进制,手动分析这些函数的上下文
- 最后用GDB动态验证,确认漏洞是否可利用
说白了,Ghidra是侦察兵,IDA是狙击手,GDB是验尸官。三个角色缺一不可。
Lumina服务器功能。它能自动匹配已知的函数签名,省去你手动识别库函数的时间。我在分析思科IOS固件时,Lumina帮我识别出了libc里的malloc和free,省了至少半天功夫。
1.4 本章知识体系
下面这张图,是我自己画的本章知识结构。你可以把它当作一个检查清单,看看自己有没有遗漏什么。
环境搭好了,后面的事情就顺了。下一章,我们会拿一个真实的思科固件漏洞来练手,从分析到利用,一步步走通。嗯,先把今天的内容消化掉,别急着往前赶。