看主函数这里,可以知道 v4 就是 unk_403040 这个数组,关键部分是 vm_operad 函数并且将 v4 传了进去

# vm_operad 函数

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
int __cdecl vm_operad(int *a1, int a2)
{
int result; // eax
char Str[200]; // [esp+13h] [ebp-E5h] BYREF
char v4; // [esp+DBh] [ebp-1Dh]
int v5; // [esp+DCh] [ebp-1Ch]
int v6; // [esp+E0h] [ebp-18h]
int v7; // [esp+E4h] [ebp-14h]
int v8; // [esp+E8h] [ebp-10h]
int v9; // [esp+ECh] [ebp-Ch]

v9 = 0;
v8 = 0;
v7 = 0;
v6 = 0;
v5 = 0;
while ( 1 )
{
result = v9;
if ( v9 >= a2 )
return result;
switch ( a1[v9] )
{
case 1:
Str[v6 + 100] = v4;
++v9;
++v6;
++v8;
break;
case 2:
v4 = a1[v9 + 1] + Str[v8];
v9 += 2;
break;
case 3:
v4 = Str[v8] - LOBYTE(a1[v9 + 1]);
v9 += 2;
break;
case 4:
v4 = a1[v9 + 1] ^ Str[v8];
v9 += 2;
break;
case 5:
v4 = a1[v9 + 1] * Str[v8];
v9 += 2;
break;
case 6:
++v9;
break;
case 7:
if ( Str[v7 + 100] != a1[v9 + 1] )
{
printf("what a shame...");
exit(0);
}
++v7;
v9 += 2;
break;
case 8:
Str[v5] = v4;
++v9;
++v5;
break;
case 10:
read(Str);
++v9;
break;
case 11:
v4 = Str[v8] - 1;
++v9;
break;
case 12:
v4 = Str[v8] + 1;
++v9;
break;
default:
continue;
}
}
}

这里 a1 就是 v4 数组,并且以其中的值来确定 case

转到 16 进制窗口,获取一下数据,因为 case 中没有 0 所以 0 可以去掉

1
2
3
4
5
6
7
8
9
0x0A,0x04,0x10,0x08, 0x03,0x05,0x01,0x04, 0x20,0x08,0x05,0x03, 0x01,0x03,0x02,0x08, 
0x0B,0x01,0x0C,0x08, 0x04,0x04,0x01,0x05, 0x03,0x08,0x03,0x21, 0x01,0x0B,0x08,0x0B,
0x01,0x04,0x09,0x08, 0x03,0x20,0x01,0x02, 0x51,0x08,0x04,0x24, 0x01,0x0C,0x08,0x0B,
0x01,0x05,0x02,0x08, 0x02,0x25,0x01,0x02, 0x36,0x08,0x04,0x41, 0x01,0x02,0x20,0x08,
0x05,0x01,0x01,0x05, 0x03,0x08,0x02,0x25, 0x01,0x04,0x09,0x08, 0x03,0x20,0x01,0x02,
0x41,0x08,0x0C,0x01, 0x07,0x22,0x07,0x3F, 0x07,0x34,0x07,0x32, 0x07,0x72,0x07,0x33,
0x07,0x18,0x07,0xA7,0xFF,0xFF,0xFF, 0x07,0x31,0x07,0xF1,0xFF,0xFF,0xFF,
0x07,0x28,0x07,0x84,0xFF,0xFF,0xFF, 0x07,0xC1,0xFF,0xFF,0xFF,0x07,0x1E, 0x07,0x7A

第一个数据是 0x0A 也就是 10,看到 case10,点进去之后发现 case10 就是输入 flag 的函数,储存在 Str 中,长度为 15

# case1

1
2
3
4
5
6
7
case 1:
Str[v6 + 100] = v4;
++v9;
++v6;
++v8;
break;

在后面的几个 case 中,分别对输入的 flag 进行了一些运算,然后我们通过 case1 来储存计算后的值的下标,即 100-114,15 个数据

