固件模拟

什么是固件模拟?

固件模拟是通过一些模拟器实现的在x86等其他架构电脑环境中运行物联网设备固件的方式。

**主要的模拟架构:**主流的物联网设备固件的架构是arm和mips。

**模拟方式:**单应用模拟和系统模拟

主流的模拟器:

  • qemu:目前使用最多的模拟器,支持用户态模拟和系统态的模拟
  • qiling:只支持单应用模拟,但是由于qiling是模拟框架,所以更加灵活

安装方式:pip3 install qiling

官方文档https://docs.qiling.io/en/latest

qiling介绍

基本流程

  1. 基本代码编写
1
2
3
4
5
6
7
8
from qiling import Qiling

if __name__ = '__main__':
argv = r'./bin/httpd'.split()
rootfs = r'./'

ql = Qiling(argv,rootfs)
ql.run()
  1. 问题定位

一般有时候会出现错误信息,不同的固件错误信息不一样,要找到错误信息进行,定位问题所在

  1. 修改代码

根据问题所在修改代码,添加一些函数等

qiling示例

window、linux 相互执行 对方可执行文件

在 linux 下使用 qiling 框架执行 exe 文件

在 linux 上使用 qiling 运行 exe 时,需要模拟 windows系统环境( window 的 dll 和 注册表 等组件 )才能运行。qiling 提供了一个脚本:在qiling/examples/scripts/dllscollector.bat,在 windows 上执行,就能得到全部所需要的依赖

image-20250816152743326

在qiling-master处以管理员模式打开cmd,执行.\examples\scripts\dllscollector.bat,即可在/examples/rootfs中找到运行后的结果,如下:

image-20250816154423703

然后将rootfs整个文件夹移到已安装qiling的linux的系统里的一个文件夹中

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from qiling import *

# sandbox to emulate the EXE
def my_sandbox(path, rootfs):
# setup Qiling engine
ql = Qiling(path, rootfs)
# now emulate the EXE
ql.run()

if __name__ == "__main__":
# 将 rootfs 的路径修正为 "rootfs/x8664_windows"
my_sandbox(["rootfs/x8664_windows/bin/hello.exe"], "rootfs/x8664_windows")

hello.c在win11中用gcc编译gcc hello.c -o hello.exe -static的,源码如下:

1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

int main(int argc, const char **argv)
{
printf("Hello, World!\n");

return 0;
}
image-20250816162802144

在 windows 下使用 qiling 框架执行 linux 可执行文件

  • 在 linux 上执行 dylibcollector.sh 生成 linux 环境文件,然后把生成的文件放到 windows 上

Qltool 工具

文档:https://docs.qiling.io/en/latest/qltool/

Qiling 还提供了一个名为 qltool 的强大工具,可以快速模拟出目标 Shellcode 或可执行文件的源码。qltool 有三个可用的命令

Qiling Framework 还提供了一个名为的友好工具,qltool用于快速模拟 shellcode 和可执行二进制文件。

命令

qltool有三个可用命令: - run:模拟程序二进制文件 - code:执行 shellcode 摘录 - qltui:显示 qltool 的终端用户界面 - examples:发出使用示例

运行选项

选项名称 缩写 参数 描述
--filename -f 文件名 要模拟的二进制文件名
--rootfs 目录名 仿真根目录;这是所有库所在的位置
--args 模拟程序命令行参数

注意: - 如果--filename未指定,最后一个参数将被视为程序二进制文件 - 如果--args未指定,所有尾随参数将被视为程序命令行参数

代码选项

选项名称 缩写 参数 描述
--filename -f 文件名 输入文件名
--input -i 十六进制 输入十六进制字符串;仅当--format设置为时相关hex
--format asm,, hex``bin 指定文件或输入格式:汇编、十六进制字符串或二进制文件
--arch x86,,,,,,,, x8664``arm``arm_thumb``arm64``mips``a8086``evm 目标架构
--endian littlebig 目标字节顺序(默认值little:)
--os linux,,,,,,, freebsd``macos``windows``uefi``dos``evm 目标操作系统

注意:- 当--format设置为时hexqltool将首先在 中查找数据--input。如果没有指定输入字符串,它将引用 中指定的文件--filename

常用选项

