ARM相关的笔记

Published: 三 10 十一月 2021

In Misc.

ARM体系现在已经有9个版本了,不同体系指令集架构(ISA)和ABI有区别,但一般是向前兼容的,现在网上和书上的资料大多是指第6版ARMv6,本文暂时也只记录ARMv6,之后再慢慢升级。

在此要说一下当前主流应该是ARMv8,在位数上它扩充到了64位,新的指令集叫AArch64,而兼容ARMv7的指令集叫AArch32,再旧的是A32,也是此处介绍ARM32的内容。

工作状态

Arm状态和Thumb状态,前者使用4字节定长指令,后者使用2字节定长指令,重置或异常时恢复为ARM状态,可通过BX/BLX指令转换状态,当目标地址的低位为0时表示转换到thumb状态,否则为arm状态,返回时根据LR恢复状态,在插桩时特别要小心这一点,可通过指令长度确定目标片段的代码类型。

工作模式

这里的工作模式是指当前运行的级别,在ARMv6下有如下七种:

表. 工作模式
ModeModeCode说明
User10000用户模式,用户进程执行时处于该模式
System11111普通特权模式,如通常的系统内核执行时会进入该模式
Supervisor10011重置时的模式,软中断与系统调用(swi指令)时也进入该模式
Undefined11011获取到不能识别的指令时进入该模式,此时用户可通过软件仿真来模拟自定义的指令(类似中断服务例程)
IRQ10010普通中断模式,一般硬件中断时会进入
FIQ10001快速中断模式,处理紧急中断/高速数据处理时
Abort10111访问异常触发保护机制时(如segment fault)会进入该模式

(在后来的版本中又增加了3种之后再补充。)

寄存器

一般只提Arm有37个可见的寄存器,但在某一时刻只有部分寄存器能使用,如下所示不同的模式可见的寄存器差不多,但是对应的物理寄存器不一定一致,例如User和Supervisor模式下的R0-R12指向同样的物理寄存器,而R13和R14在两种模式下实际对应的物理寄存器并不一致,所以一般我们在代码中只能看到10多个寄存器,而实际上有30多个:

表. ARMv6 ARM寄存器
UserSystem SupervisorAbortUndefinedIRQ FIQ说明
R0 R0 R0 R0 R0 R0 R0这一组寄存器在所有模式下指向相同的物理寄存器,其中R0-R3通常用于传参,R0-R1通常用于传输返回值
R1 R1 R1 R1 R1 R1 R1
R2 R2 R2 R2 R2 R2 R2
R3 R3 R3 R3 R3 R3 R3
R4 R4 R4 R4 R4 R4 R4
R5 R5 R5 R5 R5 R5 R5
R6 R6 R6 R6 R6 R6 R6
R7 R7 R7 R7 R7 R7 R7
R8 R8 R8 R8 R8 R8R8_fiq这三个寄存器和R11,R12在thumb下不可见
R9 R9 R9 R9 R9 R9 R9_fiq
R10 R10 R10 R10 R10 R10R10_fiq
R11 R11 R11 R11R11R11R11_fiq又叫FP(Frame Pointer)寄存器,通常用于指向栈桢
R12 R12 R12 R12 R12 R12R12_fiq又名IP(Intra Procedure call scratch)寄存器,它不会被保存也默认数据不可用,因此可在过程中直接写读
R13R13R13_svcR13_abtR13_undR13_irqR13_fiq又名SP(Stack Pointer)寄存器,保存了栈顶指针
R14R14R14_svcR14_abtR14_undR14_irqR14_fiq又名LR(Linked Register),保存了函数返回地址
R15R15R15R15R15R15R15又名PC(Program Counter)寄存器,在计组中它可以保存当前指令位置也可以保存下一指令位置,在3级流水线的ARM系列中(据传未验证)保存了下一条要读取的指令的地址,它的值是当前正在执行的指令的地址+8
CPSRCPSRCPSRCPSRCPSRCPSRCPSRCurrent Program State Register保存当前程序的状态
SPSR_svcSPSR_abtSPSR_undSPSR_irqSPSR_fiqStored Program State Register用于备份CPSR,如出现异常时就会保存并在异常处理完后恢复

在这里面CPSR也是按位表示一些状态,它的结构如下(新版会使用更多位):

表. 程序状态寄存器
Bit31302928---76543210
NZCV---IFTM4M3M2M1M0
DescriptionNegativeZeroCarryoVerflowIRQFIQThumbWork Mode

