NSSCTF四周年竞赛暨Round#31竞赛 WriteUp (Reverse方向)

3.1k 词

checkit

java层没东西,so层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
__int64 __fastcall Java_com_test_ezre_MainActivity_checkInput(_JNIEnv *a1, __int64 a2, __int64 a3)
{
__int64 address_1; // rax
__int64 *address_2; // rax
void *v6; // [rsp+8h] [rbp-C8h]
unsigned __int64 n50; // [rsp+10h] [rbp-C0h]
__int64 StringUTFChars; // [rsp+18h] [rbp-B8h]
__int64 address; // [rsp+58h] [rbp-78h]
__int64 p_stack; // [rsp+98h] [rbp-38h]

if ( !*(_QWORD *)__emutls_get_address(&unk_45D0) )
{
v6 = malloc(0x14u);
*(_QWORD *)__emutls_get_address(&unk_45D0) = v6;
if ( !*(_QWORD *)__emutls_get_address(&unk_45D0) )
return _JNIEnv::NewStringUTF(a1, "Memory allocation error");
}
p_stack = *(_QWORD *)__emutls_get_address(&unk_45D0);
__memset_chk(p_stack, 0, 20, -1);
__memset_chk(stack, 0, 2000, 2000);
address = __emutls_get_address(&unk_45B0);
__memset_chk(address, 0, 51, 51);
if ( !a3 )
return _JNIEnv::NewStringUTF(a1, Memory_allocation_error);
StringUTFChars = _JNIEnv::GetStringUTFChars(a1, a3, 0);
n50 = __strlen_chk(StringUTFChars, -1);
if ( n50 > 0x32 )
n50 = 50;
address_1 = __emutls_get_address(&unk_45B0);
__memcpy_chk(address_1, StringUTFChars, n50, 51);
_JNIEnv::ReleaseStringUTFChars(a1, a3, StringUTFChars);
address_2 = (__int64 *)__emutls_get_address(&unk_45D0);
exec((__int64)&code, *address_2);
return _JNIEnv::NewStringUTF(a1, Memory_allocation_error);
}

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
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
unsigned __int8 *__fastcall exec(__int64 p_code, unsigned __int8 *a2)
{
int v2; // edx
int v3; // eax
int v4; // eax
int v5; // ecx
unsigned __int8 *result; // rax
int opc; // [rsp+Ch] [rbp-D4h]

__memset_chk(Memory_allocation_error, 0, 200, 200);
*((_DWORD *)a2 + 4) = 0;
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
opc = *(_DWORD *)(p_code + 4LL * *((int *)a2 + 2));
if ( opc != '"' )
break;
*a2 += a2[3];
++*((_DWORD *)a2 + 2);
}
if ( opc != '#' )
break;
*a2 -= a2[3];
++*((_DWORD *)a2 + 2);
}
if ( opc != '$' )
break;
*a2 *= a2[3];
++*((_DWORD *)a2 + 2);
}
if ( opc != '%' )
break;
if ( !a2[3] )
return (unsigned __int8 *)__strlcpy_chk(
Memory_allocation_error,
"Error: Division by zero",
200,
200);
*a2 /= (__int16)a2[3];
++*((_DWORD *)a2 + 2);
}
if ( opc != '&' )
break;
*a2 ^= a2[3];
++*((_DWORD *)a2 + 2);
}
if ( opc != '\'' )
break;
if ( *((int *)a2 + 3) >= 500 )
return (unsigned __int8 *)__strlcpy_chk(
Memory_allocation_error,
"Error: Stack overflow",
200,
200);
v2 = *a2;
v3 = *((_DWORD *)a2 + 3);
*((_DWORD *)a2 + 3) = v3 + 1;
stack[v3] = v2;
++*((_DWORD *)a2 + 2);
}
if ( opc != 40 )
break;
if ( *((int *)a2 + 3) <= 0 )
return (unsigned __int8 *)__strlcpy_chk(
Memory_allocation_error,
"Error: Stack underflow",
200,
200);
v4 = *((_DWORD *)a2 + 3) - 1;
*((_DWORD *)a2 + 3) = v4;
*a2 = stack[v4];
++*((_DWORD *)a2 + 2);
}
if ( opc != ')' )
break;
if ( a2[1] >= 0x33uLL )
*a2 = 0;
else
*a2 = *(_BYTE *)(__emutls_get_address(&unk_45B0) + a2[1]);
++*((_DWORD *)a2 + 2);
}
if ( opc != '*' )
break;
*a2 = a2[3];
++*((_DWORD *)a2 + 2);
}
if ( opc != '+' )
break;
a2[1] = *a2;
++*((_DWORD *)a2 + 2);
}
if ( opc != ',' )
break;
a2[2] = *a2;
++*((_DWORD *)a2 + 2);
}
if ( opc != '-' )
break;
a2[3] = *a2;
++*((_DWORD *)a2 + 2);
}
if ( opc != '.' )
break;
*a2 = *(_DWORD *)(p_code + 4LL * (*((_DWORD *)a2 + 2) + 1));
*((_DWORD *)a2 + 2) += 2;
}
if ( opc != '/' )
break;
*a2 = a2[1];
++*((_DWORD *)a2 + 2);
}
if ( opc != '0' )
break;
if ( a2[1] >= 0x32u )
{
result = a2;
a2[4] = 1;
return result;
}
*a2 = cmp_data[a2[1]];
++*((_DWORD *)a2 + 2);
}
if ( opc != '1' )
break;
++*a2;
++*((_DWORD *)a2 + 2);
}
if ( opc != '2' )
break;
if ( --a2[2] )
v5 = *((_DWORD *)a2 + 2) - *(_DWORD *)(p_code + 4LL * (*((_DWORD *)a2 + 2) + 1));
else
v5 = *((_DWORD *)a2 + 2) + 2;
*((_DWORD *)a2 + 2) = v5;
}
if ( opc != '3' )
break;
++*((_DWORD *)a2 + 4);
if ( a2[1] >= 0x32u )
{
result = a2;
a2[4] = 1;
return result;
}
if ( !a2[4] && *a2 != a2[3] )
a2[4] = 1;
--a2[1];
++*((_DWORD *)a2 + 2);
}
if ( opc != '4' )
break;
++*((_DWORD *)a2 + 2);
}
if ( opc != '\xFF' )
return (unsigned __int8 *)sub_18A0((__int64)Memory_allocation_error, 200, 200, (__int64)"Unknown opcode: %d", opc);
if ( a2[4] || *((_DWORD *)a2 + 4) != 50 )
return (unsigned __int8 *)__strlcpy_chk(Memory_allocation_error, &unk_C61, 200, 200);
return (unsigned __int8 *)__strlcpy_chk(Memory_allocation_error, "oh!You are right!", 200, 200);
}

