汇编指令
汇编指令
NOP(No Operation)
机器码为0x90,没有操作数,执行这条指令时CPU什么都不干
PUSH
将数据压入栈中,指令格式为 push x。
x可取imm8,imm16,r/m16,r/m32,r/m64。(imm代表立即数,r代表寄存器,m代表内存)
栈指针寄存器ESP(RSP)自动递减。
例:push eax
POP
从栈中弹出数据,指令格式为pop x
x可取r/m16,r/m32,r/m64。
栈指针寄存器ESP(RSP)自动递增。
例:pop eax
MOV
将数据从源操作数移动到目的操作数,指令格式为mov x1,x2。意为从x2移动数据到x1。
x1,x2可以是:1、寄存器到寄存器
2、内存到寄存器,寄存器到内存
3、立即数到寄存器,立即数到内存
注:不能从内存到内存
LEA
用于载入有效地址,指令格式为lea x1,x2。
x1可以是r16/32/64,x2是内存地址,常用[]语法形式,意为引用求值。
常用于指针运算,有时用于数值计算。
例如:ebx=ox2,edx=0x1000
lea eax,[adx] (eax=edx)
ADD
计算加法操作,指令格式为add x1,x2
x1,x2可以是r/m16,r/m32,r/m64,x2还可以是立即数,但二者不能同时是内存操作数。
指令的计算结果将影响eflags寄存器,修改OF,SF,ZF,AF,PF,CF标志位。
例:
add esp,8(esp+=8)
SUB
计算减法操作,指令格式为sub x1,x2
x1,x2可以是r/m16,r/m32,r/m64,x2还可以是立即数,但二者不能同时是内存操作数。
指令的计算结果将影响eflags寄存器,修改OF,SF,ZF,AF,PF,CF标志位。
IML/MUL
IML用于实现有符号数的乘法运算。
三种指令格式:
imul r/m32;edx :eax=eax*r/m32
imul reg,r/m32 ;reg=reg*r/m32
imul reg,r/m32,imm ;reg=r/m32*imm
MUL指令是无符号乘法。
IDIV/DIV
DIV用于实现无符号数的除法运算。
两种指令格式:
无符号除法,div ax,r/m8 ;ax除以r/m8,al是商,ah是余数。
无符号除法,div eax,r/m32 ;edx:eax除以r/m32,eax是商,edx是余数
若被除数是DWORD,在指令执行前edx被置为0。
若除数是0,则抛出除零异常。
AND
实现操作数的逻辑与运算。指令格式为and x1,x2。
x1,x2可以是r/m16,r/m32,r/m64,x2还可以是立即数,但二者不能同时是内存操作数。
OR
实现操作数的逻辑或运算。指令格式为or x1,x2。
x1,x2可以是r/m16,r/m32,r/m64,x2还可以是立即数,但二者不能同时是内存操作数。
XOR
实现操作数的逻辑异或运算。指令格式为xor x1,x2。
x1,x2可以是r/m16,r/m32,r/m64,x2还可以是立即数,但二者不能同时是内存操作数。
注:XOR常用于清零运算,如xor eax,eax,效率高。
NOT
实现操作数的逻辑非运算。指令格式为not x。
x可以是r/m16,r/m32,r/m64
SHL
实现操作数的逻辑左移运算。
指令格式为shl x1,x2:
x1可取r/m16,r/m32,r/m64
x2可以是cl或者占用1字节的立即数,表示左移长度。
注:左移运算是乘2的高效实现方法,被用于乘法性能优化。
SHR
实现操作数的逻辑右移运算。
指令格式为shr x1,x2:
x1可取r/m16,r/m32,r/m64
x2可以是cl或者占用1字节的立即数,表示右移长度。
注:右移运算是除以2的高效实现方法,被用于除法性能优化。
JMP
改变 EIP(RIP)的值到目标地址,实现控制流跳转。指令格式为jmp x。
x可取 imm, r/m16, r/m32,r/m64 等。
jmp 目标地址x的主要形式:
1、短跳方式,使用相对地址
相对于jmp 指令的下一条指令,1字节(-128-127)范围
例如:0x00401021 处指令“jmp 0x00401028”为短跳方式,实际上,内存中并没有0x00401023 这一字符串或常量, 而是前跳5个字节
某些反汇编器将其标记为jmp short
2、近跳方式,使用相对地址
相对于jmp 指令的下一条指令,相对地址4字节范围
3、跳方式(段间转移)
4、绝对地址,使用直接地址
指令中硬编码方式的内存地址
5、绝对地址,使用间接地址
指令中寄存器或内存间接访问的地址
JCC
是一系列指令的集合,该指令在指定条件满足时将控制流转向目标地址。
指令格式为jcc x。
启动设置:
比较操作设置eflags寄存器的标志位。
如JNZ指令,当eflags的ZF未被设置(ZF=0)时, 指令满足跳转条件,执行控制转移到目的地址x
几个常见jcc指令的判断条件:
jz/je: if ZF == 1
jnz/jne: if ZF == 0
jle/jng: if ZF == 1 or SF != OF
jge/jnl: if SF == OF
jbe:if CF==1 or ZF ==1
jb: if CF==1
CMP
用于两个操作数的大小比较。指令格式为 cmp x1,x2。
cmp 指令通过将 x1 减去 x2 的方式实现比较,根据减法结果设置 eflags 的相应标志位,且将减法结果丢弃。
cmp <-> sub :
以相同的方式进行减法操作,以同样的方式设置 eflags 寄存器
但 sub 指令将计算结果存入x1,而 cmp 指令不保存计算结果
修改的 eflags 寄存器标志位有 CF,OF, SF,ZF,AF,PF 。
例: mov eax,1
mov bl,0x80
mov c1,0x9
cmp bl,cl
jb label1
mov eax,0
label1:
TEST
用于两个操作数的逻辑比较。指令格式为 test x1,x2。
test 指令通过计算两个操作数的逻辑与(&)位运算实现比较操作,根据与操作的结果设置 eflags 的相应标志位,且将与操作计算结果丢弃
修改的 eflags 寄存器标志位有 SF, ZF,PF。
例: mov eax.
mov ecx,0x9
test ecx, ecx
jz label1
mov eax,0
label :
CALL
将程序控制流转向一个子函数。指令格式为 call x。
x可取 r/m16, r/m32,r/m64, ptr16:16, ptr16:32。
启动设置:参数传递,使用函数调用约定
具体过程:
1.首先,将下一条指令的地址压入栈中
2.然后,改变 EIP(RIP)的值为 x
3.x可以是绝对地址,也可以是相对于 CALL 指令尾的相对地址
LEAVE
用于子函数退出时清理栈的操作,常与RET指令配套使用。
实际操作为:
1.设置 ESP(RSP)为 EBP(RBP) ;mov esp,ebp
2.弹出栈顶数据到 EBP(RBP) ;pop ebp
RET
将程序控制流从子函数返回到调用函数,
两种指令格式:
1、ret
将栈顶数据弹到 EIP(RIP)
一般用于 cdecl
2、ret x
将栈顶数据弹到 EIP(RIP)
ESP(RSP)+=x
一般用于 stdcall
REP前缀
rep 指令前缀表示重复执行某项操作。
例如 stos 是一条可单独执行的指令,表示存储字符到目标地址。rep stos 表示重复执行stos 指令,直到 ecx为 0。
类似的,还有 replods、rep movs 等指令。
REP STOS
实际操作为:
1.rep 指令前缀使用 ecx 作为其计数器,表示重复执行 stos 指令的次数,每次执行时,都会递减ecx,当ecx为0时,结束重复执行。每次存储一个字节或一个双字。
2.rep stos 表示存储 al 或 eax 的值到 [edi]
3.每次存储一个字节或一个双字后,edi 的值自增,指令的执行结果为存储到连续的内存空间
启动设置:
设置 edi 为存储的起始目标地址
设置 eax 或 al 为被存储的值
设置 ecx 为执行次数
REP MOVS
REP MOVS与 REP STOS 指令类似,重复执行 movs指令多次,直到 ecx 的值为 0.
实际操作为:
1.每次移动一个字节或一个双字,从[esi] 地址的字符移动到 [edi] 内存中
2.每次执行 movs 操作后,esi和 edi 的值都会被增加
启动设置:
设置 edi 为存储的起始目标地址
设置 esi 为存储的起始源地址
设置 ecx 为执行次数