汇编指令基础

1.EFLAGS

2.测试与比较

2.1 test

1
2
3
4
5
test rax, rbx
; 等价形式如下
push rax
and rax, rbx
pop rax
ASSEMBLY

2.2 cmp

1
2
3
4
5
cmp rax, rbx
; 等价形式如下
push rax
sub rax, rbx
pop rax
ASSEMBLY

3.跳转

3.1 JCC条件跳转

  • 短跳:2字节,1字节硬编码 + 1字节有符号偏移
  • 长跳:6字节,2字节硬编码 + 4字节有符号地址
指令 短跳 长跳 含义 EFLAGS
JZ/JE 0x74 0x0F 0x84 相等则跳转 ZF == 1
JNZ/JNE 0x75 0x0F 0x85 不相等则跳转 ZF == 0
JS 0x78 0x0F 0x88 负数则跳转 SF == 1
JNS 0x79 0x0F 0x89 正数则跳转 SF == 0
JP/JPE 0x7A 0x0F 0x8A 1出现次数为偶数则跳转 PF == 1
JNP/JPO 0x7B 0x0F 0x8B 1出现次数为奇数则跳转 PF == 0
JC/JB/JNAE 0x72 0x0F 0x82 无符号数 (进位)低于则跳转 CF == 1
JNC/JNB/JAE 0x73 0x0F 0x83 无符号数 (不进位)高于等于则跳转 CF == 0
JO 0x70 0x0F 0x80 有符号数 溢出则跳转 OF == 1
JNO 0x71 0x0F 0x81 有符号数 无溢出则跳转 OF == 0
JBE/JNA 0x76 0x0F 0x86 无符号数 低于等于则跳转 ZF == 1 || CF == 1
JNBE/JA 0x77 0x0F 0x87 无符号数 高于则跳转 ZF == 0 || CF == 0
JL/JNGE 0x7C 0x0F 0x8C 有符号数 小于则跳转 SF != OF
JNL/JGE 0x7D 0x0F 0x8D 有符号数 大于等于则跳转 SF == OF
JLE/JNG 0x7E 0x0F 0x8E 有符号数 小于等于则跳转 ZF != OF || ZF == 1
JNLE/JG 0x7F 0x0F 0x8F 有符号数 大于则跳转 SF == 0F && ZF == 0

3.2 CALL无条件跳转

1
E8 + 4字节有符号偏移:偏移CALL,最常见用法。
APACHE

跳转目标地址 = 当前指令地址 + 当前指令长度 + 4字节偏移

1
**FF 15 + 4字节:**间接CALL,长用于调用导入表中函数。
ASCIIDOC
  • x86:后面跟4字节内存地址。

    跳转目标地址 = ptr:[ 4字节内存地址 ]

  • x64:后面跟4字节偏移,再使用偏移计算公式得到4字节内存地址。

跳转目标地址 = ptr:[ 当前指令地址 + 当前指令长度 + 4字节偏移 ]

1
9A + 4字节地址 + 目标cs:绝对CALL,仅x86有效,可以跳转到绝对地址同时改变cs的值,far常用于实现调用门。
X86ASM

跳转目标地址 = 4字节地址、cs = 目标cs

3.3 JMP无条件跳转

1
EB + 1字节有符号偏移:偏移短跳转
APACHE

跳转目标地址 = 当前指令地址 + 当前指令长度 + 1字节偏移

1
E9 + 4字节有符号偏移:偏移近跳转
APACHE

跳转目标地址 = 当前指令地址 + 当前指令长度 + 4字节偏移

1
FF 25 + 4字节:间接跳转
APACHE
  • x86:后面跟4字节内存地址。

    跳转目标地址 = ptr:[ 4字节内存地址 ]

  • x64:后面跟4字节偏移,再使用偏移计算公式得到4字节内存地址。

跳转目标地址 = ptr:[ 当前指令地址 + 当前指令长度 + 4字节偏移 ]

1
EA + 4字节地址 + 目标cs:绝对跳转,仅x86有效,可以跳转到绝对地址同时改变cs的值,far常用于实现调用门。
X86ASM

跳转目标地址 = 4字节地址、cs = 目标cs

3.4 x64下的Inline HOOK

enter

1
2
3
4
5
6
7
8
9
10
11
12
13
方法1:12字节
mov rax, 0x123456789
jmp rax
方法2:12字节
mov rax, 0x123456789
push rax
ret
方法3:不破坏寄存器 13字节
push rax
mov rax, 0x123456789
jmp rax
```
pop rax
ASSEMBLY

ret

1
2
3
4
5
6
7
8
9
10
方法1:20字节
sub rsp, 0x8
mov dword ptr ss:[rsp], 0x1234
mov dword ptr ss:[rsp+0x4], 0x56789ABC
ret

方法2:14字节
push 0x12345678
mov dword ptr ss:[rsp+4], 0x9ABC
ret
ASSEMBLY

4.基本运算

4.1 除法

无符号:div + 除数

有符号:idiv + 除数,余数的符号与被除数相同。

被除数 除数 余数
ax reg/mem8 al ah
dx:ax reg/mem16 ax dx
edx:eax reg/mem32 eax edx
rdx:rax reg/mem64 rax rdx

4.2 乘法

单操作数:

​ 无符号:mul + 乘数

​ 有符号:imul + 乘数

被乘数 乘数 乘积
al reg/mem8 ax
ax reg/mem16 dx:ax
eax reg/mem32 edx:eax
rax reg/mem64 rdx:rax

对于无符号数,计算后如果dx/edx/rdx寄存器不为0,则CF位被置位。

对于有符号数,计算后如果dx/edx/rdx寄存器不为ax/eax/rax的符号扩展,则OF和CF位被置位。

双操作数:不支持8位,必须是有符号数。

1
2
3
imul reg1, reg2/mem/imm
; 等价形式如下
reg1 = reg1 * reg2/mem/imm
ASSEMBLY

三操作数:不支持8位,必须是有符号数。

1
2
3
imul reg1, reg2/mem, imm
; 等价形式如下
reg1 = reg2/mem * imm
ASSEMBLY

4.3 移位

逻辑:shl、shr

算数:sal、sar

逻辑左移与逻辑左移没区别,算数右移左边补符号位,逻辑右移坐标补0。右移除以2,左移乘以2。

5.数据传送

5.1 扩展传送

1
2
movsx:符号位扩展
movzx:0扩展
ASSEMBLY

5.2 隐式传送

1
2
3
4
5
6
7
8
9
10
movs_b/w/d/q
; 等价形式如下
if(DF == 0) ; cld
{
[edi++] = [esi++]
}
else ; std
{
[edi--] = [esi--]
}
ASSEMBLY

5.3 字符串获取

1
2
3
4
5
6
7
8
9
10
lods_b/w/d/q
; 等价形式如下
if(DF == 0) ; cld
{
eax = [esi++]
}
else ; std
{
eax = [esi--]
}
ASSEMBLY

5.4 字符串加载

1
2
3
4
5
6
7
8
9
10
stos_b/w/d/q
; 等价形式如下
if(DF == 0) ; cld
{
[edi++] = eax
}
else ; std
{
[edi--] = eax
}
ASSEMBLY

汇编指令基础
http://helloymf.github.io/2022/09/12/asm-basic/
作者
JNZ
许可协议