本文会整理在逆向中常见的指令汇总
目录
汇编符号
汇编指令的组成
mov
movzx
lea
xchg
加法指令
减法指令
带进位加法
带进位减法
自增自减
乘法运算
除法运算
and
or
xor
not
shl
shr
逻辑指令
字符串操作
STOS
REP
movss指令
eax 与 [eax] 区别
test指令
cmp指令
jle指令
jnz指令
cmovnz指令
在查询资料时,往往会遇见一些符号,需要对常见的符号先做一个了解
- reg:通用寄存器 reg8 reg16 reg32
- sreg:段寄存器
- imm:立即数 数字 123456 imm8 imm16 imm32
- mem:内存 mem8 mem16 mem32
汇编指令是由 操作码和操作数 组成的
操作数的类型:
mov指令的作用就是将源操作数赋值给目的操作数。
mov 目的操作数,源操作数
注意事项
mov eax,ebx
将源操作数(寄存器或内存位置)的内容复制到目标操作数(寄存器),并按照零扩展方式将值扩展到 16 或 32 位。转换后的值的大小取决于操作数大小属性。
mov eax,0xFFFF
低4为被FFFF覆盖,其它位置被0覆盖
将源操作数的地址传送到目标操作数的寄存器中
lea eax,dword ptr ds:[0x00BCFB64]
dword ptr 表示偏移量为4个字节,表示以0x00BCFB64向下偏移4个字节
执行操作:将目标操作数与源操作数的内容进行交换
xchg dword ptr ds:[0x00BCFB64], eax
EAX寄存器的值和0x00BCFB64并偏移4个字节的值进行交换。
add指令有两个操作数,add指令会把两个操作数相加,然后把结果存放在第一个操作数里面。操作数 的类型和mov指令一样
add eax,ecx
标志寄存器CF,x64dbg的解释
设置EAX为0xFF,令 al 加上 EAX.测试CF标志位变化
执行后,EAX被置为0
add eax,0x1
查看标志寄存器变化
用第一个操作数减去第二个操作数,结果存放到第一个操作数里面
sub eax,ecx
带进位的加法,和add加法的区别在于会在原来的基础上,加上进位标志。格式和操作数跟add一样。
mov dl,0;
mov al,0xFF;
add al,1;
adc dl,0;
结果
dl=0+0=0+1(进位标志)
dl=00000001
在原来的减法基础上,再减去进位标志
mov edx,7;
mov eax,1;
sub eax,2;
sbb edx,0;
结果
edx=7;
eax=1;
eax=1-2=0xFFFFFFFF = -1
edx=7-0-1=6
他们都只有一个操作数
inc eax
dec eax
无符号乘法
mov eax,0x12345;
mov ebx,0x10;
mul ebx;
乘法里面有两个东西,一个是乘数,一个是被乘数。 mul只有一个操作数,ebx是乘数,另外一个隐藏的操作数叫被乘数,这个操作数是隐藏的,也就是这 条指令执行的时候
mul ebx;=ebx=ebx*eax;
有符号乘法
mov eax.0x12345;
mov ebx,0x100000;
imul ebx,eax;
OF标志位发生了变化
此时注意OF位,会被置为1,这是因为计算结果 1234500000 ,已经超过了有符号位存储的范围了
补充:
imul ebx,eax,ecx;
第二个操作数和第三个操作数相乘,结果放到第一个操作数里面
无符号 div 有符号 idiv
mov eax,5
mov ebx,3
div ebx
div指令的除法,商放在eax里,余数放在edx里。 有符号除法几乎与无符号除法几乎完全一样。
将操作数1与操作数2进行按位与运算,结果存储到操作数1中
and eax,1 如果eax=0xFFFF,则结果eax=0001
将操作数1与操作数2进行按位或运算,结果存储到操作数1中
or eax,1 如果eax=0xFFF0,则结果eax=0xFFF1
将操作数1与操作数2进行按位异或运算,结果存储到操作数1中
xor eax,0x1F 如果eax=0xFFF0,则结果eax=0xFFEF
将目标操作数执行按位取反结果存储到目标操作数的位置
not eax; 如果eax=0xFF00,则结果eax=00FF
左移,等于*2
右移,等于/2
逻辑运算都不会得到运算结果,仅仅设置标志寄存器中的相应标志位,通常都是配合跳转指令,实现汇 编程序中的选择或者循环结构
cmp用于比较两个数的大小;test指令最常用的功能就是测试某一个寄存器的值是不是0
指令:cmp
操作数个数:2
操作数1:reg
操作数2:reg/mem/imm
指令结构:CMP 源操作数,源操作数
执行操作:用操作数1减去操作数2,并根据结果设置EFLAGS寄存器中的状态标志
例子:
cmp eax,1; 如果eax=0,比较之后由于最高位为1.因此符号位SF为1;
cmp eax,0; 如果eax=0,比较之后由于结果为零,因此零标志位ZF为1
指令:test
操作数个数:2
操作数1:reg
操作数2:reg/mem/imm
指令结构:TEST 源操作数,源操作数
执行操作:将操作数1和操作数2进行按位与运算,并根据结果设置SF ZF PF状态标志,然后丢弃结果
例子:
test eax,eax;
jz XXX
如果eax为零,设置ZF零标志为1,jz跳转。
要把内存中的一块区域复制从一个地方复制到另外一个地方。这种 操作我们称为串操作指令。
串操作指令用到的寄存器都是EDI和ESI,不能使用其他寄存器。ESI存储的是源地址,EDI存储的是目标 地址。
MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[ESI];------------------>简写:MOVSB
MOVS WORD PTR ES:[EDI],WORD PTR DS:[ESI];------------------>简写:MOVSW
MOVS DWORD PTR ES:[EDI],DWORD PTR DS:[ESI];----------------->简写:MOVSD
以C语言为例,EDI就相当于是dest,ESI就相当于是src,size,BYTE/WERD/DWORD就相当于是size
strcpy(dest,src,size);
MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[ESI]
movsb
当这条指令执行完成之后,从源地址复制了一个字节到目标地址;并且ESI和EDI的指针自动增加了1, 所以这种用来复制内存的方式是非常方便的。
如果想一次复制4个字节,可以用下面的指令
MOVS DWORD PTR ES:[EDI],DWORD PTR DS:[ESI];
关于DF标志位
测试DF为0时
从esi开始向后4个字节复制给edi,并且esi,edi都加4
测试DF为1时
从esi开始向后4个字节复制给edi,并且esi,edi都减4
EDI,ESI都是指向内存空间的一个地址
将AL/AX/EAX的值存储到EDI里。
和MOVS指令对比,STOS少了一个源寄存器,这个源寄存器EAX是默认省略的。
执行完成之后,EDI的值也会加或者减,加或者减取决于DF标志位。加多少取决于复制的操作数大小。
STOS BYTE PTR ES:[EDI]----->简写为STOSB
STOS WORD PTR ES:[EDI]----->简写为STOSW
STOS DWORD PTR ES:[EDI]---->简写为STOSD
此时DF标志位为1,所以地址减1,然后EAX的值复制了一个字节,也就是44。复制44的原因是因为小 端存储。
按ECX寄存器中指定的次数重复执行字符串指令
MOV ECX,0x10
REP MOVSD
REP STOSD
上面这些指令会让MOVSD和STOSD执行16次。这三条指令结合起来,就可以实现字符串拷贝的操作
用于将单精度浮点数(32 位)从源操作数移动到目标操作数
如果目标操作数是一个寄存器,那么该寄存器的低 32 位将被赋值。
在给浮点数赋值时,往往会这样操作
movss xmm0, ds:dword_417BE0
movss [ebp+var_C], xmm0
xmm0
是一个 128 位的 XMM 寄存器,ds
是一个段寄存器,指示要访问的内存段,dword_417BE0
是一个表示内存地址的符号引用,它指向存储单精度浮点数的位置,由417BE0h - 417BE4h这段内存空间(由低向高)取这段空间的值,这是小段存储,低位存地位,高位存高位
小数往往存储在内存某一位置,通过xmm0寄存器给到函数局部变量的位置
eax,就是往这个寄存器中写东西
[eax]:把eax存储的值视为地址,往这个地址写东西
"test" 指令是一种按位逻辑运算指令,其操作数是两个寄存器或内存单元的数据。两个操作数进行逐位逻辑与运算,并根据运算结果更新标志寄存器中的相应标志位,而不修改操作数的值;
对标志寄存器的影响:
零标志位(ZF):如果运算结果为零,则 ZF 置位(ZF = 1);否则复位(ZF = 0)。
符号标志位(SF):根据运算结果的最高位来设置 SF 的值。如果最高位为 1,则 SF 置位(SF = 1);否则复位(SF = 0)。
奇偶标志位(PF):根据运算结果中包含的二进制 1 的个数来设置 PF 的值。如果运算结果中包含偶数个 1,则 PF 置位(PF = 1);否则复位(PF = 0)。
"cmp" (Compare)指令是 x86 汇编语言中的比较指令,用于比较两个操作数的大小关系,并根据比较结果更新标志寄存器中的相应标志位。
cmp operand1, operand2
其中,operand1
和 operand2
是待比较的操作数。
cmp
指令执行时,会进行以下步骤:
operand1
的值与 operand2
的值进行比较。常见的比较指令结果和对应的标志位设置如下:
operand1
等于 operand2
,则 ZF 被置为 1;否则,ZF 被置为 0。operand1
小于 operand2
,则 SF 被置为 1;否则,SF 被置为 0。operand1
小于 operand2
),则 CF 被置为 1;否则,CF 被置为 0。需要注意的是,cmp
指令只进行比较操作,不会修改操作数的值。它主要用于在条件分支或循环中进行条件判断,根据比较结果来确定程序的执行路径。
"jle" 是 x86 汇编语言中的跳转指令,它根据标志寄存器中的 SF、ZF 和 OF 标志位的组合进行条件跳转。
"jle" 的含义是 "Jump if Less Than or Equal",即如果小于等于关系成立,则进行跳转。
具体的跳转条件如下:
"jnz" 是 x86 汇编语言中的条件跳转指令,它根据标志寄存器中的零标志位 (ZF) 进行跳转判断。
"jnz" 的含义是 "Jump if Not Zero",即如果零标志位为 0,则进行跳转。
具体的跳转条件如下:
"cmovnz" 的含义是 "Conditional Move if Not Zero",即如果零标志位为非零值,则执行移动操作。
"cmovnz" 是 x86 汇编语言中的条件移动指令,它根据标志寄存器中的零标志位 (ZF) 进行条件判断,并将数据从一个操作数移动到另一个操作数。
该指令将检查 ZF 标志位的值。如果 ZF = 0,则将 source 中的数据移动到 destination 中;如果 ZF = 1,则不进行移动,保持 destination 不变。
"cmovnz" 用于根据零标志位的状态来选择性地将数据从一个位置移动到另一个位置。它常用于简化逻辑或避免条件分支的情况。