2023羊城杯初赛部分Reverse

j1ya Lv5

Reverse

csgo

main_main找到字符串

直接base64解码不对

调试main_main->mainCfunc_enc->maincgo_ce3afc93dff5_Cfunc_enc->go_ce3afc93dff5_Cfunc_enc,拿到正确编码表

fkReverse函数是base64

blast

翻译过来是爆炸,应该和爆破有关

md5初始化,剩余过程应该是被混淆了,问题不大

同时sub_404010函数被调用两次,分析发现进行两次md5

循环和47个大数比较

密文

直接在可见字符范围内爆破

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
import hashlib
import string

targets = [
"14d89c38cd0fb23a14be2798d449c182",
"a94837b18f8f43f29448b40a6e7386ba",
"af85d512594fc84a5c65ec9970956ea5",
"af85d512594fc84a5c65ec9970956ea5",
"10e21da237a4a1491e769df6f4c3b419",
"a705e8280082f93f07e3486636f3827a",
"297e7ca127d2eef674c119331fe30dff",
"b5d2099e49bdb07b8176dff5e23b3c14",
"83be264eb452fcf0a1c322f2c7cbf987",
"a94837b18f8f43f29448b40a6e7386ba",
"71b0438bf46aa26928c7f5a371d619e1",
"a705e8280082f93f07e3486636f3827a",
"ac49073a7165f41c57eb2c1806a7092e",
"a94837b18f8f43f29448b40a6e7386ba",
"af85d512594fc84a5c65ec9970956ea5",
"ed108f6919ebadc8e809f8b86ef40b05",
"10e21da237a4a1491e769df6f4c3b419",
"3cfd436919bc3107d68b912ee647f341",
"a705e8280082f93f07e3486636f3827a",
"65c162f7c43612ba1bdf4d0f2912bbc0",
"10e21da237a4a1491e769df6f4c3b419",
"a705e8280082f93f07e3486636f3827a",
"3cfd436919bc3107d68b912ee647f341",
"557460d317ae874c924e9be336a83cbe",
"a705e8280082f93f07e3486636f3827a",
"9203d8a26e241e63e4b35b3527440998",
"10e21da237a4a1491e769df6f4c3b419",
"f91b2663febba8a884487f7de5e1d249",
"a705e8280082f93f07e3486636f3827a",
"d7afde3e7059cd0a0fe09eec4b0008cd",
"488c428cd4a8d916deee7c1613c8b2fd",
"39abe4bca904bca5a11121955a2996bf",
"a705e8280082f93f07e3486636f3827a",
"3cfd436919bc3107d68b912ee647f341",
"39abe4bca904bca5a11121955a2996bf",
"4e44f1ac85cd60e3caa56bfd4afb675e",
"45cf8ddfae1d78741d8f1c622689e4af",
"3cfd436919bc3107d68b912ee647f341",
"39abe4bca904bca5a11121955a2996bf",
"4e44f1ac85cd60e3caa56bfd4afb675e",
"37327bb06c83cb29cefde1963ea588aa",
"a705e8280082f93f07e3486636f3827a",
"23e65a679105b85c5dc7034fded4fb5f",
"10e21da237a4a1491e769df6f4c3b419",
"71b0438bf46aa26928c7f5a371d619e1",
"af85d512594fc84a5c65ec9970956ea5",
"39abe4bca904bca5a11121955a2996bf",
]

table = {}
for c in string.printable:
h1 = hashlib.md5(c.encode()).hexdigest()
h2 = hashlib.md5(h1.encode()).hexdigest()
table[h2] = c

flag = ''.join(table[x] for x in targets)
print(flag)
#Hello_Ctfer_Velcom_To_my_Mov_and_md5(md5)_world

ez_encrypt

先输入验证码再输入flag

根据GetFlag字符串找到密文和疑似base64表

sub_1400036A0为base64

交叉引用发现base64编码内容a000000为前面的验证码,a11111111111111为输入的flag

sub_140003A20等价下面写法,相当于6位数字,从a000000初始值为000000也可以看出来

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
bool sub_140003A20(char *a1) {
int len = strlen(a1);

len = len - 6;
a1[6] = '\0';

bool ok = (len == 0);

for (int i = 0; i < 6; i++) {
unsigned int x = a1[i] - '0';
if (x >= 10)
ok = false;
}
return ok;
}