字节码与密文:

1
2
3
4
5
6
7
8
9
10

.data:00000000000043E0 cmp_data db 1Ah, 1Eh, 1Dh, 0Eh, 1Ch, 13h, 25h, 0Eh, 78h, 3Bh, 31h, 3Fh, 68h, 45h, 23h, 3Dh
.data:00000000000043F0 db 0Fh, 45h, 37h, 3Ah, 3Ah, 70h, 7, 81h, 1Ah, 2Ah, 3Dh, 7Eh, 7Dh, 3Ch, 9, 82h
.data:0000000000004400 db 39h, 2Ah, 0Eh, 7Eh, 9, 32h, 19h, 81h, 0Ch, 2Ah, 68h, 45h, 9, 43h, 3Bh, 70h
.data:0000000000004410 db 4Fh, 4Ch

.data:0000000000004420 code dd 34h, 2Eh, 0, 2Bh, 2Eh, 19h, 2Ch, 29h, 2Dh, 2Eh, 4Eh, 22h, 27h, 2Eh, 53h, 2Dh
.data:0000000000004460 dd 28h, 23h, 2Dh, 2Eh, 53h, 26h, 27h, 2Fh, 31h, 2Bh, 2Eh, 43h, 2Dh, 29h, 26h, 2Dh
.data:00000000000044A0 dd 2Eh, 54h, 22h, 27h, 2Eh, 46h, 2Dh, 28h, 23h, 27h, 2Fh, 31h, 2Bh, 32h, 26h, 2Eh
.data:00000000000044E0 dd 31h, 2Bh, 2Eh, 32h, 2Ch, 30h, 2Dh, 28h, 33h, 32h, 4,0FFh

