首页
学习
活动
专区
圈层
工具
发布
44 篇文章
1
[PyUserInput]模拟鼠标和键盘模拟
2
银行排队模拟(离散事件模拟)
3
Linux网络模拟
4
Linux TC(Traffic Control)作为损伤仪的基础配置和使用
5
深入学习Docker网络(看这篇就完全够了)
6
【鸿蒙 HarmonyOS】鸿蒙手机模拟器 ( 鸿蒙远程模拟器 | 鸿蒙远程模拟器运行手机应用 )
7
探索嵌入式应用框架(EAF)
8
多 OS 混合部署框架
9
嵌入式系统架构浅谈:编程设计模式 (一)---访问硬件的设计模式
10
事件驱动和消息驱动
11
原来 8 张图,就能学废 Reactor 和 Proactor
12
Linux df -h 命令hang住没有反应
13
kafka消费组信息采集异常(hang住)排查
14
ext4 io hung模拟脚本
15
解决 umount 命令卸载磁盘时busy/卡死的问题
16
程序卡死在void HardFault_Handler的解决办法
17
执行sed命令卡死CPU消耗100%一例分析
18
记一次因Redis使用不当导致应用卡死过程
19
字节对齐不慎引发的挂死问题
20
解引用NULL为什么会导致程序挂死?
21
记64位地址截断引发的挂死问题
22
websocket 在线工具_websocket添加请求头
23
【嵌入式Linux应用开发】SquareLine Studio与LVGL模拟器
24
详解Handler机制中消息队列的出队逻辑
25
Android UpdateEngine模块流程(含序列图)
26
物联网时代的嵌入式开发平台
27
400+条实用C/C++框架、库、工具整理 ,你能想到的都在这里了
28
ESP32芯片IO解读
29
M5Stack在ubuntu上进行开发编译
30
【抽象那些事】不完整的抽象&多方面抽象&未用的抽象&重复的抽象
31
H264,你不知道的小技巧
32
linux 创建虚拟块设备,制作文件系统并挂载,用于测试lustre
33
基于linux开发uvc摄像头_uvc协议扩展
34
清晰讲解LSB、MSB和大小端模式及网络字节序
35
在树莓派中使用 MicroPython 接入 MQTT
36
MicroPython 玩转硬件系列1:环境搭建
37
嵌入式系统架构浅谈:编程设计模式 (二)---嵌入并发和资源管理的设计模式
38
嵌入式软件架构设计之分层设计
39
IC之路(一)Proteus-Arduino仿真环境搭建
40
图像处理基础(六)-libjpeg常用算法
41
OpenCV双目标定
42
L-K光流推导及OpenCV代码实现
43
NDI Webcam Input工具,那些你不知道的知识!
44
使用QEMU chroot进行固件本地调试
清单首页其它文章详情

使用QEMU chroot进行固件本地调试

QEMU是我们在调试一些不同架构的程序时经常使用的虚拟机软件。它有两种运行模式,全系统模拟(System mode)和单程序运行(User mode)。System mode和我们平常用的VMWare一样,模拟整个系统从加载器开始的启动和运行。在设备逆向过程中,如果仅仅是为了运行我们提取出文件系统中的某一个程序,我们就可以使用QEMU的user mode来简化整个操作流程,同时能够方便的利用 QEMU 自带的GDB服务来进行调试,免去搭建环境的烦恼。

但是,单单在命令行中调用“qemu-arm myprogram”往往没有那么简单,因为动态链接的程序都会依赖几个动态链接库。虽然可以传入 -L 参数,或者通过指定环境变量QEMU_LD_PREFIX解决,但这种方式不但不优雅,还会造成重复性的工作——每个程序依赖的库不同,因此每次都要选择不同的目录。而且使用这种方式启动的程序,所运行的程序文件夹(CWD)与原来不同,很可能访问不了程序中硬编码的一些文件的绝对路径,造成程序出错。

因此最简单直接的方法还是使用chroot配合QEMU,来完全模拟程序的文件系统环境,以固件的根目录作为chroot的根目录,程序也能够自动加载到它所需要的libc与其他各种函数库。我们绿盟的小伙伴在看雪上做过一次分享,我听过后以为很容易上手,但是操作时踩到了一些坑。这里总结一下整个流程,顺便讲解一下其中的原理。

一、编译安装

Ubuntu自带的 QEMU 版本有点老(我的18.04 LTS附带的QEMU 2.11),会遇到一些问题。在调试时会遇到类似下面的报错。

代码语言:javascript
复制
~ # ./gdbserver tcp:2333 /usr/bin/messagingagent
qemu: Unsupported syscall: 117

老版本QEMU不能够很好的处理与调试相关的ptrace系统调用,我们需要从官方的最新版源代码编译安装QEMU。