还有DES加密,对应sub_140003AE0,key为上面验证码base64编码后结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from Crypto.Cipher import DES
import base64

def base64_encode(data):
oldtable='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
newtable='abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ+/'
tmp = base64.b64encode(data.encode()).decode()
trans = str.maketrans(oldtable, newtable)
return tmp.translate(trans)

for i in range(0,1000000):
tmp_key = str(i).rjust(6,"0")
tmp_key = base64_encode(tmp_key)
des = DES.new(tmp_key.encode(),mode=DES.MODE_ECB)
c = bytes.fromhex("0723105D5C12217DCDC3601F5ECB54DA9CCEC2279F1684A13A0D716D17217F4C9EA85FF1A42795731CA3C55D3A4D7BEA")
m = des.decrypt(c)
if(b"DASCTF" in m):
print(m,i,tmp_key)
# b'DASCTF{f771b96b71514bb6bc20f3275fa9404e}\x08\x08\x08\x08\x08\x08\x08\x08' 151490 mtuNndAM
# b'DASCTF{f771b96b71514bb6bc20f3275fa9404e}\x08\x08\x08\x08\x08\x08\x08\x08' 151890 mtuNodAM
# b'DASCTF{f771b96b71514bb6bc20f3275fa9404e}\x08\x08\x08\x08\x08\x08\x08\x08' 152490 mtuOndAM
# b'DASCTF{f771b96b71514bb6bc20f3275fa9404e}\x08\x08\x08\x08\x08\x08\x08\x08' 152890 mtuOodAM

从上面可以看出DES的key不同解密结果也能一致,这是因为DES 的 8字节 key 里,每个字节只有 7 bit 真正参与加密,最低位通常是奇偶校验位,不参与密钥调度

vm_wo

自实现vmp

进入myoperate函数,下面是key和code

interpretBytecode函数写了个典型的翻译器

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
__int64 __fastcall interpretBytecode(__int64 result, int n15)
{
int v2; // w8
__int64 v4; // x20
unsigned __int8 *v5; // x9
int v6; // w12
int v7; // w13
__int64 v8; // x8
int v9; // w9
__int64 v10; // x10
char v11; // w9
int v12; // w10
int v13; // w8
__int64 v14; // x8
char v15; // w8
__int64 v16; // x8

v2 = MEMORY[0x100008114];
if ( MEMORY[0x100008114] < n15 )
{
v4 = result;
while ( 2 )
{
v5 = (unsigned __int8 *)(v4 + v2);
v6 = *v5;
v7 = (char)v5[1];
v8 = (char)v5[1];
v9 = (char)v5[2];
v10 = v9;
switch ( v6 )
{
case 0:
v11 = vm_body[v8];
vm_body[v8] = vm_body[v10];
vm_body[v10] = v11;
goto LABEL_35;
case 1:
vm_body[v8] ^= vm_body[v9];
goto LABEL_35;
case 2:
vm_body[v8] += v9;
goto LABEL_35;
case 3:
vm_body[v8] += vm_body[v9];
goto LABEL_35;
case 4:
vm_body[v8] -= v9;
goto LABEL_35;
case 5:
vm_body[v8] -= vm_body[v9];
goto LABEL_35;
case 6:
vm_body[v8] *= (_BYTE)v9;
goto LABEL_35;
case 7:
vm_body[v8] *= vm_body[v9];
goto LABEL_35;
case 8:
vm_body[v8] = (unsigned __int8)vm_body[v8] / v9;
goto LABEL_35;
case 9:
vm_body[v8] = (unsigned __int8)vm_body[v8] / (unsigned __int8)vm_body[v9];
goto LABEL_35;
case 10:
vm_body[v8] = (unsigned __int8)vm_body[v8] % v9;
goto LABEL_35;
case 11:
vm_body[v8] = (unsigned __int8)vm_body[v8] % (unsigned __int8)vm_body[v9];
goto LABEL_35;
case 12:
v12 = (unsigned __int8)vm_body[v8];
goto LABEL_18;
case 13:
v12 = (unsigned __int8)vm_body[0];
LABEL_18:
vm_body[v8] = v12 << v9;
goto LABEL_35;
case 14:
goto LABEL_28;
case 15:
v13 = (unsigned __int8)vm_body[v8];
goto LABEL_21;
case 16:
v14 = MEMORY[0x100008110] - 1LL;
--MEMORY[0x100008110];
v13 = vm_body[v14 + 16];
LABEL_21:
result = printf("%d\n", v13);
goto LABEL_35;
case 17:
if ( !vm_body[v8] )
goto LABEL_25;
goto LABEL_35;
case 18:
if ( vm_body[v8] )
LABEL_25:
MEMORY[0x100008114] = v9;
goto LABEL_35;
case 19:
MEMORY[0x100008114] = v7;
goto LABEL_35;
case 20:
v8 = (unsigned __int8)vm_body[v8];
LABEL_28:
v15 = vm_body[v8];
goto LABEL_31;
case 21:
v16 = MEMORY[0x100008110] - 1LL;
--MEMORY[0x100008110];
vm_body[0] = vm_body[v16 + 16];
goto LABEL_35;
case 22:
v15 = v7;
LABEL_31:
vm_body[MEMORY[0x100008110] + 16] = v15;
++MEMORY[0x100008110];
goto LABEL_35;
case 23:
goto LABEL_36;
case 24:
vm_body[0] = byte_100008002 | byte_100008001;
goto LABEL_35;
case 25:
vm_body[v8] = (unsigned __int8)vm_body[0] >> v9;
goto LABEL_35;
case 26:
vm_body[v8] = v9;
goto LABEL_35;
default:
LABEL_35:
MEMORY[0x100008114] += 3;
LABEL_36:
v2 = MEMORY[0x100008114];
if ( MEMORY[0x100008114] >= n15 )
break;
continue;
}
break;
}
}
MEMORY[0x100008114] = 0;
return result;
}

