题目链接
https://adworld.xctf.org.cn/challenges/list
题目详情
666
解题报告
下载得到的文件使用ida64分析,如果报错就换ida32,得到分析结果,有main函数就先看main
main函数分析
逻辑为:字符串v5(输入的串)与字符串s(已定义的串,目前是空串)经过函数encode加密后,要符合两个逻辑判断:v5和key长度要相同,s和enflag(已定义的串)内容要相同,enflag双击即可查看这里就不放了,后面写脚本时再看
(这里内部的else中puts("flag{This_1s_f4cker_flag}")显然是个恶作剧,This_1s_f4cker_flag->This_is_facker_flag,表明这是个假的flag,如果在它身上研究就是浪费时间,因为if中已经提示过了puts("You are Right"),表示这种正面的反馈才是解出flag的正确方向)
encode函数分析
跟进encode函数进行分析
- 首先注意这里函数的形参,a1接收了字符串v5(输入的字符串),a2接收了字符串s的地址(目前还是空串)
- 函数定义了一个空串v3,后面发现这个v3完全没用,拿来混淆视听的
- 比对a1(即输入的v5)和key的长度,双击进入key查看长度,是12h,注意是16进制的,转为10进制则为18
- 然后是循环对字符串a2加密
- 仔细观察发现,这里的v3纯粹是来混淆视听用的,它把a1(即输入的s5)和key分别进行3次相关运算后,存放在了v3字符数组的[i + 64]、[i + 33]和[i + 2]的位置上,然后又立刻把这三个位置的值赋值给了字符数组a2(用的是byte指针去访问内存),这么一来v3完全是没用的,等价于直接把前三行右边的运算结果赋值给a2(即s字符串)的前三个元素
- 最后的返回值就没啥意义了,因为main中没有利用它,但反正encode函数已经通过a2这个地址(就是main函数中的s字符数组的地址)完成了s的加密
到此为止,逆向思路清晰,上手写脚本
EXP
再回到main函数来整理一下思路,要进入这个红框的if逻辑,需要确保加密后的s和enflag相同,双击跟进得到enflag的值为【'izwhroz""w"v.K".Ni'】,那么对enflag逆向加密(即解密)就得到了flag
另外,像这种给的是单引号字符串且内部存在双引号的就直接用python去写脚本了,用C/C++还需要对引号转义,自添麻烦
再看看加密代码
逆向代码推理如下:
(注:异或运算的优先级小于加减,一定要记得加括号)
enflag = key ^ (flag + 6) -> enflag ^ key = flag + 6 -> 【flag = (enflag ^ key) - 6】
enflag = (flag - 6) ^ key -> enflag ^ key = flag - 6 -> 【flag = (enflag ^ key) + 6】
enflag = flag ^ 6 ^ key -> 【flag = enflag ^ 6 ^ key】
enflag = 'izwhroz""w"v.K".Ni' flag='' #这里的key就是18,因为key的十六进制的12h,等于十进制18 key = 18 for i in range(0, key, 3): flag+=chr( (ord(enflag[i]) ^ key) - 6) flag+=chr( (ord(enflag[i + 1]) ^ key) + 6) flag+=chr( ord(enflag[i + 2]) ^ 6 ^ key) print(flag)
(注:python的字符不能像C/C++那样直接当成ASCII去使用,必须借助函数互相转换,使用ord()进行字符转ASCII,使用chr()进行ASCII转字符)
总结
涉及到的关键知识点:
- byte指针访问字符数组
- 异或运算的逆向代码编写:^优先级小于+和-,如:enflag = key ^ (flag + 6) -> enflag ^ key = flag + 6 -> 【flag = (enflag ^ key) - 6】