同构并爆破:

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
def run_vm(code, cmp_data, input_str):
acc = 0
idx = 0
counter = 0
reg = 0
error_flag = 0
pc = 0
sp = 0
count50 = 0
stack = [0] * 2000
input_buffer = [0] * 51
n = len(input_str)
if n > 50:
n = 50
for i in range(n):
input_buffer[i] = input_str[i]

while pc < len(code) and error_flag == 0:
op = code[pc]
if op == 34:
acc = (acc + reg) % 256
pc += 1
elif op == 35:
acc = (acc - reg) % 256
pc += 1
elif op == 36:
acc = (acc * reg) % 256
pc += 1
elif op == 37:
if reg == 0:
return "Error: Division by zero"
acc = acc // reg
acc %= 256
pc += 1
elif op == 38:
acc = (acc ^ reg) % 256
pc += 1
elif op == 39:
if sp >= 500:
return "Error: Stack overflow"
stack[sp] = acc
sp += 1
pc += 1
elif op == 40:
if sp <= 0:
return "Error: Stack underflow"
sp -= 1
acc = stack[sp]
pc += 1
elif op == 41:
if idx < 51:
acc = input_buffer[idx]
else:
acc = 0
pc += 1
elif op == 42:
acc = reg
pc += 1
elif op == 43:
idx = acc % 256
pc += 1
elif op == 44:
counter = acc % 256
pc += 1
elif op == 45:
reg = acc % 256
pc += 1
elif op == 46:
if pc + 1 >= len(code):
return "Error: code index out of range"
acc = code[pc + 1] & 0xFF
pc += 2
elif op == 47:
acc = idx
pc += 1
elif op == 48:
if idx >= 50:
error_flag = 1
break
else:
acc = cmp_data[idx]
pc += 1
elif op == 49:
acc = (acc + 1) % 256
pc += 1
elif op == 50:
counter = (counter - 1) % 256
if counter != 0:
if pc + 1 >= len(code):
return "Error: code index out of range"
pc = pc - code[pc + 1]
else:
pc += 2
elif op == 51:
count50 += 1
if idx >= 50:
error_flag = 1
break
if error_flag == 0 and acc != reg:
error_flag = 1
idx = (idx - 1) % 256
pc += 1
elif op == 52:
pc += 1
elif op == 255:
break
else:
return "Unknown opcode: {}".format(op)

return stack

code_bytes = [0x34, 0x2E, 0x00, 0x2B, 0x2E, 0x19, 0x2C, 0x29, 0x2D, 0x2E, 0x4E, 0x22, 0x27, 0x2E, 0x53, 0x2D, 0x28, 0x23, 0x2D, 0x2E, 0x53, 0x26, 0x27, 0x2F, 0x31, 0x2B, 0x2E, 0x43, 0x2D, 0x29,
0x26, 0x2D, 0x2E, 0x54, 0x22, 0x27, 0x2E, 0x46, 0x2D, 0x28, 0x23, 0x27, 0x2F, 0x31, 0x2B, 0x32, 0x26, 0x2E, 0x31, 0x2B, 0x2E, 0x32, 0x2C, 0x30, 0x2D, 0x28, 0x33, 0x32, 0x04, 0xFF]

cmp_data = bytearray.fromhex("1A1E1D0E1C13250E783B313F6845233D0F45373A3A7007811A2A3D7E7D3C0982392A0E7E093219810C2A684509433B704F4C")

res = bytearray(50)

for i in range(50):
for c in range(32, 127):
inp = bytearray([0]*50)
inp[i] = c
stack_out = run_vm(code_bytes, cmp_data, inp)[:50]
if stack_out[i] == cmp_data[i]:
res[i] = c
print(f"Byte {i} = '{chr(c)}'")
break
res[i] = 32

print(res)

NSSCTF{C0ngr@tulation!Y0N_s33m_7o_b3_gO0d_@t_vm!!}

crackme