可以采用逐字符爆破

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
import struct

def exec(cin, a2):
start = False
v2 = buf[0]
if v2 < a2:
while True:
if start:
buf[0] += 3
v2 = buf[0]
if buf[0] >= a2:
break
if not start:
start = True

for m in range(0, 3):
vm_body[m] &= 0xff

v6 = cin[v2]
v7 = cin[v2 + 1]
v8 = cin[v2 + 1]
v9 = cin[v2 + 2]
v10 = v9

if v6 == 0:
v11 = vm_body[v8]
vm_body[v8] = vm_body[v10]
vm_body[v10] = v11
elif v6 == 1:
vm_body[v8] ^= vm_body[v9]
elif v6 == 2:
vm_body[v8] += v9
elif v6 == 3:
vm_body[v8] += vm_body[v9]
elif v6 == 4:
vm_body[v8] -= v9
elif v6 == 5:
vm_body[v8] -= vm_body[v9]
elif v6 == 6:
vm_body[v8] *= v9
elif v6 == 7:
vm_body[v8] *= vm_body[v9]
elif v6 == 8:
vm_body[v8] = vm_body[v8] / v9
elif v6 == 9:
vm_body[v8] = vm_body[v8] / vm_body[v9]
elif v6 == 10:
vm_body[v8] = vm_body[v8] % v9
elif v6 == 11:
vm_body[v8] = vm_body[v8] % vm_body[v9]
elif v6 == 12:
vm_body[v8] = vm_body[v8] << v9
elif v6 == 13:
vm_body[v8] = vm_body[0] << v9
elif v6 == 14:
vm_body[buf[1] + 16] = vm_body[v8]
buf[1] += 1
elif v6 == 15:
v13 = vm_body[v8]
elif v6 == 16:
v14 = buf[1] - 1
buf[1] -= 1
v13 = vm_body[v14 + 16]
elif v6 == 17:
if not vm_body[v8]:
buf[0] = v9
elif v6 == 18:
if vm_body[v8]:
buf[0] = v9
elif v6 == 19:
buf[0] = v7
elif v6 == 20:
v8 = vm_body[v8]
vm_body[buf[1] + 16] = vm_body[v8]
buf[1] += 1
elif v6 == 21:
v16 = buf[1] - 1
buf[1] -= 1
vm_body[0] = vm_body[v16 + 16]
elif v6 == 22:
vm_body[buf[1] + 16] = v7
buf[1] += 1
elif v6 == 23:
buf[0] -= 3
elif v6 == 24:
vm_body[0] = vm_body[2] | vm_body[1]
elif v6 == 25:
vm_body[v8] = vm_body[0] >> v9
elif v6 == 26:
vm_body[v8] = v9

