2025极客大挑战-Reverse

本文最后更新于 2025年12月4日 晚上

前面的题比较简单就没写 wp,后面的一些题挺有意思的,遂记录

Mission Ghost Signal

本质是 AES-128-CBC 加密,但 S-box、MixColumns 都是自定义的

进入 main 函数分析,输入长度必须 25 字符,IV 固定 1145141145144332”,Key 在 aSyclover2025ge 中就是 Syclover2025Geek,大概是 AES 加密,最后结果与 byte_406020 比较

byte_406020

1
0xB2, 0xB3, 0xDC, 0xB9, 0xF8, 0xD6, 0x93, 0xFF, 0xB5, 0xA1, 0xCC, 0x2A, 0x6F, 0xDE, 0x27, 0x44, 0xAF, 0x21, 0x98, 0xDD, 0x00, 0xC1, 0x0D, 0x1C, 0x53, 0x06, 0x81, 0x3E, 0x16, 0xAB, 0xDF, 0x13

sub_402B57:先进入 sub_402B11 异或再进入 sub_402992 加密

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
int __cdecl sub_402B57(_DWORD *a1, _DWORD *a2, unsigned int a3)
{
int result; // eax
int v4; // [esp+Ch] [ebp-24h]
int v5; // [esp+10h] [ebp-20h]
int v6; // [esp+14h] [ebp-1Ch]
_DWORD *v7; // [esp+20h] [ebp-10h]
unsigned int i; // [esp+24h] [ebp-Ch]

v7 = a1 + 44;
for ( i = 0; i < a3; i += 16 )
{
sub_402B11(a2, v7);
sub_402992(a2, a1);
v7 = a2;
a2 += 4;
}
v4 = v7[1];
v5 = v7[2];
v6 = v7[3];
a1[44] = *v7;
a1[45] = v4;
a1[46] = v5;
result = v6;
a1[47] = v6;
return result;
}


int __cdecl sub_402B11(int a1, int a2)
{
int result; // eax
unsigned __int8 i; // [esp+Fh] [ebp-5h]

for ( i = 0; ; ++i )
{
result = i;
if ( i > 0xFu )
break;
*(_BYTE *)(i + a1) ^= *(_BYTE *)(i + a2);
}
return result;
}

int __cdecl sub_402992(int a1, int a2)
{
unsigned __int8 i; // [esp+1Bh] [ebp-1h]

sub_401CA0(0, a1, a2);
for ( i = 1; i <= 9u; ++i )
{
sub_401D41(a1);
sub_401DB1(a1);
sub_401EA2(a1);
sub_401CA0(i, a1, a2);
}
sub_401D41(a1);
sub_401DB1(a1);
return sub_401CA0(10, a1, a2);
}

继续跟进 sub_401D41 -> sub_401929 -> sub_4018B6 -> sub_4017B8

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
int __cdecl sub_4017B8(unsigned __int8 a1)
{
char v1; // al
char v3[8]; // [esp+10h] [ebp-1Ch] BYREF
char v4[8]; // [esp+18h] [ebp-14h] BYREF
int j; // [esp+20h] [ebp-Ch]
unsigned __int8 v6; // [esp+27h] [ebp-5h]
int i; // [esp+28h] [ebp-4h]

for ( i = 0; i <= 7; ++i )
{
v1 = sub_401546(1 << i);
v4[i] = v1;
}
sub_4015B4(v4, v3);
v6 = 0;
for ( j = 0; j <= 7; ++j )
{
if ( ((a1 >> j) & 1) != 0 )
v6 ^= v3[j];
}
return v6;
}

sub_4015B4 自定义的 SBOX 变换

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
int __cdecl sub_4015B4(int a1, int a2)
{
char v3[24]; // [esp+Ah] [ebp-4Ah]
__int16 v4; // [esp+22h] [ebp-32h]
int kk; // [esp+24h] [ebp-30h]
char v6; // [esp+2Bh] [ebp-29h]
int jj; // [esp+2Ch] [ebp-28h]
int ii; // [esp+30h] [ebp-24h]
int n; // [esp+34h] [ebp-20h]
int m; // [esp+38h] [ebp-1Ch]
int v11; // [esp+3Ch] [ebp-18h]
int k; // [esp+40h] [ebp-14h]
int j; // [esp+44h] [ebp-10h]
unsigned __int8 v14; // [esp+4Bh] [ebp-9h]
int i; // [esp+4Ch] [ebp-8h]

for ( i = 0; i <= 7; ++i )
{
v14 = 0;
for ( j = 0; j <= 7; ++j )
{
if ( ((*(j + a1) >> i) & 1) != 0 )
v14 |= 1 << j;
}
*&v3[2 * i + 8] = v14 | (1 << (i + 8));
}
for ( k = 0; k <= 7; ++k )
{
v11 = -1;
for ( m = k; m <= 7; ++m )
{
if ( ((*&v3[2 * m + 8] >> k) & 1) != 0 )
{
v11 = m;
break;
}
}
if ( v11 == -1 )
return 0;
if ( v11 != k )
{
v4 = *&v3[2 * v11 + 8];
*&v3[2 * v11 + 8] = *&v3[2 * k + 8];
*&v3[2 * k + 8] = v4;
}
for ( n = 0; n <= 7; ++n )
{
if ( n != k && ((*&v3[2 * n + 8] >> k) & 1) != 0 )
*&v3[2 * n + 8] ^= *&v3[2 * k + 8];
}
}
for ( ii = 0; ii <= 7; ++ii )
v3[ii] = HIBYTE(*&v3[2 * ii + 8]);
for ( jj = 0; jj <= 7; ++jj )
{
v6 = 0;
for ( kk = 0; kk <= 7; ++kk )
{
if ( ((v3[kk] >> jj) & 1) != 0 )
v6 |= 1 << kk;
}
*(a2 + jj) = v6;
}
return 1;
}

跟进 sub_401C08 -> sub_4019B7,这里是在进行密钥扩展

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
void __cdecl sub_4019B7(int a1, int a2)
{
char v2; // [esp+10h] [ebp-14h]
char v3; // [esp+10h] [ebp-14h]
unsigned __int8 v4; // [esp+11h] [ebp-13h]
unsigned __int8 v5; // [esp+12h] [ebp-12h]
unsigned __int8 v6; // [esp+13h] [ebp-11h]
int v7; // [esp+14h] [ebp-10h]
unsigned __int8 v8; // [esp+1Bh] [ebp-9h]
int v9; // [esp+1Ch] [ebp-8h]
unsigned int i; // [esp+20h] [ebp-4h]
unsigned int j; // [esp+20h] [ebp-4h]

for ( i = 0; i <= 3; ++i )
{
*(a1 + 4 * i) = *(4 * i + a2);
*(a1 + 4 * i + 1) = *(4 * i + 1 + a2);
*(a1 + 4 * i + 2) = *(4 * i + 2 + a2);
*(a1 + 4 * i + 3) = *(4 * i + 3 + a2);
}
for ( j = 4; j <= 0x2B; ++j )
{
v2 = *(a1 + 4 * (j + 0x3FFFFFFF));
v4 = *(4 * (j + 0x3FFFFFFF) + 1 + a1);
v5 = *(4 * (j + 0x3FFFFFFF) + 2 + a1);
v6 = *(4 * (j + 0x3FFFFFFF) + 3 + a1);
if ( (j & 3) == 0 )
{
v8 = *(a1 + 4 * (j + 0x3FFFFFFF));
v3 = sub_401929(v4);
v4 = sub_401929(v5);
v5 = sub_401929(v6);
v6 = sub_401929(v8);
v2 = v3 ^ unk_407064[j >> 2];
}
v7 = 4 * j;
v9 = 4 * (j + 1073741820);
*(a1 + 4 * j) = *(a1 + v9) ^ v2;
*(v7 + 1 + a1) = *(v9 + 1 + a1) ^ v4;
*(v7 + 2 + a1) = *(v9 + 2 + a1) ^ v5;
*(4 * j + 3 + a1) = *(v9 + 3 + a1) ^ v6;
}
}

unk_407064 这组数据就是 RCON

1
0x8D, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36

解密脚本如下:

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
from typing import List

def rotl8(x:int,n:int)->int:
return ((x<<n)&0xFF)|((x&0xFF)>>(8-n))

def rot(a1:int,a2:int)->int:
return rotl8(a1&0xFF,a2)

def sub_401546(a1:int)->int:
v1=rot(a1,1)&0xFF
v1=(a1^v1)&0xFF
v2=(rot(a1,2)^v1)&0xFF
v3=(rot(a1,3)^v2)&0xFF
return (v3^rot(a1,4))&0xFF

def gf_mul_by_pow(a:int,b:int)->int:
v6=0; aa=a&0xFF; cnt=b
while cnt:
if cnt&1: v6^=aa
v2=0 if aa<0x80 else 0x1B
aa=((aa<<1)&0xFF)^v2
cnt>>=1
return v6&0xFF

def sub_4014F6(a1:int)->int:
if a1==0: return 0
v3=1
for _ in range(253):
v3=gf_mul_by_pow(v3,a1)
return v3&0xFF

def sub_4015B4(a1_bytes:List[int])->List[int]:
v3=[0]*24
for i in range(8):
v14=0
for j in range(8):
if ((a1_bytes[j]>>i)&1)!=0: v14|=(1<<j)
word=(v14&0xFF)|(((1<<(i+8))&0xFFFF)&0xFF00)
v3[2*i+8]=word&0xFFFF
for k in range(8):
v11=-1
for m in range(k,8):
if ((v3[2*m+8]>>k)&1)!=0:
v11=m; break
if v11==-1: return None
if v11!=k:
tmp=v3[2*v11+8]; v3[2*v11+8]=v3[2*k+8]; v3[2*k+8]=tmp
for n in range(8):
if n!=k and ((v3[2*n+8]>>k)&1)!=0:
v3[2*n+8]^=v3[2*k+8]
for ii in range(8):
word=v3[2*ii+8]&0xFFFF
v3[ii]=(word>>8)&0xFF
v_out=[0]*8
for jj in range(8):
v6=0
for kk in range(8):
if ((v3[kk]>>jj)&1)!=0: v6|=(1<<kk)
v_out[jj]=v6&0xFF
return v_out

def sub_4017B8(a1:int)->int:
v3=[0]*8
for i in range(8): v3[i]=sub_401546((1<<i)&0xFF)
v2=sub_4015B4(v3)
v5=0
for j in range(8):
if ((a1>>j)&1)!=0: v5^=v2[j]
return v5&0xFF

def build_sbox(a3:int=167):
S=[0]*256; Inv=[0]*256
_=sub_4017B8(a3)
for i in range(256):
v5=sub_4014F6(i)
v4=(a3^sub_401546(v5))&0xFF
S[i]=v4; Inv[v4]=i
return S,Inv

def sub_401E72(a1:int)->int:
return ((2*a1)^(27*((a1&0xFF)>>7)))&0xFF

def mix_single_column(col:List[int])->List[int]:
s0,s1,s2,s3=col
v3=s0
v2=(s2^s1^v3^s3)&0xFF
t0=v2^(sub_401E72((v3^s1)&0xFF)&0xFF)^v3
t1=s1^v2^(sub_401E72((s1^s2)&0xFF)&0xFF)
t2=s2^v2^(sub_401E72((s2^s3)&0xFF)&0xFF)
t3=s3^v2^(sub_401E72((v3^s3)&0xFF)&0xFF)
return [t0&0xFF,t1&0xFF,t2&0xFF,t3&0xFF]