选项名称 缩写 参数 描述
--verbose -v off,,,,, default``debug``disasm``dump 设置日志记录详细级别
--env 文件名 包含环境字典的 Pickle 文件的路径,计算结果为字典的 Python 字符串
--gdb -g [服务器:端口] 启用 gdb 服务器
--qdb 在入口点附加 qdb。目前仅支持 MIPS 和 ARM(Thumb 模式)
--rr 启用 qdb记录和重放功能;需要 --qdb
--profile 文件名 指定配置文件
--no-console 不要将程序输出到标准输出
--filter -e 正则表达式 在日志输出上应用过滤正则表达式
--log-file 文件名 将日志发送到文件
--log-plain 不要在日志输出中使用颜色;当将日志发送到文件时很有用
--root 启用sudo 必需模式
--debug-stop 第一次出现错误时停止模拟;需要verbose设置为debugdump
--multithread -m 以多线程模式执行程序
--timeout 微秒 以微秒为单位设置仿真超时(1000000μs = 1s)
--coverage-file -c 文件名 代码覆盖率输出文件
--coverage-format drcovdrcov_exact 代码覆盖率文件格式
--json 以 JSON 格式发出模拟报告

qltui — qltool 的终端用户界面

qltui:$ ./qltool qltui 终端用户界面qltool

  • 运行于 之上 qltool
  • 使用接口接受数据 runcode 命令。
  • 返回一个 Argparse Namespace 对象 qltool 以供执行。
  • 交互式报告查看器加上将其保存为 pdf 的选项。

Examples

shellcode:

1
2
$ ./qltool code --os linux --arch arm --format hex -f examples/shellcodes/linarm32_tcp_reverse_shell.hex
$ ./qltool code --os linux --arch x86 --format asm -f examples/shellcodes/lin32_execve.asm

binary file:

1
2
$ ./qltool run -f examples/rootfs/x8664_linux/bin/x8664_hello --rootfs  examples/rootfs/x8664_linux/
$ ./qltool run -f examples/rootfs/mips32el_linux/bin/mips32el_hello --rootfs examples/rootfs/mips32el_linux

UEFI file:

1
$ ./qltool run -f examples/rootfs/x8664_efi/bin/TcgPlatformSetupPolicy --rootfs examples/rootfs/x8664_efi --env examples/rootfs/x8664_efi/rom2_nvar.pickel

GDB debugger enable:

1
$ ./qltool run -f examples/rootfs/x8664_linux/bin/x8664_hello --gdb 127.0.0.1:9999 --rootfs examples/rootfs/x8664_linux

Binary file and argv:

1
2
$ ./qltool run -f examples/rootfs/x8664_linux/bin/x8664_args --rootfs examples/rootfs/x8664_linux --args test1 test2 test3
$ ./qltool run --rootfs examples/rootfs/x8664_linux examples/rootfs/x8664_linux/bin/x8664_args test1 test2 test3

Binary file and various output format:

1
$ ./qltool run -f examples/rootfs/mips32el_linux/bin/mips32el_hello --rootfs examples/rootfs/mips32el_linux --verbose disasm

Binary file and env:

1
$ ./qltool run -f jexamples/rootfs/x8664_linux/bin/tester --rootfs jexamples/rootfs/x8664_linux --env '{"LD_PRELOAD":"hijack.so"}' --verbose debug

qltui

1
$ ./qltool qltui

qemu-system指令介绍

QEMU (Quick Emulator) 是一款强大的开源虚拟化软件,它能够模拟多种硬件环境,让用户可以在一台物理计算机上运行不同的操作系统。 QEMU 的功能主要通过命令行指令来调用,这些指令参数丰富,可以实现灵活的虚拟机配置和管理。

QEMU 主要包含两大组件:

  • qemu-system-*: 这是一个完整的虚拟机模拟器,可以模拟一个完整的计算机系统,包括处理器和各种外围设备,从而可以运行未经修改的操作系统。 例如,qemu-system-x86_64 用于模拟 x86_64 架构的计算机系统。
  • qemu-img: 这是一个磁盘镜像管理工具,用于创建、转换和修改不同格式的虚拟磁盘镜像文件。

固件模拟场景下的 QEMU 指令

在固件模拟中,我们的目标通常是让一个为特定硬件(如路由器、摄像头)编译的固 সেনারা(Firmware)在 QEMU 模拟的环境中运行起来。这通常比运行一个标准的桌面操作系统要复杂,因为固件与硬件高度耦合。

核心指令:qemu-system-

这里的 指的是固件所针对的处理器架构,常见于物联网设备的是 arm 和 mips。