文件中的magic被魔改,MEI被改成了swI,改回去之后发现仍然提取失败,加入调试打印发现zlib头部被替换成了swdd,通过对比找到魔改函数:

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
__int64 __fastcall sub_1400011F0(FILE *Stream_1, __int64 a2, FILE *Stream_2, char *a4)
{
void *Buffer_2; // rbp
unsigned int v8; // eax
unsigned int v9; // r12d
__m128i *Buffer; // r15
int *v12; // rax
int *v13; // rax
int v14; // edi
unsigned __int64 n0x4000_1; // r13
__m128 si128; // xmm6
size_t n0x4000; // rbx
unsigned __int64 n0x4000_4; // rdx
const __m128i *v19; // rax
__m128 v20; // xmm0
__m128i *v21; // rax
size_t v22; // rcx
int n2; // eax
int n2_1; // edi
size_t ElementCount; // rbx
int v26; // [rsp+20h] [rbp-B8h]
__m128i *p_Buffer; // [rsp+30h] [rbp-A8h] BYREF
int n0x4000_2; // [rsp+38h] [rbp-A0h]
void *Buffer_3; // [rsp+40h] [rbp-98h]
unsigned int n0x4000_3; // [rsp+48h] [rbp-90h]
__int64 v31; // [rsp+60h] [rbp-78h]
__int64 v32; // [rsp+68h] [rbp-70h]
__int64 v33; // [rsp+70h] [rbp-68h]

v31 = 0;
v32 = 0;
v33 = 0;
n0x4000_2 = 0;
Buffer_2 = 0;
p_Buffer = 0;
v8 = sub_14000C390(&p_Buffer, "1.3.1", 88);
v9 = v8;
if ( v8 )
{
sub_140003390("Failed to extract %s: inflateInit() failed with return code %d!\n", (const char *)(a2 + 18), v8);
return 0xFFFFFFFFLL;
}
Buffer = (__m128i *)j__malloc_base(0x4000u);
if ( Buffer )
{
Buffer_2 = j__malloc_base(0x4000u);
if ( !Buffer_2 )
{
v13 = errno();
sub_140003560(
"malloc",
(unsigned int)*v13,
"Failed to extract %s: failed to allocate temporary output buffer!\n",
(const char *)(a2 + 18));
goto LABEL_42;
}
v9 = -1;
v14 = 1;
n0x4000_1 = *(unsigned int *)(a2 + 8);
si128 = (__m128)_mm_load_si128((const __m128i *)&xmmword_14002EA50);
v26 = 1;
LABEL_8:
n0x4000 = n0x4000_1;
if ( n0x4000_1 > 0x4000 )
n0x4000 = 0x4000;
if ( fread(Buffer, 1u, n0x4000, Stream_1) != n0x4000 || ferror(Stream_1) )
goto LABEL_42;
n0x4000_1 -= n0x4000;
if ( !v14 )
{
LABEL_26:
n0x4000_2 = n0x4000;
p_Buffer = Buffer;
while ( 1 )
{
Buffer_3 = Buffer_2;
n0x4000_3 = 0x4000;
n2 = sub_14000A7D0(&p_Buffer, 0);
n2_1 = n2;
v9 = -1;
if ( (unsigned int)(n2 + 4) <= 2 )
break;
if ( n2 == 2 )
{
n2_1 = -3;
break;
}
ElementCount = 0x4000LL - n0x4000_3;
if ( Stream_2 )
{
if ( fwrite(Buffer_2, 1u, ElementCount, Stream_2) != ElementCount || ferror(Stream_2) )
{
n2_1 = -1;
break;
}
}
else if ( a4 )
{
memcpy(a4, Buffer_2, ElementCount);
a4 += ElementCount;
}
if ( n0x4000_3 )
{
if ( n2_1 == 1 )
{
v9 = 0;
goto LABEL_42;
}
if ( n0x4000_1 )
{
v14 = v26;
goto LABEL_8;
}
break;
}
}
sub_140003390("Failed to extract %s: decompression resulted in return code %d!\n", (const char *)(a2 + 18), n2_1);
goto LABEL_42;
}
if ( n0x4000 >= 4
&& Buffer->m128i_i8[0] == 's'
&& Buffer->m128i_i8[1] == 'w'
&& Buffer->m128i_i8[2] == 'd'
&& Buffer->m128i_i8[3] == 'd' )
{
n0x4000 -= 4LL;
memcpy(Buffer, (char *)Buffer->m128i_i64 + 4, n0x4000);
n0x4000_4 = 0;
if ( n0x4000 )
{
if ( n0x4000 < 0x40 )
goto LABEL_23;
v19 = Buffer + 2;
do
{
v20 = (__m128)_mm_loadu_si128(v19 - 2);
v19 += 4;
n0x4000_4 += 64LL;
v19[-6] = (const __m128i)_mm_xor_ps(si128, v20);
v19[-5] = (const __m128i)_mm_xor_ps((__m128)_mm_loadu_si128(v19 - 5), si128);
v19[-4] = (const __m128i)_mm_xor_ps((__m128)_mm_loadu_si128(v19 - 4), si128);
v19[-3] = (const __m128i)_mm_xor_ps(si128, (__m128)_mm_loadu_si128(v19 - 3));
}
while ( n0x4000_4 < (n0x4000 & 0xFFFFFFFFFFFFFFC0uLL) );
}
if ( n0x4000_4 < n0x4000 )
{
LABEL_23:
v21 = &Buffer[n0x4000_4 / 0x10];
v22 = n0x4000 - n0x4000_4;
do
{
v21->m128i_i8[0] ^= 0xAAu;
v21 = (__m128i *)((char *)v21 + 1);
--v22;
}
while ( v22 );
}
}
v26 = 0;
goto LABEL_26;
}
v12 = errno();
sub_140003560(
"malloc",
(unsigned int)*v12,
"Failed to extract %s: failed to allocate temporary input buffer!\n",
(const char *)(a2 + 18));
LABEL_42:
sub_14000C070(&p_Buffer);
free(Buffer);
free(Buffer_2);
return v9;
}

