第七章 ARM 反汇编基础(五)(ARM 汇编指令集)

文章目录

  • ARM 汇编指令集
    • ARM 指令集分类
    • ARM 指令编码
    • ARM 指令格式解析
    • 常见 ARM 指令
      • 数据处理与杂项指令
      • 加载存储指令
      • 其他指令

ARM 汇编指令集

  • Android 平台的 ARM 汇编指令集根据架构支持类型的不同可分为四大类:
    第七章 ARM 反汇编基础(五)(ARM 汇编指令集)_第1张图片
  • 注意:
    • ARM 指令集一直在变化,armeabi 支持 ARMv7 以下版本的指令集,armeabi-v7a 支持 ARMv7 系列指令集,AArch32 支持的 ARM 指令集兼容之前的版本
    • 编译器支持方面:Clang 只能通过 -target 参数指定 EABI 版本,不能指定具体的 arch 参数;而 gcc 支持用 -arch 参数指定具体的指令集,包括 ARMv2、ARMv2A、ARMv3、ARMv3M、ARMv4、ARMv4T、ARMv5、ARMv5E、ARMv5T、ARMv5TE、ARMv6、ARMv6-M、ARMv6J、ARMv6K、ARMv6T2、ARMv6Z、ARMv6ZK、ARMv7、ARMv7-A、ARMv7-M、ARMv7-R、ARMv7e-M、ARMv7VE、ARMv8-A、ARMv8-A+crc、iWMMXT、iWMMXT2、Native。在 Android 系统不同的 EABI 设置下,ARMv5TE、ARMv7-A、ARMv8-A 是编译器默认的选择

ARM 指令集分类

  • armeabi-v8a 的 AArch32 与 armeabi-v7a 支持所有的 ARM 指令集,按指令的位域和功能可分为这几种:
    • Data-processing and miscellaneous instructions:数据处理与杂项指令
    • Load/store word and unsigned byte:加载存储指令
    • Media instructions:媒体指令
    • Branch, branch with link, and block data transfer:分支、带链接的分支与块数据传输指令
    • Supervisor Call and coprocessor instructions:软中断与协处理器指令
    • unconditionally executed instructions:无条件执行指令

ARM 指令编码

  • ARM 指令集采用 32 位的等长编码格式,按类型的位域分布:
    第七章 ARM 反汇编基础(五)(ARM 汇编指令集)_第2张图片
  • 采用位域方式对一条 ARM 指令进行划分,最基本的域有 cond、op1、op。这三个域的取值会直接影响指令的具体类别与格式。cond 域占用 28 位到 31 位,共四位,记为 bits[31:28],每个位记录了一个条件标志(Conditon Flag)。这四个标志位具体如下:
    • N:bit[31],负数标志
    • Z:bit[30],0 标志
    • C:bit[29],进位标志
    • V:bit[28],溢出标志
  • 每条 ARM 指令的 bits[31:28] 中都有条件标志,若相应的标志位为 1,表示这条指令会影响标志位的结果。指令执行后,可通过查看 APSR 寄存器的 cond 域来查看标志。在应用级别,APSR 对应 CPSR,代表了程序状态寄存器;在系统级别,APSR 对应 SPSR。在 Android 原生 ARM 程序中,我们通常只能访问 CPSR
  • 不同的标志位组合在一起,可表示不同的含义:
cond 助记符 含义(整型) 含义(浮点型) 条件标志
0000 EQ 相等 相等 Z == 1
0001 NE 不等 不等或无序 Z == 0
0010 CS 进位 大于等于或无序 C == 1
0011 CC 进位清除 小于 C == 0
0100 MI 减、负数 小于 N == 1
0101 PL 加、正数或 0 大于等于或无序 N == 0
0110 VS 溢出 无序 V == 1
0111 VC 未溢出 有序 V == 0
1000 HI 无符号大于 大于或无序 C == 1 and Z == 0
1001 LS 无符号小于或等于 小于或等于 C == 0 or Z == 1
1010 GE 有符号大于或等于 大于或等于 N == V
1011 LT 有符号小于 小于或无序 N != V
1100 GT 有符号大于 大于 Z == 0 and N ==V
1101 LE 有符号大于或等于 小于等于或无序 Z == 1 or N != V
1110 无条件 无条件 任何
  • 在浮点数的无符号表示中,至少有一个非数字操作数
  • ARM 指令中的溢出和进位有如下特点:
    • 对无符号数来说,不存在溢出,它的进位即相当于有符号数中的溢出
    • 对有符号数来说,不存在进位,其溢出有如下特点:
      • 两个正数相加,或一个正数减一个负数,结果为负数,表示溢出了
      • 两个负数相加,结果为正数,表示溢出了
      • 一个正数和一个负数相加,不可能溢出

  • op1 域位于 bits[27:25],占三位;op 域位于 bit[4],占一位。它们的取值组合在一起,决定指令所属的分类(Instruction Class)。在 cond 域不全为 1 时,它们的二进制域的值表示如下(下面表述中的值均为二进制值):
    • op1 为 000 与 001:这条指令是数据处理或杂项指令
    • op1 为 010,或 op1 为 011 且 op 为 0:这条指令是加载存储指令
    • op1 为 011 且 op 为 1:这条指令是媒体指令
    • op1 为 100 与 101:这条指令是分支、带链接的分支与块数据传输指令
    • op1 为 110 与 111:这条指令是软中断与协处理器指令
  • 在 cond 域全为 1 时,这条指令是无条件执行指令
  • op1 不同,对应的系统指令也不同。以 op1 为 100 为例,属于分支、带链接的分支与块数据传输指令系列,对应的指令格式:
    第七章 ARM 反汇编基础(五)(ARM 汇编指令集)_第3张图片
  • 可看出,指令的位域分配有了变化,bits[25:20] 的 op 域、bits[19:16] 的 Rn 域及 bit[15] 的 R 域的不同组合,可表示不同的指令。若 op 域为 001001,表示的是 LDM(LDMIA、LDMFD)指令。跟进 LDM 指令的格式:
    第七章 ARM 反汇编基础(五)(ARM 汇编指令集)_第4张图片
  • 可看到,指令本质上是一系列二进制位的数据。但在编写汇编程序时,不可能直接用二进制值,ARM 汇编引入了指令助记符的概念,便于阅读和使用 ARM 中的汇编指令
  • 为详细描述指令不同位域取不同的值可能会对指令造成的影响,ARM 指令参考手册用伪代码说明的形式给出了指令的格式,其中 LDM 指令的伪代码说明如下:
if W == '1' && Rn == '1101' && BitCount(register_list) > 1
    then SEE POP (A32);
n = UInt(Rn);
registers = register_list;
wback = (W == '1');
if n == 15 || BitCount(registers) < 1
    then UNPREDICTABLE;
if wback && registers == '1'
    then UNPREDICTABLE;
  • 上述伪代码的意思:若 W 为 1、Rn 为 1101 且 register_list 中只有 1 位,就查看 POP 指令的格式(说明在这种情况下指令的作用与 POP 指令相同);设置 registers 的值,判断 Rn 的值是否超过 15,以及 registers 中寄存器的相应位是否被设置,从而判断指令的行为是不是 UNPREDICTABLE(不可预测)的
  • LDM 指令在助记符描述中引用了 registers。完整的 LDM 指令格式的描述:
    LDM {!},

ARM 指令格式解析

  • 以十六进制值 0xE1A01002 为例,解析其对应的 ARM 汇编指令
  • 打开计算器,查看其对应的二进制位信息:
    第七章 ARM 反汇编基础(五)(ARM 汇编指令集)_第5张图片
  • 首先是 cond 域 bits[31:28],其取值不是 0b1111,表示这不是一条无条件执行指令
  • 接着是 op1 域 bits[27:25],其取值为 0b000,表示这是一条数据处理与杂项指令。数据处理与杂项指令的格式:
    第七章 ARM 反汇编基础(五)(ARM 汇编指令集)_第6张图片
  • bit[25] 的 op 域为 0,bits[24:20] 的 op1 域为 0b11010,bits[7:4] 的 op2 域为 0。查阅 ARM 指令参考手册可知,这是一条数据处理(寄存器形式)指令:
    第七章 ARM 反汇编基础(五)(ARM 汇编指令集)_第7张图片
  • bits[24:20] 的 op 域为上图(数据处理与杂项指令格式图)中的 op1 域,值为 0b11010,bit[6:5] 的 op2 域的值为 0,bit[11:7] 的 imm5 域的值为 0。查阅 ARM 指令参考手册可知,其所对应的是 MOV(寄存器形式)指令。MOV(寄存器形式)指令格式:
    MOV{S} ,
  • 完整的指令格式的域分布:
    第七章 ARM 反汇编基础(五)(ARM 汇编指令集)_第8张图片
  • bit[20] 的 S 域是符号域:若值为 1,表示这是一条 MOVS 指令;若值为 0,表示这是一条 MOV 指令。bit[15:12] 的 Rd 域表示目标寄存器,其值为 0b0001(表示 R1 寄存器)。bit[3:0] 的 Rm 域为源寄存器,其值为 0b0010(表示 R2 寄存器)
  • 综上分析,可知这条指令为 MOV R1, R2。可用反汇编器验证,如 Radare2 的 rasm2 命令
  • Radare2 是一款强大的跨平台二进制分析工具,可在 Windows、macOS、Ubuntu 等平台使用
  • 下载地址:Radare2
  • 或在 Ubuntu 执行如下命令快速安装:sudo apt-get install radare2
  • 执行如下命令,查看 MOV R1, R2 指令的十六进制值:
    第七章 ARM 反汇编基础(五)(ARM 汇编指令集)_第9张图片
  • 输出的 0x0210a0e1 与分析的 0xE1A01002 相反,因为分析的指令是小端序的,而用 rasm2 命令输出的结果是大端序的

