Google CTF 2021

Google CTF 2021

真 一边做题一边学知识的比赛。

CPP

题目下载: cpp.c

这道题目只给了一个 C 源文件,用宏定义实现了逻辑电路,有接近 6000 行代码,人吓傻了。

ROM 定义

1
2
3
...
#define ROM_01000010_4 1
...

其中 01000010 是 ROM 地址二进制, 4 表示第 4 个 bit 位, 1 表示值是 1

输入的 flag 起始 ROM 地址 10000000 (128) 地址,一共 26 字节 flag, 占 ROM 地址 128 ~ 128 + 26

ROM 访问指令

1
2
3
4
5
#define _LD(x, y) ROM_ ## x ## _ ## y
#define LD(x, y) _LD(x, y)
#define _MA(l0, l1, l2, l3, l4, l5, l6, l7) l7 ## l6 ## l5 ## l4 ## l3 ## l2 ## l1 ## l0
#define MA(l0, l1, l2, l3, l4, l5, l6, l7) _MA(l0, l1, l2, l3, l4, l5, l6, l7)
#define l MA(l0, l1, l2, l3, l4, l5, l6, l7)

l0 ~ l7 是对 ROM 进行寻址的参数。

指令逻辑

类似 vm 吧,S 定义类似于指令计数器,利用嵌套 include 实现重新进入指令 dispatch.
C语言里的 #define X 表示 X = 1, #undef X 表示 X = 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
S == 0  // GOTO 24
S == 1 // R = ~R
S == 3 // R = R + Z
S == 4 // R = R + Z
S == 5 // IF(!R) GOTO 38 ELSE 6
S == 6 // R = R + Z
S == 7 // IF(!R) GOTO 59 ELSE 8
S == 8 // R = R + Z
S == 9 // IF(!R) GOTO 59 ELSE 10
S == 10 // BUG
S == 11 // GOTO -1
S == 12 // X = 1
S == 13 // Y = 0
S == 14 // IF(!X) GOTO 22 ELSE 15
S == 15 // Z = X
S == 16 // Z = Z & B
S == 17 // IF(!Z) GOTO 19 ELSE 18
S == 18 // Y = Y + A
S == 19 // X = X + X
S == 20 // A = A + A
S == 21 // GOTO 14
S == 22 // A = Y
S == 23 // GOTO 1
S == 24 // I = 0
S == 25 // M = 0
S == 26 // N = 1
S == 27 // S = 0
S == 28 // Q = 0
S == 29 // B = 229
S == 30 // B = B + I
S == 31 // IF(!B) Goto 56 ELSE 32
S == 32 // B = 128
S == 33 // B = B + I
S == 34 // A = ROM[B]
S == 35 // B = ROM[I]
S == 36 // R = 1
S == 37 // Goto 12
S == 38 // O = M
S == 39 // O = O + N
S == 40 // M = N
S == 41 // N = O
S == 42 // A = A + M
S == 43 // B = 32
S == 44 // B = B + I
S == 45 // C = ROM[B]
S == 46 // A = C ^ A
S == 47 // P = P + A
S == 48 // B = 64
S == 49 // B = B + I
S == 50 // A = ROM[B]
S == 51 // A = A ^ P
S == 52 // Q = Q | A
S == 53 // A = 1
S == 54 // I = I + A
S == 55 // GOTO 29
S == 56 // IF(!Q) GOTO 58 ELSE 57
S == 58 // GOTO -1

用 C语言重写并 Recompile

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

unsigned char R = 0, Z = 0, X = 0, Y = 0, B = 0, A = 0, I = 0, M = 0, N = 0, S = 0, Q = 0, O = 0, C = 0, P = 0;
void bug() {
printf("bug\n");
}
void invalid() {
printf("invalid\n");
exit(0);
}
void success() {
printf("success\n");
exit(0);
}
int main() {
unsigned char ROM[500] = { 0xBB, 0x55, 0xAB, 0xC5, 0xB9, 0x9D, 0xC9, 0x69, 0xBB, 0x37, 0xD9, 0xCD, 0x21, 0xB3, 0xCF, 0xCF,
0x9F, 0x09, 0xB5, 0x3D, 0xEB, 0x7F, 0x57, 0xA1, 0xEB, 0x87, 0x67, 0x23, 0x17, 0x25, 0xD1, 0x1B,
0x08, 0x64, 0x64, 0x35, 0x91, 0x64, 0xE7, 0xA0, 0x06, 0xAA, 0xDD, 0x75, 0x17, 0x9D, 0x6D, 0x5C,
0x5E, 0x19, 0xFD, 0xE9, 0x0C, 0xF9, 0xB4, 0x83, 0x86, 0x22, 0x42, 0x1E, 0x57, 0xA1, 0x28, 0x62,
0xFA, 0x7B, 0x1B, 0xBA, 0x1E, 0xB4, 0xB3, 0x58, 0xC6, 0xF3, 0x8C, 0x90, 0x3B, 0xBA, 0x19, 0x6E,
0xCE, 0xDF, 0xF1, 0x25, 0x8D, 0x40, 0x80, 0x70, 0xE0, 0x4D, 0x1C };
printf("input flag:\n");
scanf("%s", &ROM[128]);
_0: goto _24;
_1: R = ~R;
_2: Z = 1;
_3: R = R + Z;
_4: R = R + Z;
_5: if(!R) goto _38; else goto _6;
_6: R = R + Z;
_7: if(!R) goto _59; else goto _8;
_8: R = R + Z;
_9: if(!R) goto _59; else goto _10;
_10: bug();
_11: goto end;
_12: X = 1;
_13: Y = 0;
_14: if(!X) goto _22; else goto _15;
_15: Z = X;
_16: Z = Z & B;
_17: if(!Z) goto _19; else goto _18;
_18: Y = Y + A;
_19: X = X + X;
_20: A = A + A;
_21: goto _14;
_22: A = Y;
_23: goto _1;
_24: I = 0;
_25: M = 0;
_26: N = 1;
_27: S = 0;
_28: Q = 0;
_29: B = 229;
_30: B = B + I;
_31: if(!B) goto _56; else goto _32;
_32: B = 128;
_33: B = B + I;
_34: A = ROM[B];
_35: B = ROM[I];
_36: R = 1;
_37: goto _12;
_38: O = M;
_39: O = O + N;
_40: M = N;
_41: N = O;
_42: A = A + M;
_43: B = 32;
_44: B = B + I;
_45: C = ROM[B];
_46: A = C ^ A;
_47: P = P + A;
_48: B = 64 ;
_49: B = B + I;
_50: A = ROM[B];
_51: A = A ^ P;
_52: Q = Q | A;
_53: A = 1;
_54: I = I + A;
_55: goto _29;
_56: if(!Q) goto _58; else goto _57;
_57: invalid();
_58: success();
_59: invalid();
end:
printf("end..\n");
}