buf[0] = 0

enc = [
0xDF, 0xD5, 0xF1, 0xD1, 0xFF, 0xDB, 0xA1, 0xA5, 0x89, 0xBD,
0xE9, 0x95, 0xB3, 0x9D, 0xE9, 0xB3, 0x85, 0x99, 0x87, 0xBF,
0xE9, 0xB1, 0x89, 0xE9, 0x91, 0x89, 0x89, 0x8F, 0xAD
]

alpht = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_{|}~"
flag = [0] * 30
index = 0

while index < len(enc):
for i in alpht:
vm_body = [0] * 0x10
buf = [0] * 2
flag[index] = ord(i)
out = [0] * 30

vm_body[3:7] = 0xBEEDBEEF.to_bytes(4, byteorder="little")
a1 = 0
v2 = index + 1
code = [0] * 64

while v2:
v2 -= 1

code[48:56] = 0x20D01011903001A.to_bytes(8, byteorder="little")
code[55:63] = 0x300010201180702.to_bytes(8, byteorder="little")
code[50] = flag[a1]
exec(code[48:], 15)

code[32:40] = 0x20D02011903001A.to_bytes(8, byteorder="little")
code[39:47] = 0x400010201180602.to_bytes(8, byteorder="little")
code[34] = vm_body[0]
exec(code[32:], 15)

code[16:24] = 0x20D03011903001A.to_bytes(8, byteorder="little")
code[23:31] = 0x500010201180502.to_bytes(8, byteorder="little")
code[18] = vm_body[0]
exec(code[16:], 15)

code[0:8] = 0x20D04011903001A.to_bytes(8, byteorder="little")
code[7:15] = 0x600010201180402.to_bytes(8, byteorder="little")
code[2] = vm_body[0]
exec(code, 15)

out[a1] = ((vm_body[0] >> 5) | (vm_body[0] << 3)) & 0xff
a1 += 1

candidate_index = index#
if out[candidate_index] == enc[candidate_index]:
print(chr(flag[candidate_index]), end="")
index += 1
break
#DASCTF{you_are_right_so_cool}

或者先还原汇编语句

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
opcode0 = b'\x1a\x00\x03\x19\x01\x01\r\x02\x07\x18\x01\x02\x01\x00\x03'
opcode1 = b'\x1a\x00\x03\x19\x01\x02\r\x02\x06\x18\x01\x02\x01\x00\x04'
opcode2 = b'\x1a\x00\x03\x19\x01\x03\r\x02\x05\x18\x01\x02\x01\x00\x05'
opcode3 = b'\x1a\x00\x03\x19\x01\x04\r\x02\x04\x18\x01\x02\x01\x00\x06'
ip = 0

