0
点赞
收藏
分享

微信扫一扫

汇编学习记录-简单总结

Ewall_熊猫 2022-04-22 阅读 51
开发语言

系列文章目录

第一章 汇编学习记录-二进制炸弹-x86架构
第二章 汇编学习记录-二进制炸弹-arm64架构
第三章 汇编学习记录-简单总结

文章目录


前言

这里对二进制炸弹破解过程中常用的汇编命令及其用法进行一点总结。并不是很详细,只是用来看懂(而不是自己写)汇编代码。


一、x86汇编简单总结

1、寄存器

整数寄存器

其中有几个特殊的通用寄存器我们需要知道其功能

  • rdi,rsi,rdx,rcx,r8,r9用来放函数的参数,更多参数存在栈中

  • rax用来存函数的返回值

  • 程序计数器 rip记录当前执行哪条指令

  • 堆栈指针 rsp栈指针

2、mov指令(及寻址方式)

movX src,dst
将src(可以说寄存器,立即数,内存)送到dst(可以是寄存器,内存,但不能直接在两个内存间传送),相当于c语言的dst=src

寻址方式

最一般的形式:D(Rb,Ri,S)
含义:Mem[Reg[Rb]+S*Reg[Ri]+ D]

  • D——常量,表示位移量(displacement): 1, 2, or 4 字节
  • Rb——基址寄存器(Base register): 所有16位整数寄存器
  • Ri——变址寄存器(Index register): 不可用%rsp
  • S ——比例因子(Scale): 1, 2, 4, or 8

常用的形式

  • (Rb) //Mem[Reg(Rb)]

例如 movq (%rcx),%rax 相当于rax=*rcx

  • D(Rb) //Mem[Reg[Rb]+D]

例如 movq 8(%rbp),%rdx 相当于 rdx = *(rbp+8)

  • (Rb,Ri) //Mem[Reg[Rb]+Reg[Ri]]
  • D(Rb,Ri) //Mem[Reg[Rb]+Reg[Ri]+D]
  • (Rb,Ri,S) //Mem[Reg[Rb]+S*Reg[Ri]]

这一条一般用来访问数组 S即为数组元素大小,Rb即为数组首地址,Ri即为索引
即相当于访问数组元素Rb[Ri]

条件传送指令

一般形式 cmovcc src, dst

c:表示这是条件传送
cc:表示条件
src: r16, r32, r64
dst: r/m16, r/m32, r/m64
条件在后面与跳转指令一起说,这里只需要知道条件传送指令会根据cmp或test的结果决定是否传送

3、取地址指令

leaq Src, Dst

  • Src 地址模式表达式
  • 将表达式对应的地址保存到Dst中

举个例子
leaq (%rdi,%rdi,2), %rax

我们知道(%rdi,%rdi,2)的意义应为Mem[R[rdi]+2R[rdi]]
对其取地址即为R[rdi]+2
R[rdi]
所以该指令相当于rax= 2rax+rax=3rax

4、算术运算指令

双操作数指令

  • addq Src,Dest #Dest = Dest + Src
  • subq Src,Dest # Dest = Dest - Src
  • imulq Src,Dest # Dest = Dest * Src

移位运算

  • salq Src,Dest # Dest = Dest << Src同shlq
  • sarq Src,Dest # Dest = Dest >> Src算术移位
  • shrq Src,Dest # Dest = Dest >> Src逻辑移位

位运算

  • xorq Src,Dest # Dest = Dest ^ Src
  • andq Src,Dest # Dest = Dest & Src
  • orq Src,Dest # Dest = Dest | Src

单操作数指令

  • incq Dest # Dest = Dest + 1
  • decq Dest # Dest = Dest - 1
  • negq Dest # Dest = - Dest(取负)
  • notq Dest # Dest = ~Dest(求反)

5、cmp指令

cmp 源操作数 目的操作数
CMP指令执行隐含的减法操作:目的操作数-源操作数,
并设置标志位,但不保存减法的结果,两个操作数都不
会被修改
格式与AND相同,修改OF、SF、ZF、CF、AF和PF

