GACTF2020

做了2个逆向及杂项还是有收获。

Reverse:

WannaFlag

打开程序,炫酷的界面和音乐。其实就是一个CrackeMe。image-20200913175304433

载入ida,直接定位到GetwindowText或者GetDlgItemText函数。

image-20200913174317321

关键就是那个复杂的if语句根本就执行不到,直接跳过。

对input进行了xor与ror操作。直接逆即可:

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
#include <stdio.h>
#include <math.h>

unsigned char ida_[] =
{
78, 174, 97, 186, 228, 43, 85, 170, 89, 252,
77, 2, 23, 107, 19, 161, 65, 254, 53, 11,
180, 11, 82, 47, 70, 204, 53, 130, 229, 136,
80
};

int main(void)
{
int v37 = 6, v38 = 1;
unsigned char i, j, len = 31;
char s[] = "ANNAWGALFYBKVIAHMXTFCAACLAAAAYK";

for(i = 2; i < v37; i++)
v38 *= i;
for(i = 0; i < len; i++)
{
unsigned char temp = i%8;
int ans = ((ida_[i] >> temp) | (ida_[i]&((int)pow(2, temp)-1)) << 8-temp) ^ s[i]; //rol
putchar(ans ^ v38);
}

return 0;
}
//wannaflag_is_just_a_paper_tiger

最后将其输入程序的输入框,打开解密后的flag文件得到flag。

image-20200913174837508

EasyRe

ida打开后,main函数sp-analysis failed,简单修复一下栈指针就行。

image-20200913182620575

来到main函数,首先是输入一个整型数据,从交叉引用发现对该数据后面并没有使用,猜测对函数代码有一个解密过程。发现后面有一个mprotect()函数修改指定内存保护属性,即把要解密的代码的内存改为可写。接着的函数实现解密一个函数代码功能。

动调来到解密的函数(这里我也反复修了几次,才完美的F5):一个VM且前面已经申请了存放数据的内存空间和初始化。

image-20200913225646159

开始是单步调试,由于分支比较多,后面将整个函数与opcode复制到C语言编译器,打印出函数根据opcode的执行情况:

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
a1[1] = input
a1[9] = a1[1]
a1[2] = 13
a1[1] >>= a1[2]
a1[1] ^= a1[9]
a1[9] = a1[1]
a1[2] = 9
a1[1] <<= a1[2]
a1[2] = 2029229568
a1[1] &= a1[2]
a1[1] ^= a1[9]
a1[9] = a1[1]
a1[2] = 17
a1[1] <<= a1[2]
a1[2] = -2049703936
a1[1] &= a1[2]
a1[1] ^= a1[9]
a1[9] = a1[1]
a1[2] = 19
a1[1] >>= a1[2]
a1[1] ^= a1[9]
if ( a1[1] == 653840640 ) exit(0) //首先根据输入的整型数据经过运算后与指定值进行比较,不正确则退出。

a1[1] = input //下面依次使用输入的整型数据经过运算后存放入dword_804B2A0数组。
a1[2] = 255
a1[1] &= a1[2]
a1[3] = 2
a1[1] *= a1[3]
a1[2] = 24
a1[1] += a1[2]
dword_804B2A0[0] = a1[1]

a1[1] = input
a1[2] = 8
a1[1] >>= a1[2]
a1[2] = 255
a1[1] &= a1[2]
a1[5] = 7
a1[1] /= a1[5]
a1[2] = 33
a1[1] += a1[2]
dword_804B2A0[1] = a1[1]

a1[1] = input
a1[2] = 16
a1[1] >>= a1[2]
a1[2] = 255
a1[1] &= a1[2]
a1[9] = 187
a1[1] ^= a1[9]
a1[2] = 255
a1[1] += a1[2]
dword_804B2A0[2] = a1[1]

a1[1] = 10000
a1[2] = 24
a1[1] >>= a1[2]
a1[2] = 255
a1[1] &= a1[2]
a1[4] = 160
a1[1] -= a1[4]
a1[2] = 119
a1[1] += a1[2]
dword_804B2A0[3] = a1[1]

printf("flag:") //输入flag

a1[1] = (unsigned __int8)buf[0] //下面依次对输入的flag的每一位与dword_804B2A0中的数据异或,
a1[9] = dword_804B2A0[0]
a1[1] ^= a1[9]
if ( a1[1] != 267 ) exit(0) //并与指定数据比较,不相等则退出。

a1[1] = (unsigned __int8)buf[1]
a1[9] = dword_804B2A4
a1[1] ^= a1[9]
if ( a1[1] != 122 ) exit(0)

