x86 汇编基础篇

x86 汇编基础篇

1. 进制

1.1 二进制与十六进制

  • 十六进制 : 方便阅读二进制, 由十六个符号组成(可以是任意符号), 逢十六进一
0123456789ABCDEF
0000000100100011010001010110011110001001101010111100110111101111

1.2 数据宽度

  • 由于受硬件的制约, 数据都有长度限制, 超过容量最大宽度的数据高位会被丢弃
  • 计算机仅存储数据, 数据是有符号或无符号取决于怎么解析
名字说明大小
BYTE字节8 bit
WORD16 bit
DWORD双字32 bit
QWORD四字64 bit

2. 寄存器

  • 用于保存运算使用的临时变量

2.1 32 位通用寄存器

  • 由于硬件限制, 大于寄存器宽度的数据, 高位会被丢弃
寄存器说明大小编号
EAX(Extended Accumulator Register)累加器, 通常用于运算和返回值0xFFFFFFFF0
ECX(Extended Count Register)计数, 通常用于位移、循环计数0xFFFFFFFF1
EDX(Extended Data Register)I/O 指针0xFFFFFFFF2
EBX(Extended Base Register)DS 段的数据指针0xFFFFFFFF3
ESP(Extended Stack Pointer Register)堆栈指针0xFFFFFFFF4
EBP(Extended Base Pointer Register)SS 段的数据指针0xFFFFFFFF5
ESI(Extended Source Index Register)字符串操作的源指针; SS 段的数据指针0xFFFFFFFF6
EDI(Extended Destination Index Register)字符串操作的目标指针; ES 段的数据指针0xFFFFFFFF7

在这里插入图片描述

2.2 标志寄存器

Flag说明
CF(Carry Flag)进位标志(最高位进位), 用于无符号数运算
PF(Parity Flag)奇偶标志, 操作结果的低 8 位中 1 的个数为偶数时, 值为 1
AF(Auxiliary Carry Flag)辅助进位标志
ZF(Zero Flag)零标志, 运算结果为 0 时, 值为 1
SF(Sign Flag)符号标志, 即运算结果(二进制)的最高位的值
TF(Trap Flag)陷阱标志, TF=1 时, CPU 处于单步执行方式(每执行一条指令就自动执行一次 1 号内部中断), 主要用于 Debug 中
DF(Interrupt Enable Flag)中断允许标志
DF(Direction Flag)方向标志, 字符串操作时, DF=0, 地址寄存器(SI,DI)的内容递增, DF=1 时, (SI,DI)的内容递减
OF(Overflow Flag)溢出标志, 用于有符号数运算
IOPL(I/O Privilege Level)I/O 特权标志(2 bit), 如果当前特权级别小于等于 IOPL 的值, 则 I/O 指令可执行, 否则将发生一个保持异常
NT(Nested Task)嵌套任务标志, 用于控制中断返回指令 IRET 的执行, NT=0, 执行常规的中断返回操作, 用堆栈中保存的值恢复 EFLAGS、CS 和 EIP; NT=1, 通过任务转换实现中断返回
RF(Restart Flag)重启标志, 用于控制是否接受调试故障, RF=0 表示接受
VM(Virtual 8086 Mode)虚拟 8086 方式标志, VM=1 表示处理器处于虚拟 8086 方式下的工作状态, 否则处理一般保护方式下的工作状态

在这里插入图片描述

# CF
mov eax, 0xFFFFFFFF         # 负 + 负 = 正, 说明有溢出
add eax, 0x1                # 让无符号数最高位进位, 观察 CF 位的变化


# PF
mov eax, 0x0                    
add eax, 0x1                # eax 中的二进制值 1 的数量为奇数, PF=0
add eax, 0x2                # eax 中的二进制值 1 的数量为偶数, PF=1


# AF
mov eax, 0x0000FFFF
add eax, 0x1                # 让第四个 F 进位

mov ax, 0x00FF              # 让第二个 F 进位
add ax, 0x1

mov al, 0x0F              
add al, 0x1


# ZF
mov eax, 0x2
sub eax, 0x2                # 运算结果为 0, ZF=1
xor eax, eax                # 将 eax 清零, 并且会修改 ZF=1


# SF
mov al, 0x7F                # 7F = 01111111
add al, 0x2                 # 结果为 10000001, SF=1(最高位的值) 


# OF
mov al, 0x8                 # 正 + 负, 有符号无符号都不溢出
add al, 0x8                 

