2024年京津冀大学生信息安全网络攻防大赛(首届) writeup
2024年京津冀大学生信息安全网络攻防大赛(首届)|by_Flooc
reverse
看图标
py打包的exe,解包,反编译
本来想用z3爆破,仔细看了看,a的每两位数就是flag的ascii码。直接打印:
1 | result = 0x28F811AAE094D40A82BE51B5F4BBB6924300A1D9E72C9AA02E19B8A123BD7A9EED4849CEA7F9395CE507E8C9AF284EA74EB436CD9810A3C876C150 |
beijing
打开发现用的都是同一个函数sub_8048460,传入的一个参数,这个参数就是代表后面switch case的情况,将异或的结果打印出来,不对,在数据段看到异或的第二个操作数,发现是flag的ascii码,手搓出来,打印
1 | print(chr(0x66), end='') |
swag
分析
首先jadx打开,分析发现主要逻辑在so层,改apk后缀为zip,解压,找到lib下的四个so文件,我这里看的是x86的so,比较清晰。分析完的函数重命名一下:
首先是convert_to_matrix函数,这里可以看一下arm64-v8a下的so:
1 | _QWORD *__fastcall sub_D60(unsigned __int8 *a1) |
可以理解为将输入的36个字符转换成了一个6*6的矩阵,或者二位列表
然后是turn_over_by_main_diagonal,作用是将这个矩阵沿着它的主对角线翻转(应该是这么说的,线代忘得差不多了。。
1 | int __cdecl sub_D20(_DWORD *a1) |
这部分代码分成两份看,第一部分到62行这里:
从二维列表的角度看,这里a1[n]
可以看成第n行的第一个元素,而*(a1 + 4 * n)
可以看成第一行也就是a0的第n个元素,至于为什么是4 * n,我也不太清楚,前面有个DW标识,表示它后面跟的那个变量是什么类型的,DW是双字,也就是四字节,一次交换四个字节,虽然一个char是一个字节,但是这个exe中存储的是四个字节,从后面密文的存储结构可以看出来。
所以从矩阵角度来看,就是将m(0,n)与m(n,0)交换。
第二部分同上分析,也可看出是将m(a,b)与m(b,a)交换。
这样两者结合起来,就是沿主对角线翻转。
最后就是这个加密函数了,其实不算加密,只是计算了乘积的和。
1 | _DWORD *__cdecl sub_EA0(int a1, _DWORD *a2) |
6次循环,每次循环处理翻转后的一行,这里比较简单,用z3解。
脚本
首先将密文转换为DW类型并导出,我是这样处理的:按几下d,然后将这段文本复制,写个脚本处理下:
1 | import re |
处理后,得到这种DW的数据,然后就是z3解:
1 | cipher = [0x4E36B, 0x362D6, 0x3D5F1, 0x63C4C, 0x66AF7, 0x418B7, 0x4BE2E, 0x35571, 0x3DA7F, 0x60D4A, 0x6423A, 0x3FC18, 0x3A3B6, 0x2FBEE, 0x38F5B, 0x509E4, 0x57DAE, 0x37D25, 0x2E69A, 0x28B2A, 0x363B1, 0x41DAE, 0x49FA8, 0x2D536, 0x3B440, 0x28D5B, 0x3AF48, 0x51F80, 0x59294, 0x30E5F, 0x47CF0, 0x34F47, 0x33520, 0x547A8, 0x581E0, 0x3E875 |
最后是将这个矩阵沿主对角线翻转然后输出,我这里直接输出了,因为是6*6的矩阵,行和列代表的index互换一下就行。
1 | tmp = [{'a': 109, 'b': 99, 'c': 116, 'd': 98, 'e': 107, 'f': 111}, |
最后虽然有点flag的形状,但也不太确定,在apk中提交一下,已经变成flag的形状了(