标题 | 简介 | 类型 | 公开时间 | ||||||||||
|
|||||||||||||
|
|||||||||||||
详情 | |||||||||||||
[SAFE-ID: JIWO-2024-582] 作者: ecawen 发表于: [2017-09-07]
本文共 [710] 位读者顶过
这篇教程中有一些示例程序,可以动手调试来加深理解。要调试 ARM 程序,我们需要能运行 ARM 程序的运行环境和支持 ARM 架构的调试器。本篇教程将基于 x86 平台的 Ubuntu 16.04 ,介绍如何搭建 ARM 的交叉编译、运行和调试环境。 交叉编译环境Ubuntu 16.04 的源中提供了多个 arm-gcc 的软件包,以 gcc 5 为例可以通过“ apt search ”命令找到“ gcc- 5 -arm-linux-gnueabi ”和“ gcc- 5 -arm-linux-gnueabihf ”两个软件包。这两个软件包安装的编译工具是一样的,只是与浮点数相关的默认编译选项不同。由于我们虚拟的环境没有 FPU ,只需要安装“ gcc- 5 -arm-linux-gnueabi ”就可以了。 安装完成后可以在“ /usr/bin/arm-linux-gnueabi-* ”找到相关的编译工具链,包含常用的 gcc 、 as 和 ld 等。只要使用如下两条命令,就可以实现对 ARM 汇编的编译: $ arm-linux-gnueabi-as [source file] –o [object file] $ arm-linux-gnueabi-ld [object file] –o [executable file] 可以使用如下命令编译经典的“ hello world ”程序,用于后续章节的实验: $ arm-linux-gnueabi-gcc-5 hello.c –g –o hello -static 运行环境I. qemu-user-static最简单的运行环境是使用 qemu-user-static 模拟运行静态编译的可执行程序。我们可以使用如下命令模拟运行上一节创建的 hello 程序: # 首先安装 qemu-user-static ,若已安装可以忽略这一步 $ sudo apt install qemu-user-static # 直接执行 hello 程序 $ qemu-arm-static hello # 启动 gdbserver 等待 gdb 连接 $ qemu-arm-static –g [gdbserver port] hello 上述命令运行后会启动一个 qemu 自带的 gdbserver ,监听你通过“ -g ”选项指定的端口。可以在另一个窗口中启动 gdb 进行远程调试(远程调试的细节,将在第三章介绍)。 II. 虚拟 Raspberryqemu-user-static 的方式比较简单,但功能也很局限, Azeria-labs 的教程中介绍了另一种方法,使用 qemu 创建一台虚拟树莓派。首先你需要安装 qemu-system : $ sudo apt install qemu-system 为了虚拟一台树莓派,你还需要下载专为树莓派定制的 debian 镜像( raspbian )和支持树莓派的内核文件。 raspbian 镜像下载地址: https://www.raspberrypi.org/downloads/raspbian/ 树莓派内核下载地址: https://github.com/dhruvvyas90/qemu-rpi-kernel Raspbian 的镜像有两个版本,一个带图形界面的完整版和一个没有图形界面的 lite 版本,对于我们的实验而言 lite 版本就足够了。内核文件有多个,选择内核版本最新的那个就可以了。下载完上述文件后,创建一个“ arm_vm ”目录,将上述文件一起放置在该目录下。然后执行如下命令: $ unzip <image-file>.zip $ fdisk –l <image-file> 你应该可以看到,类似如下内容: Disk 2017-08-16-raspbian-stretch-lite.img: 1.7 GiB, 1854418944 bytes, 3621912 sectors Units: sectors of 1 * 512 = 512 bytes Sector size (logical/physical): 512 bytes / 512 bytes I/O size (minimum/optimal): 512 bytes / 512 bytes Disklabel type: dos Disk identifier: 0xee397c53
Device Boot Start End Sectors Size Id Type 2017-08-16-raspbian-stretch-lite.img1 8192 93813 85622 41.8M c W95 FAT32 (LBA) 2017-08-16-raspbian-stretch-lite.img2 94208 3621911 3527704 1.7G 83 Linux 注意标红的部分,可以看到文件系统从 94208 扇区开始。我们将这个值乘以 512 ,本例中为“ 94208 * 512=48234496 ”,这就是文件系统起始位置的偏移字节数,在下面的命令中我们会用到: $ sudo mkdir /mnt/raspbian $ sudo mount -v -o offset=48234496 -t ext4 [path-of-your-img-file.img] /mnt/raspbian $ sudo vi /mnt/raspbian/etc/ld.so.preload 将上述文件中的所有内容用“ # ”注释掉,保存修改并退出。 $ sudo vi /mnt/raspbian/etc/fstab 如果 fstab 文件中有出现 mmcblk0 字符串,那么将“ /dev/mmcblk0p1 ”替换为“ /dev/sda1 ”,将“ /dev/mmcblk0p2 ”替换为“ /dev/sda2 ”,保存后退出。至此,系统配置的修改完成,可以将“ /mnt/raspbian ”卸载掉。 $ sudo umount /mnt/raspbian 你可以进入“ arm_vm ”目录,使用如下脚本启动虚拟机: #!/usr/bin/env bash
qemu-system-arm -kernel kernel-qemu-4.4.34-jessie \ -cpu arm1176 \ -m 256 \ -M versatilepb \ -serial stdio \ -append "root=/dev/sda2 rootfstype=ext4 rw" \ -drive format=raw,file=2017-08-16-raspbian-stretch-lite.img \ -redir tcp:5022::22 \ # 为 ssh 预留 -redir tcp:3011::3011 \ # 为 gdbserver 预留,用于远程调试 -no-reboot 1> /dev/null 2>&1 & 虚拟机启动后默认的登录密码是“ raspberry ”。为了更方便的使用虚拟机,我们需要开启 ssh 服务,并设置开机启动。 $ sudo service ssh start $ sudo update-rc.d ssh enable 此时,你应该已经可以使用如下命令,通过 ssh 访问虚拟机了: $ ssh pi@127.0.0.1 -p 5022 我们可以使用 scp 命令通过 ssh ,将上一节编译的 hello 程序上传到虚拟机中执行: scp -P 5022 hello pi@127.0.0.1:/tmp 进入虚拟机的 tmp 目录,可以看到我们上传的 hello 程序尝试执行,应该会输出久违的“ hello world! ”,说明我们的交叉编译环境搭建是正确的。至此我们的虚拟树莓派环境搭建完毕。 调试环境调试环境的搭建是最重要的也是坑最多的。为了模拟真实 IoT 安全实战中远程调试的场景,我们将介绍如何交叉编译 gdbserver 并上传至虚拟机进行远程调试。为了获得类似 pwndbg 那样强大的调试效果,我们将介绍如何安装使用专为 IoT 安全设计的 gef 增强脚本。 I. gdb-multiarch在使用 gdb 进行调试之前,我们需要先安装 gdb-multiarch 。顾名思义,它是 gdb 支持多中硬件体系架构的版本。之所以要安装 gdb-multiarch ,是因为 Ubuntu 默认安装的 gdb 只支持 x86/x64 架构,你可以启动 gdb 然后输入命令“ set architecture arm ”查看, gdb 会提示错误。 # 安装 gdb-multiarch $ sudo apt install gdb-multiarch # 启动 gdb-multiarch $ gdb-multiarch II. 编译 gdbserver在分析 IoT 设备的安全性时,我们往往需要上传 gdbserver 进行远程调试。在我们的实验环境中(事实上我们的 Raspbian 系统自带 gdb),我们也可以模拟搭建一个远程调试环境。首先,我们需要获取 gdb 的源码(包含了 gdb 源码和 gdbserver 源码),版本需要与我们本地的 gdb 版本一致,因为 gdbserver 需要与 gdb 版本保持一致,否则容易出现非预期的问题。你可以在这个地址,找到 gdb 各版本的源码: http://ftp.gnu.org/gnu/gdb/ 。 下载解压后进入“ gdb-<version>/gdb/gdbserver ”目录,使用如下命令编译安装: $ CC="arm-linux-gnueabi-gcc-5" CXX="arm-linux-gnueabi-g++-5" ./configure --target=arm-linux-gnueabi --host="arm-linux-gnueabi" --prefix="setup-directory" $ make install 然后,在你通过“ --prefix ”选项指定的路径下,就可以找到编译完成的 gdbserver 了。使用 file 命令查看,应该可以看到类似如下输出: $ file arm-linux-gnueabi-gdbserver arm-linux-gnueabi-gdbserver: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.3, for GNU/Linux 3.2.0, BuildID[sha1]=32ad2025951ee428276ac2fbadb199bfd39e2278, not stripped 使用 scp 将 gdbserver 上传到我们的虚拟树莓派中并启动: $ ln -s arm-linux-gnueabi-gdbserver gdbserver $ gdbserver 0.0.0.0:2333 hello Process hello created; pid = 702 Listening on port 2333 至此,我们的远程调试环境搭建完毕,下一节,我们将引入 gef 增强脚本。 III. gef 增强脚本gef 是一个支持多种硬件体系结构的 gdb 增强脚本,非常适合 IoT 安全领域应对多变的硬件平台。你可以参考 github 主页( https://github.com/hugsy/gef )的 README ,进行安装配置。不过需要注意的是, gef 依赖的第三方模块 keystone-engine 需要手动安装,因为 pip 源提供的安装是无效的。建议先通过 pip 安装,如果安装后 gef 的部分功能仍无法使用,可以卸载通过 pip 安装的第三方模块,在 github 上( https://github.com/keystone-engine/keystone )下载最新源码,手动编译安装(参见: http://www.keystone-engine.org/docs/ )。 安装完成后开启 gdb 调试,你将看到类似如下的界面:
首先设置目标硬件体系架构为 arm : gef> set architecture arm 我们使用 gef-remote 命令连接 gdbserver ,如果使用 gdb 自带的“ target remote ”命令会出现一些非预期的问题(参见:https://github.com/hugsy/gef/issues/7 )。 gef> gef-remote –q 127.0.0.1:2333 你应该能看到类似如下的输出:
至此,我们的调试环境配置完毕了。 扩展阅读
[出自:jiwo.org] [1] gef 官方文档, http://gef.readthedocs.io/en/master/ [2] gdb 调试利器, http://linuxtools-rst.readthedocs.io/zh_CN/latest/tool/gdb.html [3] gdb 中应该知道的几个调试方法, https://coolshell.cn/articles/3643.html |