ARM汇编基础(iOS逆向)

1. ARM汇编基础

在逆向一个功能的时候,往往需要分析大量的汇编代码,在iOS逆向中,ARM汇编是必须掌握的语言,本文总结了ARM汇编的基础知识,如果你想了解更多,请参考狗神的小黄书《iOS逆向逆向工程》或ARM官方手册.

1.1 寄存器,内存和栈

在ARM汇编里,操作对象是寄存器,内存和栈

ARM的栈遵循先进后出,是满递减的,向下增长,也就是开口向下,新的变量被存到栈底的位置;越靠近栈底,内存地址越小

一个名为stackPointer的寄存器保存栈的栈底地址,成为栈地址.

可以把一个变量给入栈(push)以保存它的值,也可以让它出(pop栈),恢复变量的原始值.在实际操作中,栈地址会不断变化;但是在执行一块代码的前后,栈地址应该是不变的,不然程序就要出问题,

1.2 特殊用途的寄存器

ARM处理器中的部分寄存器有特殊用途 如下所示:

寄存器 用途
R0-R3 传递参数与返回值
R7 帧指针,指向母函数于被调用子函数在栈中的交接
R9 在iOS3.0以前被系统保留
R12 内部过程调用存储器,dynamic linker会用到它
R13 sp寄存器
R14 LR寄存器,保存函数返回地址
R15 PC寄存器
1.3 分支跳转与条件判断

处理器名为”Program counter”(简称PC)的寄存器用于存放下一条指令的地址.一般情况下,计算机一条接一条地顺序执行指令,处理器执行完一条指令后将PC加1,让它指向下一条指令.(1-1)

处理器顺序执行指令1到指令5(2-2),但是如果把PC的值变一变,指令执行的顺序就完全不同

指令执行顺序被打乱,变成了指令1,指令5,指令4,指令2,指令3,指令6,这种乱序的学名叫做”分支”,或者”跳转”,它使循环和subroutime成为可能,例如:

```
// endless() 函数
endless:
    操作  操作数1, 操作数2
    分支  endless
    返回  // 死循环,执行不到这里啦!
```

在实际情况中,满足一定条件才得以触发的分支是最实用的,这种分支成为条件分支.if else 和 while都是基于条件分支实现的,在ARM汇编中,分支的条件一般有4种:

  • □ 操作结果为0(或不为0);
  • □ 操作结果为负数;
  • □ 操作结果有进位;
  • □ 运算溢出(比如两个正数相加得到的数超过了寄存器位数).

这些条件的判断准则(falg)存放在程序状态寄存器(Program Status Register,PSR)中,数据处理相关指令会改变这些flag,分支指令再根据这些flag决定是否跳转.下面的伪代码展示了一个for循环

for:
    相加   A,#1
    比较  A,#16
    不为0则跳转到for
    /* 此循环将A和#16作比较,如果两者不相等,则将A加1,继续比较.
     如果两者相等,则不再循环,继续往下执行. */

2. ARM/THUMB指令解读

ARM处理器用到的指令集分为ARM和THUMB两种:ARM指令长度均为32bit,THUMB指令长度为16bit.所有指令可大致分为三类,分别为,数组操作指令,内存操作指令和分支指令.

2.1 数据操作指令

数据操作指令有以下2条规则:

* 所有的操作数均为32bit;

* 所有的结果均为32bit,且只能存放在寄存器当中.

总的来说,数据操作指令的基本格式是:cp{cond}{s} Rd,Rn,Op2

其中,”cond”和”s”是另个可悬后缀;”cond”的作用是指定指令”op”在什么条件下执行,共有17中条件:

指令 条件
EQ 结果为0(EQual to 0)
NE 结果不为0(Not Equal to 0)
CS 有进位或借位(Carry Set)
HS 同CS(unsigned Higer or Same)
CC 没有进位或借位(Carry Clear)
LO 同CC(unsigned LOwer)
MI 结果小于0(MInus)
PL 结果大于等于0(PLus)
VS 溢出(Overflow Set)
VC 无溢出(Overflow Clear)
HI 无符号比较大于(unsigned HIger)
LS 无符号比较小于等于(unsigned Lower or Same)
GE 有符号比较大于等于(signed Greater than or Equal)
LT 有符号比较小于(signed Less Than)
GT 有符号比较大于(signed Greater Than)
LE 有符号比较小于等于(signed Less than or Equal)
AL 无条件(Always,默认)

“cond”的用法很简单,例如:

比较 R0, R1
移动 GE R2, R0
移动 LT R2, R1

比较R0和R1的值,如果R0大于等于R1,则R2 = R0;否则R2 = R1.

“s”的作用是指定指令”op”是否设置了flag,共有下面4中flag:

N(Negative)

如果结果小于0则置1,否则置0;

Z(zero)

如果结果是0则置1,否则置0;

C(Carry)

对于加操作(包括CMN)来说,如果产生进位则置1,否则置0;对于减操作(包括CMP来说),Carry相当于Not-Borrow,如果产生借位则置0,否则置1;对于有移位的非加/减操作来说,C置移出值得最后一位;对于其他的非加/减操作来说,C的值一般不变;

V(overflow)如果操作导致溢出,则置1,否则置0

需要注意一点的是,C flag表示无符号数运算结果是否溢出;V flag表示有符号数运算结果是否溢出.

算数操作指令可以大致分为4类:

  • 1.算数操作

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

ADC R0,R1,R2; ——————> R0 = R1 + R2 + C(array)

SUB R0,R1,R2; ——————> R0 = R1 - R2

SBC R0,R1,R2; ——————> R0 = R1 - R2 - !C

RSB R0,R1,R2; ——————> R0 = R2 - R1

RSC R0,R1,R2; ——————> R0 = R2 - R1 - !C

算数操作中,ADD和SUB为基础操作,其他均为两者的变种.RSB是”Reverse Sub”的缩写,仅仅是把SUB的两个操作数调换了位置而已;以”C”结尾的变种代表没有进位和借位的加减法,当产生进位或者借位时,将Carrry flag 置为1.

  • 2.逻辑操作

AND R0,R1,R2; ——————> R0 = R1 & R2

ORR R0,R1,R2; ——————> R0 = R1 | R2

EOR R0,R1,R2; ——————> R0 = R1 ^ R2

BIC R0,R1,R2; ——————> R0 = R1 &~ R2

MOV RO,R2; ——————> R0 = R2

MVN R0,R2; ——————> R0 = ~R2

逻辑操作指令都已经用C操作符说明了作用,但是C操作符里的移位操作并没有对位的逻辑操作指令,ARM采用了桶式移位,共有四种指令:

LSL 逻辑左移

LSR 逻辑右移

ASR 算术右移

ROR 循环右移

  • 3.比较操作

    CMP R1,R2; ——————> 执行R1 - R2并依结果设置flag

CMN R1,R2; ——————> 执行R1 + R2并依结果设置flag

TST R1,R2; ——————> 执行R1 & R2并依结果设置flag

TEQ R1,R2; ——————> 执行R1 ^ R2并依结果设置flag

比较操作其实就是改变flag的算术操作或逻辑操作,只是操作结果不保留在寄存器里而已.

  • 4.乘法操作

MUL R4,R3,R2 ——————> R4 = R3 * R2

MLA R4,R3,R2,R1 ——————> R4 = R3 * R2 + R1

乘法操作的操作数必须来自寄存器

2.2 内存操作指令

内存操作指令的基本格式是:

op{cond}{type} Rd,[Rn,Op2]

其中Rn是基址寄存器,用于存放基地址;”cond”的作用与数据操作指令相同;”type”指定指令”op”操作的数据类型,共有四种:

B(unsigned Byte)
无符号byte(执行时扩展到32bit,以0填充);

SB(signed Byte)
有符号byte(仅用于LDR指令;执行时扩展到32bit,以符号位填充);

H(unsigned Halfword)
无符号halfword(执行时扩展到32bit,以0填充);

SH(Signed Halfword)
有符号halfword(仅用于LDR指令;执行时扩展到32bit,以符号位填充).

如果不指定”type”,则默认是word

ARM内存操作基础指令只有2个,LDR(loaD Register)将数据从内存中读出来,存到寄存器中;STR(STore Register)将数组从寄存中读出来,存到内存中.两个指令的使用情况如下:

  • LDR