条件码

OF、SF、ZF、CF、AF和PF
执行某些指令会修改条件码,条件跳转指令,条件传送指令会根据条件码决定是否执行。
具体的条件码形式在这里我们不需要深究。只要记住助记符即可。
在这里插入图片描述
图上是设置条件码的指令setXX,助记符XX及其对应意义与条件传送,条件跳转的条件助记符意义相同

6、跳转指令

格式: jX <目标地址>

  • X表示根据条件码跳转
    在这里插入图片描述

7、函数调用

call func_label

  • 返回地址入栈(Push)
  • 跳转到func_label (函数名字就是函数代码段的起始地址)
  • 返回地址:
    紧随call指令的下一条指令的地址 (考虑PC——RIP的含义)

ret

  • 从栈中弹出返回地址(pop)
  • 跳转到返回地址

其他

跳转表

需要注意的是,当我们(C语言中)使用switch语句时
编译器会构造一个跳转表

在这里插入图片描述
如图,跳转表在地址为0x4031c0的位置
由因为地址(指针)占有8个字节
所以语句jmpq *0x4031c0(,%rax,8)
意味跳转到首地址为0x4031c0的地址数组中第rax项的位置

二、arm64汇编简单总结

1、寄存器

通用寄存器

64位:x0 ~ x28
32位:w0 ~ w28 (x0 ~ x28 的低位,如图)
在这里插入图片描述
其中有几个特殊的通用寄存器我们需要知道其功能
x0 ~ x7用来放函数的参数,更多参数存在栈zhong,类比x86架构的rdi,rci,rdx,rcx,r8,r9
x0用来存函数的返回值,类比rax

程序计数器

pc记录当前执行哪条指令,类比rip

堆栈指针

sp Stack Pointer栈指针,类比rsp
fp Frame Pointer也就是x29

链接寄存器

lr Link Register存储函数返回地址,也就是x30
在这里插入图片描述
在这里插入图片描述
一般在函数的开头与结尾会有这样的代码,实现函数的调用与返回

程序状态寄存器

cpsr:Current Program Status Register 程序状态寄存器器
cmp指令的结果会放到这里
spsr:Saved Program Status Register,异常状态下使⽤用
只是看懂正常汇编代码的话,这两个其实不太重要

零寄存器

wzr:4字节
xzr:8字节
用于清零操作

2、mov指令

mov x0, #10 立即数->寄存器 将立即数10存到寄存器x0 基本相当于C语言中 x0=10
mov x0, x1 寄存器->寄存器 将寄存器x1中的值存到寄存器x0 基本相当于C语言中 x0=x1

3、add、sub指令

add

add x0, x1, #5 相当于C语言中 x0=x1+5
add x0, x1, x2 相当于C语言中 x0=x1+x2
add x0, x1, x2, LSL #5 相当于C语言中 x0=x1+(x2<<5)

sub

sub x0, x1, x2 相当于C语言中 x0=x1-x2
sub x0, x1, x2, LSL #5 相当于C语言中 x0=x1-(x2<<5)

4、位运算

and按位与
AND R0, R0, #5 //5的二进制表示为0101,保持R0的第0位和第2位,其余位清0
orr按位或
ORR R0, R0, #5 //R0的第0位和第2位设置为1,其余位不变
eor按位异或
EOR R0, R0,#5 //R0的第0位和第2位取反,其余位不变

5、cmp指令

这里会用到cpsr程序状态寄存器
cmp x0 ,x1
会根据x0-x1的结果,修改cpsr的值。
若只想看懂汇编代码,不需要知道具体是怎样修改的,若想要了解,请参阅其他文献

6、跳转指令

b <目标地址>

无条件跳转到标记处,类比jmp

带条件的跳转

例如
b .eq

bl <函数名>

调用函数 类比call

比较+跳转

CBNZ

CBNZ X1,label //如果X1!= 0则跳转到label

CBZ

CBZ X1,label //如果X1== 0则跳转到label

TBNZ

