原文链接:https://azeria-labs.com/writing-arm-assembly-part-1/
最近在学IoT安全,这个教程挺不错的,边学边翻译出来,分享给大家。
ARM 汇编基础教程 —— ARM 汇编简介
原文链接: https://azeria-labs.com/writing-arm-assembly-part-1/
翻译: ljcnaix
欢迎来到系列文章《 ARM 汇编基础教程》。这个系列是为后续的《 ARM 漏洞利用教程》(连载中)打基础而编写的。在我们深入用 ARM 汇编编写 shellcode 和构造 ROP 链之前,我们需要先掌握一些 ARM 汇编的基础知识。
我们会一步一步覆盖如下主题:
第一篇: ARM 汇编简介
第二篇: 数据类型和寄存器
第三篇: ARM 指令集
第四篇: 内存读写
第五篇: 高级内存读写
第六篇: 条件分支
第七篇: 栈和函数
为了执行本文后续的示例,你需要搭建一个学习 ARM 汇编的实验环境。如果你没有一台 ARM 设备(比如树莓派),你可以根据这个教程( https://azeria-labs.com/emulate-raspberry-pi-with-qemu/ )使用 QEMU 模拟器创建一台虚拟设备来配置你的实验环境。如果你缺乏使用 GDB 调试程序的基础知识,你可以在这片教程中学习( https://azeria-labs.com/debugging-with-gdb-introduction/ )。在本系列教程中,我们将关注 ARM 32-bit ,所有的例子将按照 ARMv6 指令集编译。
为什么介绍 ARM ? 这篇教程是为有兴趣学习基础的 ARM 汇编知识的人而写的。尤其是那些想在 ARM 平台编写漏洞利用的人。你可能已经发现 ARM 处理器在你的生活中无处不在。当我环顾我的身边,我发现我身边的大多数设备使用着 ARM 处理器而不是 intel 。这些设备包括我的手机、路由器以及最近销售火爆的 IoT 设备。可以说, ARM 处理器已经成为了当世最广泛使用的 CPU 核心。随之而来的是和 PC 时代类似的问题, ARM 设备也易于受到缓冲区溢出之类的攻击。由于被广泛的使用和潜在的脆弱性,针对这些设备的攻击将变得越来越常见。
目前在二进制安全领域,比起 ARM 平台,我们对 x86/x64 平台有深入的多的研究,即使 ARM 汇编可能是主流 CPU 指令集中最易学的。那么,为什么没有更多的人来关注 ARM ,研究 ARM 呢?可能因为漏洞利用的学习资料,大多数是针对 Intel 平台的,而很少针对 ARM 平台的。比如著名的由 Corelan Team 编写的 Intel x86 漏洞利用教程( https://www.corelan.be/index.php/2009/07/19/exploit-writing-tutorial-part-1-stack-based-overflows/ ),帮助了很多对二进制漏洞利用感兴趣的人,通过学习和实践这个教程包含的知识,进入这个领域。如果你对 x86 平台的漏洞利用感兴趣, Corelan Team 的教程是非常好的起点。在这篇教程中,我们将关注 ARM 汇编的基础知识以及如何在 ARM 平台下编写漏洞利用。
ARM 处理器和 Intel 处理器 ARM 处理器和 Intel 处理器之间有很多的差异,其中最大的不同点就是它们的指令集。 Intel 是一个 CISC ( Complex Instruction Set Computing ,复杂指令集)处理器。因此它具有更庞大,功能更丰富的指令集,并且允许指令进行一些复杂的访存操作。它也因此具有支持更多的复杂操作和寻址方式,并且寄存器的数量比 ARM 要少的多。 CISC 处理器一般用在通用 PC ,工作站和服务器中。
ARM 是一个 RISC ( Reduced Instruction Set Computing ,精简指令集)处理器。因此它拥有一套精简的指令集( 100 个左右,甚至更少的指令)以及比 CISC 处理器更多的通用寄存器。与 Intel 处理器不同, ARM 指令只处理寄存器中的数据,并使用了 load/store 结构访问存储器,也就是说只有 load/store 指令可以访问存储器。所以如果我们要增加某个内存地址中保存的值,至少需要三种类型的指令( load 指令、加法指令和 store 指令),首先我们需要使用 load 指令将指定地址内存中的值加载到寄存器中,然后使用加法指令增加寄存器中的值,然后用 store 指令将寄存器中的值写回内存。
硬币有两面,精简指令集也有它的优势和劣势。其中一个重要的优势是指令可以被更快的执行( RISC 处理器通过引入流水线机制,减少每个指令的占用的 CPU 的时钟周期来缩短执行时间)。它的劣势也很明显,较少的指令增加了软件(事实上是编译器)的复杂性。另一个重要的事实是, ARM 具有两种运行模式(可以类比 x86 的实模式和保护模式), ARM 模式和 Thumb 模式。 Thumb 指令可以是 2 或 4 个字节的(更多的细节将在第三篇: ARM 指令集中介绍)。
ARM 和 x86/x64 之间更多的区别还包括:
· 在 ARM 中大多数指令可以用于分支跳转的条件判断。
· Intel 的 x86/x64 系列 CPU 是小端序的。
· ARM 架构在 ARMv3 之前是小端序的,在那之后, ARM 处理器提供一个配置项,可以通过配置在大端和小端之间切换。
事实上,不仅 ARM 平台和 Intel 平台之间存在差异, ARM 平台内部的不同版本之间也存在很多差别。我们努力让本系列教程尽可能通用,让你能对 ARM 平台有一个全面的了解。当你掌握了 ARM 基础之后,再去针对某个特定版本学习就轻松了。本教程的示例是在 32 bit ARMv6 (树莓派 1 代)上创建的,因此示例相关的讲解是针对这个版本的。
我们刚才谈到了 ARM 指令集有不同的版本,这可能使你感到困惑,我们用下表简单的表示 ARM 指令集版本和处理器版本之间的映射关系:
ARM 处理器家族
ARM 指令集架构
ARM7
ARM v4
ARM9
ARM v5
ARM11
ARM v6
Cortex-A
ARM v7-A
Cortex-R
ARM v7-R
Cortex-M
ARM v7-M
编写 ARM 汇编 在我们深入学习编写 ARM 平台漏洞利用之前,我们需要理解使用 ARM 汇编编写程序的基本方法。为什么我们要使用 ARM 汇编来编程呢,我们不是有很多高级语言和脚本语言吗?如果你想对 ARM 程序进行逆向工程从而了解程序的执行流程,或者构建 ROP 链来实现你自己的 ARM shellcode ,亦或者调试 ARM 程序,你都需要 ARM 汇编的知识作为基础。
为了从事 ARM 平台的逆向工程和漏洞利用开发,你不需要知道 ARM 汇编语言的所有细节,但你要对相关的主干知识有一个把握。本系列教程将介绍必要的基础知识,如果你想了解更多,你可以访问本章末尾列出的链接。
说了那么多,那么汇编语言到底是什么呢?汇编语言只是机器代码之上的一个简单语法层,它由映射了二进制机器码的助记符组成。二进制机器码是 CPU 所能理解的指令。那么为什么我们不直接写机器码呢?我只能说,那会很蛋疼(原文为 that would be a pain in the ass ,终于知道蛋疼怎么说了,新技能 get )。因此我们会使用汇编语言,这对于人类来说更易于理解。当然,我们的计算机本身不能运行汇编代码,它需要机器码。我们将使用 GNU Binutils 工具集中的汇编器 as 将汇编代码转换为对应的机器码, as 会读取后缀为“ .s ”的汇编源代码文件,然后输出汇编后的二进制目标文件。
最终的过程是这样的,当你编写了后缀为“ .s ”的汇编文件,你可以使用 as 将它汇编,最后使用 ld 链接,如下所示:
$ as program.s –o program.o
$ ld program.o –o program
深入汇编语言 这一节,让我们从最底层开始,自底向上,看看汇编语言是如何工作的。在计算机系统的最底层,是密布的传输着电信号的电路。信号是通过控制电压,在两个电平之间切换形成的,例如 0 伏(低电平代表关信号)和 5 伏(高电平代表开信号)。对于硬件系统,电路中电压的具体数值是没有意义的,所以我们用抽象的数字 0 和 1 来表示电路的开 / 关电平。有意思的是, 0 和 1 不仅代表了电信号,也构成了一个二进制系统。在这个基础上,我们将电信号序列( 01 序列)分组,每一组序列就是一个机器码指令。下面是机组机器码指令的示意(并非实际的机器码):
1110 0001 1010 0000 0010 0000 0000 0001
到目前为止一切都很顺利,但是我们马上就会迎来第一个困难,机器码序列难以记忆。为了解决这个问题,我们引入了助记符,它是我们赋予机器码指令的一个简短名称,一般由 2~4 个字符组成(这不是强制性的,有少数助记符可能有更长的长度)。我们可以使用这些助记符带上符合该助记符语法规则的操作数构成汇编指令来编写程序代码。这种程序代码称为汇编程序代码。用于表示机器代码的助记符及其附带操作数的规则构成的集合(也就是汇编指令的集合)被称为计算机的汇编语言。因此,汇编语言是人类编写程序所使用的最低级别语言。下面是一个例子:
MOV R2, R1
我们现在已经知道了汇编程序代码是由许多汇编指令组成的文本信息,所以我们需要把它转化为对应的机器代码。根据上文,对于 ARM 汇编, GNU Binutils 项目为我们提供了一个名为 as 的工具来完成这个转换。使用汇编器如 as 将 ARM 汇编程序代码转换成 ARM 机器码的过程称为汇编。总结一下就是我们知道计算机可以读取并理解电信号序列,而我们可以用 0 和 1 来表示这种序列并告知计算机(这就是机器码)。我们可以使用机器码,令计算机以一些确定的方式做出响应,所以我们可以对计算机进行编程。但这些机器码序列难以记忆,所以我们给它们命名从而引入了助记符,并用它来表示指令。这些助记符和对应的操作数语法就构成了汇编语言,我们使用一个汇编器将汇编程序代码转换为机器码。这个过程和编译器将高级语言转换为汇编代码是类似的。
拓展阅读 1. Whirlwind Tour of ARM Assembly. https://www.coranac.com/tonc/text/asm.htm
2. ARM assembler in Rasberry Pi. http://thinkingeek.com/arm-assembler-raspberry-pi/
3. Practical Reverse Engineering: x86, x64, ARM, Windows Kernel, Reversing Tools, and Obfuscation by Bruce Dang, Alexandre Gazet, Elias Bachaalany and Sebastien Josse.
4. ARM Reference Manual. http://infocenter.arm.com/help/topic/com.arm.doc.dui0068b/index.html
5. Assember User Guide. http://www.keil.com/support/man/docs/armasm/default.htm
eBPF安全开发与攻防对抗
最后于 2018-3-27 21:27
被ljcnaix编辑
,原因: 修正坛友指出的错误
上传的附件: