ARM基础知识.pptx

寄存器

寄存器的数量取决于ARM版本,

  • ARM32有30个通用寄存器(基于ARMv6-M和基于ARMv7-M的处理器除外),前16个寄存器可在用户级模式下访问,其他寄存器可在特权软件执行中使用
  • 其中,r0-15寄存器可在任何特权模式下访问。这16个寄存器可以分为两组:通用寄存器(R0-R11)和专用寄存器(R12-R15)
  • ARM32下寄存器与X86架构下寄存器大致的对照表image.png(R7在处理系统调用时用来存储系统调用号)
  • ARM64位参数调用规则遵循AAPCS64

    字节序

    大端模式(MSB)以及小端模式(LSB)均有

    指令集

    在ARM架构下有两种指令集,ARM和Thumb,ARM状态下的指令为32位,Thumb状态下的指令为16位,但是现在引入了增强的Thumb指令集(Thumbv2),该指令集允许32位Thumb指令甚至条件执行

由于四字节或二字节对齐,指令地址的最低位永远是0,所以用地址最低位来区分ARM和Thumb指令,如果使用分支指令BX(分支和交换)或BLX(分支,链接和交换)跳转的时候跳转到0xNNNNNNN1,则进入Thumb模式;如果跳转的时候跳转到0xNNNNNNN0,则进入ARM模式,并修改程序状态寄存器中的T位置来区别两种模式

ARM和Thumb之间的区别:

  • 条件执行:ARM状态下的所有指令均支持条件执行。某些ARM处理器版本允许使用“it”指令在Thumb中有条件执行。
  • 32位ARM和Thumb指令:32位Thumb指令带有.w后缀。
  • 桶式移位器(barrel shifter)是ARM模式的另一个独特功能。它可以用于将多个指令缩小为一个。例如,您可以使用左移,而不是使用两条指令,将寄存器乘以2并使用mov将结果存储到另一个寄存器中:mov r1, r0, lsl #1 ; r1 = r0 * 2

ARM指令一般采用以下模式

指令 {S} {条件码} {目标寄存器},Operand1,Operand2

(并非所有指令都使用模板中提供的所有字段)

其中Operand2被称为灵活操作数,可以用一些表达式来充当Operand2image.png
比如以下的寄存器间操作image.png
主要的指令有
image.png
条件码有
image.png
例如:ADDHI R0, R0, #1 ;若R0 > R1, 则R0 = R0 + 1

ARM使用加载存储模型进行内存访问,通过ldr、str这类指令 加载内存中的数据到寄存器、保存寄存器数据到内存
image.png
(带符号的数据类型可以同时包含正值和负值,因此范围较小;无符号数据类型可以容纳较大的正值(包括“零”),但不能容纳负值,因此范围更广)

而批量存取数据使用ldm、stm

  1. stmdb sp!,{r0,r1} ;push {ro,r1}
  2. ldria sp!,{r4,r5} ;pop {r4,r5}
  3. ;后面的"db""ia"在后面介绍

(“!”代表存取结束后移动指针,如果不用”!”,则只是简单地在内存中存取数据)

ARM32与ARM64常用指令对应关系
image.png
(在编写ARM shellcode时,我们需要摆脱NULL字节,并使用16位Thumb指令而不是32位ARM指令来减少使用它们的机会)

堆栈结构

四种栈中image.png
ARM32和ARM64都是满递减堆栈(FD)

  • 堆栈指针指向最后压入栈的有效数据项,每次存入时需要先移动栈指针再存入;取出时先取出,然后再移动栈指针
  • 随着数据的入栈,SP指针从高地址->低地址移动

出入栈一般使用LDMIA(Increase/After)/STMDB(Decrease/Before),可以理解成increase pointer after lord and decrease pointer before store,也就是满递减堆栈的操作LDMFD/STMFD,但事实上在汇编的过程中都转化为了pop/push

函数调用

在ARM ATPCS中规定,函数调用时前四个参数用寄存器R0-R3来传递,剩下还有参数的话则与x86下一样保存在栈中

在main函数调用func1时(func1是一个非叶子函数,arm架构下也有叶子函数与非叶子函数之分,与上面mips下概念一样),程序的栈帧如下所示
image.png
可以看到,在调用函数时,返回地址往上就是第五个参数

子程序返回32位的整数,使用R0返回;返回64位整数时,使用R0返回低位,R1返回高位

但是并不是所有函数调用都需要先执行push {fp, ip, lr, pc},如果在子函数调用过程中不会去改变这些值(也就是在调用叶子函数的时候),就不需要压栈,而是直接sub sp,sp,#N分配栈空间


需要注意的是在ARM汇编中对字符串的引用

在引用字符串的时候,PC 指针作为基址寄存器寻址

  1. 0xF31EF494 LDR R3, =(a_so - 0xF31EF4A0) ;R3=0x00002B78
  2. 0xF31EF498 ADD R3, PC, R3 ; " \x03\x01" ;R3=0xF31F2018
  3. 0xF31EF49C LDR R3, [R3] ; " \x03\x01" ;
  4. 0xF31EF4A0 CMP R3, #0 ;
  5. 0xF31EF4A4 BNE loc_F31EF

在执行到ADD R3, PC, R3时,PC寄存器的值为0xF31EF49C,但是由于流水线效应已经对0xF31EF4A0的指令取指了,所以在实际执行ADD指令运算的时候是将0xF31EF4A0作为PC的值计算的,所以结果就是0xF31EF4A0+0x2B78,也就是0xF31F2018