arm指令

#ARM汇编书本内容

  • look at 70
  • 条件列表 P35
  • 降序或升序 p57
    • 堆栈向下增长或向上增长,前者从一个高地址开始并向更低地址前进(降序 堆栈),而后者从一个低地址开始并向更高地址前进(升序 堆栈)。
  • 满或空 p57
    • 堆栈指针可指向堆栈中的最后一项(满 堆栈),也可指向堆栈中的下一个空闲空间(空 堆栈)。
  • ARM使用加载-存储模式控制对内存的访问,这意味着只有加载/存储(LDR或者STR)才能访问内存。这就意味着如果要增加一个32位的在内存中的值,需要做三种类型的操作(加载,加一,存储)将数据从内存中取到寄存器,对寄存器中的值加一,再将结果放回到内存中。

#ARM有三十个32位通用寄存器

  • ARM 状态下以一个字(存储单元)(四字节)为增量
  • 所有 ARM 指令的长度都是 32 位。这些指令是按字对齐方式存储的(所以每条指令地址偏移4),因此在
    ARM 状态下,指令地址的两个最低有效位始终为零。
registerfunction含义
x0 - x7arguments and result registers参数和结果寄存器
x8indirect result (large struct) location间接结果(大结构)位置
x9 - x15scratch registers刮擦寄存器 想用就拿来用
x16 - x17intra call use registers (PTL, linker)内部调用使用寄存器(PTL,链接器),一个函数呼叫另一个函数,暂存资料 自己不要用
x18TLSTLS
x19 - x28saved registers保存的寄存器 函数呼叫后 保存内容
x29frame pointer帧指针
x30link register链接寄存器
x31stack pointer堆栈指针

ARM指令的基本格式

{} ,{,}

首选语法 {} ,{,}

MNEMONIC , Operand1, Operand2

助记符{是否使用CPSR}{是否条件执行以及条件} {目的寄存器}, 操作符1, 操作符2

  • 格式中<>内容必不可少,{}中的内容可以省略
  • :表示操作码,指令
  • {}:表示指令执行的条件域,如EQ,NE等
  • :决定指令执行结果是否影响CPSR的值,使用该后缀则指令执行的结果影响CPSR的值 ,否则不影响
  • :表示目的寄存器
  • :表示第一个操作数,为寄存器
  • :表示第二个操作数,可以是立即数,寄存器,或者寄存器移位操作数

ARM64下的寄存器

