前言
本次是该系列的的第0x16篇,这一个片段很神奇,看不出来是具体做啥的,通过网上搜索其他人的解答,也很难找到答案(至少我是没找到),这里通过整理在不同输入下的输出的值的规律进行功能分析,若有师傅明白这是干嘛的,还望留言告知,在此谢过了。
实验环境:
- Windows10 + VS2022 + masm
0x16
代码片段链接:xorpd | xchg rax,rax 0x16
xor rax,rbx
xor rbx,rcx
mov rsi,rax
add rsi,rbx
cmovc rax,rbx
xor rax,rbx
cmp rax,rsi
代码分析
这里的cmovc是条件赋值指令,当进位标志位有值时,进行赋值操作,在这里就是保证超过边界的值无效
接下来对代码进行分析
手算模拟1:
rax = 1 0001
rbx = 2 0010
rcx = 4 0100
rax = rax ^ rbx = 0011
rbx = rbx ^ rcx = 0110
rsi = rax = 0011
rsi = rsi + rbx = 0011 + 0110 = 1001
rax = rax ^ rbx = 0101
本质上是计算了两个数进行比较,这两个数分别是:(rax ^ rbx)
和(rbx ^ rcx)
进行相加和异或操作后的结果
手算模拟2:
rax = 0101 = 0x5
rbx = 1011 = 0xB
rcx = 0011 = 0x3
(rax ^ rbx) = 01110 = 0xe
(rbx ^ rcx) = 01000 = 0x8
(rax ^ rbx) + ( rbx ^ rcx) = 10110 = 0x16
(rax ^ rbx) ^ ( rbx ^ rcx) = 00110 = 0x6
使用不同的输入来归纳整理,这里最后进行比较的操作是cmp,cmp的结果是影响标志位,影响等同于sub指令,结果一共有三种可能:大于0,小于0,等于0
这里到cmp指令的时候的两个操作数,一个是两数相加,一个是两数异或,所以这里会计算临时值:两数异或的结果-两数相加的结果,结果一定是非正数,对于cmp指令来说,通常是要判断结果为0的情况,故使用C语言整理一组数据来看看,C代码如下:
int main(int argv)
{
for (size_t i = 0; i < 5;i++)
{
for (size_t j = 0; j < 5;j++)
{
for (size_t k = 0; k < 5; k++)
{
int op1 = i ^ j;
int op2 = j ^ k;
if ((op1 + op2) != (op1 ^ op2) ) {
printf("rax = %d, rbx = %d, rcx = %d ", i, j, k);
printf("rax ^ rbx = %d ", op1);
printf("rbx ^ rcx = %d ", op2);
printf("op1 + op2 = %02d ", op1 + op2);
printf("op1 ^ op2 = %02d ", op1 ^ op2);
}
}
}
}
return 0;
}
整理结果得到:
rax = 0, rbx = 0, rcx = 0 | rax ^ rbx = 0 rbx ^ rcx = 0
rax = 0, rbx = 0, rcx = 1 | rax ^ rbx = 0 rbx ^ rcx = 1
rax = 0, rbx = 0, rcx = 2 | rax ^ rbx = 0 rbx ^ rcx = 2
rax = 0, rbx = 0, rcx = 3 | rax ^ rbx = 0 rbx ^ rcx = 3
rax = 0, rbx = 0, rcx = 4 | rax ^ rbx = 0 rbx ^ rcx = 4
rax = 0, rbx = 1, rcx = 1 | rax ^ rbx = 1 rbx ^ rcx = 0
rax = 0, rbx = 1, rcx = 3 | rax ^ rbx = 1 rbx ^ rcx = 2
rax = 0, rbx = 2, rcx = 2 | rax ^ rbx = 2 rbx ^ rcx = 0
rax = 0, rbx = 2, rcx = 3 | rax ^ rbx = 2 rbx ^ rcx = 1
rax = 0, rbx = 3, rcx = 3 | rax ^ rbx = 3 rbx ^ rcx = 0
rax = 0, rbx = 4, rcx = 4 | rax ^ rbx = 4 rbx ^ rcx = 0
rax = 1, rbx = 0, rcx = 0 | rax ^ rbx = 1 rbx ^ rcx = 0
rax = 1, rbx = 0, rcx = 2 | rax ^ rbx = 1 rbx ^ rcx = 2
rax = 1, rbx = 0, rcx = 4 | rax ^ rbx = 1 rbx ^ rcx = 4
rax = 1, rbx = 1, rcx = 0 | rax ^ rbx = 0 rbx ^ rcx = 1
rax = 1, rbx = 1, rcx = 1 | rax ^ rbx = 0 rbx ^ rcx = 0
rax = 1, rbx = 1, rcx = 2 | rax ^ rbx = 0 rbx ^ rcx = 3
rax = 1, rbx = 1, rcx = 3 | rax ^ rbx = 0 rbx ^ rcx = 2
rax = 1, rbx = 1, rcx = 4 | rax ^ rbx = 0 rbx ^ rcx = 5
rax = 1, rbx = 2, rcx = 2 | rax ^ rbx = 3 rbx ^ rcx = 0
rax = 1, rbx = 3, rcx = 2 | rax ^ rbx = 2 rbx ^ rcx = 1
rax = 1, rbx = 3, rcx = 3 | rax ^ rbx = 2 rbx ^ rcx = 0
rax = 1, rbx = 4, rcx = 4 | rax ^ rbx = 5 rbx ^ rcx = 0
rax = 2, rbx = 0, rcx = 0 | rax ^ rbx = 2 rbx ^ rcx = 0
rax = 2, rbx = 0, rcx = 1 | rax ^ rbx = 2 rbx ^ rcx = 1
rax = 2, rbx = 0, rcx = 4 | rax ^ rbx = 2 rbx ^ rcx = 4
rax = 2, rbx = 1, rcx = 1 | rax ^ rbx = 3 rbx ^ rcx = 0
rax = 2, rbx = 2, rcx = 0 | rax ^ rbx = 0 rbx ^ rcx = 2
rax = 2, rbx = 2, rcx = 1 | rax ^ rbx = 0 rbx ^ rcx = 3
rax = 2, rbx = 2, rcx = 2 | rax ^ rbx = 0 rbx ^ rcx = 0
rax = 2, rbx = 2, rcx = 3 | rax ^ rbx = 0 rbx ^ rcx = 1
rax = 2, rbx = 2, rcx = 4 | rax ^ rbx = 0 rbx ^ rcx = 6
rax = 2, rbx = 3, rcx = 1 | rax ^ rbx = 1 rbx ^ rcx = 2
rax = 2, rbx = 3, rcx = 3 | rax ^ rbx = 1 rbx ^ rcx = 0
rax = 2, rbx = 4, rcx = 4 | rax ^ rbx = 6 rbx ^ rcx = 0
rax = 3, rbx = 0, rcx = 0 | rax ^ rbx = 3 rbx ^ rcx = 0
rax = 3, rbx = 0, rcx = 4 | rax ^ rbx = 3 rbx ^ rcx = 4
rax = 3, rbx = 1, rcx = 0 | rax ^ rbx = 2 rbx ^ rcx = 1
rax = 3, rbx = 1, rcx = 1 | rax ^ rbx = 2 rbx ^ rcx = 0
rax = 3, rbx = 1, rcx = 4 | rax ^ rbx = 2 rbx ^ rcx = 5
rax = 3, rbx = 2, rcx = 0 | rax ^ rbx = 1 rbx ^ rcx = 2
rax = 3, rbx = 2, rcx = 2 | rax ^ rbx = 1 rbx ^ rcx = 0
rax = 3, rbx = 2, rcx = 4 | rax ^ rbx = 1 rbx ^ rcx = 6
rax = 3, rbx = 3, rcx = 0 | rax ^ rbx = 0 rbx ^ rcx = 3
rax = 3, rbx = 3, rcx = 1 | rax ^ rbx = 0 rbx ^ rcx = 2
rax = 3, rbx = 3, rcx = 2 | rax ^ rbx = 0 rbx ^ rcx = 1
rax = 3, rbx = 3, rcx = 3 | rax ^ rbx = 0 rbx ^ rcx = 0
rax = 3, rbx = 3, rcx = 4 | rax ^ rbx = 0 rbx ^ rcx = 7
rax = 3, rbx = 4, rcx = 4 | rax ^ rbx = 7 rbx ^ rcx = 0
rax = 4, rbx = 0, rcx = 0 | rax ^ rbx = 4 rbx ^ rcx = 0
rax = 4, rbx = 0, rcx = 1 | rax ^ rbx = 4 rbx ^ rcx = 1
rax = 4, rbx = 0, rcx = 2 | rax ^ rbx = 4 rbx ^ rcx = 2
rax = 4, rbx = 0, rcx = 3 | rax ^ rbx = 4 rbx ^ rcx = 3
rax = 4, rbx = 1, rcx = 1 | rax ^ rbx = 5 rbx ^ rcx = 0
rax = 4, rbx = 1, rcx = 3 | rax ^ rbx = 5 rbx ^ rcx = 2
rax = 4, rbx = 2, rcx = 2 | rax ^ rbx = 6 rbx ^ rcx = 0
rax = 4, rbx = 2, rcx = 3 | rax ^ rbx = 6 rbx ^ rcx = 1
rax = 4, rbx = 3, rcx = 3 | rax ^ rbx = 7 rbx ^ rcx = 0
rax = 4, rbx = 4, rcx = 0 | rax ^ rbx = 0 rbx ^ rcx = 4
rax = 4, rbx = 4, rcx = 1 | rax ^ rbx = 0 rbx ^ rcx = 5
rax = 4, rbx = 4, rcx = 2 | rax ^ rbx = 0 rbx ^ rcx = 6
rax = 4, rbx = 4, rcx = 3 | rax ^ rbx = 0 rbx ^ rcx = 7
rax = 4, rbx = 4, rcx = 4 | rax ^ rbx = 0 rbx ^ rcx = 0
可以看出,结果有两种情况:
- 其中一组异或(rax和rbx)或者(rbx和rcx)的结果为0,最终cmp结果为0
- 两组异或的结果再次进行异或等同于相加,最终cmp结果为0
对于第一种情况,输入的三个数里,任意两个数的值相同
对于第二种情况,修改代码判断条件if ((op1 + op2) == (op1 ^ op2) && op1 !=0 && op2!=0 )
,把数据单独筛选出来:
rax = 0, rbx = 1, rcx = 3 | rax ^ rbx = 1 rbx ^ rcx = 2
rax = 0, rbx = 2, rcx = 3 | rax ^ rbx = 2 rbx ^ rcx = 1
rax = 1, rbx = 0, rcx = 2 | rax ^ rbx = 1 rbx ^ rcx = 2
rax = 1, rbx = 0, rcx = 4 | rax ^ rbx = 1 rbx ^ rcx = 4
rax = 1, rbx = 3, rcx = 2 | rax ^ rbx = 2 rbx ^ rcx = 1
rax = 2, rbx = 0, rcx = 1 | rax ^ rbx = 2 rbx ^ rcx = 1
rax = 2, rbx = 0, rcx = 4 | rax ^ rbx = 2 rbx ^ rcx = 4
rax = 2, rbx = 3, rcx = 1 | rax ^ rbx = 1 rbx ^ rcx = 2
rax = 3, rbx = 0, rcx = 4 | rax ^ rbx = 3 rbx ^ rcx = 4
rax = 3, rbx = 1, rcx = 0 | rax ^ rbx = 2 rbx ^ rcx = 1
rax = 3, rbx = 1, rcx = 4 | rax ^ rbx = 2 rbx ^ rcx = 5
rax = 3, rbx = 2, rcx = 0 | rax ^ rbx = 1 rbx ^ rcx = 2
rax = 3, rbx = 2, rcx = 4 | rax ^ rbx = 1 rbx ^ rcx = 6
rax = 4, rbx = 0, rcx = 1 | rax ^ rbx = 4 rbx ^ rcx = 1
rax = 4, rbx = 0, rcx = 2 | rax ^ rbx = 4 rbx ^ rcx = 2
rax = 4, rbx = 0, rcx = 3 | rax ^ rbx = 4 rbx ^ rcx = 3
rax = 4, rbx = 1, rcx = 3 | rax ^ rbx = 5 rbx ^ rcx = 2
rax = 4, rbx = 2, rcx = 3 | rax ^ rbx = 6 rbx ^ rcx = 1
这里又可以分为两种情况:
- 三个输入是都不相同的数值且中间为0
- 三个输入是都不相同的数值且中间非0
对于第一种情况,输入的三个数里有0存在,如果0位于中间,则两边的数与操作为0
对于第二种情况,输入的三个数之间存在特殊关系,再次修改代码判断条件if ((op1 + op2) == (op1 ^ op2) && op1 !=0 && op2!=0 && j)
进行筛选数据:
rax = 0, rbx = 1, rcx = 3 | rax ^ rbx = 1 rbx ^ rcx = 2
rax = 0, rbx = 2, rcx = 3 | rax ^ rbx = 2 rbx ^ rcx = 1
rax = 1, rbx = 3, rcx = 2 | rax ^ rbx = 2 rbx ^ rcx = 1
rax = 2, rbx = 3, rcx = 1 | rax ^ rbx = 1 rbx ^ rcx = 2
rax = 3, rbx = 1, rcx = 0 | rax ^ rbx = 2 rbx ^ rcx = 1
rax = 3, rbx = 1, rcx = 4 | rax ^ rbx = 2 rbx ^ rcx = 5
rax = 3, rbx = 2, rcx = 0 | rax ^ rbx = 1 rbx ^ rcx = 2
rax = 3, rbx = 2, rcx = 4 | rax ^ rbx = 1 rbx ^ rcx = 6
rax = 4, rbx = 1, rcx = 3 | rax ^ rbx = 5 rbx ^ rcx = 2
rax = 4, rbx = 2, rcx = 3 | rax ^ rbx = 6 rbx ^ rcx = 1
要么左边两数或右边两数至少有一边的与操作为非0
到这里可以发现,这里的汇编片段进行对三个输入的判断:
- 3个输入里左右两组异或值存在为零的值
- 3个输入里左右两组异或值不存在为零值,且两组数的与运算为零,有两种情况:
- 中间出现零且剩下两数与操作为零
- 左边两数或右边两数至少有一边的与操作为非0
汇编运行代码:
.code
main proc
lea rax, [1];
lea rbx, [3];
lea rcx, [7];
xor rax,rbx ;rax = rax ^ rbx
xor rbx,rcx ;rbx = rbx ^ rcx
mov rsi,rax ;rsi = rax = (rax ^ rbx)
add rsi,rbx ;rsi = rsi + rbx = (rax ^ rbx) + ( rbx ^ rcx)
cmovc rax,rbx ;rax = rbx
;条件mov语句,CF=1时(进位时)则mov
xor rax,rbx ;rax = rax ^ rbx 有进位时是0,无进位时是(rax ^ rbx) ^ (rbx ^ rcx)
cmp rax,rsi ;对比此时的rax和rsi,rax 是三次异或的结果,rsi是前两次异或的和
ret
main ENDP
END