依赖的安装可以参考官方教程(https://wiki.qemu.org/Hosts/Linux)安装好依赖后从git获取最新的源码,并使用以下参数指定编译的QEMU采用静态链接,最后进行编译。我在这里指定prefix目录为当前目录下的 staging,自己操作时可以随意更改。

代码语言:javascript
复制
git clone git://git.qemu.org/qemu.git --recurse-submodules --depth 1
cd qemu
./configure --static --prefix="$PWD/staging/user-static" --disable-system --enable-linux-user
make –j8 && make install

然后 staging/user-static 目录下就是我们编译好的核武器了。

二、安装binfmt

binfmt(Binary Format)是一个内核模块,它的用处如它的名字,通过二进制文件头来识别它的格式,从而指定用哪个解释器去启动——可以理解为二进制文件的hashbang(用处类似于在Python文件的第一行写上“#!/usr/bin/env python”)。有了它我们就可以像启动原生ELF一样启动一个ARM或其他任何QEMU支持的程序了。

代码语言:javascript
复制
sudo apt install qemu-user-binfmt
update-binfmts --display

安装这个包会依赖安装系统软件源中的qemu-user。我们用不到它,但装这个包的意义在于它包含了几个自动向内核注册QEMU binfmt的脚本,这样我们就不需要再手动指定我们的ARM可执行文件需要哪个路径下的QEMU来执行,非常方便。安装成功后在命令行中执行“update-binfmts --display”。

图 1 update-binfmts输出

我们此时可以测试一下,临时将环境变量 QEMU_LD_PREFIX 设置为我们要 chroot 进去的根目录,然后运行ARM设备中提取出的ELF可执行文件,如果不报错就可以了。图中 a.out 是我编译的 arm64 的 hello world,这个程序可在我的测试设备上正常运行。

三、复制QEMU程序

hello world可以运行之后,我们还需要准备一下我们的rootfs目录。

将第一步编译目录中的“staging/user-static/qemu-aarch64”复制到“update-binfmts”中显示的对应位置(/usr/bin/qemu-aarch64),如果必要的话,将这里的aarch64替换成你所要运行的程序架构。注意必须是相同位置!当我们启动为ARM或其他架构编译的应用程序时,系统会调用binfmts识别它的类型并调用之前注册的interpreter(如/usr/bin/qemu-aarch64)来“翻译”启动。在chroot下,依然会从这个路径中寻找。因此如果chroot后这个路径下找不到QEMU,启动任何程序都会报错No such file or directory。这个报错会有很多歧义,因此一定要自己确认一下QEMU确实在rootfs的“/usr/bin”目录中。

四、运行

代码语言:javascript
复制
sudo chroot . /bin/sh

到这里,我们就可以像在虚拟机中一样,通过shell运行这个chroot中的所有程序了!

1总结 Xxx not found 相关的问题

当我运行一个命令时,

# ./run_xxx_command

报一个错

代码语言:javascript
复制
@#@$@%:No such file or directory

我的第一反应肯定是怀疑自己。程序名称输错了?但又不对,我怎么可能这么蠢呢?一路摸爬滚打下来,我发现最蠢的还是这句模糊的报错信息。它会有很多歧义。

根据我的经验,这个报错会有以下四种原因。

  • 最容易想到的:要运行的命令不存在。
  • 动态链接器不存在。如下例,运行IDA的远程调试器。

运行objdump可以看到它需要哪个解释器来读取它。一般都是ld-xxxx.so

如果ld找不到的话,这程序能运行的概率就很小了。

  • QEMU解释器没找到。如果我们注册了binfmt却没有将qemu拷贝到“rootfs/usr/bin“中,chroot时也会报一样的错误——文件没找到。如果没有踩过这个坑,大概会很久找不出原因吧。
  • 动态链接库没找到。这种情况比较显而易见,因为他会告诉你哪个库没找到。

以后拿到一个新的固件包,只需要解压到一个文件夹里,把对应架构的qemu拷贝进去,直接运行命令chroot即可。遇到需要调试的程序,我们通过运行“qemu-aarch64 -g 2331 /path/to/binary”指定-g参数开启调试选项,也可以声明一个环境变量QEMU_GDB=2331,带上这个环境变量所启动的程序,都会自动开启GDB端口并等待调试器attach,调试起来是不是很方便呢?

参考资料:

[1]How can I chroot into a filesystem with a different architechture? - Stack Exchange https://unix.stackexchange.com/a/177122

[2]QEMU on Linux hosts https:// wiki.qemu.org/Hosts/Linux

[3]Build qemu-user-static from source code http://logan.tw/posts/2018/02/18/build-qemu-user-static-from-source-code/

内容编辑:物联网安全实验室 张浩然 责任编辑:肖晴

举报
领券