首页
论坛
课程
招聘
[原创]KCTF2020 第二题 异常信号 WriteUp
2020-11-20 02:05 565

[原创]KCTF2020 第二题 异常信号 WriteUp

2020-11-20 02:05
565

肝了个一血出来哈哈哈

 

直接运行,然后x32dbg附加,注意开启sharpod的anti anti attach,然后ollydumpex,点search image,选择module、binary(virtual),dump下来,用ida打开,定位到sub_2F12B0函数

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
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
int sub_2F12B0()
{
  int v0; // esi
  char v1; // al
  char *v2; // ecx
  unsigned __int8 *v3; // ecx
  unsigned __int8 v4; // al
  signed int v5; // eax
  signed int v6; // ecx
  signed int length; // ebx
  unsigned __int8 v8; // al
  char v9; // al
  signed int v10; // edx
  int v11; // esi
  char v12; // cl
  char v13; // al
  char v14; // cl
  signed int v15; // eax
  __int16 v16; // ax
  signed int half_len; // ebx
  int v18; // edi
  int v19; // ecx
  signed int v20; // esi
  signed int v21; // esi
  int v22; // eax
  int v23; // eax
  __int16 v24; // ax
  int v25; // esi
  int v26; // eax
  int v27; // ecx
  int v28; // edx
  int v29; // eax
  __int16 v31; // [esp+Ch] [ebp-514h]
  int v32; // [esp+10h] [ebp-510h]
  int v33; // [esp+14h] [ebp-50Ch]
  unsigned __int16 v34; // [esp+18h] [ebp-508h]
  __int16 v35; // [esp+18h] [ebp-508h]
  int v36; // [esp+1Ch] [ebp-504h]
  int v37; // [esp+20h] [ebp-500h]
  unsigned __int8 input; // [esp+23h] [ebp-4FDh]
  int v39[100]; // [esp+24h] [ebp-4FCh]
  char flag[400]; // [esp+1B4h] [ebp-36Ch]
  char table[256]; // [esp+344h] [ebp-1DCh]
  __int16 dest[50]; // [esp+444h] [ebp-DCh]
  char v43[100]; // [esp+4A8h] [ebp-78h]
  char v44; // [esp+50Ch] [ebp-14h]
  int v45; // [esp+50Dh] [ebp-13h]
  int v46; // [esp+511h] [ebp-Fh]
  int v47; // [esp+515h] [ebp-Bh]
  __int16 v48; // [esp+519h] [ebp-7h]
 
  v0 = *(_DWORD *)&unk_314474;
  memset(*(_DWORD *)&unk_314474, 0, 256);
  v1 = *(_BYTE *)&unk_3138B0;
  if ( *(_BYTE *)&unk_3138B0 )
  {
    v2 = (_BYTE *)&unk_3138B0;
    do
    {
      *(++v2 - 1) = v1 ^ 0xE4;
      v1 = *v2;
    }
    while ( *v2 );
  }
  sub_2F5D70(v0, 0x3138B0, strlen((const char *)&unk_3138B0) + 1);// Correct
  v45 = 0x9091948A;
  v46 = 0x9681B7C4;
  v3 = (unsigned __int8 *)&v44;
  v47 = 0xDE88858D;
  v4 = 0xADu;
  v48 = 0xEE;
  do
  {
    *(++v3 - 1) = v4 ^ 0xE4;
    v4 = *v3;
  }
  while ( *v3 );                                // Input Serial:\n
  v36 = **(_DWORD **)(*(_DWORD *)(*(_DWORD *)&unk_314478 + 0x3C) + *(_DWORD *)&unk_314478 + 0x28);
  printf(&v44);
  memset(v39, 0, 400);
  memset(dest, 0, 200);
  memset(flag, 255, 400);
  memset(table, 255, 256);
  v5 = 0;
  do
  {
    table[v5 + '0'] = v5;
    ++v5;
  }
  while ( v5 <= 9 );
  v6 = 0;
  do
  {
    table[v6 + 'A'] = v6 + 10;
    ++v6;
  }
  while ( v6 <= 5 );
  input = 0;
  length = 0;
  scanf(0x310CDC, &input, 1);                   // %c
  v8 = input;
  if ( input != 10 )
  {
    while ( length < 360 )
    {
      v9 = table[v8];
      if ( v9 == -1 )
      {
LABEL_52:
        printf(0x310C6C);
        printf(0x310C80);                       // 一个礼貌的开场白还是要的
        return 0;
      }
      flag[length++] = v9;
      scanf(0x310CDC, &input, 1);               // %c
      v8 = input;
      if ( input == '\n' )
        goto LABEL_14;
    }
LABEL_32:
    printf(0x310C6C);
    printf(0x310C70);                           // \f换页
    return 0;
  }
LABEL_14:
  v10 = 0;
  if ( length > 0 )
  {
    v11 = *(_DWORD *)&unk_31446C;
    while ( v11 )
    {
      v12 = flag[v10];
      if ( v12 == -1 )
        goto LABEL_52;
      v13 = flag[v10 + 1];
      if ( v13 == -1 )
        goto LABEL_52;
      v14 = v13 + 16 * v12;
      v15 = v10 >> 1;
      v10 += 2;
      *((_BYTE *)dest + v15) = v14;             // 4个输入构成一个int16
      if ( v10 >= length )
        goto LABEL_20;
    }
    goto LABEL_32;
  }
LABEL_20:
  v16 = 0;
  half_len = length >> 2;
  v18 = 0;
  v33 = 0;
  if ( half_len <= 0 )
    goto LABEL_57;
  v19 = 0;
  v32 = 0;
  do
  {
    v34 = -v16;
    sub_2F6305(v19);
    v20 = 0x65;
    do
    {
      v31 = sub_2F62E4();
      --v20;
    }
    while ( v20 );
    sub_2F6305(v34);
    v21 = 0x65;
    do
    {
      v35 = sub_2F62E4();
      --v21;
    }
    while ( v21 );
    v22 = (*(int (__stdcall **)(_DWORD))&unk_30B144)(0);// GetActiveWindow(0)
    if ( v22 )
    {
      v23 = (*(int (__stdcall **)(int))&unk_30B148)(v22);// GetWindowLongA(v22,GWL_STYLE)
      if ( v23 == 0x17CF0000 || v23 == 0x16CF0000 || v23 == 0x97CF0000 )
        goto LABEL_51;
    }
    v24 = dest[v32];                            // v32是满足条件v31的输入的index
    if ( v31 == v24 )
    {
      v39[v18++] = v32;                         // 将满足条件的index入队
    }
    else if ( v35 != v24 )
    {
      goto LABEL_52;
    }
    v16 = v33++ + 1;
    v19 = (unsigned __int16)v33;
    v32 = (unsigned __int16)v33;
  }
  while ( (unsigned __int16)v33 < half_len );
  if ( v18 < 12 )
  {
LABEL_57:
    printf(0x310C6C);
    printf(0x310C9C);                           // 行星太少了
    return 0;
  }
  if ( v39[1] - v39[0] > *(&v37 + v18) - *(&v36 + v18) )
  {
    printf(0x310C6C);
    printf(0x310CA8);                           // 穿越虫洞还得讲个顺序
    return 0;
  }
  memset(v43, 0, 100);
  v25 = 0;
  if ( v18 - 1 <= 0 )
  {
LABEL_49:
    if ( !*(_DWORD *)&unk_314470 )
      return 0;
    printf(*(_DWORD *)&unk_314474);             // Correct
    return 0;
  }
  v26 = v36;
  while ( 1 )
  {
    if ( !v26 )
    {
      printf(0x310C6C);
      printf(0x310C80);                         // 一个礼貌的开场白还是要的
LABEL_47:
      v26 = v36;
      goto LABEL_48;
    }
    v27 = v25 + 1;
    if ( v25 + 1 < v18 )
      break;
LABEL_48:
    if ( ++v25 >= v18 - 1 )
      goto LABEL_49;
  }
  v28 = v39[v25];
  while ( 1 )
  {
    v29 = v39[v27] - v28;                       // v29每次必须不一样
    if ( v43[v29] )                             // 队列中的indexes,两两的差必须唯一
      break;
    ++v27;
    v43[v29] = 1;
    if ( v27 >= v18 )
      goto LABEL_47;
  }
LABEL_51:
  printf(0x310C6C);
  printf(0x310CC0);                             // 有人说这题根本无解,你信吗
  return 0;
}