mov al, 0x7F                # 正 + 正 = 负, 说明有溢出
add al, 2                   # 无符号不溢出 CF=0, 有符号溢出 OF=1

mov al, 0xFF
add al, 0x2                 # 无符号溢出 CF=1, 有符号不溢出 OF=0

mov al, 0xFE
add al, 0x80                # 无符号有符号都溢出 CF=1 OF=1


# DF
mov esi, 0x0018FF90
mov edi, 0x0018FF98
movs dword ptr es:[edi], dword ptr ds:[esi]   # DF=0, esi=esi+4, edi=edi+4

mov esi, 0x0018FF90
mov edi, 0x0018FF98
movs dword ptr es:[edi], dword ptr ds:[esi]   # DF=1, esi=esi-4, edi=edi-4

2.3 段寄存器

  • Intel 汇编与操作系统中讲解

3. 算术运算指令

  • r 表示通用寄存器, m 表示内存, imm 表示立即数

3.1 mov

  • 指令格式 : mov target, source, 两个操作数的宽度必须一致, target 和 source 不能同时为内存单元
mov r8, imm8
mov r16,imm16
mov r32,imm32

mov r/m8, r8
mov r/m16,r16
mov r/m32,r32

mov r8, r/m8
mov r16,r/m16
mov r32,r/m32
mov EAX, 12345678
mov AX, BBBB
mov AL, CC
mov AH, DD

3.2 add

add r/m8, imm8
add r/m16,imm16
add r/m32,imm32
add r/m32,imm8                              # target 宽度大于 source 宽度即可            

add r/m8, r8
add r/m16,r16
add r/m32,r32

add r8, m8
add r16,m16
add r32,m32

3.3 sub

sub r/m8, imm8
sub r/m16,imm16
sub r/m32,imm32
sub r/m32,imm8                  

sub r/m8, r8
sub r/m16,r16
sub r/m32,r32

sub r8, m8
sub r16,m16
sub r32,m32

3.4 adc

  • ADC : 带借位加法, target = target + source + CF, 两边不能同时为内存, 数据宽度要一样
# 指令格式
adc r/m, r/m/imm

mov al, 0xFF        
add al, 0x1         # 先让 CF=1
mov al, 0x1
mov cl, 0x2
adc al, cl          # AL=4, CF=0

3.5 sbb

  • ADC : 带借位加法, target = target - source - CF
# 指令格式
sbb r/m, r/m/imm

mov al, 0xFF        
add al, 0x1              # 先让 CF=1
mov al, 0x5
mov cl, 0x1
sbb al, cl               # AL=3, CF=0

3.6 xchg

  • xchg : 数据交换指令, 两边不能同时为内存
# 指令格式
xchg r/m, r/m

mov eax, 0x1
mov ebx, 0x2
xchg eax, ebx

xchg al, byte ptr ds:[0x18FF98]
xchg eax, dword ptr ds:[0x18FF98]

3.7 movs

  • movs : 用于移动两个内存之间的数据
movs byte ptr es:[edi], byte ptr ds:[esi]   
movs word ptr es:[edi], word ptr ds:[esi]
movs dword ptr es:[edi], dword ptr ds:[esi]

# 简写形式
movsb
movsw
movsd
mov esi, 0x0018FF90
mov edi, 0x0018FF94
movsb                        # 交换一个字节内存的值, 并将 esi 和 edi 值自动加 1(DF=0)或减 1(DF=1)
movsw                        # 交换两个字节内存的值, esi 和 edi 值自动加 2(DF=0)或减 2(DF=1)
movsd                        # 将 esi 指向的内存数据移动到 edi 指向的内存, esi 和 edi 值自动加 4(DF=0)或减 4(DF=1)

3.8 stos

  • stos : 将 AL/EX/EAX 的值存储到 [EDI] 指定的内存单元
stos byte ptr es:[edi] 
stos word ptr es:[edi]
stos dword ptr es:[edi]

# 简写形式
stosb
stosw
stosd
mov eax, 0x12345678
mov edi, 0x0018FF94
stosd                  # [edi] = 0x12345678, edi = DF == 0 ? (edi = edi+4) : (edi = edi-4)
stosw                  # [edi] = 0x5678, edi = DF == 0 ? (edi = edi+2) : (edi = edi-2)
stosb                  # [edi] = 0x78, edi = DF == 0 ? (edi = edi+1) : (edi = edi-1)

3.9 rep

  • rep : 重复执行指令 ecx 次
mov esi, 0x0018FF90
mov edi, 0x0018FF94

mov ecx, 0x10           
rep movsd               # 重复执行 movsd 10 次