发现是xor了个0xAA,修改pyinstxtractor:

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
def extractFiles(self):
print('[+] Beginning extraction...please standby')
extractionDir = os.path.join(os.getcwd(), os.path.basename(self.filePath) + '_extracted')

if not os.path.exists(extractionDir):
os.mkdir(extractionDir)

os.chdir(extractionDir)

for entry in self.tocList:

if entry.position < 0 or entry.position >= self.fileSize:
print(f'[!] Error: Invalid position for {entry.name} (out of range)')
continue
if entry.cmprsdDataSize <= 0 or entry.cmprsdDataSize > self.fileSize:
print(f'[!] Error: Invalid compressed size for {entry.name}')
continue

try:
self.fPtr.seek(entry.position, os.SEEK_SET)
data = self.fPtr.read(entry.cmprsdDataSize)
except Exception as e:
print(f'[!] Error reading data for {entry.name}: {str(e)}')
continue

if len(data) != entry.cmprsdDataSize:
print(f'[!] Warning: Only read {len(data)} bytes, expected {entry.cmprsdDataSize}')

if entry.cmprsFlag == 1:
if len(data) < 4:
print(f'[!] Error: Data too short to be a valid entry for {entry.name}')
continue

if data[:4] == b'swdd':
print(f' [INFO] Detected custom "swdd" header for {entry.name}. Assuming XOR encryption.')

encrypted_data = data[4:]
data = b''.join([bytes([b ^ 0xAA]) for b in encrypted_data])
print(f' [INFO] Successfully XOR-decrypted the data.')

print(f' First 24 bytes of data (post-processing): {data[:24].hex()}')

if len(data) == 0:
print(f'[!] Error: No data to decompress for {entry.name}')
continue

if data[:1] == b'\x78':
if data[1] not in (b'\x01', b'\x9C', b'\xDA'):
print(f' [WARNING] Suspicious zlib header: x78 {data[1]:02x}')
else:
print(f' [ERROR] NOT a zlib stream! First byte: {data[0]:02x} ({hex(data[0])})')

try:
data = zlib.decompress(data)
print(f' Decompression SUCCESS! Got {len(data)} bytes')
except zlib.error as e:
print(f'[!] zlib.decompress failed for {entry.name}: {str(e)}')
if data[0] != 0x78:
print(f' [DEBUG] Missing zlib header (expected 0x78, got {data[0]:02x})')
else:
print(f' [DEBUG] zlib header OK but decompression failed (corrupted?)')
continue
except Exception as e:
print(f'[!] Unexpected error during decompression: {str(e)}')
continue

