CISCN [rev] babybc

babybc

查看附件,发现是bc文件,经过一番搜索可知是LLVM IR。

在Ubuntu上安装LLVM,使用clang编译成ELF文件,用IDA Pro打开。

main函数:

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
int __cdecl main(int argc, const char **argv, const char **envp)
{
unsigned __int64 v4; // [rsp+8h] [rbp-20h]
size_t v5; // [rsp+10h] [rbp-18h]
unsigned __int64 i; // [rsp+18h] [rbp-10h]

__isoc99_scanf(&unk_402004, input, envp);
if ( (unsigned int)strlen(input) == 25 )
{
if ( input[0] )
{
if ( (unsigned __int8)(input[0] - 48) > 5u )
return 0;
v5 = strlen(input);
for ( i = 1LL; ; ++i )
{
v4 = i;
if ( i >= v5 )
break;
if ( (unsigned __int8)(input[v4] - 48) > 5u )
return 0;
}
}
if ( (fill_number((__int64)input) & 1) != 0 && (docheck() & 1) != 0 )
printf("CISCN{MD5(%s)}", input);
}
return 0;
}

可知我们的输入必须是数字0~5 。

继续查看fill_number函数:

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
__int64 __fastcall fill_number(__int64 a1)
{
unsigned __int8 *v2; // [rsp+0h] [rbp-70h]
char v3; // [rsp+Fh] [rbp-61h]
unsigned __int8 *v4; // [rsp+10h] [rbp-60h]
char v5; // [rsp+1Fh] [rbp-51h]
unsigned __int8 *v6; // [rsp+20h] [rbp-50h]
char v7; // [rsp+2Fh] [rbp-41h]
unsigned __int8 *v8; // [rsp+30h] [rbp-40h]
char v9; // [rsp+3Eh] [rbp-32h]
char v10; // [rsp+3Fh] [rbp-31h]
__int64 v11; // [rsp+40h] [rbp-30h]
__int64 v12; // [rsp+48h] [rbp-28h]
char v13; // [rsp+5Fh] [rbp-11h]
__int64 v14; // [rsp+68h] [rbp-8h]

v14 = 0LL;
do
{
v11 = v14;
v12 = 5 * v14;
v13 = *(_BYTE *)(a1 + 5 * v14);
if ( map[5 * v14] )
{
v10 = 0;
if ( v13 != 48 )
return v10 & 1;
}
else
{
map[5 * v14] = v13 - 48;
}
v8 = &map[5 * v14 + 1];
v9 = *(_BYTE *)(a1 + v12 + 1);
if ( *v8 )
{
v10 = 0;
if ( v9 != 48 )
return v10 & 1;
}
else
{
*v8 = v9 - 48;
}
v6 = &map[5 * v14 + 2];
v7 = *(_BYTE *)(a1 + v12 + 2);
if ( *v6 )
{
v10 = 0;
if ( v7 != 48 )
return v10 & 1;
}
else
{
*v6 = v7 - 48;
}
v4 = &map[5 * v14 + 3];
v5 = *(_BYTE *)(a1 + v12 + 3);
if ( *v4 )
{
v10 = 0;
if ( v5 != 48 )
return v10 & 1;
}
else
{
*v4 = v5 - 48;
}
v2 = &map[5 * v14 + 4];
v3 = *(_BYTE *)(a1 + v12 + 4);
if ( *v2 )
{
v10 = 0;
if ( v3 != 48 )
return v10 & 1;
}
else
{
*v2 = v3 - 48;
}
++v14;
v10 = 1;
}
while ( v11 + 1 < 5 );
return v10 & 1;
}

fill_number根据输入生成map,若map数组中的数字非零,则对应的input应该是字符’0’;否则会把input中的字符减去48,放入map中。

