goductf 逆向wp

First Post:

Last Update:

Check_Your_Luck

z3解方程

doublegame

这题最坑的地方就是静态的迷宫和动调出来的迷宫不一样!!!

程序打开就是一个贪吃蛇游戏,(挺好玩,先玩一会)

拖入ida,主函数没啥用,查找字符串很明显有个迷宫,(但不是贪吃蛇游戏吗),先交叉引用进去,分析代码很明显就是很简单的走迷宫求路径,但是静态的迷宫是错误的,查找迷宫的交叉引用我也没看到对他的修改,感觉应该不是出题人故意的,是ida分析错了?真正的迷宫得动调进去看,那么怎么到达这里呢,刚刚的贪吃蛇呢。那就再对这个函数一直x找交叉引用,可以一直追踪到贪吃蛇函数,可以看到跳转到迷宫的条件就是贪吃蛇的分数达到13371337.

嗯,没了,一开始没动调按照错误的迷宫卡了我三个小时

flag{md5(path)+13371337}

迷宫的代码:这里的迷宫有一个地方少了个0

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
  strcpy(Buffer, "000000000000000000000");
strcpy(v4, "0 0 0 0 0 0 0");
strcpy(&v4[22], "0 0 0 00000 00000 0 0");
strcpy(v5, "0 0 0 0");
strcpy(&v5[22], "0 000 000 0 000 0 0 0");
strcpy(v6, "0 0 0 0 0 0 0 0");
strcpy(&v6[22], "0 0 0 00000 000 000 0");
strcpy(v7, "0 0 0 0 0 0 ");
strcpy(&v7[22], "0 000 0 0 000 0 0 0 0");
strcpy(v8, "0 0 0 0 0 0 0 0 0");
strcpy(&v8[22], "0 00000 000 000 0 0 0");
strcpy(v9, "0 0 0 0 0");
strcpy(&v9[22], "000 0 0 0 000 0 0 0 0");
strcpy(v10, "0 0 0 0 0 0 * 0 0 0 0");
strcpy(&v10[22], "0 0000000 0 000 00000");
strcpy(v11, "@ 0 0 0");//这里动调看应该是@ 0 0 0 0
strcpy(&v11[22], "0 0 0 0 0 00000000000");
strcpy(v12, "0 0 0 0 0");
strcpy(&v12[22], "000 0 00000 0 000 000");
strcpy(v13, "0 0 0 0 0");
strcpy(&v13[22], "000000000000000000000");
v11[4] = 48;//破案了,这里出题人故意的
strcpy((char *)v19, "Please to save the cat!");
memset(&v19[6], 0, 0x4Cui64);
strcpy(v20, "the score is saving cat's key!\n");
memset(&v20[32], 0, 0x44ui64);
qmemcpy(v21, &unk_14001D340, 0x47ui64);
memset(&v21[71], 0, 729);
sub_1400111F9("path\n");
v23 = 0;
v24 = 0;
v15 = 15;
v16 = 0;
v17 = 7;
v18 = 20;
for ( j = 0; j <= 20; ++j )
puts(&Buffer[22 * j]);
sub_1400111F9("Please to save the cat!\n");
while ( v15 != v17 || v16 != v18 )
{
v22 = getchar();
switch ( v22 )
{
case 's':
if ( Buffer[22 * v15 + 22 + v16] != 48 )
{
Buffer[22 * v15++ + v16] = 32;
Buffer[22 * v15 + v16] = 64;
}
break;
case 'w':
if ( Buffer[22 * v15 - 22 + v16] != 48 )
{
Buffer[22 * v15-- + v16] = 32;
Buffer[22 * v15 + v16] = 64;
}
break;
case 'a':
if ( Buffer[22 * v15 - 1 + v16] != 48 )
{
if ( Buffer[22 * v15 - 1 + v16] == 42 )
v7[20] = 48;
Buffer[22 * v15 + v16--] = 32;
Buffer[22 * v15 + v16] = 64;
}
break;
default:
if ( v22 == 100 && Buffer[22 * v15 + 1 + v16] != 48 )
{
Buffer[22 * v15 + v16++] = 32;
Buffer[22 * v15 + v16] = 64;
}
break;
}
system("cls");
for ( j = 0; j <= 20; ++j )
puts(&Buffer[22 * j]);
puts((const char *)&v19[25 * v23]);
if ( v7[20] == 48 )
{
v24 = sub_140011433(0i64);
if ( v24 == 13376013 )
{
v23 = 1;
v7[20] = 32;
Buffer[22 * v15 + v16] = 32;
v15 = 15;
v16 = 0;
v11[0] = 64;
++v23;
}
else
{
sub_1400111F9("error");
}
}
}
system("cls");
Sleep(0x1F4u);
Sleep(0xBB8u);
sub_140011492();
exit(0);
}