常用且关键的选项:

  • -M : 指定要模拟的单板计算机(Machine)。这是固件模拟中最关键的参数之一。因为固件是为特定硬件板卡编译的,你需要告诉 QEMU 模拟一个尽可能接近真实设备的硬件环境。
    • 如何找到合适的 machine? 你可以使用 qemu-system-arm -M ? 来查看所有支持的 ARM 单板。有时需要选择一个通用板卡(如 vexpress-a9 或 versatilepb),或者社区已经为特定设备(如 raspi2 树莓派)创建了模型。
    • 示例: -M vexpress-a9
  • -kernel : 指定要加载的内核镜像文件。在固件模拟中,这通常是从固件包里解压出来的 Linux 内核文件(例如 zImage, uImage)。.
  • -initrd : 指定初始内存文件系统 (Initial RAM File System)。这通常是固件解压后的根文件系统,包含了所有必要的程序和库文件。
  • -append “console=ttyAMA0 root=/dev/ram rdinit=/sbin/init”: 向内核传递启动参数。这是另一个至关重要的步骤。
    • console=ttyAMA0: 指定内核的控制台输出设备。你需要根据你选择的 -M 参数来确定正确的串口设备名称。
    • root=/dev/ram: 告诉内核根文件系统在内存中。
    • rdinit=/sbin/init: 指定启动后在根文件系统中执行的第一个进程。
  • -nographic: 禁用图形界面,并将虚拟机的串口输出重定向到当前的终端。这对于调试和查看固件的启动日志至关重要。
  • -drive file=<disk.img>,format=raw,if=sd: 加载一个完整的磁盘镜像作为 SD 卡。有些固件不是以内核+内存文件系统的方式启动,而是直接从一个完整的磁盘镜像启动。

一个典型的固件模拟启动命令示例:

Generated bash

1
2
3
4
5
6
qemu-system-arm \
-M vexpress-a9 \
-kernel uImage \
-initrd rootfs.img.gz \
-append "root=/dev/ram rdinit=/bin/sh console=ttyAMA0" \
-nographic

Use code with caution.Bash

这个命令尝试在一个通用的 vexpress-a9 板卡上,加载 uImage 内核和 rootfs.img.gz 文件系统,并将启动后的 shell 重定向到当前终端。

附录

核心指令:qemu-system-*

这是启动和管理虚拟机的核心指令。其基本用法如下:
qemu-system-x86_64 [options] [disk_image]

其中 [options] 是一系列用于配置虚拟机资源的参数,而 [disk_image] 是虚拟机的磁盘镜像文件。

常用参数选项:

  • -m [size]: 设置虚拟机的内存大小。例如 -m 512M 表示分配 512MB 内存给虚拟机。
  • -cpu [model]: 指定模拟的 CPU 型号。可以使用 -cpu ? 来查询当前 QEMU 版本支持的 CPU 型号。
  • -hda [file]: 将指定文件作为虚拟机的第一个 IDE 硬盘。
  • -cdrom [file]: 将指定文件作为虚拟机的光驱镜像。
  • -boot [order=drives]: 设置虚拟机的启动顺序。例如,-boot order=dc 表示优先从光驱 (d) 启动,其次是硬盘 (c)。
  • -net nic: 为虚拟机创建一个网卡。
  • -net user: 使用用户模式网络,这是一种简单方便的网络配置方式,无需管理员权限。
  • -vnc :[display]: 启用 VNC 显示,允许通过 VNC 客户端远程连接到虚拟机的图形界面。
  • -nographic: 禁用图形界面,让虚拟机在当前终端以命令行方式启动。
  • -s: 启动 GDB 调试服务器。
  • -S: 在启动时暂停虚拟机,等待调试器连接。
  • -enable-kvm: 启用 KVM (Kernel-based Virtual Machine),利用硬件虚拟化技术来提升虚拟机性能。

磁盘管理工具:qemu-img

qemu-img 是一个功能强大的磁盘镜像管理工具,支持多种虚拟磁盘格式,包括 raw、qcow2、vmdk、vdi 等。

