3、QEMU系统模拟基础:QEMU简介、QEMU的两种模式、为嵌入式设备编译QEMU
各位同学,欢迎来到第三章。从这一章开始,我们正式进入工具链的实战环节。
QEMU,全称 Quick Emulator,是嵌入式逆向工程里绕不开的一个神器。我最早接触它是在做路由器固件分析的时候,手里没有硬件,只能靠模拟器跑。说实话,第一次跑起来的时候,那种感觉——嗯,就像你终于把一把生锈的锁给捅开了。
这一章,我们不讲虚的。直接说清楚三件事:QEMU是什么、两种模式怎么选、以及怎么给嵌入式设备编译一个能用的QEMU。
3.1 QEMU简介
QEMU是一个开源的处理器模拟器,它可以在你的x86电脑上模拟ARM、MIPS、RISC-V等各种架构。说白了,就是让你的电脑“假装”成一台路由器、摄像头或者工控设备。
它的核心原理是动态二进制翻译。什么意思呢?就是把目标设备的机器指令,实时翻译成你宿主机(比如x86)能执行的指令。这个过程是动态的,不需要提前编译。
我个人习惯把QEMU比作一个“翻译官”。你给它一段ARM的二进制代码,它一边读一边翻译成x86指令,然后执行。翻译过程中还会做缓存优化,所以跑起来其实不慢。
核心能力一览:
- 支持超过30种CPU架构
- 可以模拟完整的硬件平台(网卡、串口、硬盘等)
- 支持GDB远程调试,这是逆向工程的关键
- 支持快照和恢复,方便反复调试
我在项目中遇到过最头疼的情况是:固件里有一段自修改代码(self-modifying code),在真实硬件上跑得好好的,但QEMU默认配置下会翻车。后来发现是翻译缓存没刷新。这个问题我们后面章节会细讲,这里先提个醒。
3.2 QEMU的两种模式:User Mode vs System Mode
很多新手一上来就搞混这两个模式。我建议你先记住一句话:User Mode模拟的是“单个程序”,System Mode模拟的是“整台机器”。
我们来看个对比表格,一目了然:
| 对比项 | User Mode | System Mode |
|---|---|---|
| 模拟对象 | 单个用户态程序 | 完整硬件平台(CPU、内存、外设) |
| 是否需要内核 | 不需要,直接运行 | 需要加载内核镜像 |
| 启动速度 | 快(秒级) | 慢(分钟级) |
| 典型用途 | 分析单个二进制文件、跑CTF题 | 模拟完整设备、调试驱动、分析启动流程 |
| 调试能力 | 支持GDB,但受限 | 支持完整GDB调试,可断点内核 |
| 命令示例 | qemu-arm ./hello |
qemu-system-arm -M vexpress-a9 -kernel vmlinuz |
3.2.1 User Mode
User Mode是最轻量的模式。你只需要一个交叉编译好的二进制文件,QEMU就能直接运行它。比如你有一个ARM架构的hello程序,在x86电脑上跑:
# 安装QEMU用户态模拟器
sudo apt install qemu-user
# 直接运行ARM程序
qemu-arm ./hello_arm
# 如果想调试
qemu-arm -g 1234 ./hello_arm
# 然后在另一个终端用GDB连接
arm-none-eabi-gdb ./hello_arm
(gdb) target remote :1234
你看,就这么简单。不需要内核,不需要设备树,甚至连root权限都不需要。我经常用它来快速验证一个二进制文件的行为,比如看看它调用了哪些系统调用。
小技巧:User Mode下可以用-strace参数打印系统调用,这对逆向分析非常有用。比如:qemu-arm -strace ./hello_arm,你会看到open、read、write等调用的完整参数和返回值。
3.2.2 System Mode
System Mode就重多了。它模拟了一整台机器,包括CPU、内存、中断控制器、定时器、网卡、串口等等。你需要给它一个内核镜像,它就像一台真实的嵌入式设备一样启动。
为什么会需要System Mode?举个例子:你想分析一个路由器的完整启动流程,包括uboot引导、内核解压、驱动加载、文件系统挂载。User Mode根本做不到,因为它只模拟了用户态。
# 安装QEMU系统模拟器
sudo apt install qemu-system-arm
# 启动一个ARM虚拟机(以vexpress-a9开发板为例)
qemu-system-arm \
-M vexpress-a9 \
-kernel vmlinuz \
-dtb vexpress-v2p-ca9.dtb \
-initrd initrd.img \
-append "console=ttyAMA0 root=/dev/ram" \
-nographic
这里-M指定机器类型,-kernel指定内核,-dtb指定设备树,-initrd指定初始内存文件系统。启动后你会看到内核打印信息,就像在真机上一样。
注意:System Mode下,外设模拟并不总是完美的。我曾经调试一个网卡驱动,在QEMU里跑得好好的,烧到真机上就死机。后来发现是QEMU的网卡模型和真实硬件有差异。所以,QEMU模拟的结果只能作为参考,不能完全替代真机验证。
3.3 为嵌入式设备编译QEMU
大多数情况下,你用apt安装的QEMU就够用了。但如果你需要定制功能——比如添加新的机器类型、修改外设模型、或者开启某些调试特性——那就得自己编译。
我个人习惯自己编译一份QEMU,原因有三:
- 可以开启
--enable-debug选项,获得更详细的日志 - 可以打上一些社区补丁,比如针对特定架构的优化
- 可以静态编译,方便部署到没有动态库的环境
下面我给出一个完整的编译流程,以ARM为例:
# 1. 下载源码
git clone https://gitlab.com/qemu-project/qemu.git
cd qemu
# 2. 配置(这里只编译ARM相关目标)
./configure \
--target-list=arm-softmmu,arm-linux-user \
--enable-debug \
--enable-kvm \
--prefix=/opt/qemu-arm
# 参数说明:
# arm-softmmu -> System Mode for ARM
# arm-linux-user -> User Mode for ARM
# --enable-debug -> 开启调试支持
# --enable-kvm -> 如果宿主机支持KVM,可以加速
# 3. 编译(-j后面跟CPU核心数)
make -j$(nproc)
# 4. 安装
sudo make install
# 5. 验证
/opt/qemu-arm/bin/qemu-system-arm --version
/opt/qemu-arm/bin/qemu-arm --version
编译过程中可能会缺一些依赖库,比如glib、pixman、libfdt等。遇到报错别慌,看提示缺什么就装什么:
sudo apt install libglib2.0-dev libpixman-1-dev libfdt-dev
避坑指南:我曾经在编译时忘了加--target-list参数,结果QEMU把所有架构都编译了一遍,花了将近两个小时。如果你只需要ARM和MIPS,就只指定这两个,能省下大量时间。
编译完成后,你会在安装目录下看到两个关键的可执行文件:
qemu-system-arm—— System Mode,用于模拟完整的ARM设备qemu-arm—— User Mode,用于运行单个ARM程序
如果你需要模拟MIPS、RISC-V等其他架构,只需要在--target-list里加上对应的目标即可。比如:
--target-list=arm-softmmu,arm-linux-user,mips-softmmu,mips-linux-user
3.4 本章小结
这一章我们讲了QEMU的核心概念和两种模式。User Mode轻量快速,适合分析单个程序;System Mode完整强大,适合模拟整机。编译QEMU也不复杂,关键是选对目标列表。
嗯,这里要注意:不要贪多。刚开始学,先玩熟User Mode,把ARM的单个二进制文件跑起来、调起来。等你有感觉了,再上System Mode去模拟完整设备。一步一个脚印,比什么都重要。
下一章,我们会用QEMU跑一个真实的嵌入式固件,到时候你就知道这些基础有多重要了。