TEA

这道题加深了我对tea系列加密算法的理解

tea加密:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
for ( i = 0; i <= 8; ++i )
{
v5 = 0;
v6 = 256256256 * i;
v3 = i + 1;
do
{
++v5;
*(a1 + 4i64 * i) += v6 ^ (*(a1 + 4i64 * v3) + ((*(a1 + 4i64 * v3) >> 5) ^ (16 * *(a1 + 4i64 * v3)))) ^ (v6 + *(a2 + 4i64 * (v6 & 3)));
*(a1 + 4i64 * v3) += (v6 + *(a2 + 4i64 * ((v6 >> 11) & 3))) ^ (*(a1 + 4i64 * i)
+ ((*(a1 + 4i64 * i) >> 5) ^ (16 * *(a1 + 4i64 * i))));
v6 += 256256256;
}
while ( v5 <= 0x20 );
result = (i + 1);
}

其中do{}内的是tea加密的算法,外层套了个for()循环,因为原文有10个整数

解密脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
int main()
{
for (int i = 8; i >= 0; --i)//从后往前遍历
{
int v5 = 0;
unsigned int v6 = 256256256 * (i+33);//detal 因为v6也就是detal在加密过程中一直加256256256,加了32次,所以逆的时候先求出最后的结果,并且要注意使用unsigned int

do//这个部分就完全逆过来,一共三步嘛,去原文那里对照着看一下,+=变-=,后面的内容不需要变,直接抄
{
++v5;
v6 -= 256256256;
a1[i + 1] -= (v6 + a2[(v6 >> 11) & 3]) ^ (a1[i] + ((a1[i] >> 5) ^ (16 * a1[i])));
a1[i] -= v6 ^ (a1[i + 1] + ((a1[i + 1] >> 5) ^ (16 * a1[i + 1]))) ^ (v6 + a2[v6 & 3]);

} while (v5 <= 32);

}
for (int i = 0; i < 10; i++)
{
for (int j = 3; j >= 0; j--)
{
printf("%c", (a1[i] >> (j * 8)) & 0xFF);
}
}
return 0;
}

L!S!

考察了bindiff插件的使用

比较两个文件的不同之处,一般用来比较原文件和dump之后的文件

先用ida随便打开一个文件,再关闭,生成集成的那个文件(i64),再用ida打开另一个文件,打开插件bindiff,选择Diff Database 打开刚刚的i64文件,就可以查看这两个文件的不同之处了

image-20230420120422153

可以看到所有函数中只有一个函数不同

1
2
3
4
5
6
7
8
9
10
if ( v9 && !v9[1] )
{
*&lmao[8] = 0x3F7D132A2A252822LL;
*lmao = 0x7D2E370A180F1604LL;
*&lmao[24] = 0x31207C7C381320LL;
*&lmao[16] = 0x392A7F3F39132D13LL;
v18 = lmao;
do
*v18++ ^= **v7;
while ( &lmao[31] != v18 );

密文提取出来:

1
int aa[] = { 0x04,0x16,0x0f,0x18,0x0a,0x37,0x2e,0x7d,0x22,0x28,0x25,0x2a,0x13,0x7d,0x3f,0x13,0x2d,0x13,0x39,0x3f,0x7f,0x2a,0x39,0x20,0x13,0x38,0x7c,0x7c,0x20,0x31};

加密就仅仅只有一个异或v7,从0-256爆破遍,去结果里找flag

image-20230420120722978

还算好找