a1[1] = (unsigned __int8)buf[2]
a1[9] = dword_804B2AC
a1[1] ^= a1[9]
if ( a1[1] != 149 ) exit(0)

a1[1] = (unsigned __int8)buf[3]
a1[9] = dword_804B2A8
a1[1] ^= a1[9]
if ( a1[1] != 262 ) exit(0)

a1[1] = (unsigned __int8)buf[4]
a1[9] = dword_804B2A4
a1[1] ^= a1[9]
if ( a1[1] != 125 ) exit(0)

a1[1] = (unsigned __int8)buf[5]
a1[9] = dword_804B2AC
a1[1] ^= a1[9]
if ( a1[1] != 173 ) exit(0)

a1[1] = (unsigned __int8)buf[6]
a1[9] = dword_804B2A0[0]
a1[1] ^= a1[9]
if ( a1[1] != 303 ) exit(0)

a1[1] = (unsigned __int8)buf[7]
a1[9] = dword_804B2A8
a1[1] ^= a1[9]
if ( a1[1] != 357 ) exit(0)

a1[1] = (unsigned __int8)buf[8]
a1[9] = dword_804B2A0[0]
a1[1] ^= a1[9]
if ( a1[1] != 301 ) exit(0)

a1[1] = (unsigned __int8)buf[9]
a1[9] = dword_804B2A0[0]
a1[1] ^= a1[9]
if ( a1[1] != 303 ) exit(0)

a1[1] = (unsigned __int8)buf[10]
a1[9] = dword_804B2A8
a1[1] ^= a1[9]
if ( a1[1] != 313 ) exit(0)

a1[1] = (unsigned __int8)buf[11]
a1[9] = dword_804B2A8
a1[1] ^= a1[9]
if ( a1[1] != 269 ) exit(0)

a1[1] = (unsigned __int8)buf[12]
a1[9] = dword_804B2AC
a1[1] ^= a1[9]
if ( a1[1] != 187 ) exit(0)

a1[1] = (unsigned __int8)buf[13]
a1[9] = dword_804B2A4
a1[1] ^= a1[9]
if ( a1[1] != 8 ) exit(0)

a1[1] = (unsigned __int8)buf[14]
a1[9] = dword_804B2A8
a1[1] ^= a1[9]
if ( a1[1] != 269 ) exit(0)

a1[1] = (unsigned __int8)buf[15]
a1[9] = dword_804B2A0[0]
a1[1] ^= a1[9]
if ( a1[1] != 319 ) exit(0)

a1[1] = (unsigned __int8)buf[16]
a1[9] = dword_804B2A8
a1[1] ^= a1[9]
if ( a1[1] != 314 ) exit(0)

a1[1] = (unsigned __int8)buf[17]
a1[9] = dword_804B2A8
a1[1] ^= a1[9]
if ( a1[1] != 353 ) exit(0)

a1[1] = (unsigned __int8)buf[18]
a1[9] = dword_804B2A4
a1[1] ^= a1[9]
if ( a1[1] != 87 ) exit(0)

a1[1] = (unsigned __int8)buf[19]
a1[9] = dword_804B2A0[0]
a1[1] ^= a1[9]
if ( a1[1] != 288 ) exit(0)

a1[1] = (unsigned __int8)buf[20]
a1[9] = dword_804B2A8
a1[1] ^= a1[9]
if ( a1[1] != 269 ) exit(0)

a1[1] = (unsigned __int8)buf[21]
a1[9] = dword_804B2A0[0]
a1[1] ^= a1[9]
if ( a1[1] != 319 ) exit(0)

a1[1] = (unsigned __int8)buf[22]
a1[9] = dword_804B2A8
a1[1] ^= a1[9]
if ( a1[1] != 319 ) exit(0)

a1[1] = (unsigned __int8)buf[23]
a1[9] = dword_804B2AC
a1[1] ^= a1[9]
if ( a1[1] != 181 ) exit(0)

a1[1] = (unsigned __int8)buf[24]
a1[9] = dword_804B2A0[0]
a1[1] ^= a1[9]
if ( a1[1] != 275 ) exit(0)

a1[1] = (unsigned __int8)buf[25]
a1[9] = dword_804B2AC
a1[1] ^= a1[9]
if ( a1[1] != 160 ) exit(0)

a1[1] = (unsigned __int8)buf[26]
a1[9] = dword_804B2A0[0]
a1[1] ^= a1[9]
if ( a1[1] != 289 ) exit(0)

a1[1] = (unsigned __int8)buf[27]
a1[9] = dword_804B2A8
a1[1] ^= a1[9]
if ( a1[1] != 269 ) exit(0)

a1[1] = (unsigned __int8)buf[28]
a1[9] = dword_804B2A4
a1[1] ^= a1[9]
if ( a1[1] != 11 ) exit(0)