LDR Rt,[Rn {,#offset}]          ;   Rt = *(Rn {+ offset}),{}代表可选
LDR Rt,[Rn, #offset]!           ;   Rt = *(Rn + offset);Rn = Rn + offset
LDR Rt,[Rn], #offset            ;   Rt = *Rn;Rn = Rn + offset
  • STR
STR Rt,[Rn {,#offset}]          ;   *(Rn {+ offset}) = Rt
STR Rt,[Rn, #offset]!           ;   *(Rn + offset) = Rt;   Rn = Rn + offset
STR Rt,[Rn], #offset            ;   *Rn = Rt;  Rn = Rn + offset

此外,LDR和STR的变种LDRD和STRD还可以操作双字(DoubleWord),即一次性操作两个寄存器,其基本格式如下:op{cond} Rt,Rt2, [Rn {, #offset}]

其用法与原型类似,如下:

  • STRD
SRTD R4,R5, [R9,#offset]    ; *(R9 + offset) = R4;*(R9 + offset + 4) = R5
  • LDRD
LDRD R4,R5,[R9,#offset]     ; R4 = *(R9 + offset); R5 = *(R9+offset+4)

除LDR和STR外,还可以通过LDM(LoaD Multiple)和STM(STore Multipe)进行块传输,一次性操作多个寄存器.块传输指令的基本格式是

op{cond}{}mode] Rd{!},reglist

其中Rd是基址寄存器,可选的”!”制定Rd变化后的值是否写会Rd, reglist是一系列寄存器,用大括号括起来,它们之间可以用”,”分割,也可以用”-“表示一个范围,比如,{R4-R6,R8}表示寄存器,R4,R5,R6,R8;这些寄存器的顺序是按照自身的编号由小到大排列的,与大括号内的排列顺序无关.

需要特别注意的是,LDM和STM的操作方向与LDR和STR完全相反:LDM是把从Rd开始,地址连续的内存数据存入reglist中,STM是把reglist中的值存入从Rd开始,地址连续的内存中.此处特别容易混淆

“cond” 的作用与数据操作指令相同.”mode”指定R4值得变化的4中规律,如下所示:

IA(Increament After)每次传输后增加Rd的值;

IB(Increament Before)每次传输前增加Rd的值

DA(Decrement After) 每次传输后减少Rd的值;

DB(Decreament Before)每次传输前减少Rd的值.

这是什么意思呢?下面以LDM为代表,举一个简单的例子,相信大家一看就明白了.在下图(块传输指令模拟环境)中,R0指向的值是5.

在执行以下命令后,R4,R5,R6的值分别变成:

foo():
LDMIA R0, {R4 - R6};    R4 = 5, R5 = 6, R6 = 7
LDMIB R0, {R4 - R6};    R4 = 6, R5 = 7, R6 = 8
LDMDA R0, {R4 - R6};    R4 = 5, R5 = 4, R6 = 2
LDMDB R0, {R4 - R6};    R4 = 4, R5 = 3, R6 = 3

STM指令的作用方式与此类似,不再赘述.LDM和STM的操作与LDR和STR完全相反

2.3 分支指令

分支指令可以分为无条件分支和条件分支两种.

  • 无条件分支
B Label;PC = Label
BL Label;LR = PC - 4;PC = Label
BX Rd ;PC = Rd并切换指令集
eg:
foo():
    B Label ; 跳转到Label处并往下执行
    ......  ; 得不到执行
Label:
    ......
  • 无条件分支

跳转分支的cond是依照掐面的flag来判断的,它们的对应关系如下:

cond flag
EQ Z = 1
NE Z = 0
CS C = 1
HS C = 1
CC C = 0
LO C = 0
MI N = 1
PL N = 0
VS V = 1
VC V = 0
HI C = 1 & Z = 0
LS C = 0
GE N = V
LT N != V
GT Z = 0 & N = V
LE Z = 1

在条件分支指令钱会有一条数据操作指令来设置flag,分支指令根据falg的值来决定代码走向,举例如下:

Label:
Lable1:
    LDR R0, [R1], #4
    CMP R0, 0; 如果R0 == 0,Z =1 ; 否则Z = 0
    BNE Label ; Z == 0则跳转
2.4 THUMB指令

THUMB指令集是ARM指令集的一个子集,每条THUMB指令均为16bit;因此THUMB指令比ARM指令更节省空间,且在16位数据总线上的传输效率更高.有得必有失,除了”b”之外,所有的THUMB指令均无法条件执行;桶式移位无法结合其他指令执行;大多数THUMB指令只能使用R0-R7这8个寄存器等.相对于ARM指令,THUMB指令的特点如下:

  • 指令数量减少
  • 没有条件执行
  • 所有指令默认附带*
  • 桶式移位无法结合其他指令执行
  • 寄存器使用受限
  • 立即数和第二操作数使用有限
  • 不支持数据写回
时间: 2024-12-15 01:45:38

ARM汇编基础(iOS逆向)的相关文章

ARM汇编基础(1)--ADS1.2安装与第一个ARM汇编程序

前言 最近的工作是与逆向相关, 用到ARM汇编, 以前在大学时学过x86汇编和ARM开发, 自我感觉当时学的还挺好, 很久没用也生疏了. 正好趁着这个机会也复习一下ARM汇编. 关于ARM的介绍和理论知识,此处就不再赘述. 进入正题: 安装ADS1.2 安装过程很简单, 没什么技术含量. 但是, win7可能会碰到卡在100%的问题, 网上有很多解决的办法, 但好像都不管用. 我用XP的虚拟机装的ADS1.2, 在XP系统上安装很顺利. 第一个ARM汇编程序 新建一个工程, 然后再按照一下步骤设

ARM汇编基础

ARM是基于RISC指令架构下的处理器,现在有ARM#,ARM-Cortex-M#,ARM-Cortex-R#,ARM-Cortex-A#很多版本.学习处理器,还是从最简单的入手,就像学习x86汇编,先学习8086一样. 1.ARM汇编分类 标准汇编:ARM公司标准的汇编环境,只有ARM编译器才可以使用,如ADS集成开发环境.GNU汇编:Linux下的GNU交叉工具链中的汇编环境,我们一般选用这种模式. 2.ARM七种工作模式 (1)用户模式(USR):正常程序执行模式,不能直接切换到其他模式(

ARM汇编基础--几个简单实例

实例1: 求1+2+3+......+100的和, 并将结果存入0x80000100地址空间中. 1 area text, code, readonly 2 entry 3 code32 4 start 5 mov r1, #0 6 mov r2, #100 7 loop 8 add r3, r3, r1 ;累加 9 add r1, r1, #1 ;自增 10 cmp r1, r2 11 ble loop 12 ldr r2, =0x80000100 13 str r3, [r2] ;保存结果

[iOS逆向实战 之一]arm寄存器简介

个人原创,转帖请注明来源:cnblogs.com/jailbreaker [iOS逆向实战]这个系列的帖子,会涉及到arm汇编,以及调试工具的使用,加上实战解析一步步逆向,深入浅出. 这是第一篇帖子,简要说下arm的各个寄存器的功能. R0-R3:用于函数参数及返回值的传递 R4-R6, R8, R10-R11:没有特殊规定,就是普通的通用寄存器 R7:栈帧指针(Frame Pointer).指向前一个保存的栈帧(stack frame)和链接寄存器(link register, lr)在栈上的

iOS逆向工程之Hopper中的ARM指令

一.Hopper中的ARM指令 ARM处理器就不多说了,ARM处理器因为低功耗等原因,所以大部分移动设备上用的基本上都是ARM架构的处理器.当然作为移动设备的Android手机,iPhone也是用的ARM架构的处理器.如果你想对iOS系统以及你的应用进一步的了解,那么对ARM指令集的了解是必不可少的,ARM指令集应该也算得上是iOS逆向工程的基础了. 当你使用Hopper进行反编译时,里边全是ARM的指令,那是看的一个爽呢.下面就是使用Hopper打开MobileNote.app的一个Hoppe

大脸猫讲逆向之ARM汇编中PC寄存器详解

i春秋作家:v4ever 近日,在研究一些开源native层hook方案的实现方式,并据此对ARM汇编层中容易出问题的一些地方做了整理,以便后来人能有从中有所收获并应用于现实问题中.当然,文中许多介绍参考了许多零散的文章,本文重点工作在于对相关概念的整理收集,并按相对合理顺序引出后文中对hook技术中的一些难点的解读. Android平台大多采用了ARM架构的CPU,而ARM属RISC,与X86架构的处理器有不同的特征,本文讲介绍ARM中不容易理解的PC寄存器各种问题,包括ARM流水线.PC寄存

iOS逆向开发(1):基础工具 | ssh | scp | socat

小白:小程,我一直想问,什么是逆向来着?是逆向行驶吗? 小程:理解为逆向行驶也没错.一般的项目是从无到有,而逆向是从已有的状态入手,分析出已有的流程与结构的手段. iOS上的逆向开发,是一件有趣的事情(虽然有时很痛苦),而且还可能给你带来收益. 在接下来的几篇文章,小程会尝试介绍iOS逆向的一系列的知识与技能:从基础工具的使用,到目标类的定位.目标代码的调试,再到注入微信的实战示例. 本文介绍iOS逆向的基础工具的使用. 硬件方面,你需要一台iOS设备(iphone/itouch/ipad,以下

ARM开发入门与汇编基础

2019-12-12 关键字:汇编指令基础 首先 ARM 是一家公司,它成立于 1990 年.ARM 公司主要是设计 ARM 系列的 RISC 处理器内核,并将这些内核授权给合作伙伴进行生产与销售.ARM 公司是一家只负责设计内核而不生产芯片的公司. ARM 芯片的产品线主要分为三种: 1.应用级 应用于高端产品的芯片,如智能手机等.其芯片代号为 A 系列,如 Cortex-A8 , Cortex-A9. 2.实时嵌入式 应用于一些对性能要求稍低一些的设备.其芯片代号为 R 系列,如 Corte

Arm处理器寄存器介绍及汇编基础

1. ARM处理器支持7种工作模式 · User (usr): The normal ARM program execution state· FIQ (fiq): Designed to support a data transfer or channel process· IRQ (irq):    Used for general-purpose interrupt handling· Supervisor (svc):   Protected mode for the operating