这是一道关于异或运算xor的题拿到这道题我直接用IDA打开了没有进行查壳什么的基础题嘛应该没有打开直接找到主函数按F5反编译以下是这道题的核心内容int __fastcall main(int argc, const char **argv, const char **envp) { int i; // [rsp2Ch] [rbp-124h] char __b[264]; // [rsp40h] [rbp-110h] BYREF memset(__b, 0, 0x100u); printf(Input your flag:\n); get_line(__b, 0x100u); if ( strlen(__b) ! 33 ) goto LABEL_7; for ( i 1; i 33; i ) __b[i] ^ __b[i - 1]; if ( !strncmp(__b, global, 0x21u) ) printf(Success); else LABEL_7: printf(Failed); return 0; }其核心点就是将你输入进_b数组且长度不超过33的字符串经过异或运算的结果与程序内预定义的 global数组比较相同则成功。那这里我们就要弄懂他是怎么进行异或运算的理解异或运算XOR异或是一种逻辑运算符号是^。它的规则很简单两个位相同结果为 0不同结果为 1。例如0 ^ 0 00 ^ 1 11 ^ 0 11 ^ 1 0对于字节8个位我们可以对每个位分别进行异或。例如二进制 0110 (6) ^ 0011 (3) 0101 (5)异或有几个重要性质任何数和自己异或等于 0a ^ a 0任何数和 0 异或等于自身a ^ 0 a交换律a ^ b b ^ a结合律(a ^ b) ^ c a ^ (b ^ c)自反性如果a ^ b c那么c ^ b a且c ^ a b。这是最关键的性质也是我们解密的依据。核心在整段代码中异或运算的核心代码是__b[i] ^ __b[i - 1];它等价于__b[i] __b[i] ^ __b[i-1]。意思是把__b[i]和__b[i-1]进行异或运算结果再存回__b[i]。for ( i 1; i 33; i )而他前面的循环意思就是我输入的字符串存储在 __b 中下标从 0 开始。循环变量 i 从 1 到 32每次处理一个元素 __b[i]。实际上他一次性就执行完了那么这两个结合起来的运算流程就是__b[0]__b[1] F[1] ^ F[0]__b[2] F[2] ^ F[1] ^ F[0]__b[3] F[3] ^ F[2] ^ F[1] ^ F[0]__b[4] F[4] ^ F[3] ^ F[2] ^ F[1] ^ F[0]注意第一个数据下标0也就是__b[0]是无法执行异或运算的只有从下标1开始才会与前面一个两两相邻执行异或运算。这就是在整个异或运算的流程也就是说在正常的代码运行当中我们输入的flag会经过这样的运算得出的结果与global数组比较如果我们输入的flag正确那么程序将会输出Success如果不对则输出Failed。综上所诉我们的flag其实就是global数组只不过他是经过异或运算变成这样的了解了这些就好办多了我们要做的就是将global数组提取出来然后反向计算得到flag我们在主函数上找到global数组双击他进入数据段可以看到这并不是我们想要的数据这说明global本身并不是直接存放数据的数组而是一个指针dq表示 8 字节的地址它指向另一个地方那里才是真正的数据。这个指针指向的标号是aFKWOXZUPFVMDGH我们需要再双击aFKWOXXZUPFVMDGH或者按Enter键就能跳转到真正的数据定义处。这三行是连续的它们共同构成了完整的 33 字节数据。第一行标号aFKWOXZUPFVMDGH指向数据开头后面两行没有新标号只是接着显示剩余部分所以我们应该把这三行所有的字节按顺序合并在一起。并把它们换算成面向计算机语言的16进制接下来我们来逐行解析并列出每个字节的数值十六进制第一行db f,0Ahf 0x660Ah 0x0A第二行db k,0Ch,wO.,11h,x,0Dh,Z;U,11h,p,19h,F,1Fh,vM#D,0Eh,g仔细拆分k→ 0x6B0Ch→ 0x0C连续字符wO.→ 依次为w0x77, 0x26, O0x4F, .0x2E, 0x4011h→ 0x11x→ 0x780Dh→ 0x0D连续字符Z;U→ Z0x5A, ;0x3B, U0x5511h→ 0x11p→ 0x7019h→ 0x19F→ 0x461Fh→ 0x1F连续字符vM#D→ v0x76, 0x22, M0x4D, #0x23, D0x440Eh→ 0x0Eg→ 0x67第三行db 6,h,0Fh,G2O,06→ 0x06h→ 0x680Fh→ 0x0F连续字符G2O→ G0x47, 20x32, O0x4F最后的0是字符串结束符NULL不计入我们要取的 33 个字节。完整的合并内容如下0x66, 0x0A, 0x6B, 0x0C, 0x77, 0x26, 0x4F, 0x2E, 0x40, 0x11,0x78, 0x0D, 0x5A, 0x3B, 0x55, 0x11, 0x70, 0x19, 0x46, 0x1F,0x76, 0x22, 0x4D, 0x23, 0x44, 0x0E, 0x67, 0x06, 0x68, 0x0F,0x47, 0x32, 0x4F最终payload最后我们用编程语言写出反向计算的代码将合并内容带入进行反向计算去获取最终的flag。以下为python示例data [0x66, 0x0A, 0x6B, 0x0C, 0x77, 0x26, 0x4F, 0x2E, 0x40, 0x11, 0x78, 0x0D, 0x5A, 0x3B, 0x55, 0x11, 0x70, 0x19, 0x46, 0x1F, 0x76, 0x22, 0x4D, 0x23, 0x44, 0x0E, 0x67, 0x06, 0x68, 0x0F, 0x47, 0x32, 0x4F] flag [data[0]] [data[i] ^ data[i-1] for i in range(1, 33)] print(.join(chr(c) for c in flag))这里定义了一个表data其实就是上面讲的数组_b。在flag那一行中因为下标0不参与计算所以就取data[0]后面就是加上参与异或计算的结果然后遍历i从1到32依次处理、计算原理与上面的C语言相同。print这一行是将flag列表中的每个整数c转换成对应的字符chr(c)然后用.join()把这些字符连接成一个完整的字符串最后打印出来。得到最终flagflag{QianQiuWanDai_YiTongJiangHu}