reverse-for-the-holy-grail-350
作者:互联网
reverse-for-the-holy-....
名字太长不打了
收集信息
给的文件是64位elf文件,无壳
静态分析
打开后代码密密麻麻一堆,看起来是c++写的
大致分析逻辑,name和quest都没啥用,password存入userin中,看起来是个全局变量
要求这两个函数的返回值 >= 0,其他的咱就不管了。
在linux下gdb动态调试之后可以确定:v7两个值分别是input的字串及其长度
可见对任一项k,需要满足k & 223 <= 0x56 || k = 63[1],也就是说不能是0x57 - 0x59
这个很容易满足。
来到stringmod函数,这个程序的主要加密函数
它首先是检测了0, 3, 6, 9, 12, 15这六位的值,等于说告诉了我们这六位
firstchar的长度就6位,接着往下看
异或,注意到每次异或的值是可以直接循环算出的,与字串无关。但这里不知道到底检测多少位
如图,从注释(我写的)可得知,这整个程序检测了前18位,估计我们要输入的字串是18位。
然后这里循环检测了另外12位。
第2、5、8...位是直接检测的,可以直接得到;第1、4、7有取模运算,采用爆破应对。注意这里是异或后进行的检测。
写脚本得答案
思路是先直接求出异或后的其中12(0、2、3、5...)位,再爆破出1、4、7...位, 然后异或得到flag
master = [471, 12, 580, 606, 147, 108]
third = [751, 708, 732, 711, 734, 764]
first = [65, 105, 110, 69, 111, 97]
password = [0 for i in range(18)]
k = 666
temp = []
for i in range(18):
temp.append(k)
k += k % 5
# 第一轮 循环 初始化 0、3、6、9...位
for i in range(18):
if i % 3 == 0:
password[i] = first[int(i / 3)]
password[i] ^= temp[i]
#
for i in range(18):
if i % 3 == 2:
password[i] = third[int(i / 3)]
#
for i in range(1, 18, 3):
for k in range(20, 127):
if (k ^ temp[i]) * password[i - 1] % password[i + 1] == master[int(i / 3)]:
password[i] = (k ^ temp[i])
break
#异或
for i in range(18):
password[i] ^= temp[i]
password[i] = chr(password[i])
print("tuctf{" + ''.join(password) + "}")
注意在最后全部异或回来之前,password列表的值并非在char的表示范围内。
总结
- valid函数是第一个检测,没看懂可能纠结很久,事实上在ascii表中,可见字符为0x21 - 0x7e
条件是 k & 223 <= 0x5A || k = '?', 事实上,这里'?'也是满足前者的,& 223 其实就是小写转大写的方法,这里只有0x5B - 0x5F 和 0x7B - 0x7F(二进制方法很容易想明白的)不满足,都是符号,一般情况可以在爆破过程中剔除掉,也可以不剔除试试看先。
我就在这里浪费了好些时间
标签:reverse,holy,18,range,异或,temp,grail,检测,password 来源: https://www.cnblogs.com/AAAAAtevor/p/16199656.html