# case7

1
2
3
4
5
6
7
8
9
case 7:
if ( Str[v7 + 100] != a1[v9 + 1] )
{
printf("what a shame...");
exit(0);
}
++v7;
v9 += 2;
break;

在 a1 数组中我们可以看到最后面有很多的 7,这一段就是对之前的 flag 的计算进行比较,计算后的结果是要等于 a1 数组中 7 后面的数据的,数了一下发现,刚好有 15 个 7,也就是 flag 的长度,刚好和 flag 进行比较,将这 15 个数据单独拿出来

1
0x22,0x3f,0x34,0x32,0x72,0x33,0x18,0xa7,0x31,0xf1,0x28,0x84,0xc1,0x1e,0x7a

# 总体思路

在输入 flag 之后,函数将 flag 的每一位进行一个运算,运算之后通过 case1 来储存计算后的结果,计算并储存完之后再通过 case7 来对比是否等于它已经给出的密文

# 脚本

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
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int v9 = 0;
int v8 = 0;
int v7 = 0;
int v6 = 0;
int v5 = 0;
int v4 = 0;
int last_v9 = 0,last_v8 = 0,last_v7 = 0,last_v6 = 0 ,last_v5 = 0,last_v4 = 0;

int f(int a1[],int k,int v[])
{
v4 = k;
while(1)
{
switch ( a1[v9] )
{
case 1:
if(v[v6] == v4)
{
printf("k = %d\n",k);
++v9;
++v6;
++v8;
last_v9 = v9;
last_v8 = v8;
last_v6 = v6;
return 1;
}
else
{
v9 = last_v9; //找不到则返回上一个v9
v8 = last_v8;
v6 = last_v6;
return 0;
}
break;
case 2:
v4 = a1[v9 + 1] + v4;
v9 += 2;
break;
case 3:
v4 = v4 - a1[v9 + 1];
v9 += 2;
break;
case 4:
v4 = a1[v9 + 1] ^ v4;
v9 += 2;
break;
case 5:
v4 = a1[v9 + 1] * v4;
v9 += 2;
break;
case 6:
++v9;
break;
case 11:
v4 = v4 - 1;
++v9;
break;
case 12:
v4 = v4 + 1;
++v9;
break;
default:
v9++;
break;
}
}
}
int main()
{
int v[16] = {0x22,0x3f,0x34,0x32,0x72,0x33,0x18,0xa7,0x31,0xf1,0x28,0x84,0xc1,0x1e,0x7a};

int a1[] = {0x04,0x10,0x08,0x03,0x05,0x01,0x04, 0x20,0x08,0x05,0x03, 0x01,0x03,0x02,0x08,
0x0B,0x01,0x0C,0x08, 0x04,0x04,0x01,0x05, 0x03,0x08,0x03,0x21, 0x01,0x0B,0x08,0x0B,
0x01,0x04,0x09,0x08, 0x03,0x20,0x01,0x02, 0x51,0x08,0x04,0x24, 0x01,0x0C,0x08,0x0B,
0x01,0x05,0x02,0x08, 0x02,0x25,0x01,0x02, 0x36,0x08,0x04,0x41, 0x01,0x02,0x20,0x08,
0x05,0x01,0x01,0x05, 0x03,0x08,0x02,0x25, 0x01,0x04,0x09,0x08, 0x03,0x20,0x01,0x02,
0x41,0x08,0x0C,0x01};
int i,j,k = 0;

int flag[15] = {0};

for(i = 0 ; i < 15 ; i++)
{
for(k = 31 ; k <= 'z'; k ++) //从1到z一个一个试
{
if(f(a1,k,v))
{
flag[i] = k;
break;
}
}
}
printf("flag{");
for(i = 0 ; i < 15 ; i++)
{
printf("%c",flag[i]);
}
printf("}\n");
return 0;
}

得到 flag {757515121f3d478}

题目来源:BUUCTF 在线评测 (buuoj.cn)——[网鼎杯 2020 青龙组] singal