def mix_columns(state:List[int])->List[int]:
s=state[:]
for i in range(4):
col=[s[0+4*i],s[1+4*i],s[2+4*i],s[3+4*i]]
coln=mix_single_column(col)
for r in range(4): s[r+4*i]=coln[r]
return s

def gf_mul(a:int,b:int)->int:
return gf_mul_by_pow(a,b)

INV_MAT=[[0x0E,0x0B,0x0D,0x09],[0x09,0x0E,0x0B,0x0D],[0x0D,0x09,0x0E,0x0B],[0x0B,0x0D,0x09,0x0E]]

def inv_mix_single_column(col:List[int])->List[int]:
res=[0,0,0,0]
for row in range(4):
v=0
for k in range(4): v^=gf_mul(INV_MAT[row][k],col[k])
res[row]=v&0xFF
return res

def inv_mix_columns(state:List[int])->List[int]:
s=state[:]
for i in range(4):
col=[s[0+4*i],s[1+4*i],s[2+4*i],s[3+4*i]]
coln=inv_mix_single_column(col)
for r in range(4): s[r+4*i]=coln[r]
return s

def shift_rows(state:List[int])->List[int]:
out=[0]*16
for r in range(4):
for c in range(4): out[r+4*c]=state[r+4*((c+r)%4)]
return out

def inv_shift_rows(state:List[int])->List[int]:
out=[0]*16
for r in range(4):
for c in range(4): out[r+4*c]=state[r+4*((c-r)%4)]
return out

SBOX,INV_SBOX=build_sbox(167)

def sub_bytes(state:List[int])->List[int]:
return [SBOX[b] for b in state]

def inv_sub_bytes(state:List[int])->List[int]:
return [INV_SBOX[b] for b in state]

RCON=[0x8D,0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80,0x1B,0x36]