recompile 之后用 angr 直接可以解出 flag

1
2
3
4
5
6
from angr import *  
p = Project("./cpp")
state = p.factory.blank_state(addr=0x400000 + 0x1200)
sm = p.factory.simulation_manager(state)
res = sm.explore(find = [0x400000 + 0x1760], avoid=[0x400000+0x11C0, 0x400000+0x13D3])
print(res.found[0].posix.dumps(0)) # CTF{pr3pr0cess0r_pr0fe5sor}

Weather

题目下载: weather.zip

用 IDA 打开分析,main 函数里看不懂这道题想做什么,在翻 init_array 中的函数时发现,这道题目注册了格式化字符串回调。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
register_printf_function('W', T0, arginfo);
register_printf_function('P', T1, arginfo);
register_printf_function('T', T2, arginfo);
register_printf_function('F', (printf_function *)T3, arginfo);
register_printf_function('C', T4, retZero);
register_printf_function('M', T5, retZero);
register_printf_function('S', T6, retZero);
register_printf_function('O', T7, retZero);
register_printf_function('X', T8, retZero);
register_printf_function('V', T9, retZero);
register_printf_function('N', T10, retZero);
register_printf_function('L', T11, retZero);
register_printf_function('R', T12, retZero);
register_printf_function('E', T13, retZero);
register_printf_function('I', T14, retZero);
register_printf_function('U', T15, retZero);

例如,格式化字符串中存在 “%W” 就回调用 T0 函数来处理 %W 参数。简单分析后发现有嵌套结构意味着这是一个用格式化字符串实现的vm。

逆向 T4 ~ T15 函数对格式化串的解析,可以得出指令及操作数解析规则。

print_info 结构体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
truct printf_info
{
int prec; /* Precision. */
int width; /* Width. */
wchar_t spec; /* Format letter. */
unsigned int is_long_double:1;/* L flag. */
unsigned int is_short:1; /* h flag. */
unsigned int is_long:1; /* l flag. */
unsigned int alt:1; /* # flag. */
unsigned int space:1; /* Space flag. */
unsigned int left:1; /* - flag. */
unsigned int showsign:1; /* + flag. */
unsigned int group:1; /* ' flag. */
unsigned int extra:1; /* For special use. */
unsigned int is_char:1; /* hh flag. */
unsigned int wide:1; /* Nonzero for wide character streams. */
unsigned int i18n:1; /* I flag. */
unsigned int is_binary128:1; /* Floating-point argument is ABI-compatible
with IEC 60559 binary128. */
unsigned int __pad:3; /* Unused so far. */
unsigned short int user; /* Bits for user-installed modifiers. */
wchar_t pad; /* Padding character. */
};

指令解析规则

1
2
3
4
5
6
*(info + 12) & 0x20 => -
*(info + 12) & 0x40 => +
*(info + 13) & 2 => hh
*(info + 12) & 1 => ll
*(info + 12) & 2 => h
*(info + 12) & 4 => l

根据 vm 指令规则写了一个 decompiler

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
import re
def addrResolver(addr):
return "_" + str(addr)

def decompiler(insn, addr):
regx = r"%([-+]?)([0-9]*)(\.)?([0-9]*)([h|l|ll|hh|w]*)([IWPTFCMSOXVNLREU])"
rr = re.match(regx, insn, flags=re.X|re.M)
if rr is None:
print(insn)
return
parser = rr.groups()
# print(insn, parser)
op_name = parser[-1]
op1 = parser[1]
op2 = parser[3]
type2 = parser[-2]
type1 = parser[0]
if op_name == "C":
if type1 == "-":
return "%s: if (state[%s] < 0) goto %s" % (addrResolver(addr), op2, addrResolver(op1)), rr.group(0)
if type1 == "+":
return "%s: if (state[%s] > 0) goto %s" % (addrResolver(addr), op2, addrResolver(op1)), rr.group(0)
if parser[1][0] == "0":
op1 = op1[1:]
return "%s: if (state[%s] == 0) goto %s" % (addrResolver(addr), op2, addrResolver(op1)), rr.group(0)
return "%s: goto %s" % (addrResolver(addr), addrResolver(op1)), rr.group(0)
if op_name == "M":
if type1 == "-":
target = "a52cS[%s]" % str(op1)
elif type1 == "+":
target = "a52cS[state[%s]]" % str(op1)
else:
target = "state[%s]" % str(op1)

if type2 == "hh":
source = "a52cS[%s]" % str(op2)
elif type2 == "h": ## ??
source = "a52cS[state[%s]]" % str(op2)
elif type2 == "ll":
source = str(op2)
elif type2 == "l":
source = "state[%s]" % str(op2)
else:
source = "0"
return "%s: %s = %s" % (addrResolver(addr), target, source), rr.group(0)
if op_name == "S":
if type1 == "-":
target = "a52cS[%s]" % str(op1)
elif type1 == "+":
target = "a52cS[state[%s]]" % str(op1)
else:
target = "state[%s]" % str(op1)
if type2 == "hh":
source = "a52cS[%s]" % str(op2)
elif type2 == "h":
source = "a52cS[state[%s]]" % str(op2)
elif type2 == "ll":
source = str(op2)
elif type2 == "l":
source = "state[%s]" % str(op2)
else:
source = "0"
return "%s: %s += %s" % (addrResolver(addr), target, source), rr.group(0)
if op_name == "O":
if type1 == "-":
target = "a52cS[%s]" % str(op1)
elif type1 == "+":
target = "a52cS[state[%s]]" % str(op1)
else:
target = "state[%s]" % str(op1)

