2.17 JCC指令(jcn指令)
2.17 JCC指令
通过JCC指令可以实现有条件跳转,而标志寄存器是决定JCC指令是否实现跳转的必要条件。所以JCC指令与标志寄存器是沟通计算机的桥梁。
本节必须掌握的知识点:
JCC指令的概念
标识寄存器
JCC指令的运用
2.17.1【什么是JCC指令】
JCC指令是什么?JCC指令表示以”J”开头的一组指令,是JMP的衍生指令,JMP表示无条件跳转执行指令,而JCC指令是一组有条件跳转执行指令,称为”条件执行指令”。
为什么需要这些条件执行指令呢?在我们一个普通的程序中,充斥着很多逻辑判断。如一个卖票的程序,首先要判断是否有票,然后再询问买几张……当我们看到票数为0时,就知道票已经售罄,而计算机并不知道数字背后的意义,这个意义是我们去设定的。
计算机发展到现在可谓神通广大,但它其实是很笨的,你让它减1,它就减1,然后把结果告诉你,至于这个结果有什么意义,你不告诉它,它是没有任何的反应的。每卖一张票,我们需要知道还有没有票了,如果已经售罄,我们需要告诉买票人,已经没有票了;如果有票,就可以进行下一步操作。所以我们让计算机做这样的事情:每卖出一张票,剩余的票数如果是0,就让它执行打印”票已售罄”;如果不是0,让它执行另一段代码。这里逻辑判断就需要用到”条件执行指令”。
当我们做运算时,结果会影响标志寄存器的值,JCC指令就是通过标志寄存器里的值来进行条件判断是否跳转的。接下来先介绍标志寄存器。
2.17.2【标志寄存器】
标志寄存器:标志寄存器又称为程序状态和控制寄存器(Program Status and Control Register),主要用于记录当前的程序状态。
标志寄存器就是一个4字节的内存,而这个4字节的内存是在处理器(CPU)中,4字节包含32位,这32位中的每一位都有自己特定的意义,下面我们来看图2-17-1:
图2-17-1
标志寄存器:EFLAGS
在本节中我们介绍CF位:进位标志,PF位:奇偶标志,AF位:辅助进位标志,ZF位:零标志位,SF位:符号标志,TF位:单步标志,DF位:方向标志,至于剩下几位在Windows内核的相关课程
中会有介绍。
我们借用DTDebug.exe软件来看一下标志寄存器在软件中是怎么显示的。
在DTDebug.exe软件中打开飞鸽软件,如图2-17-1所示。
图2-17-1中,EFL代表标志寄存器,它后面存储的数据是00000202,转换为二进制为0000 0000 0000 0000 0000 0010 0000 0010,这些位通过结合JCC指令会有特定的意义,如果没有JCC指令,那标志寄存器存在就没有什么意义了。标志寄存器是运算指令和JCC指令的桥梁,为什么这么说呢?因为运算指令是修改标志寄存器,而JCC指令是根据标志寄存器来跳转的,标识寄存器让运算指令间接的控制JCC指令。下面详细介绍标志寄存器。
1.(第0位)进位标志CF(Carry Flag):如果运算结果的最高位产生了一个进位或借位,那么其值为1,否则其值为0。
在使用CF位时,首先要先确定数据宽度。
第一步:输入以下汇编指令输入到DTDebug.exe软件中实验,如图2-17-2所示:
(1) MOV AL,0xEF
ADD AL,2
(2) MOV AL,0xFE
ADD AL,2
第二步:按F8两次,观察CF位的变化,如图2-17-3所示。
按两次F8执行完后,EAX寄存器存储的数据发生了变化,变为了0x000000F1,但CF位并没有发生变化依然是0,因为我们的操作数是存在了低8位AL寄存器中,若想改变CF位,我们需要使AL存储的数据最高位产生进位。
第三步:为了更直观的体现出实验结果,我们手动修改EAX存储的数据,修改为0x00000000,如图2-17-4所示。
第四步:按F8两次,执行并观察AL存储的数据是否发生了进位及CF位是否有发生变化,如图2-17-5所示。
图2-17-5中,按两次F8执行完后,发现EAX寄存器变为了0,因为数据最高位发生了进位,CF位由0变成了1,发生了进位。
介绍几个与进位相关的其他指令:
ADC指令:带进位加法,如果CF位为1,将相加的结果再加1。
格式:ADC r/m,r/m/imm 两边宽度一致,且不能同时为内存。
例:
如果CF位为1,ADC AL,CL 的结果 = (ADD AL,CL 1),如图2-17-6输入指令,当前AL寄存器存储的数据为0x00,CL寄存器存储的数据为0x01;
单步按F8观察CF位和AL寄存器的变化,如图2-17-7。
F8执行完后发现,CF位变成了0,AL CL应该为0x1,但AL的值却变成了0x2,说明CF位为1时,在AL CL的基础上再加1。
我们实验观察当CF位为0时会出现什么结果,当前AL为0x2,CL为0x1,按F8执行,如图2-17-8所示。
按F8执行完后,CF位为0时,结果为AL CL=0x03。下面的SBB指令请大家自己动手实验,并自己总结得出结论。
SBB指令:带借位减法,如果CF位为1,将相减的结果再减1。
格式:ADC r/m,r/m/imm 两边宽度一致,且不能同时为内存。
例:
如果CF位为1,SBB AL,CL 的结果 = (SUB AL,CL -1);
如果CF位为0,SBB AL,CL 的结果 = (SUB AL,CL)。
2. (第2位)奇偶标志PF(Parity Flag):如果结果的最低有效字节(least-significant byte)包含偶数个1位则该位置为1,否则置0。
【注:最低有效字节:低8位】
【注:包含偶数个1位则该位置为1:指如果在低8位中1的个数为偶数则PF位则PF位变成1】
例:
MOV AL,0x02
ADD AL,0x01
第一步:输入以上指令,当前PF位为0,如图2-17-9所示。
第二步:按F8两次,如图2-17-10所示。
看图2-17-10,执行F8两次,第一次执行指令MOV AL,0x02,F8执行后AL存储的数据变为了0x02;第二次执行指令ADD AL,0x1执行后AL存储的数据变为了0x03,而此时PF位发生了变化,从0变成了1.为什么PF位会变成1哪?因为0x03转化成二进制位0011,1的个数为偶数,PF位时奇偶校验,当1的个数为偶数时则PF位为1,当1的个数为奇数时PF位为0.
了解:
利用PF位可进行奇偶校验检查:
【需要传输”11001110″,数据中含5个”1″,所以其奇校验位为”0″,同时把”11001110″传输给接收方,接收方收到数据后再一次计算奇偶性,”11001110″中仍然含有5个”1″,所以接收方计算出的奇校验位还是”0″,与发送方一致,表示在此次传输过程中未发送错误。】
3.(第4位)辅助进位标志AF[Auxiliary Carry Flag],如果算术操作在结果的第3位发生进位或借位则将该标志置1,否则清零。这个标志在BCD(binary-code decimal)算术运算中被使用。【注:平时较少用到,不在深入讲解。】
这里第3位是从0开始的,也就是说AF位只看第3位是否进位。例如:
第3位 第0位
(1)MOV AL,0x08
ADD AL,0x08
第一步:输入以上指令,如图2-17-11所示。
第二步:按F8两次,并观察AF位变化,如图2-17-12所示。
图2-17-12中,AF位发生了变化,变成了1,说明第三位发生进位,致使AF位发生变化。请大家动手实验下面的2组指令,并总结出AF位变化的结果
(2)MOV AX,0x80
ADD AX,0x80
(3)MOV EAX,0X8000
ADD AX,0X8000
3. (第6位)零标志ZF(Zero Flag),零标志ZF用来反映运算结果是否为0。如果运算结果为0,则ZF位的值为1,若运算结果不为0,则ZF位的值为0。
例:
(1) XOR EAX,EAX
第一步:输入指令,如图2-17-13所示。
第二步:按F8执行,并观察CF位变化,如图2-17-14。
图2-17-14中,指令XOR EAX,EAX是清零的操作,因为发生了清零操作导致ZF位发生变化。
下面一组指令自己动手实验并观察ZF位变化。
(2) MOV EAX,2
SUB EAX,2 //运算结果为0,所以ZF标志为1。
5、(第7位)符号标志位SF(Sign Flag),SF符号标志位用来反映运算结果的符号位,与运算结果的最高位相同。【主要看最高位,是0还是1。如果最高位为0,则SF位为0;如果最高位为1,则SF位为1;】
第7位 第0位
如果数据宽度为8位,SF的值与第7位相同;
如果数据宽度为16位,SF的值与第15位相同;
如果数据宽度为32位,SF的值与第31位相同。
例:
MOV AL,7F
ADD AL,2
第一步:输入指令,如图2-17-15所示。
图2-17-15中,SF位为0,我们汇编指令中设定的寄存器宽度大小为低8位。我们知道
0x7F转化为二进制为01111111,0x02转化成二进制为00000010。
我们算出来的最高位为1,那么SF位应该变为1,我们单步走看下结果。
第二步:按F8两次,执行并观察SF位变化。
看图2-17-16中,AL存储的数据为0x81转化为二进制10000001,最高位为1,看SF位也变为了1,和我们得到的结果一致。以上例题只验证了8位,而16位、32位希望大家动手实验验证SF位与运算结果的最高位有关系,并总结出结论。
6、 (第11位)溢出标志OF(Overflow Flag),OF溢出标志用于反映有符号数加减运算所得的结果是否溢出。如果运算结果超过当前运算位数所能表示的范围,则称为溢出,OF的值被置为1,否则,OF的值置0。
最高位进位CF与溢出OF的区别:
(1)CF进位标志表示无符号数运算结果是否超出范围;【可以解释为CF位为宽度溢出位,如果所用相应宽度的容器放不下,则CF位置为1,否则为0】
(2)OF溢出标志表示有符号数运算结果是否超出范围;
溢出主要是给有符号运算使用的,在有符号的运算中,有如下规律:
正 正 = 正 如果结果是负数,说明有溢出
负 负 = 负 如果结果为正数,说明有溢出
正 负 永远都不会溢出。
总结:符号位相同的两个数相加,最终结果符号位与运算前不同,OF置1,否则置0;符号位不同不会溢出,OF置0。
前面我们学了有符号数和无符号数,它们存储在计算机上是没有区别的,关键看使用者怎么使用。在使用无符号数时,我们不用考虑它的符号位,也就不用去管符号标志位SF位的值,但是SF位的值不管我们用不用它,都会默默地工作。
当我们把那个数当成有符号数时,由于它的最高位为符号位,所以判断它是否进位,由它的次高位决定。
例:
MOV AL,7f
ADD AL,2
第一步:输入指令,如图2-17-17所示。
图2-17-17中,AL为0x00,OF位为0,我们输入的汇编指令中,0x7F转化为二进制为01111111设定它是正数,0x02转化成二进制为00000010设定它是正数。
那麽根据我们之前总结出来的正 正 = 正 如果结果是负数,说明有溢出。我们单步执行看一下OF位是否有变化。
第二步:按F8两次,执行并观察OF位的变化,如图2-17-18所示。
图2-17-18中,AL的数据为0x81,0x81转化成二进制为10000001,OF位变为了1。
有时候OF位和CF位都会发生改变,自已动手做以下例题。
例:MOV AL,0xFE
ADD AL,80
由于我们设定的是正数,我们看次高位也就是第6位,如果次高位向最高位进1则OF位为1,如果次高位向最高位进0则OF位为0。
如果是有符号数,我们看次高位也就是第6位,如果次高位向最高位进1则OF位为0,如果次高位向最高位进0则OF位为1。
总结:由于计算机不知道什么是有符号什么是无符号,所以计算机不会通过符号来判断,所以计算机判断的是通过,最高位,我们总结了三点:
1、 最高位相同的两个数作运算,是不会改变OF位;
2、 最高位均为0的两个数做运算,次高位进1则OF位为1,次高位进0则OF位为0;
3、 最高位均为1的两个数做运算,次高位进1则OF位为0,次高位进0则OF位为1;
7、 (第10位)方向标志DF(Direction Flag):DF位在我们介绍MOVS指令、STOS指令中介绍了用法,这里我们总结一下。
在串处理指令中,控制每次操作后ESI,EDI的增减。
如果DF位为1时,字符串指令(MOVS、STOS)处理字符串从高地址到低地址,ESI、EDI的数据自减。
如果DF位为0时,字符串指令(MOVS、STOS)处理字符串从低地址到高地址,ESI、EDI的数据自增。
STD(Set Director)指令用来设置DF(方向标志位)置为1,CLD(Clear Director)指令用来设置DF(方向标志位)置为0。
常用的标志寄存器我们已经介绍完了,为了验证”运算指令是修改标志寄存器,而JCC指令是根据标志寄存器来跳转的,标识寄存器让运算指令间接的控制JCC指令。”我们接下来结合运算指令介绍JCC指令的运用。
说到JCC指令我们不得不提CMP指令和TEST指令。为什么要介绍这两个指令哪?因为CMP指令、TEST指令执行后的结果需要JCC指令来表现出来。
首先介绍CMP指令,CMP指令进行比较两个操作数大小的指令,CMP指令的功能相当于减法指令,只是不保存结果;
CMP指令的格式:CMP 操作对象1,操作对象2 ;详细说明操作对象1减去操作对象2,但相减的结果不影响操作对象,它影响EFL(标志寄存器)的CF位,ZF位,OF位,AF位,PF位。
例:
MOV EAX, 10
MOV EBX, 10
CMP EAX, EBX ;相减求出结果,为0,将1存入ZF
TEST指令在每对操作对象的对应数据位之间进行隐含的”与”操作,并设置标志位,但不修改操作对象。
TEST指令的格式:TEST 操作对象1,操作对象2 ;详细说明操作对象1与操作对象2之间的进行按位与运算,并设置标志位,但不修改操作对象,它影响EFL(标志寄存器)的CF位,ZF位,OF位,PF位。
TEST ECX,ECX (检测ECX是否是0)
我们今天要介绍的JCC指令如下图2-17-19所示。
首先介绍第一条,JE指令它的英文全称为Jump if equal,中文意思为如果相等则跳转;JZ指令它的英文全称为Jump if zero,中文意思为如果为0则跳转;紧跟着ZF=1,这条是满足条件。当满足ZF=1时,则JE或JZ指令跳转。
格式:
1. JZ/JE R32/M32/IMM32 。
第一步:借用DTDebug.exe软件打开飞鸽软件,并输入以下指令,如图2-17-20所示。
MOV EAX,0x10
MOV EBX,0x10
CMP EAX,EBX
JE 0x772301E0
看图2-17-20中,JE 0x772301E0这行指令的前面,有一个向下的箭头,一直指到内存地址0x772301E0这一行。如果我们执行JE指令,若是满足JE指令则直接跳到内存地址0x772301E0这一行,若是不满足JE指令则不会执行到内存地址0x772301E0。使JE指令跳转满足条件是ZF=1。
第二步:按F8两次,如图2-17-21所示。
第三步:按F8,执行CMP指令,观察寄存器EAX、EBX有没有变化和标识寄存器变化。
执行完CMP指令,影响了PF位、ZF位使他们变成了1,但EAX 、EBX并没有发生变化,只是EAX、EBX里存储的数据相减后为0,满足JE相等。所以执行JE指令会跳转到内存地址0x772301E0。
第四步:按F8执行JE指令,看是否会跳转到内存地址0x772301E0吗?还会不会改变ZF位哪?如图2-17-23所示。
看图2-17-23所示,当ZF位等于1时则JE指令则跳转,当执行CMP指令时修改了ZF位所以,满足JE指令跳转。
动手实验一下例题,并观察标志位的变化。
1、
MOV EAX,0x10
MOV EBX,0x10
CMP EAX,EBX
JZ 0x772301E0(0x772301E0这个跳转地址根据实验情况而定)
2、
MOV EAX,0x10
MOV EBX,0x20
CMP EAX,EBX
JZ 0x772301E0(0x772301E0这个跳转地址根据实验情况而定)
第二条指令,JNZ指令它的英文全称为Jump if not zero,中文意思为如果不为0则跳转;JNE指令它的英文全称为Jump if not equal,中文意思为如果不相等则跳转;紧跟着ZF=0,这条是满足条件。当满足ZF=0时,则JNZ或JNE指令跳转。
格式:
JNZ/JNE R32/M32/IMM32 。
第一步:借用DTDebug.exe软件打开飞鸽软件,并输入以下指令,如图2-17-24所示。
MOV EAX,0x10
MOV EBX,0x20
CMP EAX,EBX
JNZ 0x772301E0
看图2-17-24中,JNZ 0x772301E0这行指令的前面,有一个向下的箭头,一直指到内存地址0x772301E0这一行。如果我们执行JNZ指令,若是满足JNZ指令则直接跳到内存地址0x772301E0这一行,若是不满足JNZ则不会执行到内存地址0x772301E0。使JNZ指令跳转满足条件是ZF=0。
第二步:按F8两次,如图2-17-25所示。
第三步:按F8,执行CMP指令,观察寄存器EAX、EBX有没有变化和标识寄存器变化,如图2-17-26所示。
执行完CMP指令,影响了PF位、CF位、SF位使他们变成了1,但EAX 、EBX并没有发生变化,只是EAX、EBX里存储的数据相减后不为0,ZF=0满足JNZ指令跳转。所以执行JNZ指令会跳转到内存地址0x772301E0。
第四步:按F8执行JNZ指令,看是否会跳转到内存地址0x772301E0吗?还会不会改变ZF位哪?如图2-17-27所示。
看图2-17-27所示,当ZF位等于0时则JNZ指令则跳转,当执行CMP指令时并没有修改了ZF位所以,ZF位为0满足JNZ指令跳转。
动手实验一下例题,并观察标志位的变化。
1、
MOV EAX,0x10
MOV EBX,0x20
CMP EAX,EBX
JNE 0x772301E0(0x772301E0这个跳转地址根据实验情况而定)
图2-17-19中剩余14条的JCC指令与我们介绍的第一条JZ/JE指令和第二条JNZ/JNE指令是一样的,只要满足相应指令的条件,则对应的指令会做相应的跳转功能。熟练掌握图2-17-19,自己多动手实验。
例题:
1、
MOV EAX,0x10
MOV ECX,0x09
CMP EAX,EBX
JS 0x12345678(0x12345678这个跳转地址根据实验情况而定)
2、
MOV EAX,0x10
MOV ECX,0x09
CMP EAX,EBX
JNS 0x12345678(0x12345678这个跳转地址根据实验情况而定)
3、
MOV EAX,0x10
TEST EAX,EAX
JP 0x12345678(0x12345678这个跳转地址根据实验情况而定)
4、
MOV AL,0x10
ADD AL,0xFF
JO 0x12345678(0x12345678这个跳转地址根据实验情况而定)
5、
MOV EAX,0x10
MOV ECX,0x09
CMP EAX,EBX
JNL 0x12345678(0x12345678这个跳转地址根据实验情况而定)
……
以上是部分JCC相关的练习,希望大家能自己多练习,总结出自己对图2-17-19中JCC指令的认识。
练习:
1、 执行0x80-0x81后,CF位是0还是1哪?
2、 说出TEST指令和CMP指令有什么区别?
3、 TEST EAX,EBX 与 AND EAX,EBX 这两条指令是运行的原理是一样的吗?哪里不一样?为什么?
4、 自己多找资料多做练习,达到能熟练运用JCC指令。