---
name: re-fw-emulate
description: >
  固件仿真：QEMU 用户态/全系统。
  触发词：仿真、QEMU、firmadyne、跑固件
capabilities: [emulation]
---

# 固件仿真（QEMU）

## 何时使用 / 何时不用

- 用：需要运行固件内程序观察行为/验证假设（固件 web 界面、服务端逻辑）
- 用：rootfs 已解出、需要整体启动（全系统仿真）
- 不用：只需静态分析（[[re-fw-rootfs]] 已覆盖）
- 不用：有实物板子且必须真实硬件交互（走 [[re-hardware-io]]，硬件提取/接口调试）
- 不用：目标只是解包看内容（走 [[re-fw-extract]]）

## 工具准备

所有工具先验证再使用。仿真 = 动态执行，默认沙箱 + 网络隔离（[[re-analyze/platform-tips]] 最高原则）；用户态仿真优先（最轻可行方案）。

### qemu-user —— 用户态仿真（最轻）

- Linux: `apt install qemu-user` / `dnf install qemu-user` / `pacman -S qemu-user`
- macOS: `brew install qemu`（含用户态与系统态）
- Windows/WSL: WSL 内 Linux 版
- 验证: `qemu-arm --version`、`qemu-mips --version`（大端）、`qemu-mipsel --version`（小端）

### qemu-system —— 全系统仿真

- Linux: `apt install qemu-system-arm qemu-system-mips` / `dnf install qemu-system-arm qemu-system-mips` / `pacman -S qemu-system-arm qemu-system-mips`
- macOS: `brew install qemu`
- Windows/WSL: WSL 内 Linux 版
- 验证: `qemu-system-arm --version`

### binfmt_misc —— 直接执行交叉程序

- Linux: `apt install binfmt-support qemu-user-static`（Debian/Ubuntu 装 qemu-user-static 即自动注册各架构 binfmt 条目；Fedora 用 systemd binfmt 配置）；手动注册: `update-binfmts --enable qemu-arm`（或 /etc/binfmt.d/ 配置文件）
- macOS/Windows: 不支持，用 `qemu-<arch>` 显式调用
- 验证: `ls /proc/sys/fs/binfmt_misc/` 可见 qemu-arm 条目；之后可直接执行 `./rootfs_out/usr/sbin/httpd`

### gdb-multiarch + gdbserver —— 交叉调试

- Linux: `apt install gdb-multiarch` / `dnf install gdb-multiarch` / `pacman -S gdb-multiarch`；gdbserver: `apt install gdbserver`（或随 gdb 包提供）
- macOS: `brew install gdb`（需 Developer Tools 授权，见 [[re-analyze/platform-tips]] macOS 分支）或 WSL 内 Linux 版
- Windows/WSL: WSL 内 Linux 版
- 验证: `gdb-multiarch --version`

### firmadyne —— 全系统自动仿真框架（思路参考，可选装）

- 安装: `git clone https://github.com/firmadyne/firmadyne`，依赖 qemu-system-* 与预编译内核（scripts/ 下下载），首次搭建较重
- 验证: `ls sources/` 有 getArch.py 等脚本；`which qemu-system-mips`
- 多数单程序分析不需要它——用户态优先（[[re-analyze/platform-tips]] 先给最轻可行方案）

## 操作步骤

按顺序执行，每步记下结果。

1. **用户态仿真（最轻，优先）**：
   ```sh
   file rootfs_out/usr/sbin/httpd                    # 确认架构：ARM 32/64、MIPS(BE/EL)、RISC-V
   qemu-arm -L rootfs_out rootfs_out/usr/sbin/httpd  # -L 把 rootfs 当 sysroot（动态库/链接器从 rootfs 加载）
   # 大端 MIPS: qemu-mips -L rootfs_out ...
   # 小端 MIPS: qemu-mipsel -L rootfs_out ...
   ```
   缺库报错 → 交叉 `ldd` / `readelf -d` 看依赖，从 rootfs 补库；Web 服务类程序可加 `-E` 传环境变量。
   - `-strace` 记录客户程序系统调用（等效仿真内 strace——排查 mmap/ioctl 崩溃点直接用它）
   - `-0 <argv0>` 伪造 argv[0]（程序按调用名分支时用）；`-cpu <型号>` 指定 CPU（如 `qemu-arm -cpu cortex-a9`）
   - 跑不起来但静态可分析 → 回 [[re-fw-rootfs]] / [[re-binary-core]]，不在仿真上死磕。