if type2 == "hh":
source = "a52cS[%s]" % str(op2)
elif type2 == "h":
source = "a52cS[state[%s]]" % str(op2)
elif type2 == "ll":
source = str(op2)
elif type2 == "l":
source = "state[%s]" % str(op2)
else:
source = "0"
return "%s: %s -= %s" % (addrResolver(addr), target, source), rr.group(0)
if op_name == "X":
if type1 == "-":
target = "a52cS[%s]" % str(op1)
elif type1 == "+":
target = "a52cS[state[%s]]" % str(op1)
else:
target = "state[%s]" % str(op1)
if type2 == "hh":
source = "a52cS[%s]" % str(op2)
elif type2 == "h":
source = "a52cS[state[%s]]" % str(op2)
elif type2 == "ll":
source = str(op2)
elif type2 == "l":
source = "state[%s]" % str(op2)
else:
source = "0"
return "%s: %s *= %s" % (addrResolver(addr), target, source),rr.group(0)
if op_name == "V":
if type1 == "-":
target = "a52cS[%s]" % str(op1)
elif type1 == "+":
target = "a52cS[state[%s]]" % str(op1)
else:
target = "state[%s]" % str(op1)
if type2 == "hh":
source = "a52cS[%s]" % str(op2)
elif type2 == "h":
source = "a52cS[state[%s]]" % str(op2)
elif type2 == "ll":
source = str(op2)
elif type2 == "l":
source = "state[%s]" % str(op2)
else:
source = "0"
return "%s: %s /= %s" % (addrResolver(addr), target, source), rr.group(0)
if op_name == "N":
if type1 == "-":
target = "a52cS[%s]" % str(op1)
elif type1 == "+":
target = "a52cS[state[%s]]" % str(op1)
else:
target = "state[%s]" % str(op1)
if type2 == "hh":
source = "a52cS[%s]" % str(op2)
elif type2 == "h":
source = "a52cS[state[%s]]" % str(op2)
elif type2 == "ll":
source = str(op2)
elif type2 == "l":
source = "state[%s]" % str(op2)
else:
source = "0"
return "%s: %s = %s" % (addrResolver(addr), target, source), rr.group(0)

if op_name == "L":
if type1 == "-":
target = "a52cS[%s]" % str(op1)
elif type1 == "+":
target = "a52cS[state[%s]]" % str(op1)
else:
target = "state[%s]" % str(op1)

if type2 == "hh":
source = "a52cS[%s]" % str(op2)
elif type2 == "h":
source = "a52cS[state[%s]]" % str(op2)
elif type2 == "ll":
source = str(op2)
elif type2 == "l":
source = "state[%s]" % str(op2)
else:
source = "0"
return "%s: %s <<= %s" % (addrResolver(addr), target, source), rr.group(0)

if op_name == "R":
if type1 == "-":
target = "a52cS[%s]" % str(op1)
elif type1 == "+":
target = "a52cS[state[%s]]" % str(op1)
else:
target = "state[%s]" % str(op1)

if type2 == "hh":
source = "a52cS[%s]" % str(op2)
elif type2 == "h":
source = "a52cS[state[%s]]" % str(op2)
elif type2 == "ll":
source = str(op2)
elif type2 == "l":
source = "state[%s]" % str(op2)
else:
source = "0"
return "%s: %s >>= %s" % (addrResolver(addr), target, source), rr.group(0)

if op_name == "E":
if type1 == "-":
target = "a52cS[%s]" % str(op1)
elif type1 == "+":
target = "a52cS[state[%s]]" % str(op1)
else:
target = "state[%s]" % str(op1)

if type2 == "hh":
source = "a52cS[%s]" % str(op2)
elif type2 == "h":
source = "a52cS[state[%s]]" % str(op2)
elif type2 == "ll":
source = str(op2)
elif type2 == "l":
source = "state[%s]" % str(op2)
else:
source = "0"
return "%s: %s ^= %s" % (addrResolver(addr), target, source), rr.group(0)

if op_name == "I":
if type1 == "-":
target = "a52cS[%s]" % str(op1)
elif type1 == "+":
target = "a52cS[state[%s]]" % str(op1)
else:
target = "state[%s]" % str(op1)

if type2 == "hh":
source = "a52cS[%s]" % str(op2)
elif type2 == "h":
source = "a52cS[state[%s]]" % str(op2)
elif type2 == "ll":
source = str(op2)
elif type2 == "l":
source = "state[%s]" % str(op2)
else:
source = "0"
return "%s: %s &= %s" % (addrResolver(addr), target, source), rr.group(0)
if op_name == "U":
if type1 == "-":
target = "a52cS[%s]" % str(op1)
elif type1 == "+":
target = "a52cS[state[%s]]" % str(op1)
else:
target = "state[%s]" % str(op1)

if type2 == "hh":
source = "a52cS[%s]" % str(op2)
elif type2 == "h":
source = "a52cS[state[%s]]" % str(op2)
elif type2 == "ll":
source = str(op2)
elif type2 == "l":
source = "state[%s]" % str(op2)
else:
source = "0"
return "%s: %s |= %s" % (addrResolver(addr), target, source), rr.group(0)


入口第一段 vm 指令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
src = '''%52C%s\x00
%3.1hM
%3.0lE
%+1.3lM
%1.4llS
%3.1lM
%3.2lO
%-7.3C\x00
%0.4096hhM
%0.255llI
%1.0lM
%1.8llL
%0.1lU
%1.0lM
%1.16llL
%0.1lU
%1.200llM
%2.1788llM
%7C%-6144.1701736302llM
%0.200hhM
%0.255llI
%0.37llO
%0200.0C\x00'''

其中的 \x00 可以理解为基本块的结尾,由于 goto 指令 fprintf 调用递归的特性,代码如下

1
2
# goto 指令的实现
fprintf(stream, &a52cS[info->width]);

基本块结尾会返回到 goto 指令的下一条指令(可以理解为 call 指令)

入口指令反编译结果如下

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
_0:  goto _52
_7: state[3] = a52cS[state[1]]
_13: state[3] ^= state[0] # flag0 解密表1
_19: a52cS[state[1]] = state[3]
_26: state[1] += 4
_33: state[3] = state[1]
_39: state[3] -= state[2]
_45: if state[3] < 0 goto _7

_52: state[0] = a52cS[4096]
_62: state[0] &= 255
_71: state[1] = state[0]
_77: state[1] <<= 8
_84: state[0] |= state[1]
_90: state[1] = state[0]
_96: state[1] <<= 16
_104: state[0] |= state[1]
_110: state[1] = 200
_119: state[2] = 1788
_129: goto _7

_152: state[0] = a52cS[200]
_161: state[0] &= 255
_170: state[0] -= 37
_178: if state[0] == 0 goto _200

入口第一段指令对 a52cS[200] 开始的字符串进行解密,密钥是输入flag的第 1 个字节,解密完成后跳转到 a52cS[200] 处开始执行。