指令全称参考

  • 通用寄存器(用来存放一般性的数据),放通用数据,32bit

    • x0~x30 (64位)
    • x29 又名fp (用于保存栈底的地址)
    • x30 又名lr 链接寄存器,存储子程序返回地址(bl跳转后就会把下一条指令地址写到lr中)
    • w0~w30 (32位)它些就是x0~x30的低32位
    • PC 程序计数器 ,指向当前指令地址(正在取指的指令)。由于CPU的三级流水(FDE)PC指向的是相对于执行的那条指令(E)第三条指令,pc=当前程序执行位置+4,或者+8,可以看成游标卡尺的游标
    • 栈寄存器
    • SP (任意时刻会保存我们栈顶的地址) 可用于切换进程
    • FP = x29 (用于保存栈底的地址)
  • 浮点寄存器(CPU中专门提供浮点数寄存器来处理浮点数)

    • D0~D31(64位)
    • S0~S31 (32位)这些就是D0~D31的低32位
  • 向量寄存器 (现在的CPU支持向量运算.(向量运算在图形处理相关的领域用得非常的多)为了支持向量计算系统了也提供了众多的向量寄存器.

    • V0-V31(128位)
  • 状态寄存器(又称 CPSR【current program status register】寄存器),由32bit组成 youtube

    • CPSR寄存器是32位的,每一位的功能如下 31 30 29 28 27~8 7 6 5 4 3 2 1 0 N Z C V 保留 I F T M4 M3 M2 M1 M0
    • CPSR的低8位(包括I、F、T和M[4:0])称为控制位,程序无法修改,除非CPU运行于特权模式下,程序才能修改控制位! M有5位,控制CPU运行模式
    • N【负数标志】、Z【0标志】、C【进位标志】、V【溢出标志】均为条件码标志位。
    • 高4位31-28,条件标志
    • 27-8, 保留位
    • 后8位7-0, 控制位
    3130292827...76543210
    N ZCVQ...IFTM4M3M2M1M0
    • N:nagetive 正负标志,N=1表示运算结果为负,N=0表示运算结果为正或0
    • Z:zero零标志, Z=1表示运算结果为0,Z=0表示运算结果为非0
    • C carray
      • 进位标志,加法运算产生了进位时则C=1,否则C=0;
      • 借位标志,减法运算产生了借位则C=0,否则C=1;
    • V:overflow 溢出标志,V=1表示有溢出,V=0表示无溢出
    • Q:累计饱和instructions
    • T:说明执行的指令是ARM指令(0),还是Thumb指令(1)
    • F: 6,第7位 FIQ的缩写,是否禁用FIQ 为1的话表示不允许快速中断
    • I: disable IRQ
    • A: 8 是否disable异步abort
    • E: 9,endian 操作储存的字节顺序,0=;1=
      • 32位一个字长为4个字节
      • 进行存取时都是按字长进行存取。一小片一小片的进行存取,不进行分割,存储到地址里面,字节的顺序,
      • 低位存低地址,高位存高地址。小端
      • 低位存高地址,高位存低地址。大端
    • 10-15: 针对Thumb2指令
    • 16-19: SIMD(单指令,多数据)指令使用
    • 20-23: 保留
    • 24: 是否jzalle状态
    • 25-26: Thumb-2指令的if...then条件执行

#ARM64下常用的汇编指令

这里我只是简单整理一下,后面再写一下代码案例来介绍

####基础指令

  • MOV - MOV X1,X0 ; 将寄存器X0的值传送到寄存器X1
  • ADD - ADD X0,X1,X2 ; 寄存器X1和X2的值相加后传送到X0
  • SUB - SUB X0,X1,X2 ; 寄存器X1和X2的值相减后传送到X0
  • AND - AND X0,X0,#0xF ; X0的值与0xF相位与后的值传送到X0
  • ORR - ORR X0,X0,#9 ; X0的值与9相或后的值传送到X0
  • EOR - EOR X0,X0,#0xF ; X0的值与0xF相异或后的值传送到X0

####跳转操作

  • 跳转有两种方式
    • 使用跳转执令直接跳转
    • 直接向PC寄存器赋值实现跳转
  • BL 将下一条指令的地址放入lr(x30)寄存器
  • RET 默认使用lr(x30)寄存器的值,通过底层指令提示CPU此处作为下条指令地址!
  • CMP 比较指令,相当于SUBS,影响程序状态寄存器
    • B.GT 比较结果是大于,执行标号,否则不跳转
    • B.GE 比较结果是大于等于,执行标号,否则不跳转
    • B.EQ 比较结果是等于,执行标号,否则不跳转
    • B.HI 比较结果是无符号大于,执行标号,否则不跳转
  • CBZ - CBZ ; 比较(Compare),如果结果为零(Zero)就转移(只能跳到后面的指令)
  • CBNZ - CBNZ ; 比较,如果结果非零(Non Zero)就转移(只能跳到后面的指令)

#寻址,就是找地址ARM最核心的工作之一

  • 立即数寻址, add x0, x1, #0x2f
  • 寄存器寻址, add x0, x1, x2
  • 寄存器间接寻址 load store
    • ldr x0, [x1] //x1寄存器记录的内存地址
    • str x0, [x1]
    • stur x0, [x1, #-0x4] 多于负地址偏移寻址
  • 寄存器移位寻址 add x3,x2,x1,lsl #2 //左乘右除
  • 基地址寻址
    • ldr x0, [x1, #4] //x1+4 的地址的值,读到x0 => x0=x1+4
    • ldr x0, [x1], #4 //x1的地址的值+4后,给x0 => x1=x1+4;x0=x1
    • ldr x0,[x1, x2] //x0=x1+x2,两个地址之和的地址的值
  • 多寄存器寻址 ldmia x0,{x1,x2,x3,x4} 用的少一点 m代表多,ia立刻
  • 相对寻址
    • bl next //跳转到next,并把当前状态保存到 LR
    • mov pc,lr //把lr给到pc,下一条指令是pc,相当于返回,跳转

#ARM汇编算术操作
####ADRP 是计算指定的数据地址 (eg:adrp x0, 1)

  1. 将1的值,左移12位 1 0000 0000 0000 == 0x1000
  2. 将PC寄存器的低12位清零 0x1002e6874 ==> 0x1002e6000
  3. 将1 和 2 的结果相加 给 X0 寄存器!
0x1002a6764 <+68>:  adrp   x8, 2
0x1002a6768 <+72>:  add    x8, x8, #0x88             ; =0x88 
# x8 最后地址 = 2 << 12 +  0x1002a6000 + 0x88 = 0x1002a8088

####ARM汇编内存操作
####单寄存器读写指令

    1. 内存最小的单元是Byte,但是为了便于管理,存储器会分页,会用到另外一个单位叫字长word。在32位里1个word=4Byte,以字长为一个单位。
    1. ARM采用的是小端模式,低位放低地址,高位放高地址。先判断字空间(0-3,4-7这样分),起始位顺读1234,内存地址就是 4321,4是高位,1是低位 ,从右往左读就OK了
  • ldr(load registor)把内存中的地址加载到寄存器 按字长操作
    move x1, #0x12
    ldr x0, [x1]
  • str (store registor) 把寄存器的值写入到内存地址 按字长操作
    move x1, #0x0f
    str x1, [x0, #ox0C] //写到 0c
  • ldrb 按字节操作
    mov x1, #0x0f
    strb x1, [x0, #0x0c]
    ldrb x0, [x4, #0x2c]
  • ldrh 半字操作
  • strh 半字存储
  • ldrbt user模式下
  • strbt
  • ldrt 不带b表示字节
  • strt
  • ldrsb s-代表有符号 会影响CPSR
  • ldrsh
  • STR - 将数据从寄存器中读出来,存到内存中. STR - STR X0, [SP, #0x8] ;X0寄存器的数据传送到SP+0x8地址值指向的存储空间
  • STP - STR 的变种指令,可以同时操作两个寄存器 STP x29, x30, [sp, #0x10] ; 将x29,x30存入栈中
  • LDR - 将数据从内存中读出来,存到寄存器中 LDR X5,[X6,#0x08] ;X6寄存器加0x08的和的地址值内的数据传送到X5
  • LDP - LDR 的变种指令,可以同时操作两个寄存器 LDP x29, x30, [sp, #0x10] ; 将栈中的值取出存放到x29, x30

####多寄存器的读写指令,操作一片连续内存

  • ldm (load multiple) LDMIA(IA 地址模式) R0!(基址寄存器 -> 指向存储器, !:意思是最后把值保存到r0){R1,R2,R3或者 R1-R3} 意思是把R0的值加载到这一推寄存器里
  • 感叹号的意思 是最后把值 保存到这个地方
  • LDMIA R0(基址寄存器,表示基本地址),
  • 地址模式;I-加法,D-减法 A-after B-before
    • 数据块模式:IA(传输后地址加4个字节); IB(付送前地址加4);DA(传输后地址减4); DB(传送前减4)
    • 堆栈模式:满底层堆栈
  • STM (存储)
    move x1, #0x00
    stmia x1,

####数据交换指令

  • swp (swap) swp r0, r1, [r2] 内存和寄存器字交换 r0相当于一个中间值
    mov r1, #0x0f
    mov r2, #0x12
    swp r1, r1, [r2]
  • swpb 字节交换

####跳转指令,状态操作
在语言学习的时候,经常有函数调用,这个过程用指令来描述 就是跳转
实际上我们的跳转就是通过往PC计数器传入相应的值,来告诉下一步应该去哪执行
如果没有跳转指令的话,每次在想要跳转的地方都要写

mov lr, pc  长指令跳转
mov pc, x1
  • B (break 跳转) 跳转过去 语法 B 标签
    b sundy
  • BR 无条件跳转到寄存器地址
  • BL 带返回的连接跳转
    bl sundy
    bl sundy 跳过去执行完了 再回来继续执行
  • BX
  • BLX

####状态寄存器操作

  • mrs (move to registor form status registor) 把程序状态寄存器的值传送到通用寄存器
    mrs x1, cpsr
  • msr (move to status registor form registor) 通用寄存器到程序状态寄存器
    msr cpsr, x2

####异常产生指令

  • swi 软中断指令
  • bkpt 断点中断指令

####伪指令
我们的指令已经可以做各类操作了,但我们操作起来太麻烦了。
比如我现在要设置一个值给寄存器R0,但下次我修改了寄存器R0后又需要读出来刚才的值,那我们就要先临时
保存值 到SPSR or CPSR,然后为断切换
再比如,我们要做一个循环,就要用label结合BL 不断进行,但如果我们要循环很多次。
我们就定义了一些类似于带参数的宏的操作一样,来定义我们的伪指令,方便我们更好的实现汇编程序逻辑。伪指令只是在汇编器之前作用,汇编之后会翻译为标准的汇编指令集。
又分为ARM汇编伪指令和GUN汇编伪指令。

  • 常用伪指令
    • AREA 声明区域段,数据区,代码区等等
    • CODE16/CODE32 声明以下是32位还是16位。
    • ENITY 用于指定程序的入口点。程序至少得有一个 就像C语言的 main函数。
    • END 用于通知编译器已经到了源程序的结尾
    • EQU 用于为程序中的常量 定义一个标识符 SUNDYCON1 EQU 0x32000000
    • EXPORT 导出标识,其他文件可以引用到
    • IMPORT 相当于静态引用
    • EXTERN 相当于动态引用
    • GET 相当于引用文件 GET "file.S"
    • RN 用于给一个寄存器定义一个别名,采用这种方式可以方便程序员记忆该寄存器的功能。 和EQU有点像
  • 符号命名约定
    • 符号区分大小写,同名的大,小写符号会被编译器认为是两个不同的符号
    • 符号在其作用范围内必需唯一
    • 自定义的符号不能与系统的保留字相同
    • 符号名不应与指令或者伪指令同名
  • 符号,变量
    • GBLA/GBLL/GBLS gbla test3 定义全局数字/逻辑/字符串变量
    • LBLA/LBLL/LBLS lbal test3 声明局部变量
    • SETA/SETL/SETS test3 seta 0xaa 设定变量值
    • RLIST(reglist RLIST {R0-R5,R8,R10}) 将寄存器列表名称定义为reglist,可以ARM指令LD,/STM中通过该名称访问寄存器列表
    • $可以变量代换 ,有点像 shell $

#寄存器操作

  • LDR 大范围寻址到寄存器-绝对寻址
  • ADR 小范围寻址到寄存器 +- 255- 相对寻址
  • ADRL 中范围寻址到寄存器- 相对寻址

#实际反编译中不懂的指令

  1. STP x29, x30, [sp, #0x10] ;入栈指令 //1.把x29 和 x30存方到sp-8 sp-10的位置 2.sp -= 0x18
  2. LDP x29, x30, [sp, #0x10] ;出栈指令 //1.将sp-8 sp-10位置的值取出来,放入x29 和 x30,2.sp += 0x18
  3. CBZ ;比较(Compare),如果结果为零(Zero)就转移(只能跳到后面的指令)
  4. CBNZ ;比较,如果结果非零(Non Zero)就转移(只能跳到后面的指令)
  5. CMP ;比较指令,相当于SUBS,影响程序状态寄存器CPSR status = op1 - (op2)
  6. CMN -- 比较取负的值 CMN R0, #1 @把R0与-1进行比较 status = op1 - (-op2)
  7. RET ;子程序返回指令,返回地址默认保存在LR(X30)
  8. tbz ;寄存器的 #imm位上 为0就跳转 imm是立即缩写
  9. cset ;
  10. adr ; x1, off_100381190, 到当前PC值的相对偏移, 相对寻址。

链接
sub_ 指令和子函数起点
locret_ 返回指令
loc_ 指令
off_ 数据,包含偏移量
seg_ 数据,包含段地址值
asc_ 数据,ASCII字符串
byte_ 数据,字节(或字节数组)
word_ 数据,16位数据(或字数组)
dword_ 数据,32位数据(或双字数组)
qword_ 数据,64位数据(或4字数组)
flt_ 浮点数据,32位(或浮点数组)
dbl_ 浮点数,64位(或双精度数组)
tbyte_ 浮点数,80位(或扩展精度浮点数)
stru_ 结构体(或结构体数组)
algn_ 对齐指示
unk_ 未处理字节