查看docheck函数:

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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
__int64 docheck()
{
char *v1; // [rsp+0h] [rbp-C8h]
char *v2; // [rsp+8h] [rbp-C0h]
char *v3; // [rsp+10h] [rbp-B8h]
char *v4; // [rsp+18h] [rbp-B0h]
char v5; // [rsp+21h] [rbp-A7h]
char v6; // [rsp+22h] [rbp-A6h]
char v7; // [rsp+23h] [rbp-A5h]
char v8; // [rsp+24h] [rbp-A4h]
char v9; // [rsp+25h] [rbp-A3h]
char v10; // [rsp+26h] [rbp-A2h]
char v11; // [rsp+27h] [rbp-A1h]
__int64 v12; // [rsp+28h] [rbp-A0h]
__int64 v13; // [rsp+38h] [rbp-90h]
char v14; // [rsp+46h] [rbp-82h]
char v15; // [rsp+47h] [rbp-81h]
__int64 v16; // [rsp+48h] [rbp-80h]
__int64 v17; // [rsp+50h] [rbp-78h]
char v18; // [rsp+5Fh] [rbp-69h]
char *v19; // [rsp+60h] [rbp-68h]
__int64 v20; // [rsp+68h] [rbp-60h]
char *v21; // [rsp+70h] [rbp-58h]
char v22; // [rsp+7Fh] [rbp-49h]
char *v23; // [rsp+80h] [rbp-48h]
__int64 v24; // [rsp+88h] [rbp-40h]
char *v25; // [rsp+90h] [rbp-38h]
__int64 v26; // [rsp+A0h] [rbp-28h]
__int64 v27; // [rsp+B0h] [rbp-18h]
char v28[6]; // [rsp+BCh] [rbp-Ch] BYREF
char v29[6]; // [rsp+C2h] [rbp-6h] BYREF

v27 = 0LL;
do
{
v24 = v27;
memset(v29, 0, sizeof(v29));
v25 = &v29[map[5 * v27]];
if ( *v25
|| (*v25 = 1, v23 = &v29[map[5 * v27 + 1]], *v23)
|| (*v23 = 1, v2 = &v29[map[5 * v27 + 2]], *v2)
|| (*v2 = 1, v1 = &v29[map[5 * v27 + 3]], *v1)
|| (*v1 = 1, v29[map[5 * v27 + 4]]) )
{
v22 = 0;
return v22 & 1;
}
++v27;
}
while ( v24 + 1 < 5 );
v26 = 0LL;
while ( 1 )
{
v20 = v26;
memset(v28, 0, sizeof(v28));
v21 = &v28[map[v26]];
if ( *v21 )
break;
*v21 = 1;
v19 = &v28[map[v26 + 5]];
if ( *v19 )
break;
*v19 = 1;
v4 = &v28[map[v26 + 10]];
if ( *v4 )
break;
*v4 = 1;
v3 = &v28[map[v26 + 15]];
if ( *v3 )
break;
*v3 = 1;
if ( v28[map[v26 + 20]] )
break;
++v26;
if ( v20 + 1 >= 5 )
{
v16 = 0LL;
while ( 1 )
{
v17 = v16;
v18 = row[4 * v16];
if ( v18 == 1 )
{
if ( map[5 * v16] < map[5 * v16 + 1] )
goto LABEL_27;
}
else if ( v18 == 2 && map[5 * v16] > map[5 * v16 + 1] )
{
LABEL_27:
v22 = 0;
return v22 & 1;
}
v15 = row[4 * v16 + 1];
if ( v15 == 1 )
{
if ( map[5 * v16 + 1] < map[5 * v16 + 2] )
goto LABEL_27;
}
else if ( v15 == 2 && map[5 * v16 + 1] > map[5 * v16 + 2] )
{
goto LABEL_27;
}
v6 = row[4 * v16 + 2];
if ( v6 == 1 )
{
if ( map[5 * v16 + 2] < map[5 * v16 + 3] )
goto LABEL_27;
}
else if ( v6 == 2 && map[5 * v16 + 2] > map[5 * v16 + 3] )
{
goto LABEL_27;
}
v5 = row[4 * v16 + 3];
if ( v5 == 1 )
{
if ( map[5 * v16 + 3] < map[5 * v16 + 4] )
goto LABEL_27;
}
else if ( v5 == 2 && map[5 * v16 + 3] > map[5 * v16 + 4] )
{
goto LABEL_27;
}
++v16;
if ( v17 + 1 >= 5 )
{
v12 = 0LL;
while ( 1 )
{
v13 = v12 + 1;
v14 = col[5 * v12];
if ( v14 == 1 )
{
v11 = 0;
if ( map[5 * v12] > map[5 * v13] )
goto LABEL_26;
}
else if ( v14 == 2 )
{
v11 = 0;
if ( map[5 * v12] < map[5 * v13] )
{
LABEL_26:
v22 = v11;
return v22 & 1;
}
}
v10 = col[5 * v12 + 1];
if ( v10 == 1 )
{
v11 = 0;
if ( map[5 * v12 + 1] > map[5 * v13 + 1] )
goto LABEL_26;
}
else if ( v10 == 2 )
{
v11 = 0;
if ( map[5 * v12 + 1] < map[5 * v13 + 1] )
goto LABEL_26;
}
v9 = col[5 * v12 + 2];
if ( v9 == 1 )
{
v11 = 0;
if ( map[5 * v12 + 2] > map[5 * v13 + 2] )
goto LABEL_26;
}
else if ( v9 == 2 )
{
v11 = 0;
if ( map[5 * v12 + 2] < map[5 * v13 + 2] )
goto LABEL_26;
}
v8 = col[5 * v12 + 3];
if ( v8 == 1 )
{
v11 = 0;
if ( map[5 * v12 + 3] > map[5 * v13 + 3] )
goto LABEL_26;
}
else if ( v8 == 2 )
{
v11 = 0;
if ( map[5 * v12 + 3] < map[5 * v13 + 3] )
goto LABEL_26;
}
v7 = col[5 * v12 + 4];
if ( v7 == 1 )
{
v11 = 0;
if ( map[5 * v12 + 4] > map[5 * v13 + 4] )
goto LABEL_26;
}
else if ( v7 == 2 )
{
v11 = 0;
if ( map[5 * v12 + 4] < map[5 * v13 + 4] )
goto LABEL_26;
}
++v12;
v11 = 1;
if ( v13 >= 4 )
goto LABEL_26;
}
}
}
}
}
v22 = 0;
return v22 & 1;
}