爆破 flag 第一个字节,当为 T 的时候,解密出来的数据有点像 vmcode

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
%4.5000llM
%0.13200llM
%337C
%0.0llM
%500C
%1262C
%0653.0C\x00
%1.0llM\x00
%3.0lM
%3.2lN
%0253.3C
%2.1llS
%3.2lM
%3.3lX
%3.0lO
%3.1llO
%-261.3C\x00
%+4.0lM
%4.2llS\x00
%1.1llM
%2.2llM
%261C
%+322.1C
%0.1llS
%1.13600llM
%1.0lO
%+337.1C\x00
%0.0llM\x00
%0.2llV\x00
%0.3llX
%0.1llS\x00
%1.0lM
%1.2llN
%0405.1C
%+413.1C
%470C
%0.1llS\x00
%1.0lM
%1.1llO
%0397.1C
%+428.1C\x00
%2.0lM
%2.4096llS
%4.2hM
%4.255llI
%+540.4C\x00
%2.0lM
%2.2llX
%2.5000llS
%2.2hM
%2.255llI
%4.2lE
%0.1llS
%2.0lM
%470C
%4.0lS
%4.255llI
%0.2lM
%2.1llO
%2.4500llS
%+2.4lM
%500C\x00
%0.123456789llM
%1.0llM
%1.4096llS
%1.1hM
%0.1lE
%2.0llM
%2.846786818llS
%2.0lE
%1.0llM
%1.6144llS
%+1.2lM
%1.4llM
%1.4096llS
%1.1hM
%0.1lE
%2.0llM
%2.1443538759llS
%2.0lE
%1.4llM
%1.6144llS
%+1.2lM
%1.8llM
%1.4096llS
%1.1hM
%0.1lE
%2.0llM
%2.1047515510llS
%2.0lE
%1.8llM
%1.6144llS
%+1.2lM
%1.12llM
%1.4096llS
%1.1hM
%0.1lE
%2.0llM
%2.359499514llS
%2.1724461856llS
%2.0lE
%1.12llM
%1.6144llS
%+1.2lM
%1.16llM
%1.4096llS
%1.1hM
%0.1lE
%2.0llM
%2.241024035llS
%2.0lE
%1.16llM
%1.6144llS
%+1.2lM
%1.20llM
%1.4096llS
%1.1hM
%0.1lE
%2.0llM
%2.222267724llS
%2.0lE
%1.20llM
%1.6144llS
%+1.2lM
%1.24llM
%1.4096llS
%1.1hM
%0.1lE
%2.0llM
%2.844096018llS
%2.0lE
%1.24llM
%1.6144llS
%+1.2lM\x00
%0.0llM
%1.0llM
%1.4500llS
%1.1hM
%2.0llM
%2.1374542625llS
%2.1686915720llS
%2.1129686860llS
%1.2lE
%0.1lU
%1.4llM
%1.4500llS
%1.1hM
%2.0llM
%2.842217029llS
%2.1483902564llS
%1.2lE
%0.1lU
%1.8llM
%1.4500llS
%1.1hM
%2.0llM
%2.1868013731llS
%1.2lE
%0.1lU
%1.12llM
%1.4500llS
%1.1hM
%2.0llM
%2.584694732llS
%2.1453312700llS
%1.2lE
%0.1lU
%1.16llM
%1.4500llS
%1.1hM
%2.0llM
%2.223548744llS
%1.2lE
%0.1lU
%1.20llM
%1.4500llS
%1.1hM
%2.0llM
%2.1958883726llS
%2.1916008099llS
%1.2lE
%0.1lU
%1.24llM
%1.4500llS
%1.1hM
%2.0llM
%2.1829937605llS
%2.1815356086llS
%2.253836698llS
%1.2lE
%0.1lU\x00

