通过 qemu 运行并调试 IoT 固件和不同架构的二进制文件

0x10 前言

对于常用的 IoT 固件,其架构往往是基于 MIPS 或者 ARM 的,如果不能运行整体的固件,那么就应该尝试运行固件中的单个二进制文件,而我们常用的 PC 是 X86 环境,如何在 X86 环境下运行并调试其他架构的程序,就是本文要介绍的初衷。

环境说明:编写程序的代码是基于ARM架构的树莓派,运行环境是 Ubuntu 18.04.4 LTS (x86_64)

测试程序:

在树莓派上输入以下测试代码

# include<stdio.h>

int main()
{
	printf("Hello, this is the test!\n");
	return 0;
}

编译并运行

pi@raspberrypi:~/Documents $ gcc test.c -o test
pi@raspberrypi:~/Documents $ ./test
Hello, this is the test!
pi@raspberrypi:~/Documents $ 

现在将该二进制放到 x86 架构的 Linux 系统中,尝试运行代码,出现如下错误,这也是很长正常的事情,x86 架构是不能直接执行 arm 指令的。
在这里插入图片描述
这时候就需要 qemu 的帮助了。qemu 有如下两种模式

一是系统模式 qemu-system,作为虚拟机监管器,模拟全系统,利用其他VMM(Xen, KVM, etc)来使用硬件提供的虚拟化支持,创建接近于主机性能的虚拟机;二是用户模式 qemu-user,作为用户态模拟器,利用动态代码翻译机制来执行不同于主机架构的代码

也就是说,我们有两种方式在 x86 的 Linux 跑 arm 的树莓派程序:qemu 创建一个 arm 虚拟机(编译生成目标二进制文件的系统),这样可以直接在虚拟机中运行目标二进制。但是这种模式的弊端是,我们常常没有办法直接模拟目标环境,比如,对于路由器,很难直接使用 qemu 创建一个与目标路由器环境一样的系统。因此,最好的方式是,直接使用 qemu-user 模式,不创建虚拟机。以下三种方法循序渐进,都可以用来模拟不同架构的固件。


0x20 qemu-user 模式运行目标二进制文件

对于静态链接的程序,因为没有外部依赖,可直接使用如下命令运行程序

qemu-arm ./test

事实上,很多程序是动态链接生成的,有很多依赖环境,比如我们刚刚写的例子,gcc 默认是用动态链接生成可执行程序的,因此,如果直接在 ubuntu 上运行该程序会出现如下错误
在这里插入图片描述
提示没有找到链接器。此处有两种方式,找到所谓的链接器即共享库

1.直接在网上安装对应的共享库
在这里插入图片描述
这时,所对应的库,就放在如下目录
在这里插入图片描述
备注:对于arm,我们下载的时 libc6-armhf-cross,其他架构就是替换中间的 armhf 即可。

2.将目标二进制所在的源文件系统中的库,拷贝到本地 ubuntu:将树莓派上的该文件 /lib/ld-linux-armhf.so.3 拷贝到 ubuntu 中,同时也要将相应的库拷贝上,/lib/arm-linux-gnueabihf/。目录结构需要与树莓派保持一致。有以下三种方式,运行动态链接的二进制

对于IoT固件,如果不清楚lib库放在哪,可以直接将整个文件系统复制下来。


0x21 通过指定环境变量的方式,设置共享库

设置环境变量,要让当前环境默认的根目录更改

export QEMU_LD_PREFIX=/home/lys/Documents/test

这里假设,你将共享库或者整个固件的文件系统放在 /home/lys/Documents/test 目录下(实际上只需要依赖的链接器和库文件),运行成功!
在这里插入图片描述

0x22 通过 qemu 参数 -L 设置共享库

qemu-arm -L /home/lys/Documents/test 目标二进制

运行结果如下(假设依赖文件与要执行的二进制在同一目录下,当然可以是不在一个文件夹内的)
在这里插入图片描述

0x23 通过 chroot 命令,更改当前系统根目录

chroot 命令用来在指定的根目录下运行指令。chroot,即 change root directory (更改 root 目录)。在 linux 系统中,系统默认的目录结构都是以/,即是以根 (root) 开始的。而在使用 chroot 之后,系统的目录结构将以指定的位置作为/位置。因此,为了避免每次都要更改环境变量来运行不同架构的程序,可以使用 chroot 命令,在复制过来的文件系统的根目录下,直接运行。


0x30 使用 binfmt 自动识别

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

sudo apt install qemu-user-binfmt
update-binfmts --display

安装完成之后,可以直接执行 ./test,binfmt 会自动调用 qemu 进行仿真。这个包会依赖安装系统软件源中的 qemu-user。我们用不到它,但这个包的意义在于它包含了几个自动向内核注册 QEMU binfmt 的脚本,这样我们就不需要再手动指定我们的 ARM 可执行文件需要哪个路径下的 QEMU 来执行
在这里插入图片描述


0x40 调式 IoT 固件程序

qemu 工具自带 gdbserver,通过 -g 选项可以指定监听端口

qemu-arm -g 2222 ./test

新建一个终端,启动 gdb-multiarch 进行远程调试,当然,gdb-multiarch 软件包是需要安装的请注意:一定要使用 file test,即在 gdb 客户端也要加载目标文件!

sudo apt-get install gdb-multiarch
gdb-multiarch
(gdb)$ file test
(gdb)$ set architecture arm
(gdb)$ target remote localhost:2222

如果你还为 gdb 安装了 gef 插件,还可以通过以下方式连接远程调试

gef > gef-remote -p [PID] localhost:2222

笔者在Ubuntu 18.04 以及 Kali 2019.2上均遇到一下问题:No executable has been specified and target does not support determining executable automatically,请参考相关论坛即可。warning: remote target does not support file transfer, attempting to access files from local filesystem,有待解决。

–更新–
其一,使用 gdb-multiarch 进行调试的时候,虽然是用远程调试,但是这里也必须加上参数,即被调试的程序;
其二,在 gdb 中,需要使用 set sysroot/libso-path 设置动态链接库

终端1

lys@lys-virtual-machine:~/Documents/arm_test$ qemu-arm -g 3333 -L /usr/arm-linux-gnueabihf/ ./test

终端2

root@lys-virtual-machine:~/Documents/arm_test# gdb-multiarch ./test
GNU gdb (Ubuntu 8.1-0ubuntu3.2) 8.1.0.20180409-git
Copyright (C) 2018 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
GEF for linux ready, type `gef' to start, `gef config' to configure
47 commands loaded for GDB 8.1.0.20180409-git using Python engine 3.6
[*] 7 commands could not be loaded, run `gef missing` to know why.
Reading symbols from ./test...(no debugging symbols found)...done.

gef➤  set architecture arm
The target architecture is assumed to be arm
gef➤  set solib-absolute-prefix /usr/arm-linux-gnueabihf/
gef➤  target remote localhost:3333

结果如下,虽然没能够访问堆栈,但是还是能看到寄存器的值
在这里插入图片描述

0x50 总结

qemu-user 模式运行不同架构的二进制文件,核心部分在于对动态链接库的正确处理,至于调试,则需要用到 gdb-multiarch,可结合相应的 gdb 插件,如 gef,方便观察二进制运行过程中内存的变化。


  • 2020-05-22 更新:gef 远程连接时,使用 gef-remote -q localhost:2222 命令,即可解决 gef 不显示内容的问题
  • 2021-01-23 更新:添加 gdb 客户端必须加载目标文件的描述
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

江下枫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值