mov ecx, 0x10               
rep stosd               # 重复执行 stosd 10 次

4. 条件判断指令

4.1 cmp

  • cmp : 用于比较两个值, 其结果只影响标志寄存器, 不会修改操作数; 运算指令会同时修改操作数和标志寄存器
  • ZF=1 表示相等, SF=1 表示第一个操作类小于第二个操作数
# 指令格式
cmp r/m, r/m/imm

mov eax, 0x100
mov ecx, 0x100
cmp eax, ecx             # ZF=1, SF=0
sub eax, ecx             # ZF=1, SF=0, eax=0
cmp eax, ecx             # ZF=0, SF=1

cmp al, byte ptr ds:[0x00401006]
cmp ax, word ptr ds:[0x00401006] 
cmp eax, dwrod ptr ds:[0x00401006]

4.2 test

  • test : 对两个操作数进行或操作, 结果不保存, 仅改变相应的标志位
test r/m, r/m/imm

mov eax, 0x100
test eax, eax          # ZF=0
sub eax, eax           # eax=0
test eax, eax          # ZF=1

5. 逻辑运算指令

5.1 and &

and r/m8, imm8
and r/m16,imm16
and r/m32,imm32
and r/m32,imm8                  

and r/m8, r8
and r/m16,r16
and r/m32,r32

and r8, m8
and r16,m16
and r32,m32

5.2 or |

or r/m8, imm8
or r/m16,imm16
or r/m32,imm32
or r/m32,imm8                  

or r/m8, r8
or r/m16,r16
or r/m32,r32

or r8, m8
or r16,m16
or r32,m32

5.3 xor ^

xor r/m8, imm8
xor r/m16,imm16
xor r/m32,imm32
xor r/m32,imm8                  

xor r/m8, r8
xor r/m16,r16
xor r/m32,r32

xor r8, m8
xor r16,m16
xor r32,m32

5.4 not !

not r/m8
not r/m16
not r/m32

6. 内存寻址

  • 每个内存单元的宽度为一个字节(byte), 每个内存单元都有其编号, 即内存地址

6.1 立即数寻址

# 写内存
mov BYTE  PTR DS:[0x18FF94], 0xA            # [] 表示该值是一个内存编号, 而不是立即数
mov WORD  PTR DS:[0x18FF98], 0xBB
mov DWORD PTR DS:[0x18FF9C], 0x12345678

# 读内存
mov EAX, BYTE PTR DS:[0x18FF94]
mov EAX, DWORD PRT DS:[0x18FF98]

# 获取内存编号
lea EAX, [0x18FF94]                         # 等同于 lea EAX, DWORD PTR DS:[0x18FF94]
lea EAX, [ESP+8]                            # 等同于 lea EAX, DWORD PTR DS:[ESP+8]

6.2 寄存器寻址

mov EBX, 0x18FF90

# 写内存
mov BYTE  PTR DS:[EBX], 0xA
mov WORD  PTR DS:[EBX], 0xBB
mov DWORD PTR DS:[EBX], 0x12345678

# 读内存
mov AL, BYTE PTR DS:[EBX]
mov AX, WORD PTR DS:[EBX]
mov EAX, DWORD PTR DS:[EBX]

# 获取内存编号
lea EAX, [EBX]

6.3 寄存器+偏移寻址

mov EBX, 0x18FF90

# 写内存
mov BYTE  PTR DS:[EBX+0xC], 0xA
mov WORD  PTR DS:[EBX+0xC], 0xBB
mov DWORD PTR DS:[EBX+0xC], 0x12345678

# 读内存
mov AL, BYTE PTR DS:[EBX+0xC]
mov AX, WORD PTR DS:[EBX+0xC]
mov EAX, DWORD PTR DS:[EBX+0xC]

# 获取内存编号
lea EAX, [EBX+0xC]

6.4 寄存器+寄存器*[1,2,4,8]寻址

mov EBX, 0x18FF90
mov ECX, 0x4

# 写内存
mov BYTE  PTR DS:[EBX+ECX*2], 0xA           # EBX+ECX*2 = 0x18FF98
mov WORD  PTR DS:[EBX+ECX*2], 0xBB
mov DWORD PTR DS:[EBX+ECX*2], 0x12345678

# 读内存
mov AL, BYTE PTR DS:[EBX+ECX*2]
mov AX, WORD PTR DS:[EBX+ECX*2]
mov EAX, DWORD PTR DS:[EBX+ECX*2]

# 获取内存编号
lea EAX, [EBX+ECX*2]