反编译如下

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
_200: state[4] = 5000;
_210: state[0] = 13200;
_221: goto _337;
_226: state[0] = 0;
_233: goto _500;
_238: goto _1262;
_244: if (state[0] == 0) goto _653;
== block end ==
_253: state[1] = 0;
== block end ==
_261: state[3] = state[0];
_267: state[3] = state[2];
_273: if (state[3] == 0) goto _253;
_281: state[2] += 1;
_288: state[3] = state[2];
_294: state[3] *= state[3];
_300: state[3] -= state[0];
_306: state[3] -= 1;
_313: if (state[3] < 0) goto _261;
== block end ==
_322: a52cS[state[4]] = state[0];
_329: state[4] += 2;
== block end ==
_337: state[1] = 1;
_344: state[2] = 2;
_351: goto _261;
_356: if (state[1] > 0) goto _322;
_364: state[0] += 1;
_371: state[1] = 13600;
_382: state[1] -= state[0];
_388: if (state[1] > 0) goto _337;
== block end ==
_397: state[0] = 0;
== block end ==
_405: state[0] /= 2;
== block end ==
_413: state[0] *= 3;
_420: state[0] += 1;
== block end ==
_428: state[1] = state[0];
_434: state[1] = 2;
_441: if (state[1] == 0) goto _405;
_449: if (state[1] > 0) goto _413;
_457: goto _470;
_462: state[0] += 1;
== block end ==
_470: state[1] = state[0];
_476: state[1] -= 1;
_483: if (state[1] == 0) goto _397;
_491: if (state[1] > 0) goto _428;
== block end ==
_500: state[2] = state[0];
_506: state[2] += 4096;
_516: state[4] = a52cS[state[2]];
_522: state[4] &= 255;
_531: if (state[4] > 0) goto _540;
== block end ==
_540: state[2] = state[0];
_546: state[2] *= 2;
_553: state[2] += 5000;
_563: state[2] = a52cS[state[2]];
_569: state[2] &= 255;
_578: state[4] ^= state[2];
_584: state[0] += 1;
_591: state[2] = state[0];
_597: goto _470;
_602: state[4] += state[0];
_608: state[4] &= 255;
_617: state[0] = state[2];
_623: state[2] -= 1;
_630: state[2] += 4500;
_640: a52cS[state[2]] = state[4];
_647: goto _500;
== block end ==
_653: state[0] = 123456789;
_668: state[1] = 0;
_675: state[1] += 4096;
_685: state[1] = a52cS[state[1]];
_691: state[0] ^= state[1];
_697: state[2] = 0;
_704: state[2] += 846786818;
_719: state[2] ^= state[0];
_725: state[1] = 0;
_732: state[1] += 6144;
_742: a52cS[state[1]] = state[2];
_749: state[1] = 4;
_756: state[1] += 4096;
_766: state[1] = a52cS[state[1]];
_772: state[0] ^= state[1];
_778: state[2] = 0;
_785: state[2] += 1443538759;
_801: state[2] ^= state[0];
_807: state[1] = 4;
_814: state[1] += 6144;
_824: a52cS[state[1]] = state[2];
_831: state[1] = 8;
_838: state[1] += 4096;
_848: state[1] = a52cS[state[1]];
_854: state[0] ^= state[1];
_860: state[2] = 0;
_867: state[2] += 1047515510;
_883: state[2] ^= state[0];
_889: state[1] = 8;
_896: state[1] += 6144;
_906: a52cS[state[1]] = state[2];
_913: state[1] = 12;
_921: state[1] += 4096;
_931: state[1] = a52cS[state[1]];
_937: state[0] ^= state[1];
_943: state[2] = 0;
_950: state[2] += 359499514;
_965: state[2] += 1724461856;
_981: state[2] ^= state[0];
_987: state[1] = 12;
_995: state[1] += 6144;
_1005: a52cS[state[1]] = state[2];
_1012: state[1] = 16;
_1020: state[1] += 4096;
_1030: state[1] = a52cS[state[1]];
_1036: state[0] ^= state[1];
_1042: state[2] = 0;
_1049: state[2] += 241024035;
_1064: state[2] ^= state[0];
_1070: state[1] = 16;
_1078: state[1] += 6144;
_1088: a52cS[state[1]] = state[2];
_1095: state[1] = 20;
_1103: state[1] += 4096;
_1113: state[1] = a52cS[state[1]];
_1119: state[0] ^= state[1];
_1125: state[2] = 0;
_1132: state[2] += 222267724;
_1147: state[2] ^= state[0];
_1153: state[1] = 20;
_1161: state[1] += 6144;
_1171: a52cS[state[1]] = state[2];
_1178: state[1] = 24;
_1186: state[1] += 4096;
_1196: state[1] = a52cS[state[1]];
_1202: state[0] ^= state[1];
_1208: state[2] = 0;
_1215: state[2] += 844096018;
_1230: state[2] ^= state[0];
_1236: state[1] = 24;
_1244: state[1] += 6144;
_1254: a52cS[state[1]] = state[2];
== block end ==
_1262: state[0] = 0;
_1269: state[1] = 0;
_1276: state[1] += 4500;
_1286: state[1] = a52cS[state[1]];
_1292: state[2] = 0;
_1299: state[2] += 1374542625;
_1315: state[2] += 1686915720;
_1331: state[2] += 1129686860;
_1347: state[1] ^= state[2];
_1353: state[0] |= state[1];
_1359: state[1] = 4;
_1366: state[1] += 4500;
_1376: state[1] = a52cS[state[1]];
_1382: state[2] = 0;
_1389: state[2] += 842217029;
_1404: state[2] += 1483902564;
_1420: state[1] ^= state[2];
_1426: state[0] |= state[1];
_1432: state[1] = 8;
_1439: state[1] += 4500;
_1449: state[1] = a52cS[state[1]];
_1455: state[2] = 0;
_1462: state[2] += 1868013731;
_1478: state[1] ^= state[2];
_1484: state[0] |= state[1];
_1490: state[1] = 12;
_1498: state[1] += 4500;
_1508: state[1] = a52cS[state[1]];
_1514: state[2] = 0;
_1521: state[2] += 584694732;
_1536: state[2] += 1453312700;
_1552: state[1] ^= state[2];
_1558: state[0] |= state[1];
_1564: state[1] = 16;
_1572: state[1] += 4500;
_1582: state[1] = a52cS[state[1]];
_1588: state[2] = 0;
_1595: state[2] += 223548744;
_1610: state[1] ^= state[2];
_1616: state[0] |= state[1];
_1622: state[1] = 20;
_1630: state[1] += 4500;
_1640: state[1] = a52cS[state[1]];
_1646: state[2] = 0;
_1653: state[2] += 1958883726;
_1669: state[2] += 1916008099;
_1685: state[1] ^= state[2];
_1691: state[0] |= state[1];
_1697: state[1] = 24;
_1705: state[1] += 4500;
_1715: state[1] = a52cS[state[1]];
_1721: state[2] = 0;
_1728: state[2] += 1829937605;
_1744: state[2] += 1815356086;
_1760: state[2] += 253836698;
_1775: state[1] ^= state[2];
_1781: state[0] |= state[1];
== block end ==

注意 == block end == 要返回到 goto 之前的地方。

重建控制流

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
_200: state[4] = 5000;
_210: state[0] = 13200;
do{
_337: state[1] = 1;
_344: state[2] = 2;

_261: state[3] = state[0];
_267: state[3] = state[2];
_273: if (state[3] == 0) goto _253;
_281: state[2] += 1;
_288: state[3] = state[2];
_294: state[3] *= state[3];
_300: state[3] -= state[0];
_306: state[3] -= 1;
_313: if (state[3] < 0) goto _261;
_356: if (state[1] > 0) {
_322: a52cS[state[4]] = state[0];
_329: state[4] += 2;
}
_364: state[0] += 1;
_371: state[1] = 13600;
_382: state[1] -= state[0];
}while(state[1] > 0);



_226: state[0] = 0;
// _233: goto _500;
_500: state[2] = state[0];
_506: state[2] += 4096;
_516: state[4] = a52cS[state[2]]; // read flag[i]
_522: state[4] &= 255;
_531: if (state[4] > 0) {
_540: state[2] = state[0];
_546: state[2] *= 2;
_553: state[2] += 5000;// (i * 2) + 5000
_563: state[2] = a52cS[state[2]]; // a52cS[(i * 2) + 5000]
_569: state[2] &= 255;
_578: state[4] ^= state[2]; // state[2] 与 下标关系参考 tab3
_584: state[0] += 1; // i' = i + 1
_591: state[2] = state[0]; // state[2] = i'
//_597: goto _470; // 这里的数据直接打断点获取。


_602: state[4] += state[0]; // state[0] 与 下标关系参考 tab2
_608: state[4] &= 255;
_617: state[0] = state[2];
_623: state[2] -= 1;
_630: state[2] += 4500;
_640: a52cS[state[2]] = state[4];
_647: goto _500;
};