一般在指令集手册里它们叫ArmRegister,除此之外还有很多不可见的寄存器(我猜的)和协处理器,协处理器用于处理特殊任务,如多媒体任务,算数任务,加解密任务等,它只会执行和它功能相关的指令而忽略其他指令。

指令

本段记录的是ARM指令,Thumb指令以后有需要再补充。ARM指令有如下特点:

  1. 几乎所有指令都有4位共16种条件标志位,当使用时表示满足条件才执行指令,在汇编上以两个字符的助记符表示
  2. 很多指令助记符通过S表示该操作会影响程序状态字

注:下面的翻译并不一定正确,我只是为了好记!

表. 条件码
编码条件助记符标志位含义
0000EQZ=1相等
0001NEZ=0不相等
0010CSC=1无符号大于或等于
0011CCC=0无符号小于
0100MIN=1负值
0101PLN=0正值或 0
0110VSV=1溢出
0111VCV=0无溢出
1000HIC=1 且 Z=0无符号大于
1001LSC=0 且 Z=1无符号小于或等于
1010GEN 和 V 相同有符号大于或等于
1011LTN 和 V 不相同有符号小于
1100GTZ=0 且 N 等于 V有符号大于
1101LEZ=1 且 N 不等于 V有符号小于或等于
1110AL忽略无条件执行(默认可省略)
1111NV忽略从不执行(系统保留)
表. 常用指令
类型指令助记说明例子
跳转指令B/BL/BX/BLXBranch{Link}{eXchange}跳转到其他位置,带L将把返回地址放到LR,带X会根据目标切换状态BLEQ main;Z标志置位时调用main函数
数据处理指令MOV/MVN 数据传输指令与取反传输
CMP/CVN/TST/TEQ {算数比较|反值比较|逻辑位测|逻辑位相等}
ADD/ADC/SUB/SBC/RSB/RSC 加减运算,C表示是否带进位,R表示逆向
MUL/MULA/{S|U}MULL/{S|U}MUAL 乘法与乘加指令,所谓乘加就是先乘再加,开头加S或U表示是否有符号,结束加L表示64位
{L|A}S{L|R}/ROR/RRX {逻辑|算数}{左|右}移,循环右移,带扩展的循环右移
AND/ORR/EOR/BIC 逻辑{与|或|异或|位清除}
PSR处理指令MSR/MRSMove State from Register|...可通过该指令读写状态寄存器MSR CPSR, R0; 将R0的值赋给CPSR
加载/存储指令{LD|ST}R{|B|H}{LoaD|STore} Register{Doword|Byte|sHort}从加载一个{双字|字节|字}到寄存器或相反LDR R0, [SP]; R0=[SP]
{LD|ST}M{LoaD|STore}Multi从寄存器组存储数据到内存或反向操作
协处理器指令CDPCoprocessor Data Operation让指定编码的协处理器执行指定的指令CDP P3, 2, C12, C10, C3, 4; 让协处理器3执行2号指令,后面是它的源目的寄存器等参数
LDC/STC{LoaD|STore} Coprocessor让协处理器指定寄存器从指定内存加载/存储数据LDC P15, C4, [R0]; 从R0所指位置加载一个dword的数据到15号协处理器的C4寄存器
MCR/MRCMove Coprocessor from Register|...协处理器与ARM寄存器间传送数据MCR P3, 3, R0, C4, C5, 6; 将R0的内容传送到P3的C4 C5中
异常产生指令SWISoftWare Interrupt产生软中断,用于系统调用SWI 2; 调用编号为2的系统例程
BKPTBreaKPoinT断点BKPT 0x2233;

寻址方式

1.立即数寻址:

ADD R0, R0, #0x10       ; R0=R0+0x10,立即数以#开头

2.寄存器寻址:

ADD R0, R1, R2          ; R0=R1+R2

3.寄存器间接寻址:

ADD R0, R1, [R2]        ; R0=R1+[R2]

4.基址变址寻址:

ADD R0, [R1, #4]        ;   R0=[R1+4]
ADD R0, [R1, #4]!       ; R0=[R1+4],R1+=4
ADD R0, [R1], #4        ; R0=[R1], R1+=4
ADD R0, [R1, R2]        ;   R0=[R1+R2]

5.多寄存器寻址:

LDMIA R0, {R1, R2, R3, R4}  ; R1=[R0],R2=[R0+4]...

这里的后缀有如下四种,用于表示每执行一个寄存器复制操作后原地址如何变化:

mode Full Name 说明
IA Increase After 先传送再加
DA Decrease After 先传送再减
IB Increase Before 先加再传送
DB Decrease Before 先减再传送

6.相对寻址:

BL main                         ; call main

7.堆栈寻址:

LDMFD SP!, {R1, R2, R3}; R1=[SP], SP-=4, R2=[SP], SP-=4, R3=[SP], SP-=4

这里的后缀有如下四种,用于表示当前栈指针所指位置是否存放了元素(如若指向的是最后压入的元素则为满栈)以及栈的增长方向(如递减表示由高地址到低地址增长):

mode Full Name 说明
FA Full Ascending 满递增栈
FD Full Descending 满递减栈
EA Empty Ascending 空递增栈
ED Empty Descending 空递减栈

伪指令

伪指令是针对汇编器的,又汇编器转换成某特定结构,因此它大多和架构无关(但也有.thumb这种是特定于架构的),比如在GCC上那些伪指令在ARM上也能用,因此这里只记录一些在在IDA里常看到的:

表. 常用伪指令
类型伪指令助记说明例子
符号定义伪指令GBL<A|L|S>GloBaL<Arithmetic|Logical|String> var定义全局<数字|逻辑|字符串>变量,默认值分别为<0|False|"">GBLA x;定义一个全局数字变量x它的值为0
LCL<A|L|S}LoCaL<Arithmetic|Logical|String> var定义本地<数字|逻辑|字符串}变量,默认值分别为<0|False|"">LCLA x;定义一个局部数字变量x它的值为0
SET<A|L|S}SET<Arithmetic|Logical|String> var为<数字|逻辑|字符串>变量赋值SETA x 233;x=233
RLISTRegister LIST将某个寄存器列表定义为一个变量r0_5 RLIST {R0-R5};另R0-R5这个寄存器列表为r0_5
数据定义伪指令DC<B|W|D|FD|FS|Q>{U}

DataContent<Byte|Word|Dowrd

|FloatSingle|FloatDouble|Qword>{Unpad}

定义各种长度的数据,U表示不对齐Str DCB "B3taMa0";
SPACE 定义连续的一片空间,初始化为0somespace Sapce 100;定义somespace为100字节长度的空间并初始为0
MAP 申明一个数据结构MAP 0x10, R0; 在R0+0x10处申明一个结构,之后用Field指令定义结构域
FIELD 定义结构里的域vtbl FIELD 0x04; 定义一个域名为vtbl长度为4字节

调用约定

在ARM中还有个概念易于和ISA混淆,且和ISA密切相关,那就是ABI,ARM支持多种ABI,移动和嵌入式上常见的是EABI,不过这不重要,我们需要关注的是ABI里的调用约定,这里面常用的调用约定是AAPCS(Procedure Call Standard for the Arm Architecture)和AAPCS64,它们分别对应32位和64位,这里介绍前者,在官方文档的调用约定里它的字表示32字节,太奇怪了,它应该叫双字!!!它的寄存器使用如下:

RegisterSynonymSpecial说明
r15 PCThe Program Counter.
r14 LRThe Link Register.
r13 SPThe Stack Pointer.
r12 IPThe Intra-Procedure-call scratch register.
r11v8FPFrame Pointer or Variable-register 8.
r10v7 同v1-v5
r9

v6

SB

TR

特定于平台,可做不同用途,如在PIC中当SB(Static Base),在TLS中当TR(Thread Register),也可以把它当作v6用。

r4-r8v1-v5 作为本地变量,需要函数执行前保存结束前恢复。
r0-r3a1-a4 依次传递参数,若参数大于32位可占用多个连续的寄存器,若4个寄存器不够用则通过栈传输,方向为从右到左入栈;也可保存返回值,返回值为32位保存在r0里,与参数类似若返回值大于32位依次存放

而它的栈使用满递减栈,使用时必须4字节对齐,关于字节序arm似乎可以采取大端和小端但默认是小端,根据官方文档可通过一个配置引脚修改,这是ABI的内容,现在一般遇到的其实都是小端序。

参考

  1. Whirlwind Tour of ARM Assembly
  2. arm-directives-reference
  3. ARM and Thumb Instructions
  4. 《Android 系统安全和反编译实战》-- 刘云[著];朱桂英[著]
  5. 《ARM处理器开发详解:基于ARM Cortex-A9处理器的开发设计》-- 秦山虎[著];刘洪涛[著]