常用命令:

  • create: 创建一个新的虚拟磁盘镜像文件。
    • 用法: qemu-img create [-f fmt] filename [size]
    • 示例: qemu-img create -f qcow2 my_disk.qcow2 10G 创建一个 10GB 大小的 qcow2 格式镜像文件。 qcow2 是 QEMU 目前使用最广泛的镜像格式。
  • convert: 转换虚拟磁盘镜像的格式。
    • 用法: qemu-img convert [-f src_fmt] -O dst_fmt src_image dst_image
    • 示例: qemu-img convert -O qcow2 rhel7.img rhel7-a.qcow2 将 raw 格式的镜像转换为 qcow2 格式。
  • info: 显示虚拟磁盘镜像文件的信息。
    • 用法: qemu-img info filename
    • 示例: qemu-img info my_disk.qcow2 会显示镜像的格式、虚拟大小、实际占用磁盘空间等信息。
  • resize: 调整虚拟磁盘镜像文件的大小。
    • 用法: qemu-img resize filename [+|-]size
    • 示例: qemu-img resize my_disk.qcow2 +2G 将镜像文件增加 2GB。 需要注意的是,增加或减少镜像文件大小后,还需要在客户机操作系统内部进行相应的分区和文件系统调整才能生效。
  • snapshot: 管理虚拟机的快照。
    • 用法: qemu-img snapshot [-l | -a snapshot | -c snapshot | -d snapshot] filename
    • 选项: -l 列出快照,-c 创建快照,-d 删除快照,-a 应用快照。

支持的镜像格式简介:

  • raw: 原始的磁盘镜像格式,简单且易于移植。
  • qcow2: QEMU 的原生格式,支持写时复制、稀疏文件、加密和压缩等高级功能,是目前最常用的格式。
  • vmdk: 兼容 VMware 的镜像文件格式。
  • vdi: 兼容 VirtualBox 的镜像文件格式。

通过灵活运用 qemu-system-*qemu-img 的各种命令和参数,用户可以高效地创建、配置和管理虚拟机,满足不同的开发、测试和虚拟化需求。

系统内核编译

Buildroot

Buildroot 可以通过交叉编译生成嵌入式 Linux 系统,提供选择的库有 glibc、uClibc、musl,支持的架构包括 arm、mips、PowerPC 等主流架构。

1
curl -O https://buildroot.org/downloads/Vagrantfile; vagrant up

如何使用

实操一下

第一步安装,直接去官网下载就行,其中这个vargant来这里下载

第二步安装解压后cd configs,关于这个configs目录介绍:

configs 目录是 Buildroot 的官方模板库。你的工作流程永远是:

  1. 从 configs 目录中选择一个最合适的 _defconfig 文件作为起点。
  2. 使用 make _defconfig 命令将其应用为项目当前的 .config 文件。
  3. 使用 make menuconfig 进行个性化定制。
  4. 使用 make 进行编译。

第三步构建.config

第四步make menuconfig设定好后就可以make了

你正在 Windows Subsystem for Linux (WSL) 环境下工作。WSL 为了方便,默认会将你 Windows 系统的 PATH 环境变量自动追加到 Linux 的 PATH 中。

问题就出在这里。你的 Windows PATH 中包含了大量 Buildroot 编译脚本无法处理的条目:

  1. 包含空格的路径:这是最主要的问题。
  • /mnt/c/Program Files/Eclipse Adoptium/…
  • /mnt/c/Program Files (x86)/Windows Kits/…
  • /mnt/d/Web/Microsoft VS Code/bin
  1. 包含非英文字符(非 ASCII)的路径:这也是一个致命问题。
  • /mnt/d/虚拟机/vmware/bin/
  • /mnt/d/杂物/vivo 套件/pcsuite/

Buildroot 的检查脚本非常严格,当它在 PATH 变量中看到这些空格或特殊字符时,就会认为 PATH “不干净”,并立即报错退出。

解决方案

你有两种解决方案,一种是临时的(推荐,快速解决当前问题),一种是永久的(更彻底)。

方案一:临时指定一个“干净”的 PATH (推荐)

这是解决当前编译问题的最快、最直接的方法。我们可以在执行 make 命令时,临时为它指定一个只包含标准 Linux 路径的、干净的 PATH 变量。

请在你的 buildroot-2025.02.4/ 目录下,执行下面这行命令:

Generated bash

1
>PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin make

Use code with caution.Bash

命令解释:

  • PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:这部分会在执行 make 命令之前,临时将 PATH 环境变量设置为一个只包含最核心、最标准、最安全的 Linux 路径的集合。
  • make:紧跟在后面的 make 命令就会使用这个临时的、干净的 PATH 来执行。

这个修改只会对这一条命令生效,不会影响你终端后续的其他命令,非常安全。

执行后,Buildroot 的环境检查应该就能通过,并正式开始编译了。


进入./output/images文件夹下有rootfs.ext2(系统文件)和zImage(这个解压一些就是内核)

创建一个tmp空目录,目的挂载文件系统,命令mount rootfs.ext2 tmp

取消挂载umount tmp