本教程将是裸机逆向工程系列的一部分。
自从拆解了几部安卓手机后,我对嵌入式系统的兴趣越来越大。
虽然手机本身并不是嵌入式系统,但我知道手机最终会取代计算机;因此,我想学习更多关于它们的知识。
就在那时,我开始学习ARM,并发现它们与嵌入式设备有许多相似之处。
从那以后,我就开始狂奔:我开始学习关于嵌入式设备的一切知识。
什么是嵌入式系统?
嵌入式系统是一种最有效、最经济地实现单一目的的设备。最常见的例子是物联网设备(如摄像头)。这通常需要只运行所需软件的软件;因此,需要嵌入式操作系统(例如嵌入式Linux)。
我首先决定探索的是树莓派,因为我之前已经有过一些经验(主要是针对基于黑客马拉松的项目,而不是逆向工程或二进制漏洞利用)。
而且这个系统的文档非常全面,使得了解它的某些方面(包括软件和硬件)的工作原理变得更容易。最后,找到开发工具也更容易,包括模拟器,这样我就可以在旅途中进行项目开发。
你也不一定非得从树莓派开始……还有其他的设备,比如Beaglebone设备、路由器、摄像头等等。然而,对于本教程来说,我将主要关注树莓派0。
在继续阅读之前,你需要了解的一些基础知识:
- 串行通信:这是通常用于调试输入和输出的信号。一般来说,这需要将电缆连接到引脚上;然而,使用QEMU,我们可以模拟到stdin/stdout。
- 引脚:这是嵌入式系统发送信号的方式(串行、JTAG等)。
- 工具链:这是我们如何编译代码的。我们经常进行交叉编译(因为我们假设在本教程中我们没有任何ARM硬件)。你可能听到的工具链是eabi、gnueabi和gnueabi-hf。对于本教程,我们只考虑eabi,因为它是裸机工具链(例如没有操作系统)。要了解更多关于gnueabi和gnueabi-hf的信息,请点击这里。
要开始,我们需要某种Linux平台。为此,我使用了Ubuntu 20.04,但你可以选择你想要兼容下面列出软件的任何发行版。在你的发行版中,我们需要安装以下软件:
- GDB Multiarch —sudo apt install gcc-multiarch
- qemu (I am using 5.2 in this demo) — https://www.qemu.org/download/#source
- 逆向工程工具(我推荐Ghidra,但这是个人偏好)
- Optional: gcc-arm-none-eabi — sudo apt install arm-none-eabi-gcc
对于这个,我们将使用qemu-system-arm(这也是qemu-system-aarch64的一部分)。
它默认支持许多不同的设备,其他设备则可以通过设置实现。在使用qemu-system-arm时,需要注意一些重要事项。
首先,您需要指定如何读取输出和写入输入(特别是对于串行)。
由于我们不是在处理实际的硬件,因此我们不能使用引脚来与设备交互。
例如,假设我们要模拟一个名为kernel.img的内核,我们需要运行以下命令来运行它:
- qemu-system-aarch64 -M raspi0 -kernel kernel.img -serial null -serial stdio
如果我们想做同样的事情,但是我们可以连接到GDB,我们可以运行以下命令:
- qemu-system-arm -M raspi0 -kernel kernel.img -serial null -serial stdio -S -gdb tcp::4269 -boot c
此命令具体打开一个GDB服务器,我们可以通过gdb-multiarch连接到它,并与之交互。要连接到它,我们运行以下命令。
- gdb-multiarch - add-symbol-file kernel.elf <addr> — Run this command if you have an elf file for your kernel, otherwise, it is optional. - target remote localhost:<port>
我强烈建议将其制作成gdb脚本文件。这样,您也可以使gdb更易于管理。有关GDB脚本的优秀教程可以在这里找到:
- https://www.adacore.com/gems/gem-119-gdb-scripting-part-1
- https://www.adacore.com/gems/gem-120-gdb-scripting-part-2
您也可以使用GEF进行调试;然而,您需要运行以下命令:
gdb-gef-multiarch (or however you bring it up on the console) set architecture arm add-symbol-file kernel.elf <addr> gef-remote -q localhost:<port>
现在我们已经有了执行代码的环境,我们可以评估内核的几种方法。除了GDB,我们还需要一个反汇编器/反编译器,如Binary Ninja或Ghidra。这将在我们逆向工程内核时提供帮助,特别是对于裸机程序。
裸机软件是指直接在硬件上运行的软件,没有操作系统功能。有时甚至没有标准库,如stdio.h或stdlib.h。
因此,除非我们有ELF文件,否则我们不会有符号。即使如此,如果使用标准库,反编译器和反汇编器也可能无法识别它们。
最后,学习如何阅读ARM汇编代码至关重要。大多数嵌入式系统使用ARM架构;然而,一些仍然使用MIPS,而在极少数情况下使用Power PC。您需要熟悉的术语有:
- Calling Conventions
- Prolog
- Epilog
- Registers
- Memory
在继续之前,您应该做的一个很好的教程是Azeria的教程。
它有七个部分,您将能够开始学习如何逆向和利用基于ARM的代码。完成所有部分!链接:
https://azeria-labs.com/writing-arm-assembly-part-1/
您可能要考虑的其他一些事情:
- 开始学习一些Python,您将经常使用它来进行漏洞利用和逆向工程脚本(这将使您的生活变得更加轻松)。
- 获取pwntools,这将帮助您进行漏洞利用,并支持arm和aarch64 -
https://docs.pwntools.com/en/latest/
- 获取angr:这将帮助您找到漏洞并逆向工程重复任务 - https://docs.angr.io/
现在您准备好了!现在您应该尝试解决CTF挑战或逆向工程您感兴趣的内核。稍后,我将介绍一些您可能错过的重要概念(并且我是通过艰难的方式学习的)。
CTF(Capture The Flag)挑战是一种网络安全竞赛,参赛者需要在限定的时间内找到并提交目标程序中的隐藏标志。CTF挑战通常涉及到各种网络安全技术和技巧,包括逆向工程、漏洞利用、密码学、网络分析等。
逆向工程重复任务是指对已经编译的程序进行反编译,并对其代码进行逐行分析,以理解其功能、查找漏洞或进行修改。在CTF挑战中,参赛者可能需要逆向工程一个二进制程序,以找到隐藏在其中的标志或秘密信息。
下一步工作:
现在我们的环境已经设置好了,我们准备好对二进制文件进行逆向工程了!
如何使用我为UMDCTF编写的CTF挑战Furor进行基本的ret2text漏洞利用。你可以在这里下载副本:https://github.com/UMD-CSEC/UMDCTF-2021-Public-Challenges/tree/master/Pwnables/furor
由于仅解决此CTF挑战就涉及很多概念,我们主要需要了解两个方面的内容:
- 一专注于初始的高级逆向工程
- 二专注于ARM的深入逆向工程和漏洞利用。
要做的事情:
- 如何找到关于裸机固件映像的关键信息
- 了解如何找到目标设备的信息
- 如何设置Ghidra,以便它能够理解如何找到信息(例如,更容易找到我们的目标函数)
当你开始开始对任何东西进行逆向工程时,首先要确定它是什么。
虽然我们从文章的标题中对其有了很好的了解,但在大多数情况下,我们并没有这种上下文。
固件映像有点奇怪,因为它们可以包含其中的多个文件或是一个二进制blob。
为了对映像有一个大致的了解,我喜欢使用一些Linux实用程序,如file、Binwalk等。
一般来说,如果固件映像包含多个文件或是一个标准格式,它会给出输出细节。
不幸的是,Binwalk没有给我们提供太多信息。
逆向工程需要一定的技术功底和经验,涉及到汇编语言、计算机体系结构、操作系统等知识。在逆向工程过程中,常用的工具包括反汇编器、调试器、反编译器等。