selph
selph
Published on 2022-02-02 / 336 Visits
0
1

《xchg rax,rax》片段分析0x16--三输入判断

前言

本次是该系列的的第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

可以看出,结果有两种情况:

  1. 其中一组异或(rax和rbx)或者(rbx和rcx)的结果为0,最终cmp结果为0
  2. 两组异或的结果再次进行异或等同于相加,最终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

这里又可以分为两种情况:

  1. 三个输入是都不相同的数值且中间为0
  2. 三个输入是都不相同的数值且中间非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


到这里可以发现,这里的汇编片段进行对三个输入的判断:

  1. 3个输入里左右两组异或值存在为零的值
  2. 3个输入里左右两组异或值不存在为零值,且两组数的与运算为零,有两种情况:
    1. 中间出现零且剩下两数与操作为零
    2. 左边两数或右边两数至少有一边的与操作为非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

Comment