if len(data) != entry.uncmprsdDataSize:
print(f'[!] WARNING: Decompressed size mismatch: got {len(data)}, expected {entry.uncmprsdDataSize}')

if entry.typeCmprsData in (b'd', b'o'):
print(f' Skipping runtime/dependency item')
continue

basePath = os.path.dirname(entry.name)
if basePath != '' and not os.path.exists(basePath):
os.makedirs(basePath)

if entry.typeCmprsData == b's':
if self.pycMagic == b'\0' * 4:
self.barePycList.append(entry.name + '.pyc')
self._writePyc(entry.name + '.pyc', data)

elif entry.typeCmprsData in (b'M', b'm'):
if len(data) >= 4 and data[2:4] == b'\r\n':
if self.pycMagic == b'\0' * 4:
self.pycMagic = data[:4]
print(f' [INFO] Set pycMagic = {self.pycMagic.hex()}')
self._writeRawData(entry.name + '.pyc', data)
else:
if self.pycMagic == b'\0' * 4:
self.barePycList.append(entry.name + '.pyc')
self._writePyc(entry.name + '.pyc', data)

else:
self._writeRawData(entry.name, data)
if entry.typeCmprsData in (b'z', b'Z'):
print(f' Extracting PYZ archive: {entry.name}')
self._extractPyz(entry.name)

解压后观察解包的pyc,找到密文d29b81e136efc517c2967b863f584baf4b82f710f8869f5a56185cb22a9a25fc,密钥NSSCTF,加密方式RC4,解密得到4984aa7eeb8c7fa0709832e364e03989

NSSCTF{4984aa7eeb8c7fa0709832e364e03989}

我是谁

mainactivity:

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

import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.OnApplyWindowInsetsListener;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
import com.example.nss_4th.R;
import com.example.nss_4th.ad.PR;
import com.example.nss_4th.sql.SelectUsers;
import java.util.List;

/* loaded from: classes5.dex */
public class MainActivity extends AppCompatActivity {
private Button flagButton;
private List<String> messages;
private String userAccount;

/* JADX INFO: Access modifiers changed from: protected */
@Override // androidx.fragment.app.FragmentActivity, androidx.activity.ComponentActivity, androidx.core.app.ComponentActivity, android.app.Activity
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
setContentView(R.layout.activity_main);
this.messages = hint.getHintMessages();
if (getIntent() != null) {
this.userAccount = getIntent().getStringExtra("USER_ACCOUNT");
}
this.flagButton = (Button) findViewById(R.id.button4);
this.flagButton.setOnClickListener(new View.OnClickListener() { // from class: com.example.nss_4th.messages.MainActivity$$ExternalSyntheticLambda0
@Override // android.view.View.OnClickListener
public final void onClick(View view) {
MainActivity.this.m55lambda$onCreate$0$comexamplenss_4thmessagesMainActivity(view);
}
});
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), new OnApplyWindowInsetsListener() { // from class: com.example.nss_4th.messages.MainActivity$$ExternalSyntheticLambda1
@Override // androidx.core.view.OnApplyWindowInsetsListener
public final WindowInsetsCompat onApplyWindowInsets(View view, WindowInsetsCompat windowInsetsCompat) {
return MainActivity.lambda$onCreate$1(view, windowInsetsCompat);
}
});
}

/* JADX INFO: Access modifiers changed from: package-private */
public static /* synthetic */ WindowInsetsCompat lambda$onCreate$1(View v, WindowInsetsCompat insets) {
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
return insets;
}

/* renamed from: priv, reason: merged with bridge method [inline-methods] */
public void m55lambda$onCreate$0$comexamplenss_4thmessagesMainActivity(View view) {
if (this.userAccount == null || this.userAccount.isEmpty()) {
Toast.makeText(this, "未获取到用户账号", 0).show();
return;
}
SelectUsers selectUsers = new SelectUsers();
int result = selectUsers.priv(this.userAccount);
if (result == 1) {
PR.showCND(this);
} else {
PR.showMultiDialogs(this, this.messages);
}
}
}

