题目链接
https://adworld.xctf.org.cn/challenges/list
题目详情
xxxorrr
解题报告
下载得到的文件使用ida64分析,如果报错就换ida32,得到分析结果,有main函数就先看main
main函数分析
v6
main函数中,v6的值是__readfsqword(0x28u)用于反调试
a2、a3与sub_916函数一起被作为sub_A90函数的参数
输入的字符串s与已经定义好的s1相互异或
s1
是一个定义好的字符串
sub_916函数分析
v1
v1用于反调试
除此外该函数只是对字符串s1和s2进行比对,如果相同则给出成功提示(flag引导),说明要从s1和s2着手,在main函数中对s1加密(与输入串s异或)后的结果需要和s2相同
s1&s2
发现这里的s1和main函数中的s1是同一个变量,这时碰到一个问题,sub_916函数在进行s1和s2的比对时,按照顺序结构的话,应该是先执行sub_916函数(即先比对s1和s2),再让s1和输入串s异或才对啊?可是如果是这样的执行顺序,就不可能使s1与s2相等,说明不是这样的执行顺序,应该是遗漏了某些条件,再回main函数中看看有一个还没分析的sub_A90函数
sub_A90函数分析
C 库函数 – atexit() | 菜鸟教程 (runoob.com)
这个函数直接给出了一个返回值__cxa_atexit函数,查阅资料后得知它的原型就算atexit()函数,而atexit()函数的作用是,当程序正常终止时,调用传入其中的函数
这下便恍然大悟,表面上看main函数中的sub_916函数在对字符串s1循环异或加密之前,而实际它是被作为参数传递给__cxa_atexit函数的,是在程序执行结束后才被调用
在完成以上的分析之后,博主一时也是认为稳如泰山,不曾料想按这个逆向思路去简单写了脚本却始终不能ctf,也是有了这题,让我养成一个习惯——对于关键加密串一定要仔细查看它的交叉引用!
选中s1后按x键查看交叉引用,赫然发现它在sub_84A函数也曾被引用过
sub_84A函数分析
跟进sub_84A函数,果不其然,发现这个函数也对s1进行过相关加密操作,但蒟蒻博主实在想不明白这个函数被调用的机理是什么,main函数中并没有对其调用过的痕迹,希望有清楚的大佬能够路过时指点一二!
最终我只能直接主观认为sub_84A函数是在main函数调用之前就被调用了
因此,在对比字符串s1和s2前,s1是被进行了两次加密,第一次是sub_84A函数的如下图的加密公式【s1[i] ^= 2 * i + 65】,第二次是main函数中的for循环与输入串s异或
EXP
先整理一下正向加密思路
经过加密1和加密2后的字符串s1和s2相同,逆向思路则为s2与完成第一次解密后的s1,去进行解密2
字符串s2处理
刚准备写脚本时突然发现这个s2不是常规的字符串啊,小白博主也不太明白为什么ida会这样分析,既有字符(ASCII码是十进制)又有十六进制数(h标志),所以这里得多做一步操作了——把字符串s2统一成相同的十六进制
把这些末尾有h标志的十六进制数先提取出来(注意结果要去掉h标志),各显神通吧,蒟蒻博主直接手抠了...
0x17, 0x46, 0x54, 0x5A, 0x59, 0x59, 0x1F, 0x48, 0x32, 0x5B, 0x6B, 0x7C, 0x75, 0x6E, 0x7E, 0x6E, 0x2F, 0x77, 0x4F, 0x7A, 0x71, 0x43, 0x2B, 0x26, 0x89, 0xFE, 0x00
对s2前面的字符也做相同处理,这里可以用ida的【shift+e】快捷键快速获取
凑成一个总列表
s2 = [0x56, 0x4E, 0x57, 0x58, 0x51, 0x51, 0x09, 0x46,0x17, 0x46, 0x54, 0x5A,0x59, 0x59,0x1F, 0x48, 0x32, 0x5B, 0x6B, 0x7C,0x75, 0x6E, 0x7E, 0x6E, 0x2F, 0x77, 0x4F, 0x7A, 0x71, 0x43, 0x2B, 0x26, 0x89, 0xFE, 0x00]
最终脚本
(注意运行出的结果少一个右大括号,自行补上后提交正确)
s2 = [0x56, 0x4E, 0x57, 0x58, 0x51, 0x51, 0x09, 0x46,0x17, 0x46, 0x54, 0x5A,0x59, 0x59,0x1F, 0x48, 0x32, 0x5B, 0x6B, 0x7C,0x75, 0x6E, 0x7E, 0x6E, 0x2F, 0x77, 0x4F, 0x7A, 0x71, 0x43, 0x2B, 0x26, 0x89, 0xFE, 0x00] s1 = 'qasxcytgsasxcvrefghnrfghnjedfgbhn' flag = '' for i in range(0, 33): #先对s1解密1,再拿s2和解密1后的s1进行解密2,得到的字符就是flag的一部分 #同时注意python的字符不能直接当ASCII码来用 flag += chr(s2[i] ^ ord(s1[i]) ^ (2 * i + 65) ) print(flag)