骑麦兜看落日

[Asm]ARMPwn

字数统计: 1.5k阅读时长: 6 min
2018/11/05 Share

ARM

ARM处理器用到的指令集分为 ARM 和 THUMB 两种

ARM指令长度固定为32bit,THUMB指令长度固定为16bit

精简指令集(RISC)

ARM属于精简指令集,具有以下特点

  • 简单的指令集:只提供很有限的操作
  • 等长指令集:执行指令速度快且性能稳定,可将一条指令分割成若干个进程或线程,交由多个处理器同时执行。
  • Load/Store架构:CPU并不会对内存中的数据进行操作,所有的计算都要求在寄存器中完成,而寄存器和内存的通信由Load/Store指令完成。
  • 更多的寄存器:基于RISC的处理器具有更多的通用寄存器可以使用,且每个寄存器都可以进行数据存储或寻址。
  • 效率更高:RISC指令集能够非常有效地适合于采用流水线、超流水线和超标量技术,从而实现指令集并行操作,提高处理器的性能。

ARM环境

调试arm架构文件需要安装qemugdb-multiarch

qemu安装

1
2
3
4
5
6
$ sudo apt-get install qemu
$ sudo apt-cache search "libc" | grep "arm"
libc6-dev-arm64-cross - GNU C Library: Development Libraries and Header Files (for cross-compiling)
libc6-dev-armel-cross - GNU C Library: Development Libraries and Header Files (for cross-compiling)
$ sudo apt-get install libc6-dev-arm64-cross
$ sudo apt-get install libc6-dev-armel-cross

gdb-multiarch安装

1
$ sudo apt-get install gdb-multiarch

ARM文件调试

首先用qemu运行并打开监听

1
$ qemu-aarch64 -L /usr/aarch64-linux-gnu/ -g 2333 armfile

然后运行gdb-multiarch

1
2
3
$ gdb-multiarch armfile
pwndbg> set architecture aarch64
pwndbg> target remote :2333

不过不知道为什么我的pwndbg没有x30寄存器,这里推荐使用gef

pwntools运行

1
2
3
context(log_level = "debug",arch = "aarch64",os = "linux")

io = process(['qemu-aarch64','-L','/usr/aarch64-linux-gnu','-g','2333','filename'])

ARM寄存器

通用寄存器

寄存器 位数 描述
X0 64bit 参数传递,返回值传递
X0-X7 64bit 参数传递
X0-X30 64bit 64位通用寄存器,Wn:低32位
V0-V31 64bit 128位浮点计数器,Bn:8bit,Hn:16bit,Sn:32bit,Dn:64bit,Qn:128bit
FP 64bit X29,保存栈帧地址,指向栈底
LR 64bit X30,程序链接寄存器,指向函数返回地址
SP 64bit X31,保存栈指针,指向栈顶
PC 64bit 程序计数器,俗称PC指针,总是指向即将要执行的下一条指令

状态寄存器

寄存器 位数 描述
CPSR 64bit 状态寄存器,N为负数,Z为零,C为无符号运算溢出,V为有符号运算溢出,Q
SPSR 64bit

ARM指令

数据传输指令

