前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >使用QEMU chroot进行固件本地调试

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

作者头像
绿盟科技研究通讯
发布2019-12-11 11:36:13
5.7K0
发布2019-12-11 11:36:13
举报

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/

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

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-10-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 绿盟科技研究通讯 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
物联网
腾讯连连是腾讯云物联网全新商业品牌,它涵盖一站式物联网平台 IoT Explorer,连连官方微信小程序和配套的小程序 SDK、插件和开源 App,并整合腾讯云内优势产品能力,如大数据、音视频、AI等。同时,它打通腾讯系 C 端内容资源,如QQ音乐、微信支付、微保、微众银行、医疗健康等生态应用入口。提供覆盖“云-管-边-端”的物联网基础设施,面向“消费物联”和 “产业物联”两大赛道提供全方位的物联网产品和解决方案,助力企业高效实现数字化转型。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档