前言

这是一项来自Cyberweek会议的介绍,EMUX

该项目是一个固件仿真项目,原来叫ARMX,由于加入了mips仿真改为EMUX

原文视频及pdf地址

EMUX项目地址

项目原理及框架介绍

固件的仿真一直以来都是IoT的一个问题,仿真可以让研究者更方便地进行实验,同时降低实验成本

但是,在仿真的过程中(例如qemu-system),不可避免地会遇到各种问题,这个相信很多人都遇到过

emux的模拟思路就是尽可能地提高还原度,从bootloader,到kernel,再到用户态,做到完全一样

emux已经完成了诸如Tenda AC15Archer C9等设备的模拟,除此之外还有部分已经成功但未发布在公开版本中的设备

emux的整体框架

emux,docker与qemu

EMUX 是驻留在 /emux 目录中的脚本、内核和文件系统的集合。 它使用 qemu-system-armqemu-system-mipsqemu-system-mipsel 来启动虚拟 ARM 和 MIPS Linux 环境

运行 qemu-system-arm|mips|mipsel 的主机系统被分配了 IP 地址 192.168.100.1(即emux的ip地址),QEMU 客户机通过 tap0 接口分配了 192.168.100.2

关于这三者之间的联系,官方用了这样的两张图来描述,大家可以自行研究一下:

image.png

image.png

从主机到容器内运行的 QEMU 的端口使用 socat 完成转发

/emux目录

主目录

emux目录下包含以下内容:

image.png

如下:

  • 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设备的启动过程如下所示:

image.png

  • 通电
  • 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
2
3
cd emux
./build-emux-volume
./build-emux-docker

在运行build-emux-docker来build docker镜像的时候,git失败了,原因就是/etc/hosts里的github.com的ip地址不太对,如下:

image.png

一个解决办法就是在build的时候修改/etc/hosts,也可以跟我一样从github国内镜像来git

如下修改Dockerfile-emux

image.png

即可构建成功

Run

接下来run:

1
./run-emux-docker

可以看到在启动的时候做了端口映射:

image.png

可以自行修改

可以看到这样就跑起来了:

image.png

接下来运行:

1
[EMUX-DOCKER 🐳] ~$ launcher

然后就可以下选择你想模拟运行的设备了,如下:

image.png

例如我这里选择Tenda AC15

然后以root登录,启动选项的话我这里选第二个(第三个的话就是给你它路由器的shell)

image.png

稍等一会,访问本地的20080端口,即可看到模拟成功:

image.png

初始密码为ringzer0

image.png

成功!看起来还不错?

那么,如何管理该路由器呢?

开一个shell来执行emux项目下的emux-docker-shell

1
./emux-docker-shell

然后输入userspace

1
[emux-docker 🐚] ~$ userspace

如下:

image.png

这时候再进到shell里面去,就可以管理啦

image.png

关于调试

这个感觉我还玩得不是很明白

不过我摸索了一下,通过如下方式是成功了的:

首先,在emux的shell下,起gdbserver,连接设备中你想调试的进程:

1
[emux-docker 🐚] ~$ ssh -p 22222 -T root@192.168.100.2 gdbserver :1234 --attach 1770

然后使用emuxgdb,设置好架构和大小端,就可以连上去了,如下:

image.png

可以看到是成功了的

拓展:创建自己的模拟物联网设备

当然,最关键的还是要创建自己的模拟设备

在开始模拟 IoT 设备之前,需要具备以下条件:

  • IoT设备的详细分析
  • CPU架构(ARMv5/ARMv6/ARMv7/MIPS)
  • Linux内核版本
  • 提取的flash memory的内容(可选)
  • 根文件系统
  • nvram的内容(可选)
  • 使用 Buildroot 或 Linux Kernel 源生成兼容的内核
  • 最后,你可能需要肝上一周时间来解决各种问题

创建需要以下几步:

  • files/emux目录下有一个template目录,提供了构建所需的模板
  • 从源代码编译匹配的内核,并将其放在 kernel/ 目录中。如果您愿意,您也可以符号链接现有的内核
  • 将提取的rootfs从设备固件复制到 rootfs/ 目录中。通常这些是 SquashFSCramFS 文件系统,使用 binwalkunsquashfscramfsck 解压缩。您也可以选择创建根文件系统的压缩 tar.bz2 存档
  • 将提取的 nvram 的内容放在 nvram.ini
  • 如果您想模拟 MTD 闪存,请转储设备闪存的内容并创建两个名为 flash0.binflash1.bin 的 64MB 文件,并将它们放在 flashmem/ 目录中。您也可以选择将它们压缩到 tar.bz2 存档中。然后,您需要在 mtdparts 文件中定义要传递给内核的 MTD 分区布局
  • 将您希望使用 LD_PRELOAD 注入的任何共享库放在 preload/ 目录中。通常,这些共享库包含某些模拟二进制文件正常工作所必需的hook函数
  • 使用新填充的设备固件内容编辑配置文件
  • devices-extra 文件中创建一个新的设备记录。密切注意QEMU命令行选项,这里说的类似于files/emux目录下的devices配置文件

项目里面提供了部分版本的Linux内核文件,但建议还是编译更合适的内核

  • 至于具体的操作我还没探索过,等成功了之后补上

总结

感觉还是可以的,可操作性很强,虽然要搭起来可能比较费时,但是最后的效果还是非常不错的

大家如果自己成功搭建了某个固件的环境的话也可以发出来,这样我就可以拿着别人搭的环境一把梭