分析:
直接先用JEB对这个APK进行逆向,通过解密后的AndroidManifest.xml文件发现入口是MainActivity,但是并没有这个文件,考虑到可能被加密,阅读已有的代码中,发现程序运行后会对test.zip进行解密,解压这个APK,找到这个文件,无法对这个文件直接解压,于是push到虚拟机,运行,在data文件夹下面可以把load.dex文件pull下来,然后重新使用JEB对pull下来的文件进行反编译,拿到了MainActivity(这里我先用了dex2jar再用jd-gui拿了Java级源码,发现结果是一坨翔。。。)。
通过阅读代码可以知道,要输入24个ASCII字符,然后每八个作为一组来生成square,每个字符生成一个square,并且每组内的字符要求前面的小于后边的。square是一个5*5的网格,包含25个点。square生成的时候有如下规则:
v0[v2] = new Square((v1[v5 + v2] << 8) + v12 + 255, v5 / 2 + 4);
其中,v12=828309504,一定要转换为16进制。。v12+255=0X315F00FF,注意低位第二个字节为零,注意到v1将会左移八位,实质上是将ASCII码填充到这个第二个字节中,第二个参数是循环的次数,可以取4,8,12。
注意到最后的检测是要每个square的check均为true才可以,通过分析check函数可以知道这里是要求它的主副对角线均为1。
另外还要注意一点,square在生成的时候如果check为true,会调用turnpoint,也就是说要求square在turnpoint前后的check均为true。turnpoint是将每个点的坐标x-1,如果在边界上则y-1。
下面给出代码:
c = [0 for i in range(25)]
move = [4, 8, 12]
a = []
def fill_one(v):
for i in range(5):
v[i * 5 + i] = 1
v[(i + 1) * 4] = 1
for i in range(5):
print(v[i * 5 : i * 5 + 5])
print()
def getr(v):
return v[8:16][::-1]
fill_one(c)
for x in move:
a.append(c[-x:] + c[:-x])
for l in a:
fill_one(l)
ans = getr(l)
print(ans)
print()
首先生成一个主副对角线均为1的square,然后经过三种不同循环次数的turnpoint,拿到square,之后将变换后的square的主副对角线填充为1,再在ans中取出低位第二个字节。
下面枚举符合条件的字符:
b=[0b00010101, 0b01010001, 0b00011001]
print("SCTF{", end="")
for t in b:
k = 48
for i in range(8):
while((k & t) < t):
k += 1
print(chr(k), end="")
k += 1
print("}")
其中b列表是通过前边那段程序拿到的,这样可以获取flag。
实际上我太弱鸡了。。这道题看了夜影大佬的WP。。