2. **全系统仿真**：
   ```sh
   # ARM（vexpress 平台）+ 内核与 initramfs：
   qemu-system-arm -M vexpress-a9 -kernel vmlinuz -initrd initramfs.img -nographic \
     -append "console=ttyAMA0 root=/dev/ram rdinit=/sbin/init"
   # MIPS（malta 平台，大端示例）：
   qemu-system-mips -M malta -kernel vmlinuz -initrd initramfs.img -nographic \
     -append "console=ttyS0 rdinit=/sbin/init"
   ```
   把 rootfs 制作成磁盘镜像（ext2 挂 root）或 initramfs（cpio 打包 rootfs_out）；firmadyne 的脚本就是自动化这套流程。
   rootfs 已是磁盘镜像时直接挂盘：
   ```sh
   qemu-system-arm -M vexpress-a9 -kernel vmlinuz -drive file=rootfs.ext2,format=raw \
     -nographic -append "console=ttyAMA0 root=/dev/mmcblk0 rdinit=/sbin/init"
   ```
   root 设备名按平台磁盘控制器定（vexpress=mmcblk0、malta 的 IDE=/dev/sda 等，以 `-M` 平台文档为准）。

3. **外设缺失用 stub/回环**：
   - 用户态：程序 mmap 固定地址（GPIO/UART 寄存器）崩溃 → `strace` 定位访问点，`LD_PRELOAD` 提供 stub 库返回假寄存器值
   - 全系统：`-device` 挂虚拟外设（e1000 / virtio 等）；真实芯片外设（wifi/基带）QEMU 无法模拟 → 打补丁跳过初始化或 stub 该 ioctl
   - 先确定程序初始化到哪一步崩（串口输出/日志），再决定 stub 哪部分

4. **交叉调试（qemu -g + gdb-multiarch）**：
   ```sh
   qemu-arm -g 1234 -L rootfs_out rootfs_out/usr/sbin/httpd &   # -g 起 gdbstub
   gdb-multiarch rootfs_out/usr/sbin/httpd
   (gdb) target remote :1234
   ```
   全系统内：把 gdbserver 放进 rootfs，`gdbserver :1234 /usr/sbin/httpd`，宿主 `target remote <qemu_ip>:1234`；调试手法按 [[re-gdb]]。

5. **网络隔离下仿真**：
   - 用户态：不是网络沙箱（socket/connect 经 syscall 转发到宿主内核），需要受控网络时改用全系统方案
   - 全系统：QEMU 不加网络参数会默认建 NIC（e1000）+ user 后端——先显式 `-nic none` 确认行为，需要受控网络再加 `-nic user,restrict=on`（用户态 NAT，仅模拟出站）
   - 分析回连/协议前先隔离（[[re-analyze/platform-tips]] 最高原则），流量抓包与协议重建转 [[re-protocol]]；firmadyne 默认带网卡也需按此原则先行隔离

## 跨域联合

- [[re-firmware]]：工作流第 4 步固定调用本技能
- 架构识别与指令级深挖：ARM（向量表/Thumb/MMIO 外设交叉）→ [[re-arm]]；RISC-V（RV32/RV64/ecall）→ [[re-riscv]]（选对 qemu-<arch> 前先对照）
- 仿真内动态行为观察 → [[re-tracing]] + [[re-gdb]]（默认沙箱内，[[re-analyze/platform-tips]] 最高原则）
- 固件运行产生通信 → [[re-protocol]]；仿真内 ELF 深挖 → [[re-binary-core]]
- 仿真不成的程序回退静态 → [[re-fw-rootfs]]

## 常见坑与陷阱

- **外设寄存器访问崩溃**：现象——程序 mmap 固定地址后读 GPIO/UART 寄存器段错误；原因——QEMU 用户态不模拟外设，地址无映射；对策——strace 定位访问点，LD_PRELOAD stub 返回模拟值（步骤 3）
- **架构选错直接 segfault**：现象——qemu-arm 跑 MIPS 程序秒崩；原因——没先 `file`/`readelf` 确认架构与字节序（大端 mips ≠ mipsel）；对策——步骤 1 先确认，选对 qemu-<arch>
- **网卡型号不符 → 初始化卡死**：现象——程序在网卡初始化处挂起不退出；原因——固件按特定芯片初始化，默认挂的 e1000 不匹配，ioctl 无返回；对策——按固件预期加 `-device <型号>` 等虚拟网卡，或先 `-nic none` 观察是否跳过（步骤 5）
- **时间戳/时钟函数陷阱**：现象——程序读时间怪异（1970/倒退），行为与真实设备不同；原因——QEMU 虚拟时钟与墙钟不同步；对策——`-rtc base=utc` 固定，或 stub 掉 clock_gettime 相关调用
- **网络未隔离就仿真**：现象——固件真实回连外网（C2/升级服务器）；原因——QEMU 默认就建 NIC + user 后端（restrict=off），跳过显式隔离即联网；对策——全系统仿真显式 `-nic none`，用户态也非网络沙箱（步骤 5），回连分析前按 [[re-analyze/platform-tips]] 隔离
- 命令族速查与操作序列见 [[commands]]；工具特有坑与版本差异见 [[gotchas]]
