第一章 漏洞挖掘环境搭建:QEMU模拟环境、GDB调试器配置、IDA Pro与Ghidra使用

做思科固件漏洞挖掘,第一件事不是翻代码,而是先把战场铺好。我见过太多人,拿到固件就往IDA里拖,结果跑不起来,调试器连不上,最后折腾半天连入口点都找不到。说白了,环境没搭好,后面全是白费功夫。

这一章,我就带你把这套环境从头到尾捋一遍。QEMU模拟、GDB调试、IDA Pro和Ghidra逆向,一个都不能少。嗯,咱们开始吧。

1.1 QEMU模拟环境搭建

思科的路由器固件,大部分跑在MIPS或ARM架构上。你手头没有真机?没关系,QEMU就是你的虚拟路由器。我个人习惯用qemu-system-mipsqemu-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连上了但断点打不上。后来发现是固件里的代码段是只读的,硬件断点不支持。解决办法是用gdbhbreak命令设置硬件断点,或者修改内核参数允许软件断点。

1.2.3 调试技巧

调试嵌入式固件,最常用的命令就这几个:

  • info registers — 查看寄存器状态,尤其关注$pc$sp
  • disassemble $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加载思科固件时,有几个关键步骤:

  1. 选择正确的处理器类型。MIPS选MIPSELMIPSB,ARM选ARM
  2. 设置加载地址。思科固件通常加载到0x800000000x00400000。怎么知道?看固件头里的入口点。
  3. 运行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批量扫描固件,找出可疑函数(比如strcpysprintf的调用点)
  • 然后用IDA Pro打开具体的目标二进制,手动分析这些函数的上下文
  • 最后用GDB动态验证,确认漏洞是否可利用

说白了,Ghidra是侦察兵,IDA是狙击手,GDB是验尸官。三个角色缺一不可。

提示: 如果你用IDA Pro 7.5以上版本,可以试试它的Lumina服务器功能。它能自动匹配已知的函数签名,省去你手动识别库函数的时间。我在分析思科IOS固件时,Lumina帮我识别出了libc里的mallocfree,省了至少半天功夫。

1.4 本章知识体系

下面这张图,是我自己画的本章知识结构。你可以把它当作一个检查清单,看看自己有没有遗漏什么。

漏洞挖掘环境搭建 - 知识体系 环境搭建核心 QEMU模拟环境 安装qemu-system-mips/arm binwalk解包固件 chroot挂载文件系统 GDB调试器 安装gdb-multiarch gdbserver远程调试 硬件断点 vs 软件断点 IDA Pro & Ghidra IDA: 处理器类型选择 Ghidra: 批量脚本分析 两者结合: 侦察+狙击+验尸 目标:能跑、能调、能逆向

环境搭好了,后面的事情就顺了。下一章,我们会拿一个真实的思科固件漏洞来练手,从分析到利用,一步步走通。嗯,先把今天的内容消化掉,别急着往前赶。


专注资料整理