//_238: goto _1262;
_1262: state[0] = 0;
_1269: state[1] = 0;
_1276: state[1] += 4500;
_1286: state[1] = a52cS[state[1]]; // plag[0]
_1292: state[2] = 0;
_1299: state[2] += 1374542625;
_1315: state[2] += 1686915720;
_1331: state[2] += 1129686860;
_1347: state[1] ^= state[2];
_1353: state[0] |= state[1];
_1359: state[1] = 4;
_1366: state[1] += 4500;
_1376: state[1] = a52cS[state[1]];
_1382: state[2] = 0;
_1389: state[2] += 842217029;
_1404: state[2] += 1483902564;
_1420: state[1] ^= state[2];
_1426: state[0] |= state[1];
_1432: state[1] = 8;
_1439: state[1] += 4500;
_1449: state[1] = a52cS[state[1]];
_1455: state[2] = 0;
_1462: state[2] += 1868013731;
_1478: state[1] ^= state[2];
_1484: state[0] |= state[1];
_1490: state[1] = 12;
_1498: state[1] += 4500;
_1508: state[1] = a52cS[state[1]];
_1514: state[2] = 0;
_1521: state[2] += 584694732;
_1536: state[2] += 1453312700;
_1552: state[1] ^= state[2];
_1558: state[0] |= state[1];
_1564: state[1] = 16;
_1572: state[1] += 4500;
_1582: state[1] = a52cS[state[1]];
_1588: state[2] = 0;
_1595: state[2] += 223548744;
_1610: state[1] ^= state[2];
_1616: state[0] |= state[1];
_1622: state[1] = 20;
_1630: state[1] += 4500;
_1640: state[1] = a52cS[state[1]];
_1646: state[2] = 0;
_1653: state[2] += 1958883726;
_1669: state[2] += 1916008099;
_1685: state[1] ^= state[2];
_1691: state[0] |= state[1];
_1697: state[1] = 24;
_1705: state[1] += 4500;
_1715: state[1] = a52cS[state[1]];
_1721: state[2] = 0;
_1728: state[2] += 1829937605;
_1744: state[2] += 1815356086;
_1760: state[2] += 253836698;
_1775: state[1] ^= state[2];
_1781: state[0] |= state[1];

_244: if (state[0] == 0) {
_653: state[0] = 123456789;
_668: state[1] = 0;
_675: state[1] += 4096;
_685: state[1] = a52cS[state[1]];
_691: state[0] ^= state[1];
_697: state[2] = 0;
_704: state[2] += 846786818;
_719: state[2] ^= state[0];
_725: state[1] = 0;
_732: state[1] += 6144;
_742: a52cS[state[1]] = state[2];
_749: state[1] = 4;
_756: state[1] += 4096;
_766: state[1] = a52cS[state[1]];
_772: state[0] ^= state[1];
_778: state[2] = 0;
_785: state[2] += 1443538759;
_801: state[2] ^= state[0];
_807: state[1] = 4;
_814: state[1] += 6144;
_824: a52cS[state[1]] = state[2];
_831: state[1] = 8;
_838: state[1] += 4096;
_848: state[1] = a52cS[state[1]];
_854: state[0] ^= state[1];
_860: state[2] = 0;
_867: state[2] += 1047515510;
_883: state[2] ^= state[0];
_889: state[1] = 8;
_896: state[1] += 6144;
_906: a52cS[state[1]] = state[2];
_913: state[1] = 12;
_921: state[1] += 4096;
_931: state[1] = a52cS[state[1]];
_937: state[0] ^= state[1];
_943: state[2] = 0;
_950: state[2] += 359499514;
_965: state[2] += 1724461856;
_981: state[2] ^= state[0];
_987: state[1] = 12;
_995: state[1] += 6144;
_1005: a52cS[state[1]] = state[2];
_1012: state[1] = 16;
_1020: state[1] += 4096;
_1030: state[1] = a52cS[state[1]];
_1036: state[0] ^= state[1];
_1042: state[2] = 0;
_1049: state[2] += 241024035;
_1064: state[2] ^= state[0];
_1070: state[1] = 16;
_1078: state[1] += 6144;
_1088: a52cS[state[1]] = state[2];
_1095: state[1] = 20;
_1103: state[1] += 4096;
_1113: state[1] = a52cS[state[1]];
_1119: state[0] ^= state[1];
_1125: state[2] = 0;
_1132: state[2] += 222267724;
_1147: state[2] ^= state[0];
_1153: state[1] = 20;
_1161: state[1] += 6144;
_1171: a52cS[state[1]] = state[2];
_1178: state[1] = 24;
_1186: state[1] += 4096;
_1196: state[1] = a52cS[state[1]];
_1202: state[0] ^= state[1];
_1208: state[2] = 0;
_1215: state[2] += 844096018;
_1230: state[2] ^= state[0];
_1236: state[1] = 24;
_1244: state[1] += 6144;
_1254: a52cS[state[1]] = state[2];
};

目标状态 ==state[0] == 0==

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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
state = [0] * 30
state[1] = data[0]
state[2] = 0
state[2] += 1374542625
state[2] += 1686915720
state[2] += 1129686860
state[1] ^= state[2]
state[0] |= state[1]
state[1] = 4
state[1] += 4500
state[1] = data[1]
state[2] = 0
state[2] += 842217029
state[2] += 1483902564
state[1] ^= state[2]
state[0] |= state[1]
state[1] = 8
state[1] += 4500
state[1] = data[2]
state[2] = 0
state[2] += 1868013731
state[1] ^= state[2]
state[0] |= state[1]
state[1] = 12
state[1] += 4500
state[1] = data[3]
state[2] = 0
state[2] += 584694732
state[2] += 1453312700
state[1] ^= state[2]
state[0] |= state[1]
state[1] = 16
state[1] += 4500
state[1] = data[4]
state[2] = 0
state[2] += 223548744
state[1] ^= state[2]
state[0] |= state[1]
state[1] = 20
state[1] += 4500
state[1] = data[5]
state[2] = 0
state[2] += 1958883726
state[2] += 1916008099
state[1] ^= state[2]
state[0] |= state[1]
state[1] = 24
state[1] += 4500
state[1] = data[6]
state[2] = 0
state[2] += 1829937605
state[2] += 1815356086
state[2] += 253836698
state[1] ^= state[2]
state[0] |= state[1]
print(state[0])
s = Solver()
s.add(state[0] == 0)
print(s.check())
res = s.model()
data2 = [res[rr].as_long() for rr in data]
data2_bytes = b''
for x in data2:
data2_bytes += p32(x)
print(data2_bytes)
data2_bytes = bytearray(data2_bytes)

通过调试获取关键量 + 和 xor 运行

1
2
3
tab2 = [0x0,0x1,0x7,2,5,8,0x10,3,0x13,6,0xe,9,9,0x11,0x11,0x4,0xc,0x14,0x14,0x7,0x7,0xf,0xf,0xa,0x17,0xa,0x6f,0x12]  #  _602: state[4] += state[0];  state[0] 与 下标对应的数据表

tab3 = [0x33A1, 0x33A3, 0x33AD, 0x33B9, 0x33C1, 0x33CB, 0x33D3, 0x33EB, 0x33F1, 0x33FD, 0x3401, 0x340F, 0x3413, 0x3419, 0x341B, 0x3437, 0x3445, 0x3455, 0x3457, 0x3463, 0x3469, 0x346D, 0x3481, 0x348B, 0x3491, 0x3497, 0x349D, 0x34A5, 0x34AF, 0x34BB, 0x34C9, 0x34D3, 0x34E1, 0x34F1, 0x34FF, 0x3509, 0x3517, 0x351D, 0x0000] # _578: state[4] ^= state[2] & 0xFF;

