# 附录3-3： 指令集分析-ARM

### 一、ARM简介

ARM, Advanced RISC Machine, 是一个32位RISC处理器架构。ARM处理器广泛应用于嵌入式系统和物联网设备中，例如路由器，交换机，智能手机等。

ARM处理器支持7中运行模式，每种模式有自己的堆栈空间以及一组不同的寄存器子集。

* 用户模式（user）：正常程序执行模式；
* 快速中断模式（FIQ）：高优先级的中断产生会进入该种模式，用于高速通道传输；
* 外部中断模式（IRQ）：低优先级中断产生会进入该模式，用于普通的中断处理；
* 特权模式（Supervisor）：复位和软中断指令会进入该模式；
* 数据访问中止模式（Abort）：当存储异常时会进入该模式；
* 未定义指令中止模式（Undefined）：执行未定义指令会进入该模式；
* 系统模式（System）：用于运行特权级操作系统任务；

不过在Cortex系列中稍有不用，Cortex-A 和 Cortex-R 处理器有以上7中模式，而Cortex-M则只有两种模式，Thread 模式和 Handler 模式，Thread 模式没有特权，用于应用程序代码， Handler 模式有特权，用于异常处理程序（以下情况不适用于Cortex-M处理器）。

### 二、寄存器

ARM处理器有40个寄存器，32个通用寄存器，7个状态寄存器，一个PC寄存器，每一个模式对应有一种寄存器。

几个常用的寄存器： 1. R0-R3用户函数调用参数传递 2. R13，堆栈指针寄存器，也称为SP 3. R14，又称为LR，链接寄存器，用于保存函数调用时的返回地址 4. R15，又称为PC，程序计数器。在ARM状态下，位\[1:0]为0，位\[31:2]用于保存PC；在Thumb状态下,位\[0]为0，位\[31:1]用于保存PC。对于ARM指令集而言，PC总是指向当前指令的下两条指令的地址，即PC的值为当前指令的地址值加8个字节。

ARM寻址特点，如果汇编代码中使用 pc 间接寻址的话，实际得到的是 pc+偏移量+8 的地址中的内容，如：

```bash
pc = 0x1000
ldr  r0, [ pc, #12 ]
```

那么 r0 寄存器的结果是：

```bash
r0 = [0x1000+12+8] = [0x101a]
```

### 三、指令分析

ARM架构下的栈溢出利用跟x86，mips等架构下的利用过程差不多，只需要找到合适的gadgets即可，而我们照样可以用ROPGadget来寻找我们的gadgets。接下来记录下怎么一步一步来寻找我们的gadgets。

#### 3.1 ARM指令概述

arm指令和x86有点相似之处，相比mips指令读起来更加容易理解。 ROP经常用到的指令如下:

![](https://1174461437-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FlLEV5Rrf0TFHD0Ld3R7q%2Fuploads%2FALjTZiIIOzmM2NxKoY6i%2Fimage.png?alt=media\&token=4aa850ab-2508-448a-897a-6b5223a1da53)

arm架构下的函数返回时用LDMFD指令加BX指令，首先用LDMFD指令pop保存的寄存器的值以及LR的值，然后跳转至LR。在ROP的时候很多情况下可以利用LDMFD指令来减少ROP时用到的gadgets。

#### 3.2 一步一步寻找gadgets

我们ROP的最终目的是执行system(cmd)，cmd一般来说放到栈上，虽然针对未开地址随机化的情况，可以考虑放堆上，但还是会不稳定。所以绝大多数情况需要放到栈上面，这就需要找一个gadget来设置r0为一个栈地址，然后再寻找一个gadget跳到system即可。

ARM架构下的ROP有一个好处就是在函数返回时大多数情况会pop还原一些保存的寄存器的值，例如R4-R5，R4-R7等，这种情况可以利用函数本身的gadget来减少ROP的复杂程度。而如果函数返回时没有pop出lr以外的寄存器的话，这种情况就需要另外调整寻找gadgets的思路了。

* 第一种情况

首先利用ROPGadget将libc中可利用的gadget找出来：

`ROPgadget --binary libc.so > gadgets`

```
ROPgadget --binary libc.so > gadgets
```

加入发生溢出的函数在返回时有以下指令操作：

```
LDMFD  SP!, {R4-R11,LR}
BX  LR
```

这里我们可以控制R4-R11和LR寄存器。 首先寻找类似

```
bx r5
```

的gadget，同时gadget也需要满足将栈地址赋值给一个通用寄存器。

```
grep "add r.*sp.*bx r5" gadgets
```

![](https://1174461437-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FlLEV5Rrf0TFHD0Ld3R7q%2Fuploads%2F6MY4Jl7010NQst9PO2zt%2Fimage.png?alt=media\&token=927b82c2-19c9-462c-a55c-5be1621eb406)

我们就随便选取第六个gadget即可。

```bash
gadget1:
    add r2, sp, #0x34
    mov lr, pc
    bx r5 
```

然后我们需要寻找一个类似`mov r0, r2`的gadget，但同时该gadget需要能控制程序流。 我们可以用如下命令寻找：

```bash
grep "mov r0, r2.*bx lr" gadgets
```

![](https://1174461437-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FlLEV5Rrf0TFHD0Ld3R7q%2Fuploads%2F0cVdtVQlqy04Sgq2639m%2Fimage.png?alt=media\&token=6b6883fd-18e3-4ccc-91b0-4b41359c0d9a)

我们可以选取如下gadget：

```bash
gadget2:
    mov r0, r2
    pop {lr}
    bx lr
```

然后这两个gadgets结合程序本身的LDMFD指令即可完成ROP。 这个时候payload为：

```bash
payload = fill + gadget2 + fill + gadget1 + fill +  system + fill + cmd
```

* 第二种情况

当函数返回时仅仅只有pop lr等操作时，这种情况下的ROP更加通用，跟MIPS架构下的ROP思路差不多。 这时我们只需要在上面的情况下找一个gadget来设置r5即可。满足条件的有很多，基很多函数返回时的最后两台指令都可以。

```bash
gadget3:
    LDMFD  SP!, {R4-R6,LR}
    BX   LR 
```

这个时候只需要在上述payload前面加上该gadget即可。

```bash
payload = fill + gadget3 + fill + gadget2 + fill + gadget1 + fill +  system + fill + cmd
```

### 四、知识点图

![](https://1174461437-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FlLEV5Rrf0TFHD0Ld3R7q%2Fuploads%2FE4GgMfDx2AagTedwzHnS%2Fimage.png?alt=media\&token=d0c0526d-6764-43b1-8c32-72ed96d1c137)



来源： [_**海特实验室**_](https://github.com/DasSecurity-HatLab/HatLab_IOT_Wiki)
