基于EMUX(原ARMX)的固件仿真,来自Cyberweek 会议
前言
这是一项来自Cyberweek会议的介绍,EMUX
该项目是一个固件仿真项目,原来叫ARMX,由于加入了mips仿真改为EMUX
项目原理及框架介绍
固件的仿真一直以来都是IoT的一个问题,仿真可以让研究者更方便地进行实验,同时降低实验成本
但是,在仿真的过程中(例如qemu-system
),不可避免地会遇到各种问题,这个相信很多人都遇到过
emux的模拟思路就是尽可能地提高还原度,从bootloader,到kernel,再到用户态,做到完全一样
emux已经完成了诸如Tenda AC15,Archer C9等设备的模拟,除此之外还有部分已经成功但未发布在公开版本中的设备
emux的整体框架
emux,docker与qemu
EMUX 是驻留在 /emux
目录中的脚本、内核和文件系统的集合。 它使用 qemu-system-arm
、qemu-system-mips
和 qemu-system-mipsel
来启动虚拟 ARM 和 MIPS Linux 环境
运行 qemu-system-arm|mips|mipsel
的主机系统被分配了 IP 地址 192.168.100.1(即emux的ip地址),QEMU 客户机通过 tap0 接口分配了 192.168.100.2
关于这三者之间的联系,官方用了这样的两张图来描述,大家可以自行研究一下:
从主机到容器内运行的 QEMU 的端口使用 socat 完成转发
/emux目录
主目录
emux目录下包含以下内容:
如下:
devices
: 此文件包含设备定义,每行一个devices-extra
:包含未包含在一般版本中的其他模拟设备。建议您将自己的模拟设备添加到devices-extra
qemuopts
:为各种类型的 QEMU 机器抽象的 QEMU 选项定义run/
:此文件夹包含解析设备配置、预加载 nvram 内容并最终调用被模拟设备的用户进程所需的脚本run/launcher
:主脚本。launcher
解析设备文件并显示已注册设备的菜单。选择其中一个设备将依次调用qemu-system-arm
和预定义的 QEMU 选项、相应的 Linux 内核和在设备上注册的提取的根文件系统run/userspace
:从启动器启动内核后,启动模拟设备的用户空间进程debuglogs
:如果存在,则表示将写入 EMUX 调试日志的位置。在创建新的模拟设备时对故障排除非常有帮助template/
:新设备的示例配置和布局。开始模拟新的 IoT 设备时,请复制模板
run目录
run/
目录还包含一些命令,可以从主机使用这些命令与在 EMUX 模拟设备中运行的进程进行交互
emuxhalt
:彻底关闭模拟设备,并卸载所有 NFS 挂载。 如果不彻底关闭,总是存在 NFS 句柄失效的风险emuxps
:远程枚举在 EMUX 中运行的进程emuxmaps
:远程转储在 EMUX 中运行的进程的进程内存布局emuxnetstat
:枚举 EMUX 中的网络套接字emuxkill
:远程终止在 EMUX 中运行的进程emuxgdb
:将 gdb 附加到在 EMUX 中运行的进程monitor
:连接到 QEMU monitor
设备启动过程
IoT设备的启动过程如下所示:
- 通电
bootloader
运行,加载内核并把cpu控制权交给操作系统- 将 FS 解压到
ramdisk
,调用init
进程 init
进程启动系统服务- 从
nvram
读取配置。即时构建系统配置文件 - 调用应用程序和应用程序服务
- 准备就绪
固件所需的各个文件
每个模拟设备都包含以下文件/目录:
config
:包含设备的名称和描述、ASLR 设置、其根文件系统的位置以及在内核启动并将控制权转移到用户空间后发出的命令nvram.ini
:设备非易失性存储器的内容,用于存储配置设置。在调用用户空间初始化脚本之前,nvram.ini 的内容被预加载到模拟的 nvram 中kernel/
:包含编译(主要通过 Buildroot)的 Linux 内核,以密切匹配模拟设备的属性,例如内核版本、CPU 支持、VM_SPLIT、支持的外围设备等rootfs.tar.bz2
:包含从目标设备提取的根文件系统的压缩档案。名称rootfs.tar.bz2
可从config
文件中进行配置。 EMUX 将在第一次调用时自动解压根文件系统flashmem/flash.tar.bz2
:包含两个 64MB 内存转储文件 flash0.bin 和 flash1.bin 的压缩档案
环境搭建及示例
我这里拿它master分支来做实验
Clone
首先clone:
1 | git clone --depth 1 --single-branch https://github.com/therealsaumil/emux.git |
Build
然后搭建docker:
1 | cd emux |
在运行build-emux-docker
来build docker镜像的时候,git失败了,原因就是/etc/hosts
里的github.com
的ip地址不太对,如下:
一个解决办法就是在build的时候修改/etc/hosts
,也可以跟我一样从github国内镜像来git
如下修改Dockerfile-emux
:
即可构建成功
Run
接下来run:
1 | ./run-emux-docker |
可以看到在启动的时候做了端口映射:
可以自行修改
可以看到这样就跑起来了:
接下来运行:
1 | [EMUX-DOCKER 🐳] ~$ launcher |
然后就可以下选择你想模拟运行的设备了,如下:
例如我这里选择Tenda AC15
然后以root登录,启动选项的话我这里选第二个(第三个的话就是给你它路由器的shell)
稍等一会,访问本地的20080端口,即可看到模拟成功:
初始密码为ringzer0
成功!看起来还不错?
那么,如何管理该路由器呢?
开一个shell来执行emux项目下的emux-docker-shell
1 | ./emux-docker-shell |
然后输入userspace
:
1 | [emux-docker 🐚] ~$ userspace |
如下:
这时候再进到shell里面去,就可以管理啦
关于调试
这个感觉我还玩得不是很明白
不过我摸索了一下,通过如下方式是成功了的:
首先,在emux的shell下,起gdbserver
,连接设备中你想调试的进程:
1 | [emux-docker 🐚] ~$ ssh -p 22222 -T root@192.168.100.2 gdbserver :1234 --attach 1770 |
然后使用emuxgdb
,设置好架构和大小端,就可以连上去了,如下:
可以看到是成功了的
拓展:创建自己的模拟物联网设备
当然,最关键的还是要创建自己的模拟设备
在开始模拟 IoT 设备之前,需要具备以下条件:
- IoT设备的详细分析
- CPU架构(ARMv5/ARMv6/ARMv7/MIPS)
- Linux内核版本
- 提取的flash memory的内容(可选)
- 根文件系统
- nvram的内容(可选)
- 使用 Buildroot 或 Linux Kernel 源生成兼容的内核
- 最后,你可能需要肝上一周时间来解决各种问题
创建需要以下几步:
- 在
files/emux
目录下有一个template
目录,提供了构建所需的模板 - 从源代码编译匹配的内核,并将其放在
kernel/
目录中。如果您愿意,您也可以符号链接现有的内核 - 将提取的
rootfs
从设备固件复制到rootfs/
目录中。通常这些是SquashFS
或CramFS
文件系统,使用binwalk
或unsquashfs
或cramfsck
解压缩。您也可以选择创建根文件系统的压缩tar.bz2
存档 - 将提取的
nvram
的内容放在nvram.ini
中 - 如果您想模拟
MTD
闪存,请转储设备闪存的内容并创建两个名为flash0.bin
和flash1.bin
的 64MB 文件,并将它们放在flashmem/
目录中。您也可以选择将它们压缩到tar.bz2
存档中。然后,您需要在mtdparts
文件中定义要传递给内核的MTD
分区布局 - 将您希望使用
LD_PRELOAD
注入的任何共享库放在preload/
目录中。通常,这些共享库包含某些模拟二进制文件正常工作所必需的hook函数 - 使用新填充的设备固件内容编辑配置文件
- 在
devices-extra
文件中创建一个新的设备记录。密切注意QEMU
命令行选项,这里说的类似于files/emux
目录下的devices
配置文件
项目里面提供了部分版本的Linux内核文件,但建议还是编译更合适的内核
- 至于具体的操作我还没探索过,等成功了之后补上
总结
感觉还是可以的,可操作性很强,虽然要搭起来可能比较费时,但是最后的效果还是非常不错的
大家如果自己成功搭建了某个固件的环境的话也可以发出来,这样我就可以拿着别人搭的环境一把梭