JCC(Jump on Condition Code)指的是条件跳转指令,c++中的就是if-else, while, for 等分支循环条件判断的逻辑。它包括很多指令集,各自都不太一样,接下来我尽量将每一个指令的c++ 源码和汇编代码结合起来看,加深学习映像。
学习这章之前还是要了解 EFL 标记寄存器的知识: 汇编学习之《标志寄存器》-CSDN博客
以上标志寄存器部分重点看下 CF,ZF,SF,OF,PF.
理解下面的意思:
cmp x,y
x -y ==> 影响 CF, ZF 标记位
情况1 x == y , 计算结果是0, 那么 ZF == 1
情况2 x > y, 计算结果不是0,那么 ZF == 0
情况3 x < y , 计算结果不是0,但是会发生错位,所以 ZF != 0 and CF == 1
JE/JZ (ZF ==1 )
测试:cmp + je ==> c++ != 运算
je: jump if equal
je: jump if zero
判断 ZF ==1 (cmp 计算的结果是0,两个值相等情况)
je 和 jz 在OD上是一样的, 你可以尝试输入jz,但是OD会将其替换成JE
汇编代码
mov DWORD PTR [ebp-0xc], 0x1
mov DWORD PTR [ebp-0x10],0x2
mov eax,DWORD PTR [ebp-0xc]
cmp eax,DWORD PTR [ebp-0x10]
je 0x401661 <main()+81> //变量1 == 变量2则跳转走,c++代码就必须是 变量1 != 变量2
我们解释下上面汇编的代码
第一行: 分配了一个变量地址是ebp-0xc,并赋值是1
第二行: 分配了一个变量地址是ebp-0x10,并赋值是2.
第三行: 将第一个地址的值赋值给EAX 累计寄存器
第四行: EAX累计寄存器的值(也就是第一个变量的值)减去第二个变量的值。如果为0,则EFL的ZF标记就会被设置成1,否则为0
第五行: 判断ZF标记是否是1,如果是则调整到main函数的0x401661位置,准备清理资源退出。
c++ 代码
这里可以对照这个c++代码
#include <iostream>
int main() {
// JE / ZF 比较相等
int a = 1; //mov DWORD PTR [ebp-0xc], 0x1
int b = 2; //mov DWORD PTR [ebp-0x10],0x2
//mov eax,DWORD PTR [ebp-0xc]
//cmp eax,DWORD PTR [ebp-0x10]//je 0x401661 <main()+81>
if(a != b) {
std::cout << "a == b" << std::endl;
}
return 0;
}
上面的例子我们先看了汇编语言, 大家应该会发现一个问题, 汇编中跳转的地方指令是je, 也就是比较的两个对象相等(cmp 指令结果是0, zf==1)情况就跳转走了。
JNE/JNZ (ZF == 0 使用c++ ==)
测试:cmp + jne/jnz => c++ == 运算
jne: jump if not equal
jnz: jump is not zero
就是判断 ZF == 0
jne/jnz 是满足zf ==0就跳转,也就是cmp运算结果不是0,也就是两个变量是一定不相等(a != b)就会跳转, c++分支想执行就必须是和a !=0 条件相反, 那么c++ 代码就必须变成 a == 0
修改后c++ 代码
对应的汇编代码:
可以将JE/JZ 例子中的c++代码改成a == b 就可以看到了。
JB/JNAE/JC (CF == 1 )
测试:cmp + JB/JNAE/JC ==> c++ >= 运算
jb: jump if below (无符号比较 低于)
jnae: jump not above or equal
jc: jump if is carry
判断 CF == 1
同以上汇编反向推导c++代码:
第一步: 定义一个变量,赋值为2, 我们定义变量名称为a
第二步: 定义一个变量, 赋值为1 我们定义变量名称为b
第三步: 比较两个变量
但是怎么写呢? 我先看看 汇编指令是jb ,按照这个跳转指令的的规则,它是判断CF ==1。如果发生了进位或则错位, CF == 1那么就满足条件,就跳转到mian函数401661的地址处。
要满足这个要求,就必须是变量a小于变量b(a < b),这样减法cmp计算才会发生借位。c++代码这里也就是一个判断条件,返回过来它是不希望跳转走,是希望顺序执行的,那么c++代码就必须和刚刚汇编跳转指令相反,也就是a < b相反, 那么就是 a >= b
以下是c+代码
JNB / JAE / JNC (CF == 0)
测试:cmp + JNB / JAE / JNC == > c++ < 运算
jnb: jump if not below
jae: jump if above or equal
jnc: jump if not carry
判断 CF == 0
这就简单思考下,我c++代码要怎么改,才能验证 JNB/JAE的指令呢?
首先CF == 0, 说明 cmp 指令没有发生借位,也就是 a >= b 这种情况,那么就会跳转执行,我们c++代码要执行分支内的代码,就是不希望跳转走,所以就必须相反, 那么就是和 a >= b条件相反,也就是a < b, 那么我改动代码验证下,看看c++代码条件比较语句变成a < b后, 汇编指令是否是 JNB / JAE / JNC.
改动的c++ 代码
汇编代码确认
以上符合预期
JBE / JNA (CF == 1 or ZF == 1 )
测试:cmp + jbe/jna ==> c++ > 运算
jbe: jump if above or equal <=
jna: jump if not above 不大于
判断 CF == 1 or ZF == 1
汇编运算
c++ 代码
JA / JNBE (CF == 0 and ZF == 0)
测试:cmp + ja/jnbe ==> c++ <= 运算
ja: jump if above >
jnbe: jump if not below or equal 不小于不等于
判断 CF == 0 and ZF == 0
汇编代码:
c++代码:
JL / JNGE (SF ≠ OF)
jl: jump if less
jnge: jump if not greater or equal
sf: 当前计算语句是负数,sf =1
of: 当前计算语句发生了溢出 of =1
判断 SF ≠ OF 不同情况分析:
情况1 sf == 1 and of ==0
SF == 1 计算结果为负数, OF == 0 没有发生溢出
a = -5, b = 3
a - 3 = -8 sf =1 of=0, 但是没有发生溢出。
c++ 代码
这里按照上面我们的理解, 汇编要满足小于就跳转走,那c++ 代码要反着写,变成 >=
对应的汇编
情况2: sf == 0 and of == 1
sf == 0 计算结果不是负数, 但是 of ==1 意思是发生了溢出
在看下面代码前,思考下,什么情况下 cmp 减法操作计算结果不是负数,但是发生了溢出?
以32为举例: 32位有符号的整数表示的数据返回是-2147483648--2147483647
a = -2147483648, b = 1
a - b = 2147483647 //负溢出了 但是计算结果是正数
所以: sf == 0 and of == 1
c++代码:
对应汇编:
JNL / JGE (SF = OF)
jl: jump if not less
jnge: jump if greater and equal
sf: 当前计算语句是负数,sf =1
of: 当前计算语句发生了溢出 of =1
判断 SF = OF
情况1 结果是负数,并且发生溢出
a = 5, b = 6
cmp a,b ==> sf =1, of =1
c++ 代码
汇编代码:
情况2: 计算结果不是负数,且没有发生溢出。
JLE / JNG ( SF ≠ OF or ZF == 1)
jle: jump if less or equal
jng: jump if not greater
判断 (SF ≠ OF) or ZF == 1
情况1 sf == 1 and of ==0
SF == 1 计算结果为负数, OF == 0 没有发生溢出
a = -5, b = 3
a - 3 = -8 sf =1 of=0, 但是没有发生溢出。
c++ 代码
汇编
情况2 ZF == 1
c++
汇编:
JG / JNLE ( SF == OF and ZF == 0)
jg: jump if greater
jnle: jump if not less or equal
判断 (SF == OF) and ZF == 0
计算结果是负数, 并且发生溢出
a = 2147483647 b = -1
cmp a,b
汇编:
JO (OF = =1)
jo: jump if overflow
判断 OF = =1
溢出情况比较好验证,但是反推c++ 代码我还没有实现,后面生深入了后在来补充。
JNO (OF == 0)
jo: jump if not overflow
判断 OF == 0
反推c++ 代码我还没有实现,后面生深入了后在来补充。
JS (SF == 1)
js: jump if sign
判断 SF == 1
反推c++ 代码我还没有实现,后面生深入了后在来补充。
JNS (SF == 0)
jns: jump if not sign
判断 SF == 0
反推c++ 代码我还没有实现,后面生深入了后在来补充。
JP / JPE (PF ==1)
jp: jump if parity (奇偶校验事件)
jpe: jump if parity Event (奇偶校验事件)
判断 PF ==1 表示数据里面的二进制数,1的个数是偶数的情况
JNP / JPO ( PF == 0)
jnp: jump if not parity
jpo: jump if pariry odd (奇数)
判断 PF == 0 表示数据里面的二进制数, 1的个数是奇数
上一篇: 汇编学习之《jmp, nop指令》
下一篇:汇编学习之《call, return指令》