常见的汇编指令
常见指令
NOP
NOP
:No Operation,不执行任何动作的指令,只消耗 CPU 时钟;- NOP 的机器码是
90
; - NOP 是空指令,就是没有操作,无操作;
- 使用
NOP
来填充指令执行后多出来的字节,而不是用00
来填充;
PUSH 压入堆栈
PUSH 的用法:
PUSH 0x1 / 0xF
:把十六进制数字压入堆栈顶部;PUSH EAX / EBX
:把指定寄存器中的数据压入堆栈顶部;PUSH [00401000]
:把指定内存地址中的数据压入堆栈顶部;
POP 弹出堆栈
POP 的用法:
POP EAX / EBX
:将堆栈顶部的内容弹出并放入指定的寄存器;
PUSHAD
- 通用寄存器指:EAX / ECX / EDX / EBX / ESP / EBP / ESI / EDI;
- PUSHAD 保护现场 / 备份现场,将通用寄存器的数据自上而下依次压入堆栈,最后一个寄存器的数据处于堆栈顶部;
- PUSHAD 是 32 位操作;
PUSHAD
相当于是PUSH EAX, PUSH ECX, PUSH EDX, PUSH EBX, PUSH ESP, PUSH EBP, PUSH ESI, PUSH EDI
八条命令的合集;
POPAD
- 通用寄存器指:EAX / ECX / EDX / EBX / ESP / EBP / ESI / EDI;
- POPAD 还原现场,将堆栈顶部的数据依次弹出并自下而上依次填充到通用寄存器;
- POPAD 是 32 位操作;
POPAD
相当于是POP EDI, POP ESI, POP EBP, POP ESP, POP EBX, POP EDX, POP ECX, POP EAX
八条命令的合集;
PUSHA
- PUSHA 和 PUSHAD 功能相同,用于备份现场;
- PUSHA 是 16 位操作;
PUSHA
相当于是PUSH AX, PUSH CX, PUSH DX, PUSH BX, PUSH SP, PUSH BP, PUSH SI, PUSH DI
八条命令的合集;
POPA
- POPA 和 POPAD 功能相同,用于还原现场;
- POPA 是 16 位操作;
POPA
相当于是POP DI, POP SI, POP BP, POP SP, POP BX, POP DX, POP CX, POP AX
八条命令的合集;
PUSHF
- 将标志寄存器的值压栈;
POPF
- 从栈中弹出数据,送入标志寄存器;
MOV
- MOV 本意为
move,移动
,但功能相当于复制/赋值; MOV EAX, ECX
:两个操作数的长度必须相同;MOV EAX, ECX
:复制ECX(第二个参数)
的数据到EAX(第一个参数)
,4 字节操作;MOV AX, CX
:复制CX(第二个参数)
的数据到AX(第一个参数)
,2 字节操作;MOV AL, CL
:复制CL(第二个参数)
的数据到AL(第一个参数)
,1 字节操作;MOV [00402000], EAX
:复制EAX(第二个参数)
的数据到[指定内存地址](第一个参数)
,4 字节操作;MOV [00402000], AX
:复制AX(第二个参数)
的数据到[指定内存地址](第一个参数)
,2 字节操作;MOV [00402000], AH
:复制AH(第二个参数)
的数据到[指定内存地址](第一个参数)
,1 字节操作;
MOVSX
MOVSX EAX, CX
:复制CX(第二个参数)
的数据到EAX(第一个参数)
,如果EAX(第一个参数)
的长度比CX(第二个参数)
长,剩余的长度用CX(第二个参数)
的符号位填充;- 正数用 0 填充,负数用 F 填充;
MOVZX
MOVZX EAX, CX
:复制CX(第二个参数)
的数据到EAX(第一个参数)
,如果EAX(第一个参数)
的长度比CX(第二个参数)
长,剩余的长度用 0 填充;- 带 0 扩展传送命令;
LEA
- LEA:复制
第二个参数的内存地址
或第二个参数运算后的内存地址
到第一个参数
; LEA EAX, [00401000]
:第一个参数必须是通用寄存器,第二个参数必须是内存地址;LEA EAX, [ECX + 16]
:第一个参数必须是通用寄存器,第二个参数必须是内存地址;- LEA 是取地址指令,仅操作地址,如:
LEA EAX, DWORD PTR DS:[ECX+38]
这里虽然有括号,但不会获取 ECX+38 指向内存的值,只计算 ECX+38 的值;
XCHG 互换 / 交换
XCHG EAX, ECX
:第一个参数可以是通用寄存器或内存地址,第二个参数必须是通用寄存器;XCHG [00401000], ECX
:第一个参数可以是通用寄存器或内存地址,第二个参数必须是通用寄存器;
SHR 二进制右移
- 将一个寄存器或内存单元中的数据向右位移;
- 将最后移出的一位写入 CF 中;
- 最高位用 0 补充;
- 如果移动位数大于 1 时,必须将要移动的位数放在 CL 中;
SHL 二进制左移
- 将一个寄存器或内存单元中的数据向左移位;
- 将最后移出的一位写入 CF 中;
- 最低位用 0 补充;
- 如果移动位数大于 1 时,必须将要移动的位数放在 CL 中;
CLD
- 用来操作方向标志位 DF,CLD 使 DF 复位,即让 DF = 00;
STD
- 用来操作方向标志位 DF,STD 使 DF 置位,即让 DF = 01;
数学指令
ADD / SUB | 加法 / 减法
ADD / SUB
的返回结果放在第一个参数中;
ADC 带进位的加法
ADC EAX, ECX
=EAX + ECX + 0 | 1
:两个参数累加并且加上进位标识符CF
的值为最终结果;
SBB 带进位的减法
SBB EAX, ECX
=EAX - ECX - 0 | 1
:两个参数相减并且减去进位标识符CF
的值为最终结果;
INC / DEC | 自增 / 自减
INC EAX
:每执行一次该指令,参数的值自增 1;DEC EAX
:每执行一次该指令,参数的值自减 1;- 可以操作寄存器,也可以操作内存单元;
MUL 无符号乘法
- 无符号乘法只有一个操作数,另一个操作数默认为 EAX(AX | AL);
MUL ECX
:MUL CL | CX | ECX
默认和AL | AX | EAX
做乘法运算,运算结果分别存放到AH:AL | DX:AX | EDX:EAX
中,其中AH | DX | EDX
存放高位,AL | AX | EAX
存放低位;
DIV 无符号除法
DIV CL
:默认的被除数为EAX
,如果CL(除数)
是 8 位,那么商
放在AL
中,余数
放在AH
中;DIV CX
:默认的被除数为EAX
,如果CX(除数)
是 16 位,那么商
放在AX
中,余数
放在DX
中;DIV ECX
:默认的被除数为EAX
,如果ECX(除数)
是 32 位,那么商
放在EAX
中,余数
放在EDX
中;
IMUL 有符号乘法
- 立即数:自然数;
- 单操作数:
IMUL CL | CX | ECX
默认和AL | AX | EAX
做乘法运算,运算结果分别存放到AH:AL | DX:AX | EDX:EAX
中,其中AH | DX | EDX
存放高位,AL | AX | EAX
存放低位; - 双操作数:
IMUL AX, CX
:第一个参数必须是通用寄存器,第二个参数可以是通用寄存器、内存地址或立即数,运算结果存放到第一个参数中; - 三操作数:
IMUL AX, CX, 0x2
:第一个参数必须是通用寄存器,第二个参数可以是通用寄存器或内存地址,第三个参数必须是立即数,将第二个参数和第三个参数的运算结果存放到第一个参数中;
IDIV 有符号除法
IDIV CL
:默认的被除数为EAX
,如果CL(除数)
是 8 位,那么商
放在AL
中,余数
放在AH
中;IDIV CX
:默认的被除数为EAX
,如果CX(除数)
是 16 位,那么商
放在AX
中,余数
放在DX
中;IDIV ECX
:默认的被除数为EAX
,如果ECX(除数)
是 32 位,那么商
放在EAX
中,余数
放在EDX
中;- 双操作数和三操作数的做法与 IMUL 类似;
XADD 先交换后相加
XADD EAX, ECX
:先交换两个参数的值,然后进行加法运算,运算结果保存在第一个参数中;- 这个指令其实是 XCHG 和 ADD 两个简单指令的组合;
NEG 取反
NEG EAX
:操作数符号取反;
逻辑指令
AND / OR / XOR / NOT
- 全部以二进制形式进行比较(操作);
AND EAX, ECX
:双 1 为 1,否则为 0;OR EAX, ECX
:逢 1 为 1,否则为 0;XOR EAX, ECX
:不同为 1,相同为 0;NOT EAX
:二进制取反(按位取反);
比较指令
CMP
CMP EAX, ECX
:两个参数相减进行比较,如果运算结果为 0,则ZF(0 标志位)
为 1;- 通用寄存器、内存地址和立即数之间可以相互比较;
- 相当于 SUB 指令,但相减的结果不保存,只影响 0 标志位,当两个参数相等时,0 标志位置 1;
TEST
TEST EAX, EAX
:判断 EAX 自身是否为 0;- 如果运算结果为 0,且 ZF(0 标志位)为 1,则说明 EAX 自身为 0;
- 与
AND
相同,仅改变标志位而不改变操作数的值,若 2 个操作数中的一个为 0,则运算结果为 0,ZF 置 1;
跳转指令
JMP 无条件跳转
JMP 00401018
:无条件跳转;
JE / JZ
ZF(0 标志位)
为 1 则跳转;- 结果为 0 则跳转;
JNE / JNZ
ZF(0 标志位)
为 0 则跳转;- 结果不为 0 则跳转;
- 与
JE / JZ
相反;
JS
SF(符号位标志位)
为 1 则跳转;- 结果为负则跳转;
JNS
SF(符号位标志位)
为 0 则跳转;- 结果不为负则跳转;
- 与
JS
相反;
JP / JPE
PF(奇偶标志位)
为 1 则跳转;- 结果中 1 的个数为偶数则跳转;
JNP / JNPE / JPO
PF(奇偶标志位)
为 0 则跳转;- 结果中 1 的个数为奇数则跳转;
- 与
JP / JPE
相反;
JO
OF(溢出标志位)
为 1 则跳转;- 结果溢出则跳转;
JNO
OF(溢出标志位)
为 0 则跳转;- 结果没有溢出则跳转;
- 与
JO
相反;
JB /JNAE
CF(进位 / 借位 标志位)
为 1 则跳转;- 小于则跳转(无符号数);
- JB 不关注符号位,只关注无符号位的运算;
JNB / JAE
CF(进位 / 借位 标志位)
为 0 则跳转;- 大于等于则跳转(无符号数);
- 与
JB
相反;
JBE / JNA
OF(溢出标志位)为 1
或ZF(0 标志位)为 1
则跳转;- 小于等于则跳转(无符号数);
JNBE / JA
OF(溢出标志位)为 0
并且ZF(0 标志位)为 0
则跳转(都为 0);- 大于则跳转(无符号数);
- 与
JBE
相反;
JL / JNGE
- 小于则跳转(有符号数);
- 与 JB 不同的是,JL 关注有符号位的运算;
- JL 会忽略
CF(进位 / 借位 标志位)
的变化;
JNL / JGE
- 大于等于则跳转(有符号数);
- 与
JL
相反;
JLE / JNG
- 小于等于则跳转(有符号数);
JNLE / JG
- 大于则跳转(有符号数);
转移指令
转移指令就是可以控制 CPU 执行内存中某处代码的指令,也称为跳转指令;
LOOP
1 | cs:code |
LOOP 相当于
1
2
3
4
5; XOR CX, CX
; MOV CX, 6
DEC CX
TEST CX, CX ; CMP CX, 0
JNZ [DEC CX 所在的地址]与以上指令不同的是:LOOP 执行时第一次循环计数器不会自减;
约定俗成:使用 CX 作为计数器;
CALL / RETN
CALL 可以理解为 CALLBACK;
CALL 会执行一个子程序,可以理解为执行了一个函数;
CALL 指令会进行两步操作,首先将当前 IP 或 CS 和 IP 压栈,然后转移;
RET 在 CALL 所执行的子程序中时,会返回该 CALL 所在的主程序,并继续向下执行;
RET 与其他指令单独使用时,和 JMP 的功能相同;
RET 指令用栈中的数据,修改 IP 的内容,从而实现近转移;
1
POP IP
RETF 指令用栈中的数据,修改 CS 和 IP 的内容,从而实现远转移;
1
2POP IP
POP CS
寻址方式
- 直接寻址:指令后面直接写出此次运算使用的地址,称为直接寻址;
- 间接寻址:只有执行到某一行指令才能知道此次运算使用的地址,称为间接寻址;