遇到30xxxx的地址,从x32dbg里查看到底是啥内容

 

解密脚本:

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
int gl;
 
int sub_2F62E4()
{
    unsigned int v1;
 
    v1 = 214013 * gl + 2531011;
    gl = v1;
    return (v1 >> 16) & 0x7FFF;
}
 
void sub_2F6305(int a1)
{
    gl = a1;
}
 
int main()
{
    int list[] = { 0,2,6,24,29,40,43,55,68,75,76,85 };
    __int16 v16, v31, v35;
    unsigned __int16 v34;
    int v18, v19, v20, v21, v32;
    int n = 85;
    v16 = 0;
    v19 = 0;
    v32 = 0;
    do
    {
        v34 = -v16;
        sub_2F6305(v19);
        v20 = 0x65;
        do
        {
            v31 = sub_2F62E4();
            --v20;
        } while (v20);
        sub_2F6305(v34);
        v21 = 0x65;
        do
        {
            v35 = sub_2F62E4();
            --v21;
        } while (v21);
        bool isin = false;
        for (size_t i = 0; i < 12; i++)
        {
            if (list[i] == v16)
            {
                isin = true;
                break;
            }
        }
        if (isin)
        {
            if ((v31 / 16 & 0xF) < 10)
                cout << (v31 / 16 & 0xF);
            else
                cout << char((v31 / 16 & 0xF) - 10 + 'A');
            if ((v31 & 0xF) < 10)
                cout << (v31 & 0xF);
            else
                cout << char((v31 & 0xF) - 10 + 'A');
            if ((v31 / 4096 & 0xF) < 10)
                cout << (v31 / 4096 & 0xF);
            else
                cout << char((v31 / 4096 & 0xF) - 10 + 'A');
            if ((v31 / 256 & 0xF) < 10)
                cout << (v31 / 256 & 0xF);
            else
                cout << char((v31 / 256 & 0xF) - 10 + 'A');
        }
        else
        {
            if ((v35 / 16 & 0xF) < 10)
                cout << (v35 / 16 & 0xF);
            else
                cout << char((v35 / 16 & 0xF) - 10 + 'A');
            if ((v35 & 0xF) < 10)
                cout << (v35 & 0xF);
            else
                cout << char((v35 & 0xF) - 10 + 'A');
            if ((v35 / 4096 & 0xF) < 10)
                cout << (v35 / 4096 & 0xF);
            else
                cout << char((v35 / 4096 & 0xF) - 10 + 'A');
            if ((v35 / 256 & 0xF) < 10)
                cout << (v35 / 256 & 0xF);
            else
                cout << char((v35 / 256 & 0xF) - 10 + 'A');
        }
        v16++;
        v19++;
        v32++;
    } while (n--);
    return 0;
}

