一、PUSH xxxxx指令和ESP的关系
ESP的值,是由PUSH 指令后面的操作数的宽度决定的。也就是xxxxx的宽度,假如PUSH 后面跟的是一个立即数,由于整数是四字节,所以esp-4。
假如PUSH 后面跟的是容器,那么就看这个容器的宽度。
例:假如目前ESP的值是FFFFFFFF
当PUSH 1 这条指令执行后,ESP=FFFFFFFB,因为1是个立即数,只要PUSH后面是立即数,那么ESP的值就会减4
例:假如目前ESP的值是FFFFFFFF
当PUSH ax 这条指令执行后,ESP=FFFFFFFD,因为AX是个寄存器,该寄存器一共是16位,也就是2个字节,所以ESP的值就会减2
例:假如目前ESP的值是FFFFFFFF
当PUSH eax 这条指令执行后,ESP=FFFFFFFB,因为EAX是个寄存器,该寄存器一共是32位,也就是4个字节,所以ESP的值就会减4
例:假如目前ESP的值是FFFFFFFF
当PUSH DWORD PTR DS:[XXXXXX] 这条指令执行后,ESP=FFFFFFFB,因为DWORD宽度为4个字节,所以ESP的值就会减4
例:假如目前ESP的值是FFFFFFFF
当PUSH WORD PTR DS:[XXXXXX] 这条指令执行后,ESP=FFFFFFFD,因为DWORD宽度为2个字节,所以ESP的值就会减2
二、PUSHAD,POPAD指令
PUSHAD指令压入32位寄存器,其入栈顺序是:EAX,ECX,EDX,EBX,ESP,EBP,ESI,EDI .
也就是将8个通用寄存器EAX,ECX,EDX,EBX,ESP,EBP,ESI,EDI 全部压入堆栈,作用是保护堆栈。
当你执行完毕PUSHAD指令后,可以对寄存器进行任意操作,操作完毕后,只需要再执行POPAD指令,即可还原刚才压入的8个通用寄存器的值。
三、标志寄存器(EFLAGS)
标志寄存器在OD中体现形式就是下图的EFL,EFL的值其实就是CF,PF,AF,ZF,SF,TF,IF,DF,OF这几个标志综合表现形式。
具体是怎么拆分的呢:
例如图中的EFL的值是213,那么换算成2进制的话就是
2:0010
1:0001
3:0011
合起来就应该是001000010011
a、【CF】最高位进位标志(Carry flag):如果运算结果最高位产生了一个进位或者借位,那么其值为1,否则为0
例:
MOV AX,0xFFFF
ADD AX,1
最高位为F,如果再加1就会进位,所以这两句指令执行完毕后CF标志应该是1
执行前:
执行后:
这里有个现象就是,EAX为什么不是00010000,因为我们操作的是AX,就算AX存储超过最大限制,也不会越位去改变其他寄存器的值。
b、【PF】奇偶标志(Parity Flag):奇偶标志PF,用于反映运算结果中“1”的个数的奇偶性,如果“1”的个数为偶数,那么PF为1,否则为0
例1:
MOV AL,3
ADD AL,1
其结果AL应该等于4=0100,0100中1的个数为奇数,所以PF标志应该为0.
执行前:
执行后:
例2:
MOV AL,3
ADD AL,2
其结果为5=0101,0101中“1”的个数为偶数,所以P标志为应该为1
执行前:
执行后:
c、【AF】辅助进位标志(Auxiliary Carry Flag)
在发生下列情况时,辅助进位AF的标志会被置为1,否则其值为0.
(1)、在字操作时,发生低字节向高字节进位或借位时
(2)、在字节操作时,发生低4位向高四位进位或借位时
例1:字操作,也就是16位,双字节操作
MOV AX,0xEEFF
ADD AX,1
因为倒数第二个F有进位,所以A标志为1
执行前
执行后:
例2:
MOV EAX,0xEEEEFFFF
ADD EAX,1
因为倒数第四个F发生了进位,所以标志A的值为1
执行前:
执行后:
d、【ZF】零标志(Zero Flag):零标志ZF用来反映运算结果是否为0,如果运算结果为0,其值为1。在判断运算结果为0时,可借用此标志位。
例:
XOR EAX,EAX
因为XOR指令的特性,当与自身异或操作时,其值肯定为0,所以这句语句可以理解为EAX清零,但是他和MOV EAX,0 有区别,就是在ZF标志位。
MOV EAX,0并不会影响ZF标志位,但XOR EAX,EAX就会将ZF置为1
执行前
执行后:
e、【SF】符号标志(Sign Flag):符号标志SF用来反映运算结果的符号位,它与运算结果的最高位相同。
例1:
MOV AL,0x7F
ADD AL,1
因为结果为80,最高位为8,二进制形式为1000,所以SF应该为1
执行前:
执行后:
例2:
MOV AL,7E
ADD AL,1
因为结果为7F,最高位为7,二进制形式为0111,所以SF应该为0
执行前:
执行后:
f、【OF】溢出标志(Overflow Flag):溢出标志OF用于反映有符号数加减运算所得的结果是否溢出
如果运算结果超过当前位数所能表示的范围,则称为溢出,OF被置为1,否则OF为0
OF与CF的区别(溢出与最高位进位的区别):
CF表示无符号数运算结果是否超过范围。
OF表示有符号数运算是否超过范围。
溢出主要针对有符号数之间运算的结果,在有符号数运算中有如下规律:
正数+正数 如果结果是负数,则说明溢出
负数+负数 如果结果是正数,则说明溢出
正数+负数永远不可能溢出
例如7E+7E=FC,FC已经在负数区间了,证明溢出
执行前:
执行后:
例如 FE+FE=1FC 结果已经超过负数区间了,证明溢出
执行前:
执行后:
总结:逆向时,该关注CF还是OF,取决逆向者对两个操作数希望执行的是有符号运算还是无符号运算。
如果是有符号,则关注OF,无符号则关注CF。
g、【DF】方向标志:其值决定了下文中第六点,第七点这样的指令结束后,edi,esi的增长方向。
如果DF=0,指令执行完毕后ESI和EDI都增加对应的宽度
如果DF=1,指令执行完毕后ESI和EDI都减少对应的宽度
四、ADC指令:带进位加法,和ADD类似,但是相加的时候会把CF标志位的值也加进去
格式:ADC R/M,R/M/IMM 两边不能同时为内存,宽带要一致
例如:
ADC AL,CL
ADC BYTE PTR DS:[0x12ffff],2
ADC BYTE PTD DS:[0X12FFC4],AL
五、SBB指令:带借位减法,和SUB类似,但是相减的时候会再减去一个CF标志位的值
格式:SBB R/M R/M/IMM 两边不能同为内存,宽度要一致
例如:
ADC AL,CL
ADC BYTE PTR DS:[0x12ffff],2
ADC BYTE PTD DS:[0X12FFC4],AL
六、XCHG指令:交换数据,
格式:XCHG R/M 两边不能同为内存,也不能同位立即数,宽度要一致
例如:
XCHG AL,CL
XCHG DOWRD PTR DS:[0x12FFC4],EAX
XCHG BYTE PTR DS:[0x12FFC4],AL
七、MOVS指令:移动数据 内存-内存
把ESI的地址指向的值移动到EDI地址指向的值,执行完毕后ESI EDI会进行自加或自减,具体是自加还是自减,受DF标志位影响
BYTE/WORD/DWORD
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
通常MOVS指令可以用于字符串复制。
八、STOS指令:将AL/AX/EAX的值存放到[EDI]指定的内存单元,具体是使用哪个寄存器,看STOS指令后面的宽度
执行完毕后ESI EDI会进行自加或自减,具体是自加还是自减,受DF标志位影响
例:
STOS BYTE PTR ES:[EDI] 简写为STOSB
STOS WORD PTR ES:[EDI] 简写为STOSW
STOS DWORD PTR ES:[EDI] 简写为STOSD
九、REP指令,按计数寄存器中(ECX)中指定的次数重复执行字符串指令
MOV ECX,10
REP MOVSD