6.5 寄存器+寄存器*[1,2,4,8]+立即数寻址

mov EBX, 0x18FF90
mov ECX, 0x4

# 写内存
mov BYTE  PTR DS:[EBX+ECX*2+4], 0xA           # EBX+ECX*2+4 = 0x18FF9C
mov WORD  PTR DS:[EBX+ECX*2+4], 0xBB
mov DWORD PTR DS:[EBX+ECX*2+4], 0x12345678

# 读内存
mov AL, BYTE PTR DS:[EBX+ECX*2+4]
mov AX, WORD PTR DS:[EBX+ECX*2+4]
mov EAX, DWORD PTR DS:[EBX+ECX*2+4]

# 获取内存编号
lea EAX, [EBX+ECX*2+4]

7. 堆栈

  • 由于寄存器数量有限, 堆栈用于存储一些数据

7.1 堆栈模拟(满降栈)

mov EBX, 0x0018FF94                   # 表示栈底指针
mov EDX, 0x0018FF94                   # 表示栈顶指针


# 压栈
mov DWORD PTR DS:[EDX-4],0xAAAAAAAA   # 先放数据, 再移动栈顶指针(通过 sub 指令实现)
sub EDX, 4                            # 模拟满降栈((栈顶指针指向最后入栈的数据, 栈顶指针从高地址向低地址移动)

lea EDX, [EDX-4]                      # 先移动栈顶指针(通过 lea 指令实现), 再先放数据
mov DWORD PTR DS:[EDX],0xBBBBBBBB


# 堆栈寻址
mov ESI,DWORD PTR DS:[EBX-8]          # 通过栈底指针+偏移取值
mov ESI,DWORD PTR DS:[EDX+4]          # 通过栈顶指针+偏移取值


# 出栈
mov EAX, DWORD PTR DS:[edx]
add edx, 4

lea edx, [EDX+4]
mov eax, DWORD PTR DS:[edx-4]

7.2 push 和 pop

  • ESP : 栈顶指针
  • EBP : 栈底指针
push
# 立即数无论大小, ESP 都是移动 4 字节
push imm8       
push imm16
push imm32

push r16         # ESP 移动 2 字节
push r32

push m16         # ESP 移动 2 字节
push m32
pop
pop r16
pop r32

pop m16
pop m32
pushad 和 popad
  • pushad : 将所有通用寄存器压栈
  • popad : 从栈中弹出 8 个数据, 依次放到通用寄存器中
pushad 
mov eax, 0x1
mov ecx, 0x2
mov edx, 0x3
mov ebx, 0x4
mov esi, 0x5
mov edi, 0x6
popad

8. JCC(修改 EIP)

8.1 jmp

  • 无条件修改 EIP 的值
jmp r/imm

jmp 0x00401012                             # EIP=0x00401012
jmp short 0x00401012                       # 与上一条指令效果一样, short 表示跳转范围在 128 以内

8.2 call 和 retn

  • call : 修改 EIP 地址为目标地址, 并将返回地址压栈, 即 call 指令下一条指令地址
  • retn : 等同于 pop eip(非法指令, 仅用于理解)
Address    Hex dump      Command
00401000   E8 01000000   CALL 00401006     # 等同于 EIP=0x00401006, push 00401000+5(E8 03000000 长度为 5 字节)
00401005   B8 AAAAAAAA   MOV EAX,AAAAAAAA
0040100A   C3            RETN              # 等同于 pop eip, 即 EIP=0x00401005

8.3 条件跳转

指令说明条件
je jz结果相等则跳转ZF=1
jne jnz结果不相等则跳转ZF=0
js结果为负则跳转SF=1
jns结果为正则跳转SF=0
jp jpe结果中 1 的个数为奇数则跳转PF=1
jnp jpo结果中 1 的个数为偶数则跳转PF=0
jo结果溢出则跳转OF=1
jno结果不溢出则跳转OF=0
jb, jnae小于则跳转(无符号数)CF=1
jnb jae大于则跳转(无符号数)CF=0
jbe jna小于等于则跳转(无符号数)CF=1 or ZF=1
jnbe ja大于则跳转(无符号数)CF=0 and ZF=0
jl jnge小于则跳转(有符号数)SF!=OF
jnl jge大于等于跳转(有符号数)SF=OF
jle jng小于等于跳转(有符号数)ZF=1 or SF!=OF
jnle jg大于则跳转(有符号数)ZF=0 and SF=OF
mov eax, 0x100
cmp eax, 0x100
jz 0x00401006
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值