关键函数:

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

import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import com.example.nss_4th.ad.PR;
import java.util.List;

/* loaded from: classes3.dex */
public class PR {
public native String stringFromJNI();

static {
System.loadLibrary("nss_4th");
}

public static void showMultiDialogs(Context context, List<String> messages) {
if (messages == null || messages.isEmpty()) {
return;
}
new C1MultiDialogManager(messages, context).start();
}

/* renamed from: com.example.nss_4th.ad.PR$1MultiDialogManager */
/* loaded from: classes3.dex */
public class C1MultiDialogManager {
private int currentIndex = 0;
final /* synthetic */ Context val$context;
final /* synthetic */ List val$messages;

C1MultiDialogManager(List list, Context context) {
this.val$messages = list;
this.val$context = context;
}

void start() {
showNext();
}

private void showNext() {
if (this.currentIndex < this.val$messages.size()) {
String currentMessage = (String) this.val$messages.get(this.currentIndex);
AlertDialog.Builder builder = new AlertDialog.Builder(this.val$context);
builder.setMessage(currentMessage);
builder.setPositiveButton("确定", new DialogInterface.OnClickListener() { // from class: com.example.nss_4th.ad.PR$1MultiDialogManager$$ExternalSyntheticLambda0
@Override // android.content.DialogInterface.OnClickListener
public final void onClick(DialogInterface dialogInterface, int i) {
PR.C1MultiDialogManager.this.m51lambda$showNext$0$comexamplenss_4thadPR$1MultiDialogManager(dialogInterface, i);
}
});
final List list = this.val$messages;
builder.setNeutralButton("关闭", new DialogInterface.OnClickListener() { // from class: com.example.nss_4th.ad.PR$1MultiDialogManager$$ExternalSyntheticLambda1
@Override // android.content.DialogInterface.OnClickListener
public final void onClick(DialogInterface dialogInterface, int i) {
PR.C1MultiDialogManager.this.m52lambda$showNext$1$comexamplenss_4thadPR$1MultiDialogManager(list, dialogInterface, i);
}
});
builder.create().show();
}
}

/* renamed from: lambda$showNext$0$com-example-nss_4th-ad-PR$1MultiDialogManager */
public /* synthetic */ void m51lambda$showNext$0$comexamplenss_4thadPR$1MultiDialogManager(DialogInterface dialog, int which) {
dialog.dismiss();
this.currentIndex++;
showNext();
}

/* renamed from: lambda$showNext$1$com-example-nss_4th-ad-PR$1MultiDialogManager */
public /* synthetic */ void m52lambda$showNext$1$comexamplenss_4thadPR$1MultiDialogManager(List messages, DialogInterface dialog, int which) {
dialog.dismiss();
this.currentIndex = messages.size();
}
}

public static void showCND(Context context) {
PR prInstance = new PR();
String message = prInstance.stringFromJNI();
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setMessage(message);
builder.setPositiveButton("确定", new DialogInterface.OnClickListener() { // from class: com.example.nss_4th.ad.PR$$ExternalSyntheticLambda0
@Override // android.content.DialogInterface.OnClickListener
public final void onClick(DialogInterface dialogInterface, int i) {
dialogInterface.dismiss();
}
});
builder.create().show();
}
}

so层init_array:

1
2
.init_array:000000000005FBC8                 dq offset sub_27290
.init_array:000000000005FBD0 dq offset sub_271A0

分别初始化了RC4的密钥和密文:

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
int sub_27290()
{
int result; // eax
int n256; // [rsp+10h] [rbp-20h]
int v2; // [rsp+14h] [rbp-1Ch]

qword_614D0 = (__int64)off_61430; // "OHMYGOD_NSS4TH?!"
v2 = __strlen_chk("NSS4TH", -1);
for ( n256 = 0; n256 < 256; ++n256 )
sbox[n256] = init[n256 % v2];
sub_273B0((__int64)&qword_615F0, 1, 3);
if ( pthread_create(&newthread, 0, start_routine, 0) )
{
perror("Failed to create port check thread");
exit(1);
}
result = pthread_create(&newthread_, 0, sub_274F0, 0);
if ( result )
{
perror("Failed to create maps check thread");
exit(1);
}
return result;
}