常见 ARM 指令

数据处理与杂项指令

  • 包括数据传送指令、算术运算指令、逻辑运算指令、比较指令等
  • 主要用于对寄存器间的数据进行操作
  • 所有的数据处理指令均可用 S 后缀设置是否影响状态标志。比较指令不需要 S 后缀,它们会直接影响状态标志
  • 常用的数据处理与杂项指令:
    • MOV

      • MOV 是 ARM 指令集中使用最频繁的指令
      • 功能:将 8 位的立即数或寄存器的内容传送到目标寄存器
      • 格式:MOV{S} ,
      • 示例:
      MOV R0, #8        @R0=8
      MOV R1, R0        @R1=R0
      MOV R2, R1, LSL #2        @R2=R1*4,影响状态标志
      
    • MVN

      • 数据非传送指令
      • 功能:将 8 位的立即数或寄存器的内容按位取反后传送到目标寄存器
      • 格式:MVN{S} , #
      • 示例:
      MVN R0, #0xFF        @R0=0xFFFFFF00
      MVN R1, R2        @将 R2 寄存器的数据取反后存入 R1
      
    • ADD

      • 加法指令
      • 功能:将 Rn 寄存器的值与 operand2 的值相加,将结果保存到 Rd 寄存器
      • 格式:ADD{S} , , {, }
      • 示例:
      ADD R0, R1, #2        @R0=R1+2
      ADDS R0, R1, R2        @R0=R1+R2,影响标志位
      ADD R0, R1, LSL #3        @R0=R1*8
      
    • ADC

      • 带进位的加法指令
      • 功能:将 Rn 寄存器的值与 Rm 寄存器的值相加,再加上 CPSR 寄存器的 C 条件标志位的值,最后将结果保存到 Rd 寄存器
      • 格式:ADC{S} , , {, }
      • 示例:
      ADD R0, R0, R2
      ADC R1, R1, R3        @这两条指令完成了 64 位加法运算,(R1, R0) = (R1, R0) + (R3, R2)
      
    • SUB

      • 减法指令
      • 功能:用 Rn 寄存器的值减 Rm 寄存器的值,将结果保存到 Rd 寄存器
      • 格式:SUB{S} , , {,
      • 示例:
      SUB R0, R1, #4        @R0=R1-4
      SUBS R0, R1, R2        @R0=R1-R2,影响状态标志
      
    • MUL

      • 32 位乘法指令
      • 功能:将 Rm 寄存器的值与 Rn 寄存器的值相乘,将结果的低 32 位保存到 Rd 寄存器
      • 格式:MUL{S} , ,
      • 示例:
      MUL R0, R1, R2        @R0=R1*R2
      MULS R0, R2, R3        @R0=R2*R3,影响 CPSR 的 N 位于 Z 位
      
    • SDIV

      • 有符号数除法指令
      • 格式:SDIV , ,
      • 示例:
      SDIV R0, R1, R2
      
    • UDIV

      • 无符号数除法指令
      • 格式:UDIV , ,
      • 示例:
      UDIV R0, R1, R2        @R0=R1/R2
      
    • ASR

      • 算术右移指令
      • 功能:将 Rm 寄存器的值算术右移 operand2 位,并用符号位填充空位,将结果保存到 Rd 寄存器
      • 格式:ASR{S} , , #
      • 示例:
      ASR R0, R1, #2        @将 R1 寄存器的值作为有符号数右移 2 位后赋给 R0 寄存器
      
    • AND

      • 逻辑与指令
      • 格式:AND{S} , , {, }
      • 示例:
      AND R0, R0, #1        @用来测试 R0 的最低位
      
    • ORR

      • 逻辑或指令
      • 格式:ORR{S} , , #
      • 示例:
      ORR R0, R0, #0x0F        @指令执行后,保留 R0 的高四位,其余位置为 1
      
    • EOR

      • 异或指令
      • 格式:EOR ,
      • 示例:
      EOR R0, R0, R0        @执行后,R0=0
      

加载存储指令

  • 加载存储指令完成的工作包括:从寄存器中加载数据与将数据存储到存储器中
  • 常见的加载存储指令:
    • LDR

      • 功能:将数据从存储器加载到寄存器
      • 格式:
        LDR , [, ]
        LDR , [], #+/-
        LDR , [{, #+/-}]|
        LDR , [, #+/-]!
        LDR , label
        LDR , [PC, #-0]
      • 示例:
        • 直接偏移量,示例:
          LDR R8, [R9, #04]
        • 寄存器偏移量,示例:
          LDR R8, [R9, R10, #04]
        • 相对 PC 寄存器,示例:
          LDR R8, label1
    • STR

      • 功能:将数据存储到指定地址的存储单元
      • 格式:
        STR , [{, #+/-}]
        STR , [], #+/-
        STR , [, #+/-]!
        STR , [, #+/-{, }]{!}
        STR , [], #+/-{, }
      • 示例:
        STR R0, [R2, #04] @将 R0 寄存器中的数据存储到 R2+4 所指向的存储单元

其他指令

  • 媒体指令使用较少
  • 分支指令也称“跳转指令”,会根据条件标志的不同取值执行不同的分支
  • 常见的分支指令:
    • 跳转指令 B

      • 格式:B
      • B 指令是最简单的分支指令。执行 B 指令时,若条件 c 满足,ARM 处理器将立即跳转到 label 指定的地址处执行
    • 带链接的跳转指令 BL

      • 格式:BL
      • 执行 BL 指令时,若条件 c 满足,首先将当前指令的下一条指令的地址复制到 R14(即 LR)寄存器,然后跳转到 label 指定的地址处继续执行。此指令通常用于调用子程序。在子程序尾部,可通过 MOV PC, LR 指令返回主程序
    • 带状态切换的跳转指令 BX

      • 格式:BX
      • 执行 BX 指令时,若条件 c 满足,处理器会判断 label 指向的地址的 bit[0] 是否为 1,若为 1 则跳转时自动将 CPSR 寄存器的标志 T 置位,并将目标地址处的代码解释为 Thumb 代码来执行,即处理器会切换到 Thumb 状态;反之,若 label 指向的地址的 bit[0] 为 0,则跳转时自动将 CPSR 寄存器的标志 T 复位,并将目标地址处的代码解释为 ARM 代码来执行,即处理器会切换到 ARM 状态
      • 示例:
      .code 32
             ...
             ADR R0, thumbcode+1
             BX R0        @跳转到 thumbcode 处执行,并将处理器切换为 Thumb 模式
      thumbcode:
      .code 16
             ...
      
    • 带链接和状态切换的跳转指令 BLX

      • 格式:BLX
      • BLX 指令集合了 BL 与 BX 指令的功能。若条件 c 满足,除了会设置链接寄存器,还会根据 label 指向地址的 bit[0] 的值切换处理器状态
    • 块数据传输指令

      • 功能:用于一次处理一个块的数据传输
      • 常见的块数据传输指令:
        • STMDA

          • 格式:STMDA {!},
          • 示例:STMIA R0!, {R1-R3} @将 R1 ~ R3 寄存器的内容存储到 R0 寄存器指向的存储单元
        • LDMDA

          • 格式:LDMDA {!},
          • 示例:LDMIA R0!, {R1-R3} @从 R0 寄存器指向的存储单元读取 3 个字,将其放入 R1 ~ R3 寄存器
    • 软中断指令

      • 在汇编中使用较多的软中断指令是 SVC 指令。用户态的 ARM 程序通常将系统调用号传入 R7 寄存器,然后用 SVC 指令调用 0 号中断来直接执行系统调用
    • 协处理器指令

      • 上一节学过 SIMD 系列的 VMOVVADD 指令
    • 无条件执行指令

      • 使用较多的有 LDCSTC 指令
      • BLBLX 指令也属于无条件跳转指令

你可能感兴趣的:(《Android,软件安全权威指南》学习笔记)