完整解密脚本如下

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
from z3 import *
from pwn import *


tab2 = [0x0,0x1,0x7,2,5,8,0x10,3,0x13,6,0xe,9,9,0x11,0x11,0x4,0xc,0x14,0x14,0x7,0x7,0xf,0xf,0xa,0x17,0xa,0x6f,0x12]

tab3 = [0x33A1, 0x33A3, 0x33AD, 0x33B9, 0x33C1, 0x33CB, 0x33D3, 0x33EB, 0x33F1, 0x33FD, 0x3401, 0x340F, 0x3413, 0x3419, 0x341B, 0x3437, 0x3445, 0x3455, 0x3457, 0x3463, 0x3469, 0x346D, 0x3481, 0x348B, 0x3491, 0x3497, 0x349D, 0x34A5, 0x34AF, 0x34BB, 0x34C9, 0x34D3, 0x34E1, 0x34F1, 0x34FF, 0x3509, 0x3517, 0x351D, 0x0000]

data = [BitVec("bv%d" % i, 33) for i in range(7)]

state = [0] * 30
state[1] = data[0]
state[2] = 0
state[2] += 1374542625
state[2] += 1686915720
state[2] += 1129686860
state[1] ^= state[2]
state[0] |= state[1]
state[1] = 4
state[1] += 4500
state[1] = data[1]
state[2] = 0
state[2] += 842217029
state[2] += 1483902564
state[1] ^= state[2]
state[0] |= state[1]
state[1] = 8
state[1] += 4500
state[1] = data[2]
state[2] = 0
state[2] += 1868013731
state[1] ^= state[2]
state[0] |= state[1]
state[1] = 12
state[1] += 4500
state[1] = data[3]
state[2] = 0
state[2] += 584694732
state[2] += 1453312700
state[1] ^= state[2]
state[0] |= state[1]
state[1] = 16
state[1] += 4500
state[1] = data[4]
state[2] = 0
state[2] += 223548744
state[1] ^= state[2]
state[0] |= state[1]
state[1] = 20
state[1] += 4500
state[1] = data[5]
state[2] = 0
state[2] += 1958883726
state[2] += 1916008099
state[1] ^= state[2]
state[0] |= state[1]
state[1] = 24
state[1] += 4500
state[1] = data[6]
state[2] = 0
state[2] += 1829937605
state[2] += 1815356086
state[2] += 253836698
state[1] ^= state[2]
state[0] |= state[1]
print(state[0])
s = Solver()
s.add(state[0] == 0)
print(s.check())
res = s.model()
data2 = [res[rr].as_long() for rr in data]
data2_bytes = b''
for x in data2:
data2_bytes += p32(x)
print(data2_bytes)
data2_bytes = bytearray(data2_bytes)

for idx in range(len(data2_bytes)):
bb = data2_bytes[idx]
tmp = (bb - tab2[idx] & 0xff)
tmp ^=( tab3[idx] & 0xff)
data2_bytes[idx] = tmp

print(data2_bytes)

for i in range(7):
data2[i] = u32(data2_bytes[i * 4:i * 4 + 4])

flag = []
state[0] = 123456789
state[1] = data2[0]
state[0] ^= state[1]
state[2] = 0
state[2] += 846786818
state[2] ^= state[0]
flag.append(state[2])
state[1] = 4
state[1] += 4096
state[1] = data2[1]
state[0] ^= state[1]
state[2] = 0
state[2] += 1443538759
state[2] ^= state[0]
state[1] = 4
state[1] += 6144
flag.append(state[2])
state[1] = 8
state[1] += 4096
state[1] = data2[2]
state[0] ^= state[1]
state[2] = 0
state[2] += 1047515510
state[2] ^= state[0]
state[1] = 8
state[1] += 6144
flag.append(state[2])
state[1] = 12
state[1] += 4096
state[1] = data2[3]
state[0] ^= state[1]
state[2] = 0
state[2] += 359499514
state[2] += 1724461856
state[2] ^= state[0]
state[1] = 12
state[1] += 6144
flag.append(state[2])
state[1] = 16
state[1] += 4096
state[1] = data2[4]
state[0] ^= state[1]
state[2] = 0
state[2] += 241024035
state[2] ^= state[0]
state[1] = 16
state[1] += 6144
flag.append(state[2])
state[1] = 20
state[1] += 4096
state[1] = data2[5]
state[0] ^= state[1]
state[2] = 0
state[2] += 222267724
state[2] ^= state[0]
state[1] = 20
state[1] += 6144
flag.append(state[2])
state[1] = 24
state[1] += 4096
state[1] = data2[6]
state[0] ^= state[1]
state[2] = 0
state[2] += 844096018
state[2] ^= state[0]
state[1] = 24
state[1] += 6144
flag.append(state[2])

flag1 = b''
for i in flag:
flag1 += binascii.a2b_hex(hex(i)[2:])[::-1]
print(flag1)

POLYMORPH

题目数据下载:

https://www.jianguoyun.com/p/Dd96zLgQsOuICRinloEE ( 访问密码:ygw3TH )

要求编写一个病毒自动检测程序,并提交检测程序的 bin 文件,要求检测程序平均 5 秒内检测一个样本是否为病毒。

由于是提交 bin 文件,用 C 来开发比较方便。

病毒样本会在标准输出输出 “STANDARD-ANTIVIRUS-TEST-FILE” 字符串。

简单分析了一下病毒集中的样本,发现有一些静态特征可以快速判断病毒(非病毒样不太刁钻,没有这些静态特征)

1
2
3
4
5
6
7
8
STANDARD-ANTIVIRUS-TEST-FILE!$H+H*
crypt_defenses
crypt_badstuff
{ 0x55, 0x48, 0x89, 0xE5, 0x48, 0x83, 0xEC, 0x20, 0x48, 0x89, 0x7D, 0xE8, 0xC7, 0x45, 0xFC, 0x00,
0x00, 0x00, 0x00, 0xB8, 0x65, 0x00, 0x00, 0x00, 0xBF, 0x00, 0x00, 0x00, 0x00, 0xBE, 0x00, 0x00,
0x00, 0x00, 0xBA, 0x01, 0x00, 0x00, 0x00, 0x0F, 0x05, 0x89, 0x45, 0xFC, 0x8B, 0x45, 0xFC, 0x83,
0xC0, 0x01, 0x48, 0x98, 0x48, 0x8D, 0x14, 0xC5, 0x00, 0x00, 0x00, 0x00, 0x48, 0x8D, 0x05, 0x21,
0x51, 0x00, 0x00, 0x48, 0x8B, 0x04, 0x02, 0xBF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xD0, 0xC9};

