一、JMP
-
jmp指令的作用:修改EIP的值,且jmp执行时只有eip发生变化
EIP是什么:EIP中的地址值就是CPU要读取指令的地址,但是jmp准确说没有跳转的作用,只是去修改EIP中的值,只有CPU才根据eip中的值去跳转,而且不是所有的指令都可以改EIP中的值,比如mov指令没法对EIP做修改
-
格式:
jmp short 立即数 #简写:jmp 立即数 jmp 寄存器如果转移的目标地址的范围在JMP指令所处地址的-128~+127字节范围之内,则会自动加上short,如果超过此范围再用short会报错
二、call
-
作用:修改EIP中的值,并且将call的下一跳指令的地址压入堆栈中!所以栈顶地址也会发生变化,即ESP会发生变化,此地址也称返回地址
-
格式:
CALL 地址A/寄存器 -
计算机是怎么知道下一跳指令的地址的:根据硬编码知识,不同的指令用编码表示有不同的字节,见下图。那么比如现在call指令的地址为0040116E,则在call执行之前,计算机就通过call的地址+call指令的硬编码长度5,把下一跳的地址算出来,并且将返回地址入栈
三、RETN
-
作用:修改EIP中的值,将栈顶地址中存的值—即返回地址出栈,并赋给EIP。因为要出栈,所以esp的值会+4,即栈顶指针下移
类似于指令
pop eip #将现在栈顶中的值出栈赋给eip,CPU根据eip中的返回地址值跳转到原来call函数的下面 -
一般RET和CALL指令时成对出现的
四、CMP
-
作用:只改变标志寄存器,不会修改任何一个操作数
-
原理:执行从目的操作数中减去源操作数的隐含减法操作(SUB),但是不对任何一个操作数做修改!只是根据相减的结果来改变零标志位的,当两个操作数相等的时候,零标志位置1
-
格式:
CMP EAX,ECX CMP AX,WORD PTR DS:[405000] #可以将内存中的值与寄存器作比较,但是数据宽度必须一致 CMP EAX,DWORD PTR DS:[405000] -
通过CMP执行后的标志寄存器来判断两个操作数的大小
-
如果比较的是两个无符号数,则零标志位ZF和进位标志位CF表示的两个操作数之间的关系
CMP结果 ZF CF 目的操作数 < 源操作数 0 1 目的操作数 > 源操作数 0 0 目的操作数 = 源操作数 1 0 -
如果比较的是两个有符号数,则符号标志位、零标志位和溢出标志位表示的两个操作数之间的关系
CMP结果 标志位 目的操作数 < 源操作数 SF ≠ OF 目的操作数 > 源操作数 SF=OF 目的操作数 = 源操作数 ZF=1
一般ZF位和SF位比较重要
-
五、TEST
-
作用:该指令在一定程序上和CMP指令类似,两个数值进行与AND操作,结果不保存,但是会改变相应标志位
-
格式:
TEST R/M,R/M/IMM #同样前面不能同时为操作数 -
常见用法:一般用来判断某寄存器值是否为0(一般看ZF标志寄存器判断)
test eax,eax #用来判断eax寄存器中值是否为0,如果为0,则最后ZF标志寄存器置为1因为实际上就是做与运算:eax and eax,如果eax中的值为0x00000000,则and运算结果还是全0,所以zf置为1;但是如果eax中的值某一位不为0,则结果就不为0
六、JCC指令
1.JE、JZ
-
JE:jmp equal;JZ:jmp zero(je和jz作用差不多)
-
作用:也是用来跳转的,但是是有条件的跳转,不像jmp是无条件的跳转
-
如何跳转:根据标志寄存器ZF位来判断是否跳转,如果执行此指令时的ZF零标志位为1,则跳转;否则不跳转继续执行后面的指令。简单来说就是结果相等就跳转
虽然一般je或者jz指令上面都会有比较相关的语句,因为cmp和je一般都和c语言的ifelse语句有关系,所以其实就是做一个比较,然后决定是否跳转到哪里执行
-
格式:
cmp eax,ecx #cmp会改变标志寄存器,当eax与ecx值一样,则ZF位为1 je 0x0019aaef #看此时标志寄存器ZF,如果为1则跳转到0x0019aaef这个地址执行;否则不跳转,继续往下执行
2.JNE、JNZ
- JNE:jmp if not equal;JNZ:jmp if not zero
- 作用:有条件的跳转,根据标志寄存器ZF位来决定是否跳转,如果ZF标志位为0,则跳转;否则不跳转。简单来说就是结果不相等就跳转
3.JS
-
JS:jmp if sign
-
作用:如果标志寄存器SF位为1,则跳转;否则不跳。即结果为负数则跳转
4.JNS
- JNS:jmp if not sign
- 作用:如果SF位为0,则跳转;否则不跳。即结果为非负则跳转
5.JP、JPE
-
JP:jmp if parity;JPE:jpm if parity even
-
作用:结果低8位中1的个数为偶数则跳转,即PF=1跳转
6.JNP、JPO
-
JNP:jmp if not parity;JPO:jump when parity flag is odd
-
作用:结果低8位中1的个数为奇数则跳转,即PF=0跳转
7.JO
- JO:jmp if overflow
- 作用:结果溢出了则跳转,即OF=1跳转
8.JNO
- 作用:结果没有溢出则跳转,即OF=0跳转
9.JB、JNAE
- JB:jmp if below;JNAE:jmp if not above or equal
- 作用:JB指令前面的cmp等比较指令中前面的数跟后面的数比较小于则跳转 (无符号数),即CF=1跳转
10.JNB、 JAE
- JNB:jmp if not below;JAE:jmp if above or equal
- 作用:大于等于则跳转 (无符号数),即CF=0跳转
11.JBE、JNA
-
JBE:jmp if below or equal;JNA:jmp if not above
-
作用:小于等于则跳转(无符号数),即CF=1或者ZF=1跳转
为什么JNB没有ZF=1条件?因为大于等于时,只需要根据CF就可以判断;但是JBE,如果小于出现借位的情况,此时CF=1,但是如果等于,则CF=0但是ZF=1,所以JBE,CF=1成立时跳转,CF=0时也不要着急,再看ZF,如果=1也可以跳转
12.JNBE、 JA
-
JNBE:jmp if not below or equal;JA:jmp if not above
-
作用:大于则跳转(无符号数),即CF=0并且ZF=0跳转
因为大于就表示小于等于,所以要CF=0和ZF=0同时满足
13.JL、JNGE
-
JL:jmp if less;JNGE:jmp if not greater or equal
-
作用:小于则跳转(有符号数),即SF ≠ OF跳转(注意这里的小于是指我们平时所说的有符号的数的大小,比如3小于5,-3小于-1,-3小于5等,就是我们平时写代码时if判断里面写的数,不是看计算机中存的二进制数大小哦!!!)
为什么SF ≠ OF?
- 如果比较的是两个正数,cmp比较会隐含一个sub操作,因为被减数小于减数,那么最后结果必定在左半球(即80到ff),则SF=1。再判断OF位:因为计算机会把减法当做加一个负数来处理,所以此时相当于一个正数加一个负数,则OF位永远为0,此时SF≠OF
- 如果比较的是两个负数,因为被减数要小于减数(90000000-a0000000),它们相减结果一定在左半球(即80到ff),所以SF为1;但是同样负数减负数相当于一个负数加一个正数,则OF永远为0
- 如果比较的是一个负数一个正数,因为要满足小于,所以只能是负数减正数,即
cmp 负数,正数,那么相当于两个负数相加,则OF位可能溢出也可能不溢出:当溢出时,OF=1,此时结果应该在右半球(0-7f),则SF=0;当没有溢出时,OF=0,此时结果应该在右半球(80-ff),则SF=1
综上所述,有符号数的小于跳转条件其实就是SF≠OF
14.JNL、JGE
-
JNL:jmp if not less;JGE:jmp if greater or equal
-
作用:大于等于则跳转 (有符号数),即SF=OF跳转
为什么SF=OF?
- 如果比较的是两个正数,cmp比较会隐含做一个sub操作,因为被减数大于等于减数,所以结果一定在右半球(0-7f),SF=0。而同理,正数减正数计算机会处理为正数加负数,永远不会溢出,则OF=0
- 如果比较的是两个负数,被减数大于等于减数(左半球任意取两个数),结果总是在右半球(0-7f),SF=0,且永远不会溢出,则OF=0
- 如果比较的是一个正数一个负数,被减数大于等于减数,则一定是正数减负数,相当于两个正数相加,则可能溢出可能不溢出:如果溢出,OF=1,结果一定在左半球,则SF=1;若干不溢出,OF=0,即如果一定在右半球,则SF=0
综上所述:SF=OF等价于大于等于(有符号数)
15.JLE、 JNG
- JLE:jmp if less or equal;JNG:jmp if not greater
- 作用:小于等于则跳转(有符号数),即SF≠OF或者ZF=1(在小于的基础上加一个等于即可)
16.JNLE、JG
-
JNLE:jmp if not less or equal;JG:jmp if greater
-
作用:大于则跳转(有符号数),即SF=OF并且ZF=0
七、有无符号的区别
-
假如现在:
eax=0xffff0000;ecx=0x7ffffffff cmp eax,ecx #此时执行完后,当成无符号:CF=0,ZF=0,SF=0;当成有符号:OF=1 如果当成无符号的数,那么JA 0x00401139应该跳转, ①使用JA的文字条件判断:大于则跳转,eax确实比ecx大,所以跳转 ②使用JA的条件判断:CF=0 and ZF=0,因为eax-ecx结果不等于0,且最高位没有发生借位,所以跳转 如果当成有符号的数,那么JG 0x00401129不跳转, ①使用JG文字条件判断:大于则跳转(有符号数),因为当成有符号数,所以ecx表示正数,eax表示负数。而eax-ecx是负数减正数,不满足大于的情况,所以不跳转 ②使用JG的条件判断:SF=OF and ZF=0,因为发生了溢出,所以OF=1,且结果不等于0,即ZF=0,所以不跳转
八、作业
-
CALL执行时堆栈有什么变化?EIP有变化吗?
-
有变化,堆栈的栈顶地址-4,并且将call指令下一条指令的地址压栈;EIP的值为call函数后面跟的地址
-
-
RET执行时堆栈有什么变化?EIP有变化吗?
-
有变化,栈顶中的值出栈,将值赋给EIP,堆栈的栈顶地址+4
-
-
使用汇编指令修改标志寄存器中的某个位的值,实现JCC的十六种跳转
要通过汇编指令的执行去影响标志位,能用CMP和TEST实现的优先考虑.
-
cmp影响cf位
mov eax,0x77777777 mov ecx,0x88888888 -
cmp影响pf位
mov eax,0x1 cmp eax,eax #结果为0 -
cmp影响af位
mov eax,0x2222 mov ecx,0x3333 cmp eax,ecx -
TEST影响zf位
mov eax,0 TEST eax,eax -
CMP影响SF位
mov eax,0xffffffff mov ecx,0x81111111 cmp eax,ecx -
cmp影响OF位
mov eax,0xc0000000 mov ecx,0x50000000 cmp eax,ecx
-
JE跳转(ZF=1)
mov eax,0 test eax,eax je 0x4010cf -
JNE跳转(ZF=0)
mov eax,0x1 mov ecx,0x2 cmp eax,ecx jne 0x4010e2 -
JS跳转(SF=1)
mov eax,0xffffffff mov ecx,0x11111111 cmp eax,ecx js 0x4010f1 -
JNS跳转(SF=0)
mov eax,0x2 mov ecx,0x1 cmp eax,ecx jns 0x401109 -
JP跳转(PF=1)
mov eax,0x4 mov ecx,0x1 cmp eax,ecx jp 0x40111e -
JNP跳转(PF=0)
mov eax,0x4 mov ecx,0x2 cmp eax,ecx jnp 0x40112f -
JO跳转(OF=1)
mov eax,0xaaaaaaaa mov ecx,0x55555555 cmp eax,ecx jo 0x401143 -
JNO跳转(OF=0)
mov eax,0xffffffff mov ecx,0x11111111 cmp eax,ecx jno 0x401154 -
JB跳转(CF=1)
mov eax,0x10000000 mov ecx,0x20000000 cmp eax,ecx jb 0x40116B -
JNB跳转(CF=0)
mov eax,0 test eax,eax jnb 0x40118a -
JBE跳转(CF=1 or ZF = 1)
mov eax,0x11111111 mov ecx,0x22222222 cmp eax,ecx jbe 0x4011b3mov eax,0 test eax,eax jbe 0x4011c2 -
JA跳转(CF=0 and ZF=0)
mov eax,0x20000000 mov ecx,0x10000000 cmp eax,ecx JA 0x4011D3 -
JL跳转(OF≠SF)
mov eax,0xaaaaaaaa mov ecx,0x11111111 cmp eax,ecx jl 0x4011ea记忆的话直接记住jmp if less,有符号数前操作数小于后操作数即可成立,两个正数就是小的在前,大的在后;两个负数就是小的在前(按照左半球的负数的排列,其实二进制值小的,表示的负数也是小的,以为ff总是最大的那个负整数-1,比它小的负数二进制表示出来也比它小);一个负数一个正数,则负数在前,正数在后即可。OF≠SF也是这么推出来的
-
JGE跳转(OF=SF)
mov eax,0x11111111 mov ecx,0xaaaaaaaa cmp eax,ecx jnl 0x4011f9OF=SF怎么想到的:因为无非就三种情况,正数减正数因为前大后小,则OF=0且SF=0;负数减负数因为前大后小,则OF=0且SF=0;正数减负数(因为前大后小),则溢出的时候,此时SF也=1,不溢出的时候,此时SF也等于0。所有情况中OF都等于SF
-
JLE跳转(OF≠SF or ZF=1)
mov eax,0xaaaaaaaa mov ecx,0x11111111 cmp eax,ecx jle 0x40120amov eax,0 test eax,eax jle 0x40121b -
JG跳转(OF=SF and ZF=0)
mov eax,0x11111111 mov eax,0xaaaaaaaa cmp eax,ecx jg 0x40122a
-
九、记忆技巧
虽然可以推出来这些有条件的跳转指令(JCC)的跳转条件是什么,但是如果逆向的时候还要现推就很费时间
- 先根据这些JCC的指令的缩写退出它的英文全称,就可以知道它满足什么条件才会跳转,比如JNL就是jmp if not less,表示如果上面一条cmp指令的前操作数不小于后操作数,即前操作数大于等于后操作数,则跳转。这个其实是推c语言if等判断语句,就是可以推出来c语言究竟在比较什么,但是如果我们想直接改变这些JCC指令的跳转与否,本质上是修改标志寄存器的某些关键位,通过直接修改这条JCC指令相关的标志寄存器某位来达到是否让它跳转的效果
- 所以此时我们就要记忆每一个JCC指令是哪些标志寄存器位影响的:前面几个JCC指令都好记,比较复杂的是后面四个有符号的JCC指令
- JL和JGE,一个小于,一个大于等于,刚好加起来是全集,所以这两个一起记:JL的跳转条件是OF≠SF,那么JGE的跳转条件就是OF=SF
- 接着因为JL记住了,所以JLE就记住了:加一个或等于0的条件即可,所以为OF≠SF or ZF=1
- 接着因为JGE记住了,所以JG就记住了:加一个不等于0的条件即可,所以为OF=SF and ZF=0
本文详细介绍了x86指令系统中JMP、CALL、RET、CMP、TEST以及JCC指令的用法和原理。JMP用于无条件跳转,CALL在跳转的同时保存返回地址,RET则从堆栈取出返回地址继续执行。CMP用于比较操作数,改变标志寄存器,TEST则进行与运算并更新标志寄存器。JCC指令如JE、JNE等根据标志寄存器的特定位进行有条件跳转。文章还探讨了无符号和有符号数比较的区别,并提供了相关作业和记忆技巧。
5852

被折叠的 条评论
为什么被折叠?