def key_expansion_bytes(key_bytes:bytes)->List[List[int]]:
w=[0]*44
for i in range(4):
b0=key_bytes[4*i]; b1=key_bytes[4*i+1]; b2=key_bytes[4*i+2]; b3=key_bytes[4*i+3]
w[i]=(b0)|(b1<<8)|(b2<<16)|(b3<<24)
for j in range(4,44):
temp=w[j-1]
if j%4==0:
b0=temp&0xFF; b1=(temp>>8)&0xFF; b2=(temp>>16)&0xFF; b3=(temp>>24)&0xFF
rb=[b1,b2,b3,b0]
sb=[SBOX[x] for x in rb]
sb[0]^=RCON[j//4]&0xFF
temp=(sb[0])|(sb[1]<<8)|(sb[2]<<16)|(sb[3]<<24)
w[j]=w[j-4]^temp
round_keys=[]
for r in range(11):
rk=[0]*16
for i in range(4):
word=w[4*r+i]
rk[4*i:4*i+4]=[(word>>(8*k))&0xFF for k in range(4)]
round_keys.append(rk)
return round_keys

def add_round_key(state:List[int],round_key:List[int])->List[int]:
return [s^rk for s,rk in zip(state,round_key)]

def aes_decrypt_block(cipher_block:bytes,round_keys:List[List[int]])->bytes:
state=list(cipher_block)
state=add_round_key(state,round_keys[10])
for r in range(9,0,-1):
state=inv_shift_rows(state)
state=inv_sub_bytes(state)
state=add_round_key(state,round_keys[r])
state=inv_mix_columns(state)
state=inv_shift_rows(state)
state=inv_sub_bytes(state)
state=add_round_key(state,round_keys[0])
return bytes(state)

CIPHER_HEX="b2b3dcb9f8d693ffb5a1cc2a6fde2744af2198dd00c10d1c5306813e16abdf13"
CIPHER=bytes.fromhex(CIPHER_HEX)
KEY=b"Syclover2025Geek"
IV=b"1145141145144332"

round_keys = key_expansion_bytes(KEY)
c1, c2 = CIPHER[:16], CIPHER[16:32]

p1 = aes_decrypt_block(c1, round_keys)
plain1 = bytes(x^y for x,y in zip(p1, IV))

p2 = aes_decrypt_block(c2, round_keys)
plain2 = bytes(x^y for x,y in zip(p2, c1))

plaintext = plain1 + plain2

print(plaintext.decode('latin-1'))

解除来的是:

1
We_ve_Trapped_in_The_Sink

作为压缩包密码,打开有一个 wav 音频,可以听出这个音频像信号,所以有可能是 sstv

1
sstv -d 1nn3r.wav

解出来是一个有二维码的图片,识别不出来,然后我拿手机微信识别出来的,会跳转网页下载一个 secret.zip 文件

解压得到一个音频文件,摩斯电码,在线网站解码

1
55 31 6C 44 65 7A 64 6F 4D 54 56 66 4D 56 4E 66 4E 46 38 35 63 6A 52 75 52 46 39 6A 4D 45 34 31 63 44 46 79 51 47 4E 5A 4C 6E 30 3D

hex + base64 解密

1
SYC{7h15_1S_4_9r4nD_c0N5p1r@cY.}

stack_bomb

一个 tea 加密

无法反编译,分析汇编代码

1
2
3
4
5
6
7
8
9
10
11
push    offset loc_411307		eax = [ebp+8] add [ebp+0Ch]				return a2 + a1
push offset sub_41109B eax = [ebp+arg_0] sub [ebp+arg_4] return a1 - a2
push offset sub_4113F2 eax = [ebp+arg_0] xor [ebp+arg_4] return a2 ^ a1
push offset loc_411186 eax = [ebp+8] and [ebp+0Ch] return a2 & a1
push offset sub_41115E eax = [ebp+arg_0] shl [ebp+arg_4] return a1 << a2
push offset sub_411091 eax = [ebp+arg_0] shr [ebp+arg_4] return a1 >> a2
push offset sub_4112D0 eax = [ebp+arg_0] mod [ebp+arg_4] return a1 % a2
push offset sub_411163 eax = [ebp+arg_0] imul [ebp+arg_4] return a2 * a1
push offset sub_411005 eax = [ebp+arg_0] idiv [ebp+arg_4] return a1 / a2
push offset loc_41139D 回调处理
push offset sub_411014 回调处理

输入的 32 字节:

1
2
3
4
5
6
7
8
9
10
11
12
13
cmp     [ebp+ecx+var_54], 9A8C0C4Bh
cmp [ebp+eax+var_54], 0C412FF1Ch
cmp [ebp+ecx+var_64], 0BFC3A488h
cmp [ebp+eax+var_64], 0B16C8FD0h
cmp [ebp+ecx+var_74], 4136E319h
cmp [ebp+eax+var_74], 8835E4FFh
cmp [ebp+ecx+var_84], 118263A7h
cmp [ebp+eax+var_84], 7C85D629h

var_54 = Buffer[0:8]
var_64 = Buffer[8:16]
var_74 = Buffer[16:24]
var_84 = Buffer[24:32]

sub_411FE0 中找到 key:1,2,3,4

1
2
3
4
mov     [ebp+var_18], 1
mov [ebp+var_14], 2
mov [ebp+var_10], 3
mov [ebp+var_C], 4

跟踪到 sub_411920,这里是加密核心,循环 a48 的值次数

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
int __cdecl sub_411920(
__int128 a1,
int a2,
int a3,
int a4,
int a5,
int a6,
int a7,
int a8,
int a9,
int a10,
int a11,
int a12,
int a13,
int a14,
int a15,
int a16,
int a17,
int a18,
int a19,
int a20,
int a21,
int a22,
int a23,
int a24,
int a25,
int a26,
int a27,
int a28,
int a29,
int a30,
int a31,
int a32,
int a33,
int a34,
int a35,
int a36,
int a37,
int a38,
int a39,
int a40,
int a41,
int a42,
int a43,
int a44,
int a45,
int a46,
int a47,
signed int a48)
{
int v48; // eax
int v49; // eax
int v50; // eax
int v51; // eax
int v52; // eax
int v53; // eax
int v54; // eax
int v55; // eax
int v56; // eax
int v57; // eax
int v59; // [esp+D0h] [ebp-78h]
int v60; // [esp+DCh] [ebp-6Ch]
int v61; // [esp+DCh] [ebp-6Ch]
int v62; // [esp+DCh] [ebp-6Ch]
int v63; // [esp+DCh] [ebp-6Ch]
int v64; // [esp+E8h] [ebp-60h]
int v65; // [esp+F4h] [ebp-54h]
int v66; // [esp+100h] [ebp-48h]
int v67; // [esp+10Ch] [ebp-3Ch]
signed int v68; // [esp+118h] [ebp-30h]
int v69; // [esp+124h] [ebp-24h]
int v70; // [esp+130h] [ebp-18h]
int v71; // [esp+13Ch] [ebp-Ch]

v71 = *(_DWORD *)a1;
v70 = *(_DWORD *)(a1 + 4);
v69 = 0;
v67 = *(_DWORD *)DWORD1(a1);
v66 = *(_DWORD *)(DWORD1(a1) + 4);
v65 = *(_DWORD *)(DWORD1(a1) + 8);
v64 = *(_DWORD *)(DWORD1(a1) + 12);
v59 = 1;
v68 = STACK[0x294];
while ( v59 )
{
while ( 1 )
{
STACK[0x26C] = a1;
STACK[0x268] = *(_DWORD *)((char *)&a1 + 1);
STACK[0x264] = v69;
STACK[0x260] = DWORD1(a1);
STACK[0x25C] = *(_DWORD *)((char *)&a1 + 5);
STACK[0x258] = *(_DWORD *)((char *)&a1 + 6);
STACK[0x254] = *(_DWORD *)((char *)&a1 + 7);
STACK[0x250] = dword_41B198;
v69 = ((int (__cdecl *)(unsigned int, unsigned int))STACK[0x2C0])(STACK[0x250], STACK[0x264]);
v48 = ((int (__cdecl *)(int, unsigned int))STACK[0x2B0])(v70, STACK[0x284]);
v60 = ((int (__cdecl *)(int, int))STACK[0x2C0])(v67, v48);
v49 = ((int (__cdecl *)(int, int))STACK[0x2C0])(v69, v70);
v61 = ((int (__cdecl *)(int, int))STACK[0x2B8])(v49, v60);
v50 = ((int (__cdecl *)(int, unsigned int))STACK[0x2AC])(v70, STACK[0x280]);
v51 = ((int (__cdecl *)(int, int))STACK[0x2C0])(v66, v50);
v52 = ((int (__cdecl *)(int, int))STACK[0x2B8])(v61, v51);
v71 = ((int (__cdecl *)(int, int))STACK[0x2C0])(v71, v52);
v53 = ((int (__cdecl *)(int, unsigned int))STACK[0x2B0])(v71, STACK[0x284]);
v62 = ((int (__cdecl *)(int, int))STACK[0x2C0])(v65, v53);
v54 = ((int (__cdecl *)(int, int))STACK[0x2C0])(v69, v71);
v63 = ((int (__cdecl *)(int, int))STACK[0x2B8])(v54, v62);
v55 = ((int (__cdecl *)(int, unsigned int))STACK[0x2AC])(v71, STACK[0x280]);
v56 = ((int (__cdecl *)(int, int))STACK[0x2C0])(v64, v55);
v57 = ((int (__cdecl *)(int, int))STACK[0x2B8])(v63, v56);
v70 = ((int (__cdecl *)(int, int))STACK[0x2C0])(v70, v57);
if ( v68 >= a48 )
break;
++v68;
}
v59 = 0;
}
*(_DWORD *)a1 = v71;
*(_DWORD *)(a1 + 4) = v70;
return 0;
}

这里对应 key

1
2
3
4
v67 = *DWORD1(a1);				arg_0		*(uint32_t*)(*(uint32_t*)ptr + 0)			k[0]
v66 = *(DWORD1(a1) + 4); arg_4 *(uint32_t*)(*(uint32_t*)ptr + 4) k[1]
v65 = *(DWORD1(a1) + 8); arg_8 *(uint32_t*)(*(uint32_t*)ptr + 8) k[2]
v64 = *(DWORD1(a1) + 12); arg_12 *(uint32_t*)(*(uint32_t*)ptr + 12) k[3]

x 和 y,sum

1
2
3
v71 = *a1;				arg_0		x
v70 = *(a1 + 4); arg_4 y
v69 = 0 sum

这里一堆 STACK[],是因为反编译失败,要获取每个 STACK 代表什么可以在前面 push 每个调用的函数 return 下断点,调试时会调用此函数,同时可以获取 delta 和 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
a48 = 0x1F+1 = 32;
uint32_t sum = 0;
uint32_t i = STACK[0x284] = 0x4;
uint32_t j = STACK[0x280] = 0x5;
uint32_t delta = STACK[0x250] = 0x9000000;

STACK[0x2C0] -> " + "
STACK[0x2B0] -> " << "
STACK[0x2B8] -> " ^ "
STACK[0x2AC] -> " >> "

.data:00BFB198 dword_BFB198 dd 9000000h

v69 = (STACK[0x2C0])(STACK[0x250], STACK[0x264]) sum = (STACK[0x2C0])(delta,sum) -> sum = delta + sum
v48 = (STACK[0x2B0])(v70, STACK[0x284]); t0 = (STACK[0x2B0])(y,i) -> t0 = y << 4
v60 = (STACK[0x2C0])(v67, v48); t1 = (STACK[0x2C0])(k[0],t0) -> t1 = 1 + t0
v49 = (STACK[0x2C0])(v69, v70); t2 = (STACK[0x2C0])(sum,y) -> t2 = sum + y
v61 = (STACK[0x2B8])(v49, v60); t3 = (STACK[0x2B8])(t2,t1) -> t3 = t2 ^ t1
v50 = (STACK[0x2AC])(v70, STACK[0x280]); t4 = (STACK[0x2AC])(y,5) -> t4 = y >> 5
v51 = (STACK[0x2C0])(v66, v50); t5 = (STACK[0x2C0])(k[1],t4) -> t5 = 2 + t4
v52 = (STACK[0x2B8])(v61, v51); t6 = (STACK[0x2B8])(t3,t5) -> t6 = t3 ^ t5
v71 = (STACK[0x2C0])(v71, v52); x = (STACK[0x2C0])(x, t6) -> x = x + t6
v53 = (STACK[0x2B0])(v71, STACK[0x284]); t7 = (STACK[0x2B0])(x,4) -> t7 = x << 4
v62 = (STACK[0x2C0])(v65, v53); t8 = (STACK[0x2C0])(k[2],t7) -> t8 = 3 + t7
v54 = (STACK[0x2C0])(v69, v71); t9 = (STACK[0x2C0])(sum, x) -> t9 = sum + x
v63 = (STACK[0x2B8])(v54, v62); t10 = (STACK[0x2B8])(t9, t8) -> t10 = t9 ^ t8
v55 = (STACK[0x2AC])(v71, STACK[0x280]); t11 = (STACK[0x2AC])(x, 5) -> t11 = x >> 5
v56 = (STACK[0x2C0])(v64, v55); t12 = (STACK[0x2C0])(k[3], t11) -> t12 = 4 + t11
v57 = (STACK[0x2B8])(v63, v56); t13 = (STACK[0x2B8])(t10, t12) -> t13 = t10 ^ t12
v70 = (STACK[0x2C0])(v70, v57); y = (STACK[0x2C0])(y,t13) -> y = y + t13


x += (((sum+y) ^ (k[0]+(y<<4)) ^ (k[1]+(y>>5)))
y += (((sum+x) ^ (k[2]+(x<<4)) ^ (k[3]+(x>>5)))

由此可还原 AES 加密逻辑

1
2
3
4
5
6
7
8
9
10
11
void encrypt(uint32_t *v, uint32_t *k) {
uint32_t y = v[1], x = v[0];
uint32_t sum = 0, delta = 0x9000000;
for (int n = 0; n <= 32; ++n) {
sum += delta;
x += ((y << 4) + k[0]) ^ (y + sum) ^ ((y >> 5) + k[1]);
y += ((x << 4) + k[2]) ^ (x + sum) ^ ((x >> 5) + k[3]);
}
v[0] = x;
v[1] = y;
}

解密脚本如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def decrypt(v, k):
v0, v1 = v
delta, sum = 0x09000000, 0x09000000 * 32
for _ in range(32):
v1 = (v1 - (((v0 + s) ^ (k[2] + (v0 << 4)) ^ (k[3] + (v0 >> 5))))) & 0xFFFFFFFF
v0 = (v0 - (((v1 + s) ^ (k[0] + (v1 << 4)) ^ (k[1] + (v1 >> 5))))) & 0xFFFFFFFF
sum = (sum - delta) & 0xFFFFFFFF
return v0, v1

k = [1, 2, 3, 4]
data = [0x9A8C0C4B, 0xC412FF1C, 0xBFC3A488, 0xB16C8FD0,
0x4136E319, 0x8835E4FF, 0x118263A7, 0x7C85D629]

plain = b''
for i in range(0, len(data), 2):
v0, v1 = decrypt(data[i:i+2], k)
for x in (v0, v1):
for j in range(4):
plain += bytes([(x >> (8 * j)) & 0xFF])

print(''.join(chr(b) for b in plain))

拿到 flag

1
syc{QYQS_F1nD_Th3_@nswer_H3re~~}

GeekBinder

加密很简单,按 i%8 和 geek2025 异或,密文在 attr_get_hidden_cipher 函数中

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
__int64 __fastcall attr_xor_cipher(__int64 a1, size_t a2, _QWORD *a3, size_t *a4)
{
void *v7; // [rsp+28h] [rbp-8h]

if ( !a1 || !a2 || !a3 || !a4 )
return 0xFFFFFFFFLL;
v7 = malloc(a2);
if ( !v7 )
return 4294967294LL;
sub_1119(a1, a2, v7);
*a3 = v7;
*a4 = a2;
return 0LL;
}

unsigned __int64 __fastcall sub_1119(__int64 a1, unsigned __int64 a2, __int64 a3)
{
unsigned __int64 result; // rax
unsigned __int64 i; // [rsp+20h] [rbp-8h]

for ( i = 0LL; ; ++i )
{
result = i;
if ( i >= a2 )
break;
*(a3 + i) = *(a1 + i) ^ aGeek2025[i % 8];
}
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
import struct

data_qwords = [
0x7C725E7310263C34,
0x5D666F5505541F1E,
0x4601535D19153A54,
0x4266037034165614,
0x505E5974340B0002,
0x5B5D536D18543A54,
0x5A666F4B19251713,
0x6A5E705F19550B38,
0x0651594608251717,
0x506D5560340B5438,
0x0440555705540209,
]

buf = b''.join(struct.pack("<Q", x) for x in data_qwords)
buf += struct.pack("<H", 521)
buf += struct.pack("B", 24)
assert len(buf) == 91

key = b"geek2025"
out = bytearray(len(buf))
for i in range(len(buf)):
out[i] = buf[i] ^ key[i % len(key)]

print(out.decode('ascii', errors='replace'))
1
SYC{An@Iyz1ng_Th3_proc3ss3s_B3Tween_File3_1s_contr@ry_To_n0rm@l_pr@ctic3_1n_Re_eng1neer1ng}

Her

这个题有个 SEH,也就是 Windows 的异常处理,反编译隐藏正确的调用代码

主要跟进 sub_4011B3() 函数

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
int sub_4024A0()
{
unsigned int v0; // eax
char v2; // [esp+0h] [ebp-F0h]
char v3; // [esp+0h] [ebp-F0h]
char v4; // [esp+0h] [ebp-F0h]
char v5; // [esp+0h] [ebp-F0h]
size_t i; // [esp+D0h] [ebp-20h]
int v7; // [esp+DCh] [ebp-14h]
size_t v8; // [esp+E8h] [ebp-8h]

sub_40123A(&unk_40D0A4);
v0 = sub_402760(0);
srand(v0);
sub_401177();
sub_401087("Enter the flag: ", v2);
sub_401023("%127s", (char)&Str);
v8 = strlen(&Str);
if ( v8 == dword_40B020 )
{
sub_4011B3();
sub_401087("\nVerifying encrypted flag...\n", v3);
v7 = 1;
for ( i = 0; i < v8; ++i )
{
if ( byte_40B260[i] != byte_40B000[i] )
v7 = 0;
}
if ( v7 )
{
sub_401087("\n", v4);
sub_401087("YES!YES!", v5);
}
else
{
sub_401087("\nWrong\n", v4);
}
return 0;
}
else
{
sub_401087("\nWrong\n", v3);
return 1;
}
}

一直跟进到 sub_401D30 函数,这里有一个 BUG 函数,反汇编能看到 SEH 的关键代码

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
size_t sub_401D30()
{
size_t result; // eax
size_t i; // [esp+F4h] [ebp-14h]
size_t v2; // [esp+100h] [ebp-8h]

v2 = strlen(&Str);
if ( sub_4010CD() )
{
for ( i = 0; ; ++i )
{
result = i;
if ( i >= v2 )
break;
rand();
byte_40B260[i] = sub_401177();
}
}
else
{
result = 0;
if ( v2 )
{
dword_40B1DC = 0;
BUG();
}
}
return result;
}

如下,这里是 SEH 链的注册,这里注册了三个函数,根据栈的规则 sub_401091 最先被调用,sub_401091->sub_401186->sub_401181

1
2
3
4
5
6
7
8
9
10
.text:00401DCD                 push    offset sub_401181			// 将异常处理函数的地址压栈
.text:00401DD2 push large dword ptr fs:0 // 将当前的 SEH 链头指针压栈
.text:00401DD9 mov large fs:0, esp // 将 fs:0 更新为当前栈顶
.text:00401DE0 push offset sub_401186
.text:00401DE5 push large dword ptr fs:0
.text:00401DEC mov large fs:0, esp
.text:00401DF3 push offset sub_401091
.text:00401DF8 push large dword ptr fs:0
.text:00401DFF mov large fs:0, esp
.text:00401E06 ud2 // 抛出非法异常

跟进 sub_401091 ,与 0xAA 异或

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int __cdecl sub_401EA0(_DWORD *a1, int a2, int a3)
{
char v3; // bl

if ( *a1 != -1073741795 || dword_40B1DC >= strlen(Str) )
return 1;
byte_40B2E0[dword_40B1DC] = ++dword_40B1D8 ^ Str[dword_40B1DC] ^ 0xAA;
if ( sub_4010CD() )
{
rand();
v3 = sub_401177();
byte_40B260[dword_40B1D8 % strlen(Str)] = v3;
}
else
{
*(_DWORD *)(a3 + 184) += 2;
++dword_40B360;
}
return 0;
}

并且这里还调用了 sub_401186 函数

1
2
3
4
5
6
7
8
.text:00401F60                 push    offset sub_401186
.text:00401F65 push large dword ptr fs:0
.text:00401F6C mov large fs:0, esp
.text:00401F73 xor ecx, ecx
.text:00401F75 mov eax, 1
.text:00401F7A div ecx
.text:00401F7C mov eax, [esp+0E0h+var_E0]
.text:00401F7F mov large fs:0, eax

跟进 sub_401186 ,进行加法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int __cdecl sub_402010(_DWORD *a1, int a2, int a3)
{
char v3; // bl

if ( *a1 != -1073741676 || dword_40B1DC >= strlen(&Str) )
return 1;
byte_40B2E0[dword_40B1DC] += ++dword_40B1D8 % 0x100u + dword_40B1DC * dword_40B1DC;
if ( sub_4010CD() )
{
rand();
v3 = sub_401177();
byte_40B260[dword_40B1D8 % strlen(&Str)] = v3;
}
else
{
*(_DWORD *)(a3 + 184) += 2;
++dword_40B360;
}
return 0;
}

并且也调用了 sub_401181

1
2
3
4
5
6
7
.text:004020DD                 push    offset sub_401181
.text:004020E2 push large dword ptr fs:0
.text:004020E9 mov large fs:0, esp
.text:004020F0 xor eax, eax
.text:004020F2 mov al, [eax]
.text:004020F4 mov eax, [esp+0E0h+var_E0]
.text:004020F7 mov large fs:0, eax

sub_401181 这边进行了循环移动

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
int __cdecl sub_402190(_DWORD *a1, int a2, int a3)
{
char v3; // bl
int v5; // [esp+E8h] [ebp-8h]

sub_40123A(&unk_40D0A4);
if ( *a1 != -1073741819 )
return 1;
v5 = dword_40B1DC;
if ( dword_40B1DC >= strlen(Str) )
return 1;
++dword_40B1D8;
if ( sub_4010CD() )
{
rand();
v3 = sub_401177();
byte_40B260[dword_40B1D8 % strlen(Str)] = v3;
}
else
{
byte_40B260[v5] = ((int)(unsigned __int8)byte_40B2E0[v5] >> 5) | (8 * byte_40B2E0[v5]);
*(_DWORD *)(a3 + 184) += 2;
++dword_40B360;
}
return 0;
}

加密比较简单,动调获取密文,主要是 SEH 处理,解密脚本如下

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
def encrypt_char(c, i, cnt):
v = ord(c)
rol3 = lambda x: ((x >> 5) | (x << 3)) & 0xFF

ops = {
1: lambda v, cnt: ((cnt ^ v ^ 0xAA) & 0xFF, None),
2: lambda v, cnt: ((v + (cnt % 256 + i * i)) & 0xFF, None),
3: lambda v, cnt: (v, rol3(v)),
}

out = 0
for step in (1, 2, 3, 2, 3, 3):
cnt += 1
v, tmp = ops[step](v, cnt)
if tmp is not None:
out = tmp
cnt ^= 0x1A373
return out, cnt


def decrypt(cipher):
res = []
cnt = 0x1A373

for i, t in enumerate(cipher):
for x in range(256):
enc, new_cnt = encrypt_char(chr(x), i, cnt)
if enc == t:
res.append(chr(x))
cnt = new_cnt
break
else:
res.append('?')
_, cnt = encrypt_char('A', i, cnt)
return "".join(res)


cipher = [
0x2D, 0x4F, 0x69, 0x3D, 0x5F, 0x01, 0xBD, 0x9F,
0xA4, 0x6D, 0x89, 0xAE, 0x2A, 0xEA, 0xD1, 0x9C,
0x71, 0x6D, 0xE1, 0x1E, 0x38, 0x7E, 0x8C, 0x0A,
0xCE, 0x6B, 0xE0, 0xF7, 0x36, 0x72, 0x99
]

print(decrypt(cipher))

拿到 flag

1
SYC{M@y_bE_m3et_HeR_1s_A_Err0R}

ez_Android

加密的 flag 密文在 res/raw 中,function1 为 des 加密,key 为 12345678

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
package com.example.syc;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;

/* loaded from: classes.dex */
public abstract class function1 {
public static String function1(String input1) {
try {
byte[] data = input1.getBytes("UTF-8");
int len = data.length;
int padLen = 8 - (len % 8);
byte[] padded = new byte[len + padLen];
System.arraycopy(data, 0, padded, 0, len);
SecretKeySpec keySpec = new SecretKeySpec("12345678".getBytes("UTF-8"), "DES");
Cipher cipher = Cipher.getInstance("DES/ECB/NoPadding");
cipher.init(1, keySpec);
byte[] encrypted = cipher.doFinal(padded);
StringBuilder sb = new StringBuilder();
for (byte b : encrypted) {
sb.append(String.format("%02X", Byte.valueOf(b)));
}
return sb.toString();
} catch (Exception e) {
e.printStackTrace();
return "";
}
}
}

1
Th1s_1s_

function2,JNI_onLoad 动态调用了真正的 so,获取真正加载的 so

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
__int64 __fastcall JNI_OnLoad(__int64 a1)
{
char v3[8]; // [rsp+30h] [rbp-10h] BYREF
unsigned __int64 v4; // [rsp+38h] [rbp-8h]

v4 = __readfsqword(0x28u);
if ( (*(*a1 + 48LL))(a1, v3, 65542LL) )
{
return -1;
}
else
{
load_real_so("/data/data/com.example.syc/files/libreal_tmp.so");
return 65542;
}
}

load_real_so 加载真实 so 的 wrapper,它从内存中解密内置的 encrypted_so →->写入临时文件 -> dlopen -> dlsym 加载真正的 JNI 方法

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
void __fastcall load_real_so(const char *a1)
{
int *v1; // rax
char *v2; // rax
int *v3; // rax
char *v4; // rax
char *v5; // rax
char *v6; // rax
void *handle; // [rsp+8h] [rbp-88h]
int fd; // [rsp+14h] [rbp-7Ch]
void *ptr; // [rsp+18h] [rbp-78h]

ptr = malloc(encrypted_so_len);
if ( ptr )
{
__memcpy_chk(ptr, &encrypted_so, encrypted_so_len, -1LL);
decrypt_xor(ptr, encrypted_so_len, 170LL);
fd = open(a1, 578, 448LL);
if ( fd >= 0 )
{
if ( __write_chk(fd, ptr, encrypted_so_len, -1LL) == encrypted_so_len )
{
close(fd);
free(ptr);
handle = dlopen(a1, 2);
if ( handle )
{
qword_AEE8 = dlsym(handle, "Java_com_example_syc_function2_function2");
if ( qword_AEE8 )
{
__android_log_print(3LL, "wrapper", "Real SO loaded successfully!");
}
else
{
v6 = dlerror();
__android_log_print(3LL, "wrapper", "dlsym error: %s", v6);
dlclose(handle);
}
}
else
{
v5 = dlerror();
__android_log_print(3LL, "wrapper", "dlopen error: %s", v5);
}
}
else
{
v3 = __errno();
v4 = strerror(*v3);
__android_log_print(3LL, "wrapper", "write failed: %s", v4);
close(fd);
free(ptr);
}
}
else
{
v1 = __errno();
v2 = strerror(*v1);
__android_log_print(3LL, "wrapper", "open tmp file failed: %s", v2);
free(ptr);
}
}
else
{
__android_log_print(3LL, "wrapper", "malloc failed");
}
}

解密 so 脚本如下

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
import sys, os

if len(sys.argv) < 3:
print("Usage: python3 find_xor_elf_aa.py <inputfile> <outprefix>")
sys.exit(1)

infile = sys.argv[1]
prefix = sys.argv[2]
key = 0xAA
magic = b'\x7fELF'

with open(infile, 'rb') as f:
data = f.read()

print(f"File {infile} size {len(data)} bytes. Scanning for XOR 0x{key:02X} -> ELF magic")

hits = []
for off in range(0, len(data) - 4):
if bytes([data[off+i] ^ key for i in range(4)]) == magic:
hits.append(off)

if not hits:
print("No hits found for key 0xAA.")
sys.exit(0)

print(f"Found {len(hits)} hit(s) at offsets: {', '.join(hex(x) for x in hits)}")
for off in hits:
outname = f"{prefix}_{off:08X}_aa.so"
cand = bytes([b ^ key for b in data[off:]])
with open(outname, 'wb') as out:
out.write(cand)
print(f"Wrote {outname} (size {len(cand)} bytes)")
print("Done.")

分析发现 function2 是标准 rc4 加密,key 为 mysecret,解出来

1
Th3_R3@l

function3 是 des,但是被魔改了,提取其中数据

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

def rint(f,o,n):
f.seek(o);return list(struct.unpack(f'<{n}I',f.read(n*4)))
def rbytes(f,o,n):
f.seek(o);return f.read(n)

def pr_arr(name,arr,w):
print(f"{name} {{")
for i in range(0,len(arr),w):
print(" " + ", ".join(f"{x:3d}" for x in arr[i:i+w]) + ",")
print("};\n")

def pr_box(name,arr):
print(f"{name} = {{")
for b in range(8):
print(f" {{ // S{b+1}")
for r in range(4):
s=b*64+r*16
print(" " + ", ".join(f"{arr[s+i]:2d}" for i in range(16)) + ",")
print(" },")
print("};\n")

def extract(p):
f=open(p,'rb')
f.seek(0x20);phoff=struct.unpack('<Q',f.read(8))[0]
f.seek(0x36);phnum=struct.unpack('<H',f.read(2))[0]
ro_offset=0;ro_vaddr=0
for i in range(phnum):
o=phoff+i*0x38
f.seek(o);t=struct.unpack('<I',f.read(4))[0]
if t==1:
f.seek(o+8);off=struct.unpack('<Q',f.read(8))[0]
va=struct.unpack('<Q',f.read(8))[0]
sz=struct.unpack('<Q',f.read(8))[0]
if va<=0x1520<va+sz:
ro_offset,ro_vaddr=off,va
break
v2f=lambda x:x-ro_vaddr+ro_offset

f=open(p,'rb')

key=rbytes(f,v2f(0x1520),8)
print("unsigned char key[8] = {" + ", ".join(f"0x{b:02X}" for b in key) + "};\n")

pr_arr("int PC1[56] =", rint(f,v2f(0x800),56),8)
shifts=rint(f,v2f(0x8E0),16)
print("int SHIFTS[16] = {" + ", ".join(str(x) for x in shifts) + "};\n")
pr_arr("int PC2[48] =", rint(f,v2f(0x920),48),8)
pr_arr("int E[48] =", rint(f,v2f(0x9E0),48),8)
pr_box("int SBOX[8][64]", rint(f,v2f(0xAA0),512))
pr_arr("int P[32] =", rint(f,v2f(0x12A0),32),8)
pr_arr("int IP[64] =", rint(f,v2f(0x1320),64),8)
pr_arr("int FP[64] =", rint(f,v2f(0x1420),64),8)

extract(sys.argv[1])

这是一个魔改的 des,解密脚本如下

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
KEY = bytes([0x13, 0x34, 0x57, 0x79, 0x9B, 0xBC, 0xDF, 0xF1])
CIPHERTEXT = bytes([0xAE, 0xBC, 0xDE, 0x9B, 0x24, 0x02, 0x9C, 0xA7])

PC1 = [57,49,41,33,25,17,9,1,58,50,42,34,26,18,10,2,59,51,43,35,27,19,11,3,60,52,44,36,63,55,47,39,31,23,15,7,62,54,46,38,30,22,14,6,61,53,45,37,29,21,13,5,28,20,12,4]
SHIFTS = [1,1,2,2,2,2,2,2,1,2,2,2,2,2,2,1]
PC2 = [14,17,11,24,1,5,3,28,15,6,21,10,23,19,12,4,26,8,16,7,27,20,13,2,41,52,31,37,47,55,30,40,51,45,33,48,44,49,39,56,34,53,46,42,50,36,29,32]
E = [32,1,2,3,4,5,4,5,6,7,8,9,8,9,10,11,12,13,12,13,14,15,16,17,16,17,18,19,20,21,20,21,22,23,24,25,24,25,26,27,28,29,28,29,30,31,32,1]
P = [16,7,20,21,29,12,28,17,1,15,23,26,5,18,31,10,2,8,24,14,32,27,3,9,19,13,30,6,22,11,4,25]
IP = [58,50,42,34,26,18,10,2,60,52,44,36,28,20,12,4,62,54,46,38,30,22,14,6,64,56,48,40,32,24,16,8,57,49,41,33,25,17,9,1,59,51,43,35,27,19,11,3,61,53,45,37,29,21,13,5,63,55,47,39,31,23,15,7]
FP = [40,8,48,16,56,24,64,32,39,7,47,15,55,23,63,31,38,6,46,14,54,22,62,30,37,5,45,13,53,21,61,29,36,4,44,12,52,20,60,28,35,3,43,11,51,19,59,27,34,2,42,10,50,18,58,26,33,1,41,9,49,17,57,25]

SBOX = [
[14,4,13,1,2,15,11,8,3,10,6,12,5,9,0,7,0,15,7,4,14,2,13,1,10,6,12,11,9,5,3,8,4,1,14,8,13,6,2,11,15,12,9,7,3,10,5,0,15,12,8,2,4,9,1,7,5,11,3,14,10,0,6,13],
[15,1,8,14,6,11,3,4,9,7,2,13,12,0,5,10,3,13,4,7,15,2,8,14,12,0,1,10,6,9,11,5,0,14,7,11,10,4,13,1,5,8,12,6,9,3,2,15,13,8,10,1,3,15,4,2,11,6,7,12,0,5,14,9],
[10,0,9,14,6,3,15,5,1,13,12,7,11,4,2,8,13,7,0,9,3,4,6,10,2,8,5,14,12,11,15,1,13,6,4,9,8,15,3,0,11,1,2,12,5,10,14,7,1,10,13,0,6,9,8,7,4,15,14,3,11,5,2,12],
[7,13,14,3,0,6,9,10,1,2,8,5,11,12,4,15,13,8,11,5,6,15,0,3,4,7,2,12,1,10,14,9,10,6,9,0,12,11,7,13,15,1,3,14,5,2,8,4,3,15,0,6,10,1,13,8,9,4,5,11,12,7,2,14],
[2,12,4,1,7,10,11,6,8,5,3,15,13,0,14,9,14,11,2,12,4,7,13,1,5,0,15,10,3,9,8,6,4,2,1,11,10,13,7,8,15,9,12,5,6,3,0,14,11,8,12,7,1,14,2,13,6,15,0,9,10,4,5,3],
[12,1,10,15,9,2,6,8,0,13,3,4,14,7,5,11,10,15,4,2,7,12,9,5,6,1,13,14,0,11,3,8,9,14,15,5,2,8,12,3,7,0,4,10,1,13,11,6,4,3,2,12,9,5,15,10,11,14,1,7,6,0,8,13],
[4,11,2,14,15,0,8,13,3,12,9,7,5,10,6,1,13,0,11,7,4,9,1,10,14,3,5,12,2,15,8,6,1,4,11,13,12,3,7,10,15,6,8,0,5,9,14,2,6,11,13,8,1,4,10,7,9,5,0,15,14,2,3,12],
[0]*64
]

def bytes_to_bits(data):
return [(byte >> (7-i)) & 1 for byte in data for i in range(8)]

def bits_to_bytes(bits):
return bytes([sum(bits[i*8+j] << (7-j) for j in range(8)) for i in range(len(bits)//8)])

def permute(bits, table):
return [bits[i-1] for i in table]

def xor_bits(a, b):
return [x ^ y for x, y in zip(a, b)]

def left_shift_32bit(a_bytes, n):
val = int.from_bytes(a_bytes, 'big')
val = ((val << n) | (val >> (32 - n))) & 0xFFFFFFFF
return val.to_bytes(4, 'big')

def generate_subkeys_modified(key_bytes):
key_bits = bytes_to_bits(key_bytes)
pc1_bits = permute(key_bits, PC1)
s_bytes = bits_to_bytes(pc1_bits)
C = s_bytes[0:4]
D = s_bytes[3:7]
subkeys = []
for i in range(16):
C = left_shift_32bit(C, SHIFTS[i])
D = left_shift_32bit(D, SHIFTS[i])
cd_bytes = C[0:3] + D[0:4]
cd_bits = bytes_to_bits(cd_bytes)
subkeys.append(permute(cd_bits, PC2))
return subkeys

def s_box_modified(bits48):
result_val = 0
for j in range(8):
block = bits48[j*6 : (j+1)*6]
row = (block[0] << 1) | block[5]
col = (block[1] << 3) | (block[2] << 2) | (block[3] << 1) | block[4]
val = SBOX[j][row * 16 + col]
if j % 2 == 0:
result_val |= (val << (28 - j//2 * 8))
else:
result_val |= (val << (24 - j//2 * 8))
return bytes_to_bits(result_val.to_bytes(4, 'big'))

def feistel(R, subkey):
expanded = permute(R, E)
xored = xor_bits(expanded, subkey)
substituted = s_box_modified(xored)
return permute(substituted, P)

def des_decrypt(ciphertext, key):
cipher_bits = bytes_to_bits(ciphertext)
subkeys = generate_subkeys_modified(key)
ip_result = permute(cipher_bits, IP)
L, R = ip_result[:32], ip_result[32:]
for i in range(16):
L, R = R, xor_bits(L, feistel(R, subkeys[15 - i]))
combined = R + L
return bits_to_bytes(permute(combined, FP))

flag = des_decrypt(CIPHERTEXT, KEY)
print(flag)

拿到最后部分

1
ly_F1ag!

拼好 flag

1
SYC{Th1s_1s_Th3_R3@lly_F1ag!}

reReverse

这个题用了信号处理混淆和 xxtea

这里的 init 函数为入口

1
2
3
4
5
6
7
8
9
10
11
12
13
void __fastcall init(unsigned int a1, __int64 a2, __int64 a3)
{
signed __int64 v4; // rbp
__int64 i; // rbx

init_proc();
v4 = &off_402DD8 - &funcs_400FC9;
if ( v4 )
{
for ( i = 0LL; i != v4; ++i )
(*(&funcs_400FC9 + i))(a1, a2, a3);
}
}

跟进来到 sub_400810,自定义了一个 SIGTRAP 信号函数 sub_400EF

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
unsigned __int64 sub_400810()
{
unsigned __int64 v0; // kr00_8
struct sigaction v2; // [rsp+8h] [rbp-A8h] BYREF
unsigned __int64 v3; // [rsp+A0h] [rbp-10h]

v3 = __readfsqword(0x28u);
memset(&v2.sa_mask, 0, 0x90uLL);
v2.sa_flags = 4;
v2.sa_handler = sub_400EF0;
if ( sigaction(5, &v2, 0LL) < 0 )
abort();
v0 = __readeflags();
__writeeflags(v0 ^ 0x100);
return __readfsqword(0x28u) ^ v3;
}

sub_400EF0 通过查询 bit 表 byte_403080 判断该地址是否需要处理,最终会造成代码倒退执行,调试会乱

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
unsigned __int64 __fastcall sub_400EF0(__int64 a1, __int64 a2, __int64 a3)
{
unsigned __int64 result; // rax
int v4; // edi
unsigned __int64 v5; // r8
__int64 v6; // rdi
unsigned __int64 v7; // rsi
unsigned __int64 v8; // rax
char v9; // cl

result = *(a3 + 168);
if ( sub_400800 <= result && result < byte_400FF5 )
{
v4 = byte_403080[(result - sub_400800) >> 3];
if ( _bittest(&v4, (result - sub_400800) & 7) )
{
v5 = result - 1;
v6 = 0LL;
v7 = result + ~sub_400800;
do
{
v8 = v7;
v9 = v7--;
v6 += (byte_403080[v8 >> 3] >> (v9 & 7)) & 1;
result = v5--;
}
while ( v6 != 2 );
*(a3 + 168) = result;
}
}
return result;
}

倒着看,oc_400D8E 开始接收输入的字符,loc_400C80 有密钥常量和 tea 的 delta 值 9E3779B9h

1
2
3
4
5
6
7
8
9
10
11
12
13
key -> 0DEADh, 0BEEFh, 5A7Dh, 0C0FFh
delta -> 9E3779B9h
ciper ->
0x2973BD37, # r9 (Input[0])
0x1BA99AA3, # edi (Input[1])
0xB3C20088, # esi (Input[2])
0x0BFC393AB, # ecx (Input[3])
0x352ADCCF, # edx (Input[4])
0x3B98E6E6, # r8 (Input[5])
0xAE421991, # ebx (Input[6])
0xD7B702CF, # ebp (Input[7])
0x0EEF6889, # r11 (Input[8])
0x08662435 # r10 (Input[9])

分析汇编根据 loc_400942 的左右移位和 xor 可以找到是 xxtea

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

ciper = [
0x2973bd37, 0x1ba99aa3, 0xb3c20088, 0xbfc393ab, 0x352adccf,
0x3b98e6e6, 0xae421991, 0xd7b702cf, 0x0eef6889, 0x08662435
]

key = [0xDEAD, 0xBEEF, 0x5A7D, 0xC0FF]
DELTA = 0x9E3779B9

def xxtea_dec(v, k, delta=DELTA):
n = len(v)
if n < 2: return v
r = 6 + 52 // n
s = (r + 1) * delta & 0xffffffff
for _ in range(r):
s = (s - delta) & 0xffffffff
e = (s >> 2) & 3
for i in range(n-1, -1, -1):
y = v[(i+1) % n]
z = v[(i-1) % n]
mx = (((z>>5 ^ y<<2) + (y>>3 ^ z<<4)) ^ ((s ^ y) + (k[(i&3) ^ e] ^ z)))
v[i] = (v[i] - mx) & 0xffffffff
return v

dres = xxtea_dec(ciper.copy(), key)
flag_bytes = b"".join(struct.pack("<I", x) for x in dres)
print(flag_bytes.rstrip(b"\x00").decode("utf-8", "ignore"))

拿到 flag

1
SYC{sakurakouji_runasama_wa_seikai_ichi}

Flutter

flutter 架构的 app,flutter 使用的是 dart 语言,并且采用了 dartVM 的方法进行拍摄快照,一般需要对 .so 文件进行快照解析,解析之后在 ida 中导入生成的脚本恢复符号表,但是这道题只需要看 libverify.so

sub_19550 是主要 VM 实现

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
char __fastcall sub_19550(__int64 a1)
{
int v1; // r13d
unsigned __int8 *v2; // r14
__int64 v3; // r15
unsigned __int64 v4; // rbp
unsigned __int64 v5; // rax
__int64 (__fastcall *v6)(); // rax
unsigned __int8 *v7; // r14
int v8; // ecx
int v9; // eax
char v10; // dl

v2 = ptr;
if ( *(&ptr + 1) == ptr )
return 0;
v3 = 0LL;
v4 = 0LL;
do
{
v5 = v2[v3];
if ( v5 > 0xF || (v6 = funcs_195B4[v5]) == 0LL )
{
*(a1 + 28) = 1;
v9 = 2;
v10 = 0;
LABEL_9:
v8 = v1;
goto LABEL_10;
}
v7 = &v2[v3];
(v6)(a1, v7);
v8 = *v7;
v9 = 1;
if ( v8 == 9 )
{
v10 = 0;
v8 = 0;
}
else
{
if ( v8 == 8 )
{
LOBYTE(v8) = *(a1 + 28) == 0;
v10 = 0;
goto LABEL_10;
}
v10 = 0;
v8 = 0;
if ( !*(a1 + 28) )
{
v10 = 1;
v9 = 0;
goto LABEL_9;
}
}
LABEL_10:
if ( !v10 )
return v8 & (v9 != 2);
++v4;
v2 = ptr;
v3 += 3LL;
v1 = v8;
}
while ( v4 < 0xAAAAAAAAAAAAAAABLL * (*(&ptr + 1) - ptr) );
v9 = 2;
return v8 & (v9 != 2);
}

funcs_195B4 是 VM 根据 opcode 调用的函数表

1
2
3
4
5
6
7
8
9
10
.data.rel.ro:0000000000039E78 funcs_195B4     dq offset sub_1A1B0   // opcode 0 初始化 VM 状态                     
.data.rel.ro:0000000000039E80 dq offset sub_1A1D0 // opcode 1 读取输入到 acc
.data.rel.ro:0000000000039E88 dq offset sub_1A1F0 // opcode 2 acc -= arg1
.data.rel.ro:0000000000039E90 dq offset sub_1A200 // opcode 3 acc ^= arg1
.data.rel.ro:0000000000039E98 dq offset sub_1A210 // opcode 4 acc ^ byte_FC00[arg1] == 82
.data.rel.ro:0000000000039EA0 dq offset sub_1A230 // opcode 5 index++
.data.rel.ro:0000000000039EA8 dq offset sub_1A240 // opcode 6 检查 index ≤ agr1
.data.rel.ro:0000000000039EB0 dq offset nullsub_2 // opcode 7 空操作
.data.rel.ro:0000000000039EB8 dq offset sub_1A260 // opcode 8 检查 index == input_length
.data.rel.ro:0000000000039EC0 dq offset sub_1A270 // opcode 9 结束

校验函数

1
2
3
4
5
6
7
8
9
char __fastcall sub_1A210(__int64 a1, __int64 a2)
{
char result; // al

result = *(a1 + 24) ^ byte_FC00[*(a2 + 1)];
if ( result != 82 )
*(a1 + 28) = 1;
return result;
}

那么其实加解密流程如下

1
2
3
4
5
6
acc = ciper[i] (0x01)
acc -= 3 (0x02)
acc ^= 0x55 (0x03)
byte_FF92[i] ^ acc == 0x52 (0x04)
->
ciper[i] = (byte_FF92[i] ^ 0x07) + 3

解密脚本如下

1
2
3
4
5
6
7
8
9
10
11
12
13
byte_FF92 = [
0x57, 0x51, 0x47, 0x7F, 0x44, 0x29, 0x75, 0x76,
0x76, 0x37, 0x68, 0x5B, 0x67, 0x3A, 0x6C, 0x5B,
0x55, 0x77, 0x37, 0x5B, 0x65, 0x74, 0x37, 0x68,
0x71, 0x73, 0x42, 0x65, 0x68, 0x37, 0x19, 0x7D
]

flag = ""
for b in byte_FF92:
val = (b ^ 0x07) + 3
flag += chr(val)

print(flag)

flag 如下

1
SYC{F1utt3r_c@n_Us3_ev3rywHer3!}

国产の光

Index 中找到比对的密文

通过本地的 AES 加密

分析 check 这里,有两个 info 字符串传入

分析 libentry.so 有 AES-CBC 加密和 Base85

标准 AES,key 为 “welcometosyc2025”,iv 为 “helloimsamsaramiao” ,直接解

1
SYC{HarmonyOS_1s_right?right!}

obfuscat3

算是魔改的 RC4,改的不算多,主要是混淆

密钥 ”arasmaS“,传入 obf_encode加密,输出和 cipher 的密文比较

这里重点的 obf_encode,混淆的一大堆可以不看,根据参数来定位关键,传入的 s 在此函数中为 *a1,那么这里定位到最后,进行了加法操作

跟进 init_encode,初始化 S 盒,exchange_encode 交换 S 盒元素

1
2
3
4
5
6
7
8
9
10
11
unsigned __int8 __fastcall exchange_encode(unsigned __int8 *a1, unsigned __int8 *a2)
{
unsigned __int8 result; // al
unsigned __int8 v3; // [rsp+1h] [rbp-11h]

v3 = *a1;
*a1 = *a2;
result = v3;
*a2 = v3;
return result;
}

因此可以知道这里是 RC4 的变种算法,将异或换成了加法,并且跟进函数调用可以发现 key 是 “Samsara”,也就是 v5 的逆向

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

key_str = "Samsara"
key = [ord(c) for c in key_str]
key_len = len(key)
cipher_bytes = [
0xB4, 0xCD, 0x69, 0x54, 0xBD, 0x67, 0x20, 0x9D, 0xF2, 0xC3, 0x24, 0x14, 0xC2, 0x1B, 0xE9, 0x6A,
0x44, 0x14, 0x4E, 0x39, 0xC5, 0xC8, 0x5B, 0x11, 0x75, 0xAD, 0xDE, 0xBB, 0xFE, 0xE4, 0x6E, 0x65,
0x06, 0x9A, 0x91, 0xFE, 0xA0, 0x68, 0xA4, 0x86, 0x17, 0x6C, 0x0A, 0xCF, 0x1E, 0x67, 0xE3, 0x0D,
0x60, 0x47, 0x13, 0x6B, 0xD1, 0x36, 0xF2, 0x77, 0x58, 0x76, 0x1E, 0x98, 0xF5, 0x7F, 0x0A, 0x92,
0xB7, 0x0A, 0xEA, 0xAE, 0x46, 0x7E, 0x6A, 0x18, 0x4A, 0x59, 0x4E, 0x71, 0xB2, 0xE1, 0x41, 0x7A,
0x0B, 0x31, 0xBA, 0xC6, 0xAA, 0xCF, 0xCE, 0x09, 0xBF, 0x2E, 0xF8, 0x4D, 0x75, 0xEF, 0x14, 0xED,
0x5F, 0x66, 0x44, 0x6F, 0xDE, 0xE2, 0x7C, 0x10, 0x8C, 0xB7, 0x4E, 0x6B, 0xB2, 0xD4, 0xF6, 0x91,
0xD7, 0x84, 0x86, 0x1F, 0xF8, 0x65, 0x94, 0x0B, 0x14, 0x28, 0xFB, 0xDD, 0x47, 0xF4, 0xC1, 0x17,
0x42, 0x3F, 0x1E, 0x38, 0x07, 0xBB, 0x37, 0x33, 0x12, 0x0C, 0x16, 0x68, 0xE0, 0x23, 0x12, 0x75,
0x72, 0xD9, 0x71, 0x7A, 0x88, 0xD0, 0x46, 0x28, 0x88, 0xAD, 0x1E, 0x98, 0x8F, 0x92, 0x7E, 0x0E,
0x69, 0x29, 0x37, 0xB1, 0xFF, 0xC5, 0xAF, 0x6F, 0x41, 0x37, 0x65, 0x0E, 0xD2, 0x62, 0x11, 0x8F,
0xA6, 0x3E, 0x95, 0xF5, 0x80, 0x9A, 0xDC
]

S = list(range(256))
j = 0
for i in range(256):
j = (j + S[i] + key[i % key_len]) % 256
S[i], S[j] = S[j], S[i]

i = 0
j = 0
plaintext = []

for byte in cipher_bytes:
i = (i + 1) % 256
j = (j + S[i]) % 256
S[i], S[j] = S[j], S[i]

k = S[(S[i] + S[j]) % 256]
pt_byte = (byte - k) % 256
plaintext.append(pt_byte)

decoded_bytes = bytes(plaintext)
flag = decoded_bytes.decode('utf-8')
print(flag)
1
SYC{Alright_I_sti1l_h0pe_th3t_you_solved_the_chall3nge_by_deobfuscating_them_Geek_is_just_the_first_step_of_your_CTF_journey_Im_glad_I_could_be_part_of_your_growth_Good_luck_for_y0u!}

Last_one

sub_403A80 要求在 64-bit Windows 以 32-bit 模式运行,这就是 WoW64 机制,天堂之门

sub_402330 这里对 Buffer 的字节进行了变换,unk_40C040 是 8 段 VM 字节码,对应 off_40C000 是一个调用函数的表

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
__int64 __cdecl sub_402330(int a1)
{
__int64 v1; // rax
int v2; // edx
__int64 v4; // [esp-8h] [ebp-CC0h]
char v5; // [esp+0h] [ebp-CB8h]
int v6; // [esp+3E8h] [ebp-8D0h]
int v7; // [esp+3F4h] [ebp-8C4h]
int v8; // [esp+400h] [ebp-8B8h]
int v9; // [esp+40Ch] [ebp-8ACh]
int j; // [esp+418h] [ebp-8A0h]
int i; // [esp+424h] [ebp-894h]
void *lpAddress; // [esp+458h] [ebp-860h]
int v13; // [esp+464h] [ebp-854h] BYREF
int v14; // [esp+468h] [ebp-850h]
int v15; // [esp+46Ch] [ebp-84Ch]
int v16; // [esp+470h] [ebp-848h]
int savedregs; // [esp+CB8h] [ebp+0h] BYREF

VirtualAlloc(0, 0x1000u, 0x3000u, 0x40u);
lpAddress = (void *)sub_401186();
if ( lpAddress )
{
memcpy(lpAddress, &unk_40C260, 0x38u);
for ( i = 0; i < 8; ++i )
*((_BYTE *)lpAddress + i + 2) = sub_40105A(8 * i, 0);
::lpAddress = (int (__cdecl *)(_DWORD, _DWORD, _DWORD))lpAddress;
*((_WORD *)&::lpAddress + 2) = 51;
for ( j = 0; j < 8; ++j )
{
sub_4010B4(&v13);
if ( sub_4011DB(&v13, (char *)&unk_40C040 + 64 * j, dword_40C240[j]) )
{
sub_401091("[!] Wrong\n", v5);
VirtualFree(lpAddress, 0, 0x8000u);
LODWORD(v1) = sub_401186();
goto LABEL_12;
}
v9 = v13;
v8 = v14;
v7 = v15;
v6 = v16;
memset(&dword_40C4E8, 0, 0x28u);
dword_40C4E8 = v13;
dword_40C4EC = 0;
dword_40C4F0 = v14;
dword_40C4F4 = 0;
dword_40C4F8 = v15;
dword_40C4FC = 0;
dword_40C500 = v16;
dword_40C504 = 0;
dword_40C508 = -1;
MK_FP(*((_WORD *)&::lpAddress + 2), ::lpAddress)(v16, v2, 0);
off_40C000[dword_40C508 & 0xF](a1 + 4 * j, 4, (v8 + v6 * v7) ^ (40503 * v9));
sub_401186();
}
VirtualFree(lpAddress, 0, 0x8000u);
LODWORD(v1) = sub_401186();
}
else
{
LODWORD(v1) = sub_401091("[!] Wrong\n", v5);
}
LABEL_12:
v4 = v1;
sub_40113B(&savedregs, &dword_402674);
return v4;
}

sub_402850 是一个 VM 解释器

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
a1 (Context/CPU Environment): 指向 VM 上下文结构的指针。这个结构体包含了寄存器和堆栈
a2 (Bytecode): 指向要执行的字节码数组的指针
a3 (Bytecode Size): 字节码的长度

0x00: NOP - 空操作
0x01: PUSH IMM - 读取 4 字节立即数 (v49) 压入堆栈
0x02: PUSH REG - 读取 1 字节寄存器索引,将该寄存器的值压入堆栈
0x03: POP REG - 读取 1 字节寄存器索引,从堆栈弹出一个值存入该寄存器
0x10: MOV REG, IMM - 将 4 字节立即数赋值给寄存器
0x11: MOV REG, REG - Reg[dst] = Reg[src]
0x30: XCHG REG, REG - 交换两个寄存器的值
0x20: ADD REG, IMM
0x21: SUB REG, IMM
0x22: XOR REG, IMM
0x23: MUL REG, IMM
0x24: DIV REG, IMM (包含除0检查)
0x25: MOD REG, IMM (取模,包含除0检查)
0x26: SHL REG, IMM (左移,位移量 & 0x1F)
0x27: SHR REG, IMM (右移,位移量 & 0x1F)
0x28: AND REG, IMM
0x29: OR REG, IMM
0x2A: NOT REG (单操作数,取反)
0x2B: CALL_FUNC1 REG, IMM
0x2C: CALL_FUNC2 REG, IMM
0x31: INC REG - 寄存器自增
0x32: DEC REG - 寄存器自减
0x40: ADD REG, REG (Reg[A] += Reg[B])
0x41: SUB REG, REG
0x42: XOR REG, REG
0x43: MUL REG, REG

解析出字节码的操作

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
# 段 0:0x40C040
prog0 = [
('MOVI', 0, 0x12), # 10 00 12...
('MOVI', 1, 0x34), # 10 01 34...
('MULI', 0, 3), # 23 00 03...
('XORI', 1, 0xAB), # 22 01 AB...
('ADD', 0, 1), # 40 00 01
('MOVI', 2, 7), # 10 02 07...
('MOVI', 3, 0x11), # 10 03 11...
('END',), # FF
]

# 段 1:0x40C080
prog1 = [
('MOVI', 0, 0xAB), # 10 00 AB...
('MOVI', 1, 0xCD), # 10 01 CD...
('ROL', 0, 4), # 2B 00 04...
('ROR', 1, 2), # 2C 01 02...
('XORI', 0, 0x55), # 22 00 55...
('MOVI', 2, 9), # 10 02 09...
('MOVI', 3, 0x22), # 10 03 22...
('END',), # FF
]

# 段 2:0x40C0C0
prog2 = [
('MOVI', 0, 0xFF), # 10 00 FF...
('MOVI', 1, 0xAA), # 10 01 AA...
('SHLI', 0, 2), # 26 00 02...
('MODI', 0, 0x64), # 25 00 64...
('ADDI', 1, 0x11), # 20 01 11...
('MOVI', 2, 0x0D), # 10 02 0D...
('MOVI', 3, 0x33), # 10 03 33...
('END',), # FF
]

# 段 3:0x40C100
prog3 = [
('MOVI', 0, 0x11), # 10 00 11...
('MOVI', 1, 0x22), # 10 01 22...
('ANDI', 0, 0x0F), # 28 00 0F...
('ORI', 1, 0xF0), # 29 01 F0...
('XORI', 0, 0x33), # 22 00 33...
('MOVI', 2, 0x0E), # 10 02 0E...
('MOVI', 3, 0x44), # 10 03 44...
('END',), # FF
]

# 段 4:0x40C140
prog4 = [
('MOVI', 0, 0xDE), # 10 00 DE...
('MOVI', 1, 0xAD), # 10 01 AD...
('INC', 0), # 31 00
('DEC', 1), # 32 01
('XCHG', 0, 1), # 30 00 01
('ADDI', 0, 0x10), # 20 00 10...
('MOVI', 2, 2), # 10 02 02...
('MOVI', 3, 0x55), # 10 03 55...
('END',), # FF
]

# 段 5:0x40C180
prog5 = [
('MOVI', 0, 0xBE), # 10 00 BE...
('MOVI', 1, 0xEF), # 10 01 EF...
('MULI', 0, 2), # 23 00 02...
('DIVI', 1, 3), # 24 01 03...
('ADD', 0, 1), # 40 00 01
('MOVI', 2, 0x0A), # 10 02 0A...
('MOVI', 3, 0x66), # 10 03 66...
('END',), # FF
]

# 段 6:0x40C1C0
prog6 = [
('MOVI', 0, 0xCA), # 10 00 CA...
('MOVI', 1, 0xFE), # 10 01 FE...
('NOT', 0), # 2A 00
('ANDI', 0, 0xFF), # 28 00 FF... (通常用于掩码操作,防止NOT后溢出32位)
('XORI', 1, 0x7F), # 22 01 7F...
('MOVI', 2, 0x0F), # 10 02 0F...
('MOVI', 3, 0x77), # 10 03 77...
('END',), # FF
]

# 段 7:0x40C200
prog7 = [
('MOVI', 0, 0x99), # 10 00 99...
('MOVI', 1, 0x88), # 10 01 88...
('ADDI', 0, 0x11), # 20 00 11...
('SUBI', 1, 8), # 21 01 08...
('ROL', 0, 3), # 2B 00 03...
('MOVI', 2, 5), # 10 02 05...
('MOVI', 3, 0x88), # 10 03 88...
('END',), # FF
]

计算出 (v8 + v6 * v7) ^ (40503 * v9) 后传入的值

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

def ror(val, n, width=32):
return ((val >> n) | (val << (width - n))) & 0xFFFFFFFF

def rol(val, n, width=32):
return ((val << n) | (val >> (width - n))) & 0xFFFFFFFF

def calculate_keys():
keys = []

# MOVI R0,0x12; MOVI R1,0x34; MULI R0,3; XORI R1,0xAB; ADD R0,R1; MOVI R2,7; MOVI R3,0x11
R0 = (0x12 * 3) & 0xFFFFFFFF # 54
R1 = 0x34 ^ 0xAB # 159 (0x9F)
R0 = (R0 + R1) & 0xFFFFFFFF # 213 (0xD5)
R2 = 7
R3 = 0x11

# (R1 + R3 * R2) ^ (40503 * R0)
k = (R1 + R3 * R2) & 0xFFFFFFFF
k ^= (40503 * R0) & 0xFFFFFFFF
keys.append(k)

# MOVI R0,0xAB; MOVI R1,0xCD; ROL R0,4; ROR R1,2; XORI R0,0x55; MOVI R2,9; MOVI R3,0x22
R0 = rol(0xAB, 4) ^ 0x55 # 0xAE5
R1 = ror(0xCD, 2) # 0x40000033
R2 = 9
R3 = 0x22

k = (R1 + R3 * R2) & 0xFFFFFFFF
k ^= (40503 * R0) & 0xFFFFFFFF
keys.append(k)

# MOVI R0,0xFF; MOVI R1,0xAA; SHLI R0,2; MODI R0,0x64; ADDI R1,0x11; MOVI R2,0xD; MOVI R3,0x33
R0 = ((0xFF << 2) & 0xFFFFFFFF) % 0x64 # 20 (0x14)
R1 = 0xAA + 0x11 # 187 (0xBB)
R2 = 0x0D
R3 = 0x33

k = (R1 + R3 * R2) & 0xFFFFFFFF
k ^= (40503 * R0) & 0xFFFFFFFF
keys.append(k)

# MOVI R0,0x11; MOVI R1,0x22; ANDI R0,0xF; ORI R1,0xF0; XORI R0,0x33; MOVI R2,0xE; MOVI R3,0x44
R0 = (0x11 & 0xF) ^ 0x33 # 50 (0x32)
R1 = 0x22 | 0xF0 # 242 (0xF2)
R2 = 0x0E
R3 = 0x44

k = (R1 + R3 * R2) & 0xFFFFFFFF
k ^= (40503 * R0) & 0xFFFFFFFF
keys.append(k)

# MOVI R0,0xDE; MOVI R1,0xAD; INC R0; DEC R1; XCHG R0,R1; ADDI R0,0x10; MOVI R2,2; MOVI R3,0x55
# R0=DE->DF, R1=AD->AC. Swap: R0=AC, R1=DF. R0+=10 -> BC
R0 = 0xBC
R1 = 0xDF
R2 = 2
R3 = 0x55

k = (R1 + R3 * R2) & 0xFFFFFFFF
k ^= (40503 * R0) & 0xFFFFFFFF
keys.append(k)

# MOVI R0,0xBE; MOVI R1,0xEF; MULI R0,2; DIVI R1,3; ADD R0,R1; MOVI R2,0xA; MOVI R3,0x66
t_r1 = 0xEF // 3 # 79 (0x4F)
R0 = ((0xBE * 2) + t_r1) & 0xFFFFFFFF # 459 (0x1CB)
R1 = t_r1
R2 = 0x0A
R3 = 0x66

k = (R1 + R3 * R2) & 0xFFFFFFFF
k ^= (40503 * R0) & 0xFFFFFFFF
keys.append(k)

# MOVI R0,0xCA; MOVI R1,0xFE; NOT R0; ANDI R0,0xFF; XORI R1,0x7F; MOVI R2,0xF; MOVI R3,0x77
R0 = (~0xCA) & 0xFF # 53 (0x35)
R1 = 0xFE ^ 0x7F # 129 (0x81)
R2 = 0x0F
R3 = 0x77

k = (R1 + R3 * R2) & 0xFFFFFFFF
k ^= (40503 * R0) & 0xFFFFFFFF
keys.append(k)

# MOVI R0,0x99; MOVI R1,0x88; ADDI R0,0x11; SUBI R1,8; ROL R0,3; MOVI R2,5; MOVI R3,0x88
R0 = rol(0x99 + 0x11, 3) # 1360 (0x550)
R1 = 0x88 - 8 # 128 (0x80)
R2 = 5
R3 = 0x88

k = (R1 + R3 * R2) & 0xFFFFFFFF
k ^= (40503 * R0) & 0xFFFFFFFF
keys.append(k)

return keys

keys = calculate_keys()
for i, k in enumerate(keys):
print(f"0x{k:07X}, // Block {i}")

结果如下

1
2
3
4
5
6
7
8
0x083A2D5, // Block 0
0x46BBAC56, // Block 1
0x00C5F1E, // Block 2
0x01EE214, // Block 3
0x07431ED, // Block 4
0x11BA8D6, // Block 5
0x020C619, // Block 6
0x3488718, // Block 7

下断点调试,查看 dword_10C508 的值

1
0x0D, 0x04, 0x00, 0x0C, 0x04, 0x06, 0x03, 0x00

对应 off_40C000 的 D,4,0,C,4,6,3,0 索引的函数

1
2
3
4
5
6
D 	每字节 = HIBYTE(a3) ^ (8 * *(i + a1)) ^ (a3 * *(i + a1))
4 数组长度>=2 进行首尾元素交换
0 每字节 ^= a3 >> (8 * (i % 4))
C 按字节异或
6 反转数组
3 每字节 -= a3 >> (8 * (i % 4))

解密脚本如下

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

cipher_data = [
0x35, 0x67, 0x05, 0x2D,
0x74, 0x40, 0x53, 0x31,
0x41, 0x6F, 0x62, 0x45,
0x4B, 0x1F, 0x57, 0x36,
0x5F, 0x4B, 0x73, 0x6E,
0x4F, 0x6C, 0x5F, 0x49,
0x5D, 0x7F, 0x3F, 0x79,
0x28, 0xD2, 0x69, 0x7E
]

keys = [
0x0083A2D5,
0x46BBAC56,
0x000C5F1E,
0x001EE214,
0x007431ED,
0x011BA8D6,
0x0020C619,
0x03488718
]


def decrypt_xor(block, key):
for i in range(4):
k = (key >> (8 * i)) & 0xFF
block[i] ^= k

def decrypt_sub(block, key):
for i in range(4):
k = (key >> (8 * i)) & 0xFF
block[i] = (block[i] + k) & 0xFF

def decrypt_swap(block, key):
block[0], block[3] = block[3], block[0]

def decrypt_reverse(block, key):
block.reverse()

def decrypt_chain_xor(block, key):
c = list(block)
k0 = key & 0xFF
# p[0] = c[0] ^ k0
block[0] = c[0] ^ k0
# p[i] = c[i] ^ c[i-1]
for i in range(1, 4):
block[i] = c[i] ^ c[i-1]

def decrypt_affine(block, key):
key_lo = key & 0xFF
key_hi = (key >> 8) & 0xFF

inv_map = {}
for x in range(256):
term1 = (8 * x) & 0xFF
term2 = (key_lo * x) & 0xFF
y = key_hi ^ term1 ^ term2
inv_map[y] = x

for i in range(4):
if block[i] in inv_map:
block[i] = inv_map[block[i]]
else:
block[i] = 0x3F
final_flag = []
blocks = [bytearray(cipher_data[i:i+4]) for i in range(0, 32, 4)]

decrypt_affine(blocks[0], keys[0])
final_flag += blocks[0]

decrypt_swap(blocks[1], keys[1])
final_flag += blocks[1]

decrypt_xor(blocks[2], keys[2])
final_flag += blocks[2]

decrypt_chain_xor(blocks[3], keys[3])
final_flag += blocks[3]

decrypt_swap(blocks[4], keys[4])
final_flag += blocks[4]

decrypt_reverse(blocks[5], keys[5])
final_flag += blocks[5]

decrypt_sub(blocks[6], keys[6])
final_flag += blocks[6]

decrypt_xor(blocks[7], keys[7])
final_flag += blocks[7]

flag_str = "".join([chr(x) if 32 <= x <= 126 else '.' for x in final_flag])

print(flag_str)

拿到 flag

1
SYC{1@St_0nE_THanKs_I_lOvE_y0U!}

obfuscat3_revenge

主函数这里是有两个十六字节的 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
42
43
44
45
46
47
48
49
50
51
int __fastcall main(int argc, const char **argv, const char **envp)
{
unsigned __int8 s1[32]; // [rsp+40h] [rbp-280h] BYREF
void *src; // [rsp+60h] [rbp-260h]
size_t n; // [rsp+68h] [rbp-258h]
char s[48]; // [rsp+70h] [rbp-250h] BYREF
unsigned __int8 v8[256]; // [rsp+A0h] [rbp-220h] BYREF
unsigned __int8 v9[256]; // [rsp+1A0h] [rbp-120h] BYREF
unsigned __int8 v10[8]; // [rsp+2A0h] [rbp-20h] BYREF
__int64 v11; // [rsp+2A8h] [rbp-18h]
int v12; // [rsp+2BCh] [rbp-4h]

v12 = 0;
*v10 = 0x8877665540302010LL;
v11 = 0x2301EFDECDBCAB90LL;
gen(v9, v8, v10);
memset(s, 0, 0x24uLL);
printf("Input the flag: ");
if ( fgets(s, 36, stdin) )
{
n = strcspn(s, "\r\n");
s[n] = 0;
src = s;
if ( n >= 3 && *src == 239 && *(src + 1) == 187 && *(src + 2) == 191 )
{
src = src + 3;
n = n - 0x13DB8EEDDCB822FCLL + 0x13DB8EEDDCB822F9LL;
}
if ( n && n <= 0x20 )
{
memset(s1, 0, sizeof(s1));
memcpy(s1, src, n);
real_en(s1, v10, v9, v8);
if ( !memcmp(s1, &main::target_enc, 0x20uLL) )
printf("Congratulations!\n");
else
printf("Try again?\n");
return 0;
}
else
{
printf("Try again?\n");
return 0;
}
}
else
{
printf("Input error.\n");
return 1;
}
}

gen 这里主要是生成 S-Box,还有 S-Box 的逆置换,a1 = S-box,a2 = S-box 的逆映射表,a3 = 16-byte 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
__int64 __fastcall gen(unsigned __int8 *a1, unsigned __int8 *a2, const unsigned __int8 *a3)
{
int v3; // ecx
__int64 result; // rax
char v5; // si
int k; // [rsp+8h] [rbp-2Ch]
unsigned __int8 v7; // [rsp+Fh] [rbp-25h]
int j; // [rsp+10h] [rbp-24h]
unsigned __int8 v9; // [rsp+17h] [rbp-1Dh]
int i; // [rsp+18h] [rbp-1Ch]

for ( i = 0; i < 256; i = -v3 )
{
a1[i] = i;
v3 = -i - 2101855413 + 2101855412;
result = -v3;
}
v9 = 0;
for ( j = 0; j < 256; ++j )
{
v5 = a1[j] + v9;
v9 = a3[j % 16] + v5;
v7 = a1[j];
a1[j] = a1[v9];
a1[v9] = v7;
result = 1850779768LL;
}
for ( k = 0; k < 256; k = k - 1366467913 + 1366467914 )
{
a2[a1[k]] = k;
result = 1366467914LL;
}
return result;
}

D810 插件去掉混淆,分析发现进行了循环异或,循环左移

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
__int64 __fastcall en1(unsigned __int8 *s1, const unsigned __int8 *a2, int n32)
{
__int64 result; // rax
char v4; // r10
int i; // [rsp+10h] [rbp-30h]

for ( i = 0; ; ++i )
{
result = i;
if ( i >= n32 )
break;
v4 = a2[i % 16] & (s1[i] ^ a2[i % 16]);
s1[i] = ~(a2[i % 16] | ~s1[i] | ~v4) | (a2[i % 16] | ~s1[i]) ^ 0x8D ^ v4 ^ 0x72;
}
return result;
}


__int64 __fastcall F(int a1, unsigned int a2, const unsigned __int8 *a3)
{
int v3; // ebx
unsigned __int8 v5; // [rsp+4h] [rbp-3Ch]
unsigned __int8 v6; // [rsp+5h] [rbp-3Bh]
unsigned __int8 v7; // [rsp+6h] [rbp-3Ah]
unsigned __int8 v8; // [rsp+7h] [rbp-39h]

v8 = a3[(a1 ^ a2)];
v7 = a3[(a1 ^ a2) >> 8];
v6 = a3[((a1 ^ a2) >> 16)];
v5 = a3[(a1 ^ a2) >> 24];
v3 = ~((v6 << 16) | (v7 << 8) | v8) & 0x1E7532C0;
return not_en(
(v5 << 24) & ((v6 << 16) | (v7 << 8) | v8) | ((v5 << 24) ^ 0xE18ACD3F) & (~(~((v6 << 16) | (v7 << 8) | v8) | 0x1E7532C0 | ~v3) | ((v6 << 16) | (v7 << 8) | v8) & 0xE18ACD3F ^ v3) | ~(~(~((v6 << 16) | (v7 << 8) | v8) | 0x1E7532C0 | ~v3) | ((v6 << 16) | (v7 << 8) | v8) & 0xE18ACD3F ^ v3) & ((v5 << 24) ^ 0x1E7532C0),
5);
}


__int64 __fastcall not_en(unsigned int a1, char a2)
{
unsigned int v2; // esi
unsigned int v3; // edi

v2 = a1 << a2;
v3 = a1 >> (32 - a2);
return v3 & v2 | (v3 & 0x18B2001 | ~v3 & 0x643081A4 | 0x82444848) ^ ~v3 & 0x18001612 ^ (v3 & 0x82444848 | 0x643081A4) ^ v2 ^ 0x9A445E5A;
}

解密脚本

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
from typing import List

MASK32 = 0xFFFFFFFF

KEY_HEX = "102030405566778890ABBCCDDEEF0123"
PERM_HEX = "1F000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E"
ENC_HEX = "CA5A96FF084972393618138A14C00C78F87C49C7BEE891ED7FB002AD7774D434"


def rol32(x: int, r: int) -> int:
r &= 31
return ((x << r) & MASK32) | ((x & MASK32) >> (32 - r))


def ror32(x: int, r: int) -> int:
r &= 31
return ((x & MASK32) >> r) | ((x << (32 - r)) & MASK32)


def b2nle(b: bytes, n: int) -> List[int]:
return [int.from_bytes(b[i:i+n], 'little') for i in range(0, len(b), n)]


def n2ble(na: List[int], n: int) -> bytes:
out = bytearray()
for a in na:
out.extend((a & ((1 << (8*n)) - 1)).to_bytes(n, 'little'))
return bytes(out)


def gen_sbox(key: bytes):
S = bytearray(range(256))
j = 0
for i in range(256):
j = (j + S[i] + key[i % len(key)]) & 0xFF
S[i], S[j] = S[j], S[i]

invS = bytearray(256)
for i in range(256):
invS[S[i]] = i
return S, invS


def F(a1: int, key_word: int, S: bytearray) -> int:
idx = (a1 ^ key_word) & MASK32
b0 = S[(idx ) & 0xFF]
b1 = S[(idx >> 8 ) & 0xFF]
b2 = S[(idx >> 16) & 0xFF]
b3 = S[(idx >> 24) & 0xFF]
T = (b3 << 24) | (b2 << 16) | (b1 << 8) | b0
return rol32(T, 5)


def decrypt_real_en(enc_bytes: bytes, key: bytes, perm_bytes: bytes, S: bytearray, invS: bytearray) -> bytes:
dkey = b2nle(key, 4)
blocks = b2nle(enc_bytes, 4)[::-1]

for i in range(3, -1, -1):
for j in range(0, 8, 2):
L = blocks[j]
R = blocks[j+1]
prevL = R
prevR = L ^ F(R, dkey[i], S)
blocks[j] = prevL & MASK32
blocks[j+1] = prevR & MASK32

blocks = blocks[::-1]
mid_bytes = bytearray(n2ble(blocks, 4))
rec = mid_bytes[1:] + mid_bytes[:1]

for i in range(32):
rec[i] = invS[rec[i]]

for i in range(32):
rec[i] ^= key[i % 16]

return bytes(rec)

key = bytearray.fromhex(KEY_HEX)
perm = bytearray.fromhex(PERM_HEX)
enc = bytes.fromhex(ENC_HEX)

S, invS = gen_sbox(key)
plaintext = decrypt_real_en(enc, key, perm, S, invS)

print(plaintext.rstrip(b'\x00').decode('utf-8', errors='replace'))

拿到 flag

1
SYC{Then_you_are_1mpressivse}

2025极客大挑战-Reverse
http://example.com/2025/11/19/2025极客大挑战-Reverse/
作者
butt3rf1y
发布于
2025年11月19日
许可协议