TBNZ X1,#3, label //若X1[3]!=0,(从低到高数,从0数到3)则跳转到label
也就相当于
if(x1>=8)goto label;

TBZ

TBZ X1,#3, label //若位X1[3]==0,则跳转到label

7、加载指令(及寻址方式)

ldr

通用格式为
LDR 目的寄存器,<存储器地址>
举例

寄存器间接寻址

基址变址寻址

前索引

一般我们通过这种方式访问数组 LSL #2代表数组元素大小为4字节
该语句为将数组元素R1[R2]放入R0

自动索引
后索引

另外还有ldrX------X指某一个特定字母
ldrb 从存储器中将一个8位的字节数据传送到目的寄存器中,同时将寄存器的高24位清零。
ldrh 用于从存储器中将一个16位的半字数据传送到目的寄存器中,同时将寄存器的高16位清零。

ldur

ldur与ldr的区别:
ldr用于正数(偏移值是正数);
ldur用于负数(偏移值是负数)。

ldp

根据地址读取一对数据,按地址顺序赋值给一对寄存器:
1、先赋值给第一个寄存器;
2、偏移地址后,取值赋值给第二个寄存器。
load pair w0,w1 [x2 ,#0x10]
在这里插入图片描述
该指令一般用在程序结尾,作为出栈指令

至于具体存放顺序,这里我们不需要深究,因为我们只是需要读懂代码即可。

8、存储指令

st store存储

str

往内存中写数据(偏移值为正),用于从源寄存器中将一个32位的字数据传送到存储器中。
STR R0,[R1],#8
将R0中的字数据写入以R1为地址的存储器中,并将新地址R1+8写入R1。相当于*R1=R0;R1+=8;

STR R0,[R1,#8]
将R0中的字数据写入以R1+8为地址的存储器中。相当于*(R1+8)=R0;

stur

往内存中写数据(偏移值为负)

stp

该指令一般用在程序开头,作为入栈指令

同上,不需要太过深究

9、地址生成指令

adr与adrp
粗略地看
相当于一个赋值语句
在这里插入图片描述
x2=0x40200

10、移位运算

LSL

逻辑左移
按操作数所指定的数量向左移位,低位用零来填充。
MOV R0, R1, LSL#2;将 R1中的内容左移两位后传送到 R0中。

ASL

算术左移
同LSL

LSR

逻辑右移
按操作数所指定的数量向右移位,左端用零来填充。
MOV R0, R1, LSR#2;将 R1中的内容右移两位后传送到 R0中,左端用零来填充。

ASR

算术右移
按操作数所指定的数量向右移位,左端用第31位的值来填充。
MOV R0, R1, ASR#2;将 R1中的内容右移两位后传送到 R0中,左端用第 31位的值来填充。

ROR

循环右移
按操作数所指定的数量向右循环移位,左端用右端移出的位来填充。
MOV R0, R1, ROR#2;将 R1中的内容循环右移两位后传送到 R0中。

RRX

带扩展的循环右移
进行带扩展的循环右移的操作,按操作数所指定的数量向右循环移位,左端用进位标志位 C来填充。
MOV R0, R1, RRX#2;将 R1中的内容进行带扩展的循环右移两位后传送到 R0中。

其他

UXTW/UXTH/UXTB:零扩展single-word / half-word / byte
SXTW/SXTH/SXTB:符号扩展 single-word / half-word / byte


总结

以上就是今天要讲的内容,本文仅仅简单介绍了看懂汇编代码(由高级语言的编译器产生,且优化等级较低的,代码变形较小的机器指令反汇编形成的)需要了解的指令,而关于汇编代码还有非常多的内容,如需要更多的了解,请参阅更多的文献。

参考

十九、 ARM64汇编(一)
二十、ARM64汇编(二)
ARM的六大类指令集—LDR、LDRB、LDRH、LDM、STR、STRB、STRH、STM
ARM指令集–移位指令
ARM汇编指令 UXTW/UXTH/UXTB, SXTW/SXTH/SXTB

举报

相关推荐

0 条评论