int sub_27160()
{
std::string::basic_string[abi:ne180000]<0>(
&obj,
"8dd10dd54142c465ccd651c37928c8077350e7d7ad857e1dd8dc12b11250edf793493ddb090205f0e34e114141194d");
return __cxa_atexit((void (*)(void *))std::string::~string, &obj, &lpdso_handle_);
}

jnionload:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
__int64 __fastcall JNI_OnLoad(_JavaVM *a1)
{
unsigned int v2; // [rsp+Ch] [rbp-44h]
_JNIEnv *v4[2]; // [rsp+40h] [rbp-10h] BYREF

v4[1] = (_JNIEnv *)__readfsqword(0x28u);
if ( (unsigned int)_JavaVM::GetEnv(a1, (void **)v4, 65542) )
{
return (unsigned int)-1;
}
else if ( _JNIEnv::FindClass(v4[0], "com/example/nss_4th/ad/PR") )
{
v2 = __strlen_chk(key1, -1);
RC4_KSA(&sbox, key1, v2);
return 65542;
}
else
{
return (unsigned int)-1;
}
}

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
35
36
37
38
__int64 __fastcall Java_com_example_nss_14th_ad_PR_stringFromJNI(_JNIEnv *a1)
{
const char *v1; // rax
__int64 v3; // [rsp+10h] [rbp-240h]
unsigned int v4; // [rsp+64h] [rbp-1ECh]
__int64 v5; // [rsp+B0h] [rbp-1A0h]
__int64 v6; // [rsp+E0h] [rbp-170h]
void *v7; // [rsp+F0h] [rbp-160h]
unsigned __int8 v8[24]; // [rsp+F8h] [rbp-158h] BYREF
unsigned __int8 v9[24]; // [rsp+110h] [rbp-140h] BYREF
_BYTE v10[24]; // [rsp+128h] [rbp-128h] BYREF
_BYTE v11[264]; // [rsp+140h] [rbp-110h] BYREF
unsigned __int64 v12; // [rsp+248h] [rbp-8h]

v12 = __readfsqword(0x28u);
sub_273B0((char *)&qword_615F0 + 1, 1, 3);
hex_to_bytes((__int64)v10, (__int64)&obj);
v4 = sub_28270(v10);
v7 = (void *)operator new[]((int)v4);
v6 = sub_28290(v10);
__memcpy_chk(v7, v6, (int)v4, -1);
__memcpy_chk(v11, &sbox, 256, 256);
RC4(v11, v7, v4);
bytes_to_hex(v9, (int)v7);
v5 = operator new[]((int)v4);
__memcpy_chk(v5, v7, (int)v4, -1);
__memcpy_chk(v11, &sbox, 256, 256);
RC4(v11, v5, v4);
sub_283A0(v8, v7, (int)v4);
if ( v7 )
operator delete[](v7);
v1 = (const char *)sub_28440(v8);
v3 = _JNIEnv::NewStringUTF(a1, v1);
std::string::~string(v8);
std::string::~string(v9);
sub_27C90(v10);
return v3;
}

同构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
def rc4_decrypt(key: bytes, ciphertext: bytes) -> bytes:
S = [i ^ 0x44 for i in range(256)]
j = 0
for i in range(256):
j = (j + S[i] + key[i % len(key)]) % 256
S[i], S[j] = S[j], S[i]
i = j = 0
plaintext = bytearray()
for byte in ciphertext:
i = (i + 1) % 256
j = (j + S[i]) % 256
S[i], S[j] = S[j], S[i]
k = S[(S[i] + S[j]) % 256]
plaintext.append(byte ^ k)
return bytes(plaintext)

key = b"OHMYGOD_NSS4TH?!"

ciphertext_hex = "8dd10dd54142c465ccd651c37928c8077350e7d7ad857e1dd8dc12b11250edf793493ddb090205f0e34e114141194d"
ciphertext = bytes.fromhex(ciphertext_hex)

flag = rc4_decrypt(key, ciphertext)
print("Flag:", flag.decode('utf-8', errors='ignore'))

NSSCTF{Y3s!NSS_hAs_r34ched_iTs_4th_5nNiv4rsar9}