ARM介绍
ARM 与 Intel
ARM处理器和Intel处理器有很多不同,其中最主要的区别在指令集。Intl为CISC(复杂指令集),具有更多,
功能更丰富的指令集,并且允许很多复杂指令访问内存。因此Intel有更多操作方法、寻址方式、但是寄存器比ARM少。CISC主要用于正常的PC、工作站以及服务器。
ARM为RISC(精简指令集),RISC有很多精简指令100个指令左右),并且RISC比CISC有更多的通用寄存器。
不同于Intel,ARM使用只对寄存器操作的指令,只通过LoadStore这种方式来访问内存。这代表在ARM中,
增加一个特定内存地址的值,需要三种指令(load,increment和store)。先通过load将特定内存地址
的值加载到寄存器,寄存器递增,然后使用store将寄存器中的值加载到内存中。
指令的减少有利有弊。一方面,指令执行的速度加快,系统可能有更快的执行速度(RISC系统通过减少每条指令的时钟
周期来缩短执行时间),另一方面,更少的指令意味着在更加强调中使用有限指令编写软件中注意效率。值得注意的是ARM
有两种指令集,ARM和Thumb指令集,Thumb指令可以是2或4个字节(详情看3.ARM指令集)
ARM 与 x86 更多的不同:
1.ARM中,大多数指令能被用为条件执行
2.Intel x86和 x86-64类的处理器使用小端格式
3.ARM架构在版本3之前采用小端格式,之后ARM处理器变为大端并且增加了可以改变大小端的设置
(数据访问可以是 little-endian 或 big-endian,由程序状态寄存器 (CPSR) 的第 9 位(E 位)控制。)
不仅ARM和Intel之间有很多区别,ARM之间的版本也有很多区别。只要了解基本原理,就很容易了解所选ARM
版本间的微小区别
相关命令
1 | $ as program.s -o program.o |
大小端LSB与MSB
ARM数据类型和寄存器
ARM数据类型
和很多高级语言一样,ARM支持不同数据类型的操作,我们可以使用load(或store)操作的数据格式可以是有符号或
无符号的words,half words和bytes。这些数据类型可以扩展:-h或-sh 支持 half words,-b和-sb 支持bytes
有符号数和无符号数的区别:
1.有符号数包含正负,范围更小
2.无符号数可以储存大的整数(包括0),因此范围更大
Load 和 Store指令集如下
ARM寄存器
ARM寄存器取决与ARM的版本,根据ARM参考手册,大概30个32位通用寄存器,基于 ARMv6-M 和 ARMv7-M的除外
前16个寄存器可在用户级别下访问,其它寄存器可在高权限软件中
通用寄存器
专用寄存器
ARM寄存器与Intel寄存器之间的联系
R0-R12
在常见操作中用来临时储存值,指针(内存地址)。R0可以在算术运算中用为累加器或者用于存储之前调用函数的结果
R7存储系统调用编号,R11是帧指针,类似与RBP,此外ARM中r0 - r3 类似与rdi - rcx
R13
堆栈指针类似于RSP
R14
链接寄存器。用于存储调用下一函数时的上一函数地址
R15
类似RIP
CSRP
CSRP类似于EFLAGS
ARM Instruction set
ARM & Thumb
ARM处理器有两种主要的运行状态(不包括jazelle),ARM 和 Thumb。这两种状态与级别无关,例如在SVC模式下运行的
代码可以是ARM和Thumb。这两种状态的主要区别在于指令集,其中ARM状态的指令总是32位的,Thumb状态下的指令是16
的(也可以是32位)。了解以及如何使用Thumb对于我们的ARM漏洞利用开发十分重要。在编写ARM shellcode时,我们需
要去除NULL字符,使用16位Thumb指令而不是32位ARM指令减少了拥有它们的机会。
ARM版本的调用约定非常令人疑惑,不是所有的ARM版本都支持相同的Thumb指令集。有时候,ARM引入了增强的Thumb指
令集(Thumbv2),它允许32位Thumb指令甚至条件执行,但是这在之前的版本是不能的。为了在Thumb状态下使用条件执
行,引入了”it”指令。通过了解目标设备的ARM版本以及特定的Thumb支持,可以帮助我们调整代码。
ARM信息中心((http://infocenter.arm.com/help/index.jsp).)
正如上文所说,有不同的Thumb版本。不同的命名可以更好的区分他们(处理器始终称为Thumb)。
1.Thumb-1(16位指令):用于ARMv6和更好的架构。
2.Thumb-2(16位指令和32位指令):通过添加更多指令允许它们为16位或32位宽(ARMv6T2、ARMv7)来扩展Thumb-1。
3.ThumbEE:包括一些针对动态生成代码(在执行前不久或执行期间在设备上编译的代码)的更改和添加。
ARM和Thumb的区别:
条件执行:ARM状态下的所有指令都支持条件执行。某些ARM处理器版本允许使用IT指令在Thumb中进行条件执行。条件执
导致更多的代码,因为它减少了要执行的代码数量并减少了昂贵的分支指令数量。
32位ARM和Thumb指令:32位Thumb指令有一个.w后缀。The barrel shifter是另一个独特的ARM模式功能。它可用与将
多条指令压缩位一条指令。如果当前程序状态寄存器中的T位被设置,即处于Thumb状态。
Instruction TO ARM Instructions
ARM指令通常后面有一两个操作数,一般使用如下模板:
1 | MNEMONIC{S}{condition} {Rd}, Operand1, Operand2 |
由于ARM比较灵活,并非所有指令都使用模板中提供的所有字段。模板中字段的用途描述如下:
1 | MNEMONIC - 指令的简称(助记符) |
MNEMONIC、S、Rd和OPerand1字段比较简单,但是{condition}和CPSR寄存器的值密切相关(寄存器中特定位置的值)
。operand2被称为灵活操作数,因为我们可以以各种形式使用它–作为立即数(具有有限的值集)、寄存器或者带有移位
的寄存器。例子:
1 | #123 - 立即值(具有有限的值集)。 |
不同类型指令示例,如下
1 | ADD R0, R1, R2 - 将 R1 (Operand1) 和 R2 (Operand2 以寄存器形式) 的内容相加,并将结果存入 R0 (Rd) |
常见指令如下
内存指令load 和 store
ARM使用load 和 store 访问内存,在x86中很多指令都能直接操作内存,而arm中,数据必须在操作前从内存移动
到寄存器。这意味着在ARM的特定地址出递增32位值将需要三种指令(加载,增加和存储),先将它加载到寄存器,然后
在寄存器中递增它,然后将它返回到内存。
比如现在LDR R2,[R0];的作用是将R0指向的内容复制到R2
STR R2,[R0];的作用即将R0指向的内容替换位R2
关于ARM的几种寻址方式
1.寄存器+立即数
2.寄存器+寄存器
3.寄存器+寄存器+立即数
多个加载与储存
有时需要加载或者储存多个数据,为此可以使用LDM (load multiple) 和 STM (store multiple)指令
用法
1 | .data |
根据上面的描述可以知道,adr指令是将地址加载到寄存器中,ldm指令则是将前面寄存器指针指向的内容依次存放到后面寄存器(
按四个字节递增,因此words段也是设计为四个字节的形式),stm正好与此相反,当ldm和stm后缀加上ia和ib时,表示三个及以上的
寄存器,有ib会将寄存器中的地址增加四个字节,还有da和db指令,作用显而易见,倒置操作寄存器的顺序
条件执行
条件指令可见下图
其中N表示负位,Z表示零位,V表示溢出值
一个简单的例子
1 | .global main |
IT指令如下
语法:IT{x{y{z}}} cond
cond 指定 IT 块中第一条指令的条件
x 指定 IT 块中第二条指令的条件切换
y 指定 IT 块中第三条指令的条件切换
z 指定 IT 块中第四条指令的条件切换
IT 指令的结构是“IF-Then-(Else)”,语法是两个字母 T 和 E 的结构:
IT 指 If-Then(下一条指令是有条件的)
ITT 指 If-Then-Then(接下来的 2 条指令是有条件的)
ITE 指 If-Then-Else(接下来的 2 条指令是有条件的)
ITTE 指 If-Then-Then-Else(接下来的 3 条指令是有条件的)
ITTEE 指 If-Then-Then-Else-Else(接下来的 4 条指令是有条件的)
IT 块内的每条指令都必须指定一个条件后缀,该条件后缀可以是相同的,也可以是逻辑逆的。使用 ITE,则第一条和第二条指令(
If-Then) 必须具有相同的条件后缀,而第三条 (Else) 必须具有前两条的逻辑逆。
例子
1 | ITTE NE ; //接下来3条指令有条件 |
部分条件指令及与其相反的指令
例子
1 | .syntax unified @ this is important! |
跳转指令b
例子
1 | .global main |
需要注意的是blx指令会将arm模式切换到thumb模式,并将PC保存到R14中,方便返回,使用bx时,不用注意arm模式或者thumb模式。