先看这一部分:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// ...
do
{
v24 = v27;
memset(v29, 0, sizeof(v29));
v25 = &v29[map[5 * v27]];
if ( *v25
|| (*v25 = 1, v23 = &v29[map[5 * v27 + 1]], *v23)
|| (*v23 = 1, v2 = &v29[map[5 * v27 + 2]], *v2)
|| (*v2 = 1, v1 = &v29[map[5 * v27 + 3]], *v1)
|| (*v1 = 1, v29[map[5 * v27 + 4]]) )
{
v22 = 0;
return v22 & 1;
}
++v27;
}
while ( v24 + 1 < 5 );
// ...

已知v29是长度为5的char数组,每次循环会先将v29置零。

v27从0循环到4,那么每次map[5 * v27]、map[5 * v27 + 1]、… 、map[5 * v27 + 4]就是获取一组五个数字,那么我们可以认为map其实是二维数组。

在这里&v29[map[i]]就相当于(&v29 + map[i])。每次我们会检查v29[map[i]]是否为0,然后将v29[map[i]]置为1 。那么if部分其实就是检测map这一行五个数字是否各不相同。

这里其实有一种感觉:这是一个数独题。

再看这一部分:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
v20 = v26;
memset(v28, 0, sizeof(v28));
v21 = &v28[map[v26]];
if ( *v21 )
break;
*v21 = 1;
v19 = &v28[map[v26 + 5]];
if ( *v19 )
break;
*v19 = 1;
v4 = &v28[map[v26 + 10]];
if ( *v4 )
break;
*v4 = 1;
v3 = &v28[map[v26 + 15]];
if ( *v3 )
break;
*v3 = 1;
if ( v28[map[v26 + 20]] )
break;

有了上面的经验,这里就不难发现它是在检测每一列的5个数字是否各不相同。

检测完这个二维数组是否满足数独要求后,还会进一步检测:根据row、col数组提供的信息,判断某两个数是否满足指定的大小关系。

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
    v18 = row[4 * v16];
if ( v18 == 1 )
{
if ( map[5 * v16] < map[5 * v16 + 1] )
goto LABEL_27;
}
else if ( v18 == 2 && map[5 * v16] > map[5 * v16 + 1] )
{
LABEL_27:
v22 = 0;
return v22 & 1;
}
// ...
v13 = v12 + 1;
v14 = col[5 * v12];
if ( v14 == 1 )
{
v11 = 0;
if ( map[5 * v12] > map[5 * v13] )
goto LABEL_26;
}
else if ( v14 == 2 )
{
v11 = 0;
if ( map[5 * v12] < map[5 * v13] )
{
LABEL_26:
v22 = v11;
return v22 & 1;
}
}
//...

这里只截取部分片段,可知若row[i]col[i]为1或2,则需要二维数组中某一行/某一列相邻的两个数字满足大小关系。否则返回错误。

盗一个z3:

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
rowss = [[0x00, 0x00, 0x00, 0x01],[0x01, 0x00, 0x00, 0x00], [0x02, 0x00, 0x00, 0x01], [0x00, 0x00, 0x00, 0x00], [0x01, 0x00, 0x01, 0x00]]
colnms = [[0x00, 0x00, 0x02, 0x00,0x02], [0x00, 0x00, 0x00, 0x00, 0x00], [0x00, 0x00, 0x00, 0x01, 0x00], [0x00, 0x01, 0x00, 0x00, 0x01]]
from z3 import *
from hashlib import md5
s = Solver()
map = [[None]*5,[None]*5,[None]*5,[None]*5,[None]*5,]
for i in range(5):
for j in range(5):
map[i][j]=(Int("x%d%d"%(i, j)))
print(map)
s.add(map[2][2] == 4)
s.add(map[3][3] == 3)
for i in range(5):
for j in range(5):
s.add(map[i][j] >= 1)
s.add(map[i][j] <= 5)
for i in range(5):
for j in range(5):
for k in range(j):
s.add(map[i][j] != map[i][k])
for j in range(5):
for i in range(5):
for k in range(i):
s.add(map[i][j] != map[k][j])
for i in range(5):
for j in range(4):
if rowss[i][j] == 1:
s.add(map[i][j] > map[i][j+1])
elif rowss[i][j] == 2:
s.add(map[i][j] < map[i][j+1])
for i in range(4):
for j in range(5):
if colnms[i][j] == 2:
s.add(map[i][j] > map[i+1][j])
elif colnms[i][j] == 1:
s.add(map[i][j] < map[i+1][j])
answer = s.check()
if answer == sat:
print(s.model())
m = s.model()
flag = []
for i in map:
for j in i:
flag.append(m[j].as_long())
for i in range(len(flag)):
flag[i] += 0x30
flag[12] = 0x30
flag[18] = 0x30
flag = bytes(flag)
print(md5(flag).hexdigest())
# https://blog.csdn.net/weixin_43363675/article/details/116940992