opcode = opcode0
while ip < len(opcode):
oprand1 = opcode[ip+1]
if ip == 0:
oprand2 = 0xff
else:
oprand2 = opcode[ip+2]
match opcode[ip]:
case 0:
print('%d: xhcg reg[%d], reg[%d]' % (ip, oprand1, oprand2))
case 1:
print('%d: reg[%d] ^= reg[%d]' % (ip, oprand1, oprand2))
case 2:
print('%d: reg[%d] += %#x' % (ip, oprand1, oprand2))
case 3:
print('%d: reg[%d] += reg[%d]' % (ip, oprand1, oprand2))
case 4:
print('%d: reg[%d] -= %#x' % (ip, oprand1, oprand2))
case 5:
print('%d: reg[%d] -= reg[%d]' % (ip, oprand1, oprand2))
case 6:
print('%d: reg[%d] *= %#x' % (ip, oprand1, oprand2))
case 7:
print('%d: reg[%d] *= reg[%d]' % (ip, oprand1, oprand2))
case 8:
print('%d: reg[%d] /= %#x' % (ip, oprand1, oprand2))
case 9:
print('%d: reg[%d] /= reg[%d]' % (ip, oprand1, oprand2))
case 10:
print('%d: reg[%d] %= %#x' % (ip, oprand1, oprand2))
case 11:
print('%d: reg[%d] %= reg[%d]' % (ip, oprand1, oprand2))
case 12:
print('%d: reg[%d] <<= %#x' % (ip, oprand1, oprand2))
case 13:
print('%d: reg[%d] = reg[0] << %#x' % (ip, oprand1, oprand2))
case 14:
print('%d: push reg[%d]' % (ip, oprand1))
case 15:
print('%d: print_num(reg[%d])' % (ip, oprand1))
case 16:
print('%d: pop_and_print_num' % (ip))
case 17:
print('%d: if !reg[%d]: ip = %d' % (ip, oprand1, oprand2+3))
case 18:
print('%d: if reg[%d]: ip = %d' % (ip, oprand1, oprand2+3))
case 19:
print('%d: ip = %d' % (ip, oprand1+3))
case 20:
print('%d: push reg[reg[%d]]' % (ip, oprand1))
case 21:
print('%d: pop reg[0]' % (ip))
case 22:
print('%d: push %#x' % (ip, oprand1))
case 23:
print('%d: exit' % (ip))
case 24:
print('%d: reg[0] = reg[2] | reg[1]' % (ip))
case 25:
print('%d: reg[%d] = reg[0] >> %#x' % (ip, oprand1, oprand2))
case 26:
print('%d: reg[%d] = %#x' % (ip, oprand1, oprand2))
case _:
print('%d: error!' % (ip))
exit()
ip += 3

从opcode0到opcode3得到完整汇编,reg[3]到reg[6]其实是key

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
0: reg[0] = input
3: reg[1] = reg[0] >> 0x1
6: reg[2] = reg[0] << 0x7
9: reg[0] = reg[2] | reg[1]
12: reg[0] ^= reg[3]
0: reg[0] = input
3: reg[1] = reg[0] >> 0x2
6: reg[2] = reg[0] << 0x6
9: reg[0] = reg[2] | reg[1]
12: reg[0] ^= reg[4]
0: reg[0] = input
3: reg[1] = reg[0] >> 0x3
6: reg[2] = reg[0] << 0x5
9: reg[0] = reg[2] | reg[1]
12: reg[0] ^= reg[5]
0: reg[0] = input
3: reg[1] = reg[0] >> 0x4
6: reg[2] = reg[0] << 0x4
9: reg[0] = reg[2] | reg[1]
12: reg[0] ^= reg[6]
enc = [0xDF, 0xD5, 0xF1, 0xD1, 0xFF, 0xDB, 0xA1, 0xA5, 0x89, 0xBD, 0xE9, 0x95, 0xB3, 0x9D, 0xE9, 0xB3, 0x85, 0x99, 0x87,
0xBF, 0xE9, 0xB1, 0x89, 0xE9, 0x91, 0x89, 0x89, 0x8F, 0xAD]
key = 0xBEEDBEEF.to_bytes(4, 'little')

def decode(s):
s = s ^ key[3]
s = (s << 4 | s >> 4) & 0xFF
s = s ^ key[2]
s = (s << 3 | s >> 5) & 0xFF
s = s ^ key[1]
s = (s << 2 | s >> 6) & 0xFF
s = s ^ key[0]
s = (s << 1 | s >> 7) & 0xFF
return s

flag = []
for i in enc:
i = ((i >> 3) | (i << 5) & 0xFF)
flag.append(decode(i))
print(bytes(flag))
# b'DASCTF{you_are_right_so_cool}'
  • 标题: 2023羊城杯初赛部分Reverse
  • 作者: j1ya
  • 创建于 : 2023-09-18 22:59:48
  • 更新于 : 2026-10-03 23:06:38
  • 链接: https://j1ya-22.github.io/2023/09/18/2023羊城杯初赛部分Reverse/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论
目录
2023羊城杯初赛部分Reverse