ARM汇编基础教程——ARM汇编简介
最近在学IoT安全,这个教程挺不错的,边学边翻译出来,分享给⼤家。
翻译:ljcnaix
欢迎来到系列⽂章《ARM汇编基础教程》。这个系列是为后续的《ARM漏洞利⽤教程》(连载中)打基础⽽编写的。在我们深⼊⽤ARM汇编编写shellcode和构造ROP链之前,我们需要先掌握⼀些ARM汇编的基础知识。
我们会⼀步⼀步覆盖如下主题:
第⼀篇: ARM汇编简介
第⼆篇: 数据类型和寄存器
第三篇: ARM指令集
第四篇: 内存读写
第五篇: ⾼级内存读写
第六篇: 条件分⽀
第七篇: 栈和函数
为什么介绍ARM?
这篇教程是为有兴趣学习基础的ARM汇编知识的⼈⽽写的。尤其是那些想在ARM平台编写漏洞利⽤的⼈。你可能已经发现ARM处理器在你的⽣活中⽆处不在。当我环顾我的⾝边,我发现我⾝边的⼤多数设备使⽤着ARM处理器⽽不是intel。这些设备包括我的⼿机、路由器以及最近销售⽕爆的IoT设备。可以说,ARM处理器已经成为了当世最⼴泛使⽤的CPU核⼼。随之⽽来的是和PC时代类似的问题,AR
M设备也易于受到缓冲区溢出之类的攻击。由于被⼴泛的使⽤和潜在的脆弱性,针对这些设备的攻击将变得越来越常见。
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 v5
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来表⽰这种序列并告知计算机(这就是机器码)。我们可以使⽤机器码,令计算机以⼀些确定的⽅式做出响应,所以我们可以对计算机进⾏编程。但这些机器码序列难以记忆,所以我们给它们命名从⽽引⼊了助记符,并⽤它来表⽰指令。这些助记符和对应的操作数语法就构成了汇编语⾔,我们使⽤⼀个汇编器将汇编程序代码转换为机器码。这个过程和编译器将⾼级语⾔转换为汇编代码是类似的。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论