list中的值要满足的条件是:从[0,89]中选择12个互不相同的数,使它们任意两个数之差都不相同,这里我问了4位ACM金牌爷,2个多小时后有人把结果弄出来了

 

程序执行的思路就是,读入至多360个字符,只能0-9、A-F,把每4个输入组合成一个类似0x1234这样的short,如果与v31一样,则把下标加入v39队列,如果与v35一样,则继续,如果都不一样,则结束程序。如果v39的元素小于12则结束程序。之后会判断v39里面的数(也就是满足v31的输入的下标)是否两两之差唯一,是的话就输出Correct。

 

生成v31和v35的算法是可以本地写代码跑出来的,只要输入的满足“下标不在list里的输入等于v35,否则等于v31”的就是flag

 

补充金牌爷算法:

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
#include <stdio.h>
 
int c[100];
int a[100];
int abs(int x)
{
    return x <0?-x:x;
}
 
void work(int x,int n,int k)
{
    for(int i=0;i<n;i++)
        c[x-a[i]]+=k;
}
 
int check(int x,int n)
{
    for(int i=0;i<n;i++)
    {
        if(c[x-a[i]] > 0)
            return 0;
    }
    return 1;
}
 
void dfs(int now,int n)
{
    if(n == 12)
    {
        for(int i=0;i<n;i++)
        {
            printf("%d,",a[i]);
        }
        printf("\n");
    }
    if(90-now < 12-n) return ;
    for(int i=now;i<=89;i++)
    {
        if(check(i,n))
        {
            work(i,n,1);
            a[n] = i;
            dfs(i+1,n+1);
            work(i,n,-1);
        }
    }
}
int main()
{
    dfs(0,0);
    return 0;
}

注意,这段代码能跑出多组解,但是其余解不满足程序的要求,会输出“穿越虫洞要讲究顺序”,因此上面那组解应该是唯一满足题目限制的解


[培训]12月3日2020京麒网络安全大会《物联网安全攻防实战》训练营,正在火热报名中!地点:北京 · 新云南皇冠假日酒店

最后于 2020-11-20 20:24 被古月浪子编辑 ,原因: 补充求解算法
收藏
点赞0
打赏
分享
最新回复 (1)
雪    币: 4082
活跃值: 活跃值 (287)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
如斯咩咩咩 活跃值 2020-11-21 17:11
2
0
冲了
游客
登录 | 注册 方可回帖
返回