经过静态特征的筛选,仍然有一些病毒无法检测出,它们有些骚操作

病毒样本的骚操作:

  • 动态解密目标字符串 “STANDARD-ANTIVIRUS-TEST-FILE”
  • 通过 syscall 调用 ptraceme 反调试
  • sleep 很长的时间才触发目标字符串输出(大于5s,题目要求5s内)
  • fork 很多子进程
  • 等待用户输入
  • ……

我的解决思路是写一个沙箱,基于 ptrace 实现,并且要绕过病毒的骚操作。

主要绕过方法是通过 ptrace hook 系统调用,在一些关键函数做手脚,例如 ptrace、nanosleep、read、write、fork 等。

完整检测沙箱 C 代码如下

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
#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/user.h>

#include <syscall.h>

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

#include <string.h>
#include <pthread.h>
#include <vector>
using namespace std;
const char* callname(long call);

#if __WORDSIZE == 64
#define REG(reg) reg.orig_rax
#else
#define REG(reg) reg.orig_eax
#endif
const int long_size = sizeof(long);

std::vector<pthread_t> threads;

unsigned char fuck1[] = { 0x55, 0x48, 0x89, 0xE5, 0x48, 0x83, 0xEC, 0x20, 0x48, 0x89, 0x7D, 0xE8, 0xC7, 0x45, 0xFC, 0x00,
0x00, 0x00, 0x00, 0xB8, 0x65, 0x00, 0x00, 0x00, 0xBF, 0x00, 0x00, 0x00, 0x00, 0xBE, 0x00, 0x00,
0x00, 0x00, 0xBA, 0x01, 0x00, 0x00, 0x00, 0x0F, 0x05, 0x89, 0x45, 0xFC, 0x8B, 0x45, 0xFC, 0x83,
0xC0, 0x01, 0x48, 0x98, 0x48, 0x8D, 0x14, 0xC5, 0x00, 0x00, 0x00, 0x00, 0x48, 0x8D, 0x05, 0x21,
0x51, 0x00, 0x00, 0x48, 0x8B, 0x04, 0x02, 0xBF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xD0, 0xC9};
const char * tags[] = {
"STANDARD-ANTIVIRUS-TEST-FILE!$H+H*",
"crypt_defenses","crypt_badstuff"
};

bool detect_string(char * file) {
char * fileBuffer = nullptr;
FILE * fp = fopen(file, "rb");
if(!fp) {
fprintf(stderr, "detect_string: faield to open: %s\n", file);
return false;
}

size_t fsize = 0;
fseek(fp, 0, SEEK_END);
fsize = ftell(fp);
fseek(fp, 0, SEEK_SET);
fileBuffer = new char [fsize];

fread(fileBuffer, fsize, 1, fp);


bool ret = false;
for(int i = 0 ; i < sizeof(tags) / sizeof(char *); i++) {
ret |= (memmem(fileBuffer, fsize, tags[i], strlen(tags[i])) != nullptr);

}
ret |= memmem(fileBuffer, fsize, fuck1, sizeof(fuck1)) != nullptr;
delete fileBuffer;
fclose(fp);
return ret;
}

void getdata(pid_t child, long addr,
char *str, int len)
{ char *laddr;
int i, j;
union u {
long val;
char chars[long_size];
}data;
i = 0;
j = len / long_size;
laddr = str;
while(i < j) {
data.val = ptrace(PTRACE_PEEKDATA, child,
addr + i * 4, NULL);
memcpy(laddr, data.chars, long_size);
++i;
laddr += long_size;
}
j = len % long_size;
if(j != 0) {
data.val = ptrace(PTRACE_PEEKDATA, child,
addr + i * 4, NULL);
memcpy(laddr, data.chars, j);
}
str[len] = '\0';
}

void * process_event(void * pid) {
int status;
pid_t child = (long)pid;
int fork_cnt = 0;
waitpid(child, &status, 0);
while( ! WIFEXITED(status)) {
struct user_regs_struct regs;
int64_t sys_num = -1;
bool blocked = false;
int type;

ptrace(PTRACE_SYSCALL, child, NULL, NULL);
waitpid(child, &status, 0);
ptrace(PTRACE_GETREGS, child, NULL, &regs);

sys_num = REG(regs);

if (0x23 == sys_num) {
regs.orig_rax = 0x14; // set to invalid syscall
ptrace(PTRACE_SETREGS, child, 0, &regs);

// printf("blocked: %llx\n", regs.rip);
blocked = true;
}
if (sys_num == 0x1) {
const char * tag = "EICAEICAR-STR-ST";
size_t size = regs.rdx;
char * buffer = new char[size];
getdata(child, regs.rsi, buffer, size);
if (memmem(buffer, size, tag, strlen(tag)) != nullptr) {
printf("evil\n");
exit(-1);
}
delete buffer;
}
if(sys_num == 0) {
if (regs.rdi == 0)
{
regs.orig_rax = 0x14; // set to invalid syscall
ptrace(PTRACE_SETREGS, child, 0, &regs);
blocked = true;
}
}

if (sys_num == 0x39) { // fork
regs.orig_rax = 0x14; // set to invalid syscall
ptrace(PTRACE_SETREGS, child, 0, &regs);
printf("bypass fork\n");
fork_cnt += 1;
blocked = true;
}

if(sys_num == 0x65) {
regs.rax = 0x14;
blocked = true;
ptrace(PTRACE_SETREGS, child, 0, &regs);
}


ptrace(PTRACE_SYSCALL, child, NULL, NULL);
waitpid(child, &status, 0);

if (blocked) {
/* errno = EPERM */
regs.rax = 0;
ptrace(PTRACE_SETREGS, child, 0, &regs);
}
}
printf("exit: %d\n", child);
}
void sig_handler(int signum){
printf("time out...\n");
exit(-1);
}

int main(int argc, char* argv[]) {
pid_t child;
signal(SIGALRM,sig_handler); // Register signal handler
alarm(10);

if (argc == 1) {
exit(0);
}

if(detect_string(argv[1])) {
printf("virus: %s\n", argv[1]);
return -1;
}else {
char* chargs[argc];
int i = 0;
while (i < argc - 1) {
chargs[i] = argv[i+1];
i++;
}
chargs[i] = NULL;

child = fork();
if(child == 0) {
ptrace(PTRACE_TRACEME, 0, NULL, NULL);
execvp(chargs[0], chargs);
printf("%s non Virus\n", argv[1]);
} else {
process_event((void *)child);
}
return 0;
}

}

致敬 McAfee


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!