1
2
3
4
5
MOV X1,X0				;将寄存器X0的值传送给寄存器X1
LDR X0,[SP,#0X10] ;将SP+0x10地址的数据传送给X0
STR X0,[SP,#0X10] ;将X0的数据传送给SP+0x10地址
STP X29,X30,[SP,0X10] ;将X29和X30的数据传送给SP+0x10和SP+0x18地址
LDP X29,X30,[SP,0X10] ;将SP+0x10和SP+0x18地址的数据传送给X29和X30

操作指令

1
2
3
4
5
ADD X0,X1,X2		;将寄存器X1和X2的值相加后传送给X0
SUB X0,X1,X2 ;将寄存器X2和X2的值相减后传送给X0
AND X0,X1,#0xF ;将X1的值与0xF相位与后的值传送给X0
ORR X0,X1,#0xF ;将X1的值与0xF相位或后的值传送给X0
EOR X0,X1,#0xF ;将X1的值与0xF相位异或后的值传送给X0

跳转指令

1
2
3
4
5
6
CBZ		;比较,结果为零则跳转
CBNZ ;比较,如果非零则跳转
CMP ;相减比较指令,影响程序状态寄存器CPSR
B ;绝对跳转#imm
BL ;绝对跳转#imm,返回地址保存到LR(X30)
RET ;子程序返回指令,返回地址默认保存在LR(X30)

条件码

操作码 条件码助记符 标志 含义
0000 EQ Z=1 相等
0001 NE(Not Equal) Z=0 不相等
0010 CS/HS(Carry Set/High or Same) C=1 无符号数大于或等于
0011 CC/LO(Carry Clear/LOwer) C=0 无符号数小于
0100 MI(MInus) N=1 负数
0101 PL(PLus) N=0 正数或零
0110 VS(oVerflow set) V=1 溢出
0111 VC(oVerflow clear) V=0 没有溢出
1000 HI(HIgh) C=1,Z=0 无符号数大于
1001 LS(Lower or Same) C=0,Z=1 无符号数小于或等于
1010 GE(Greater or Equal) N=V 有符号数大于或等于
1011 LT(Less Than) N!=V 有符号数小于
1100 GT(Greater Than) Z=0,N=V 有符号数大于
1101 LE(Less or Equal) Z=1,N!=V 有符号数小于或等于
1110 AL 任何 无条件执行(默认)
1111 NV 任何 从不执行

ARM堆栈操作

堆栈

aarch64小端序,堆栈由高地址向低地址传输数据,SP指向栈顶,FP指向栈底,且16字节对齐

堆栈平衡

压栈

1
2
3
stp X29,X30,[SP,#-0x10]!
mov X29,SP
sub SP,SP,#0x10

假设现在有一个堆栈布局

1
2
3
4
5
6
7
8
9
10
11
|          |
+----------+
| |
+----------+
| |
+----------+
| |
+----------+
| |
+----------+ <- sp
| |

执行第一步stp X29,X30,[SP,#-0x10]!,将X29,X30的值写入[SP-0x10],将SP设置为SP-0x10

1
2
3
4
5
6
7
8
9
10
11
|          |
+----------+
| |
+----------+
| |
+----------+ <- sp
| x29 | <- fp
+----------+
| x30 | <- ret
+----------+
| |

执行第二步mov X29,SP,将X29设置为SP

1
2
3
4
5
6
7
8
9
10
11
|          |
+----------+
| |
+----------+
| |
+----------+ <- sp <- fp
| x29 | <- fp
+----------+
| x30 | <- ret
+----------+
| |

执行第三步sub SP,SP,#0x10,将SP设置为SP-0x10

1
2
3
4
5
6
7
8
9
10
11
|          |
+----------+ <- sp
| |
+----------+
| |
+----------+ <- fp
| x29 | <- fp
+----------+
| x30 | <- ret
+----------+
| |

出栈

1
2
3
mov SP,X29
ldp X29,X30,[sp],#0x10
ret

执行第一步mov SP,X29,将SP设置为X29

1
2
3
4
5
6
7
8
9
10
11
|          |
+----------+
| |
+----------+
| |
+----------+ <- fp <- sp
| x29 | <- fp
+----------+
| x30 | <- ret
+----------+
| |

执行第二步ldp X29,X30,[sp],#0x10,将X29,X30的值设置为[SP],将SP设置为SP-0x10

1
2
3
4
5
6
7
8
9
10
11
|          |
+----------+
| |
+----------+
| |
+----------+
| x29 | <- fp
+----------+
| x30 | <- ret
+----------+ <- sp
| |

调用约定

X0-X7传递参数,多出的参数保存在栈中

X0保存函数返回值


通用gadget

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
.text:00000000004008AC loc_4008AC                              ; CODE XREF: sub_400868+60↓j
.text:00000000004008AC LDR X3, [X21,X19,LSL#3]
.text:00000000004008B0 MOV X2, X22
.text:00000000004008B4 MOV X1, X23
.text:00000000004008B8 MOV W0, W24
.text:00000000004008BC ADD X19, X19, #1
.text:00000000004008C0 BLR X3
.text:00000000004008C4 CMP X19, X20
.text:00000000004008C8 B.NE loc_4008AC
.text:00000000004008CC
.text:00000000004008CC loc_4008CC ; CODE XREF: sub_400868+3C↑j
.text:00000000004008CC LDP X19, X20, [SP,#var_s10]
.text:00000000004008D0 LDP X21, X22, [SP,#var_s20]
.text:00000000004008D4 LDP X23, X24, [SP,#var_s30]
.text:00000000004008D8 LDP X29, X30, [SP+var_s0],#0x40
.text:00000000004008DC RET

在aarch64中同样存在通用gadget

1
2
3
4
X0=X24=[SP+0x38]
X1=X23=[SP+0x30]
X2=X22=[SP+0x28]
X3=X21=[SP+0x20] <- jmp X3

参考资料

CATALOG
  1. 1. ARM
    1. 1.1. 精简指令集(RISC)
  2. 2. ARM环境
    1. 2.1. qemu安装
    2. 2.2. gdb-multiarch安装
    3. 2.3. ARM文件调试
    4. 2.4. pwntools运行
  3. 3. ARM寄存器
    1. 3.1. 通用寄存器
    2. 3.2. 状态寄存器
  4. 4. ARM指令
    1. 4.1. 数据传输指令
    2. 4.2. 操作指令
    3. 4.3. 跳转指令
    4. 4.4. 条件码
  5. 5. ARM堆栈操作
    1. 5.1. 堆栈
    2. 5.2. 堆栈平衡
      1. 5.2.1. 压栈
      2. 5.2.2. 出栈
    3. 5.3. 调用约定
  6. 6. 通用gadget
  7. 7. 参考资料