a1[1] = (unsigned __int8)buf[29]
a1[9] = dword_804B2A8
a1[1] ^= a1[9]
if ( a1[1] != 313 ) exit(0)

a1[1] = (unsigned __int8)buf[30]
a1[9] = dword_804B2A0[0]
a1[1] ^= a1[9]
if ( a1[1] != 371 ) exit(0)

a1[1] = (unsigned __int8)buf[31]
a1[9] = dword_804B2A4
a1[1] ^= a1[9]
if ( a1[1] != 70 ) exit(0)

从上面打印出的执行,整个程序就很清楚了。

首先就是逆出输入的第一个整型数据,但看了下算法,确实麻烦。。正好之前学习了下angr符号执行,用这个题尝试一下。

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
import angr, claripy, sys

def is_success(state):
std_out = state.posix.dumps(1)
if b'flag:' in std_out:
return True
else:
return False

def is_false(state):
std_out = state.posix.dumps(1)
if b'错误信息' in std_out:
return True
else:
return False

proj = angr.Project('EasyRe') #代表程序的“初始化映像”
init_state = proj.factory.entry_state() #表示模拟从程序的入口点开始的状态
good_addr = 0x08048BE0
bad_addr = 0x08048BF1
sim = proj.factory.simgr(init_state) #申明state的模拟管理器sim来用于执行模拟的程序
sim.explore(find = is_success) #模拟程序的执行

for i in range(3): #打印出标准输入,输出,错误输出。
print(sim.found[0].posix.dumps(i))

image-20200913202220431

很快得到了答案,尝试输入一下,正确。image-20200913200733625

开始使用这个整型数据计算出dword_804B2A0数组中的4个值。

刚刚计算完准备依次逆出flag中的每一位时就想到,其实可以不用计算第一个输入的整型数据,因为给了flag的格式为GACTF。。。根据这个就可以计算出dword_804B2A0数组中的每一位。。

开始利用之前复制到C语言编译器中的代码打印出flag中每一位与dword_804B2A0[]数组异或的是哪一位,与最后需要用来比较的数据。

最后算一下,得到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
#include <stdio.h>

unsigned int a[] = {267, 122, 149, 262, 125, 173, 303, 357,
301, 303, 313, 269, 187, 8, 269, 319,
314, 353, 87, 288, 269, 319, 319, 181,
275, 160, 289, 269, 11, 313, 371, 70};

char b[] = {0, 1, 3, 2, 1, 3, 0, 2, 0, 0, 2,
2, 3, 1, 2, 0, 2, 2, 1, 0, 2, 0,
2, 3, 0, 3, 0, 2, 1, 2, 0, 1};

int main(void)
{
char flag[33] = {0};
int s[4] = {0}, i = 0;

s[0] = 'G' ^ a[0];
s[1] = 'A' ^ a[1];
s[2] = 'T' ^ a[3];
s[3] = 'C' ^ a[2];

for(i = 0; i < 32; i++)
flag[i] = a[i] ^ s[b[i]];

puts(flag);

return 0;
}

//GACTF{c7ack_m3_sh3ll_smc_vm_0k?}

其实也是很简单的一个题,利用flag的格式。。

Misc

SignIN

将二维码拼接一下,扫出flag。

image-20200913201331681

welc0me_t0_GACTF_have_Fun

crymisc

一个word文档,但是打不开。拖进010editor查看,发现504B(PK),是一个压缩包。

改文件后缀后其中一个文件解压失败。

image-20200913202250211

谷歌看见可以使用winrar来修复一下。

下载后,在winrar中打开却是提示有密码。。那就猜测是伪加密了。。下面修改为偶数即可。image-20200913203212543

360压缩还是奇妙,伪加密文件直接提示文件出错。。

然后打开一张图片和一个文本文件。文本文件没有什么,查看图片二进制信息。在最后发现:image-20200913203442903

又是一个压缩包,但在开头出现一个字符串,从组成猜测是base64加密。解密得到:image-20200913203556733

看来是后面压缩包的密码。然后把该压缩包的头补全(504b),输入密码解压:image-20200913203858388

到这一步就一直卡住了。。。实在没见过这个表情密码,谷歌百度查到的信息又不会用,,,

比赛完了才从一位师傅哪里知道要自己从github找到这个项目自己搭建一下环境,然后根据一张表映射关系解密。。。

根据师傅的指导搭建完后,从题目也很容易想到解密的key是哭脸表情。

最后解密:image-20200913204636657

还有一个坑就是,下面的表情其实是同一个,我还纠结半天。。image-20200913204805711

-------------本文结束感谢您的阅读-------------