第三届京麒CTF挑战赛·热身赛 WriteUp (Reverse方向)

2.7k 词

Re1:

程序是用rust写的,还把符号表删了,先用bindiff恢复符号表,观察main函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
int __fastcall main_0(int argc, const char **argv, const char **envp)
{
// [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-"+" TO EXPAND]

v52 = -2i64;
v43 = 0i64;
v42 = 0i64;
v41 = 0i64;
*(_OWORD *)input = 0i64;
v44 = alloc::vec::Vec$LT$T$C$A$GT$::reserve::h59c5a926bf455501(*(_QWORD *)&argc, argv, envp);
if ( (std::sys_common::backtrace::print((volatile signed __int8 **)&v44, (char *)input, 0x40ui64) & 1) != 0 ) //相当于scanf
{
_ok_n_ = (char **)n0x41;
core::result::unwrap_failed::h1ffdf61359d262b8(
(unsigned int)"called `Result::unwrap()` on an `Err` valuesrc/main.rs",
43,
(unsigned int)&_ok_n_,
(unsigned int)&off_7FF66FA3C3A0,
(__int64)&off_7FF66FA3C428);
}
if ( n0x41 >= 0x41 )
sub_7FF66FA3B520(n0x41, 64i64, &off_7FF66FA3C440);
sub_7FF66FA347D0(&_ok_n_, input, n0x41);
n0x41_2 = (__int64)_ok_n_;
Src_1 = (char *)Src;
Src_4 = (char *)Src + len;
if ( len )
{
length = 0i64;
v8 = 0i64;
Src_2 = (char *)Src;
do
{
Src_3 = Src_2;
v12 = v8;
n0xF0 = *Src_2;
n0x80 = (unsigned __int8)n0xF0;
if ( n0xF0 < 0 )
{
v15 = n0xF0 & 0x1F;
v16 = Src_3[1] & 0x3F;
if ( (unsigned __int8)n0xF0 <= 0xDFu )
{
Src_2 = Src_3 + 2;
n0x80 = v16 | (v15 << 6);
}
else
{
v17 = (v16 << 6) | Src_3[2] & 0x3F;
if ( (unsigned __int8)n0xF0 < 0xF0u )
{
Src_2 = Src_3 + 3;
n0x80 = (v15 << 12) | v17;
}
else
{
Src_2 = Src_3 + 4;
n0x80 = ((v15 & 7) << 18) | (v17 << 6) | Src_3[3] & 0x3F;
}
}
}
else
{
Src_2 = Src_3 + 1;
}
v8 = &v12[Src_2 - Src_3];
if ( n0x80 - 9 >= 5 && n0x80 != 32 )
{
if ( n0x80 < 0x80 )
goto LABEL_28;
n48 = n0x80 >> 8;
if ( n0x80 >> 8 > 0x1F )
{
if ( n48 == 32 )
{
v10 = *((_BYTE *)off_7FF66FA46078 + (unsigned __int8)n0x80) >> 1;
}
else
{
if ( n48 != 48 )
goto LABEL_28;
v10 = n0x80 == 12288;
}
}
else if ( n48 )
{
if ( n48 != 22 )
goto LABEL_28;
v10 = n0x80 == 5760;
}
else
{
v10 = *((_BYTE *)off_7FF66FA46078 + (unsigned __int8)n0x80);
}
if ( (v10 & 1) == 0 )
goto LABEL_28;
}
}
while ( Src_2 != Src_4 );
input_2 = (char *)1;
LOBYTE(v12) = 1;
v51 = (int)v12;
LABEL_60:
Src_5 = (char *)Src;
goto LABEL_61;
}
v12 = 0i64;
v8 = 0i64;
Src_2 = (char *)Src;
LABEL_28:
if ( Src_2 == Src_4 )
{
LABEL_53:
if ( len )
{
length = v8 - v12;
if ( v8 - v12 < 0 )
goto LABEL_74;
goto LABEL_55;
}
input_2 = (char *)1;
LOBYTE(v12) = 1;
v51 = (int)v12;
length = 0i64;
goto LABEL_60;
}
while ( 1 )
{
Src_6 = Src_4;
n32 = *(Src_4 - 1);
if ( (n32 & 0x80000000) != 0 )
break;
--Src_4;
if ( n32 - 9 >= 5 )
goto LABEL_43;
LABEL_32:
if ( Src_2 == Src_4 )
goto LABEL_53;
}
v23 = *(Src_4 - 2);
if ( v23 >= -64 )
{
Src_4 -= 2;
v26 = v23 & 0x1F;
}
else
{
v24 = *(Src_4 - 3);
if ( v24 >= -64 )
{
Src_4 -= 3;
v25 = v24 & 0xF;
}
else
{
Src_4 -= 4;
v25 = ((*(Src_6 - 4) & 7) << 6) | v24 & 0x3F;
}
v26 = (v25 << 6) | v23 & 0x3F;
}
n32 = (v26 << 6) | n32 & 0x3F;
if ( n32 - 9 < 5 )
goto LABEL_32;
LABEL_43:
if ( n32 == 32 )
goto LABEL_32;
if ( n32 < 0x80 )
goto LABEL_73;
n22 = n32 >> 8;
if ( n32 >> 8 <= 0x1F )
{
if ( n22 )
{
if ( n22 != 22 )
goto LABEL_73;
v20 = n32 == 5760;
}
else
{
v20 = *((_BYTE *)off_7FF66FA46078 + (unsigned __int8)n32);
}
goto LABEL_31;
}
if ( n22 == 32 )
{
v20 = *((_BYTE *)off_7FF66FA46078 + (unsigned __int8)n32) >> 1;
goto LABEL_31;
}
if ( n22 == 48 )
{
v20 = n32 == 12288;
LABEL_31:
if ( (v20 & 1) == 0 )
goto LABEL_73;
goto LABEL_32;
}
LABEL_73:
v8 = &Src_6[v8 - Src_2];
length = v8 - v12;
if ( v8 - v12 < 0 )
{
LABEL_74:
v30 = 0i64;
LABEL_75:
Src_7 = Src_1;
Size_1 = n0x41_2;
alloc::raw_vec::handle_error::h6dc72529d1078ae9(v30, length);
}
LABEL_55:
Src_5 = &v12[(_QWORD)Src];
if ( v8 == v12 )
{
input_2 = (char *)1;
LOBYTE(v12) = 1;
v51 = (int)v12;
}
else
{
Src_8 = &v12[(_QWORD)Src];
v30 = 1i64;
Src_10 = (char *)_rust_alloc(length, 1i64);
if ( !Src_10 )
goto LABEL_75;
input_2 = Src_10;
v51 = 0;
Src_5 = Src_8;
}
LABEL_61:
memcpy_0(input_2, Src_5, length);
if ( 2 * n0x41_2 )
_rust_dealloc(Src_1, n0x41_2, 1i64);
Size_1 = length;
Src_7 = input_2;
if ( length == 1 )
sub_7FF66FA3B200(1i64, 1i64, &off_7FF66FA3C410);
if ( !length )
sub_7FF66FA3B200(0i64, 0i64, &off_7FF66FA3C3F8);
input_1 = *(_WORD *)input_2;
Buf1_1 = _rust_alloc_zeroed(length, 1i64);
if ( !Buf1_1 )
alloc::raw_vec::handle_error::h6dc72529d1078ae9(1i64, length);
output = (_BYTE *)Buf1_1;
for ( i = 0i64; i != length; ++i )
{
HIWORD(v36) = (input_1 >> 2) ^ (input_1 >> 3) ^ (input_1 >> 1);
LOWORD(v36) = input_1;
input_1 = v36 >> 1;
v37 = __ROL1__(v36, 4);
v38 = (4 * (v37 & 0x33)) | (v37 >> 2) & 0x33;
output[i] = input_2[i] ^ (i + ((2 * (v38 & 0x55)) | (v38 >> 1) & 0x55));
}
if ( length == 42 && !memcmp_0(output, Buf2_, 0x2Aui64) ) //Buf2是密文
{
_rust_dealloc(output, 42i64, 1i64);
_ok_n_ = &off_7FF66FA3C4A0; // "ok\n"
Src = (void *)1;
len = 8i64;
v48 = 0i64;
sub_7FF66FA25750(&_ok_n_);
}
else
{
_rust_dealloc(output, length, 1i64);
_ok_n_ = &off_7FF66FA3C488; // "err\n"
Src = (void *)1;
len = 8i64;
v48 = 0i64;
sub_7FF66FA25750(&_ok_n_);
}
return _rust_dealloc(Src_7, Size_1, 1i64);
}

发现有一个很简单的基于明文前2字节的流加密,密钥流仅和明文的前2字节有关,编写脚本解密:

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
def encrypt(m):
d = m[0] + (m[1] << 8)
length = len(m)
output = bytearray(length)
for i in range(length):
h = ((d >> 2) ^ (d >> 3) ^ (d >> 1)) & 0xFFFF
l = d & 0xFFFF
hl = (h << 16) | l
d = (hl >> 1) & 0xFFFF
dyn = (((hl & 0xFF) << 4) | ((hl & 0xFF) >> 4)) & 0xFF
tmp = (((dyn & 0x33) << 2) | ((dyn >> 2) & 0x33)) & 0xFF
res = i + (((tmp & 0x55) << 1) | ((tmp >> 1) & 0x55))
output[i] = m[i] ^ (res & 0xFF)
return bytes(output)

def gen_arr(m):
d = m[0] + (m[1] << 8)
length = len(m)
output = [None] * length
for i in range(length):
h = ((d >> 2) ^ (d >> 3) ^ (d >> 1)) & 0xFFFF
l = d & 0xFFFF
hl = (h << 16) | l
d = (hl >> 1) & 0xFFFF
dyn = (((hl & 0xFF) << 4) | ((hl & 0xFF) >> 4)) & 0xFF
tmp = (((dyn & 0x33) << 2) | ((dyn >> 2) & 0x33)) & 0xFF
res = (i + (((tmp & 0x55) << 1) | ((tmp >> 1) & 0x55))) & 0xFF
output[i] = res
return output

m = b"fl" + b"0" * 40
encrypted_data = encrypt(m)
print("加密结果:", encrypted_data)

xor_arr = gen_arr(m)
for i in range(len(xor_arr)):
print(hex(xor_arr[i]),end = ", ")
print()
cipher = bytearray.fromhex("00A1FB531CFAF01B0640D48C16F4902742B98B0F02D731B72612067EAEDFDA68AF35CCB7B0D09A592B0B")
for i in range(len(cipher)):
cipher[i] ^= xor_arr[i]
print(cipher.decode())

得到flag:flag{1c98572d-7f7b-4fbf-8750-4a2986c695ce}.

Re2:

查看main:

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)
{
Stream *Stream; // rax
size_t n0x40; // rax
__int64 n32; // rax
unsigned __int64 n0x20; // rax
__m128i v8; // xmm2
__m128i v9; // xmm1
char Buffer[16]; // [rsp+20h] [rbp-58h] BYREF
__int128 v11; // [rsp+30h] [rbp-48h]
__int128 v12; // [rsp+40h] [rbp-38h]
__int128 v13; // [rsp+50h] [rbp-28h]

sub_140001010("Enter flag: ");
Stream = _acrt_iob_func(0);
fgets(Buffer, 64, Stream);
n0x40 = strcspn(Buffer, "\n");
if ( n0x40 >= 0x40 )
_report_rangecheckfailure();
Buffer[n0x40] = 0;
n32 = -1i64;
do
++n32;
while ( Buffer[n32] );
if ( n32 == 32 )
{
n0x20 = 0i64;
v8 = _mm_loadu_si128((const __m128i *)&xmmword_1400032F8);
xmmword_140005630 = v11;
xmmword_140005620 = *(_OWORD *)Buffer;
xmmword_140005650 = v13;
v9 = _mm_loadu_si128((const __m128i *)&xmmword_1400032E8);
xmmword_140005640 = v12;
do
{
*(__m128i *)&Buffer[n0x20] = _mm_add_epi8(_mm_shuffle_epi8(_mm_loadu_si128((const __m128i *)&Buffer[n0x20]), v8),v9);
n0x20 += 16i64;
}
while ( n0x20 < 0x20 );
if ( !memcmp(Buffer, "cge87k?9<>?@=pss393=>;8@:Cp@DAuH", 0x20ui64) )
sub_140001010("Correct! The flag is flag{%s}\n");
else
puts("Wrong!");
return 0;
}
else
{
puts("Wrong!");
return 1;
}
}

编写脚本解密:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import numpy as np

v8 = np.array([[0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00],], dtype=np.uint8)
v9 = np.array([[0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10],], dtype=np.uint8)

target = "cge87k?9<>?@=pss393=>;8@:Cp@DAuH"
target_bytes = np.frombuffer(target.encode(), dtype=np.uint8)
decrypted = []
for i in range(0, len(target_bytes), 16):
block = target_bytes[i:i+16]
sub_block = (block - v9[0]) & 0xFF
inv_shuffle = np.argsort(v8[0])
decrypted_block = sub_block[inv_shuffle]
decrypted.extend(decrypted_block)
flag = bytes(decrypted).decode()
print(f"flag{{{flag}}}")

得到flag:flag{cdb0444318e24beb8f374e9181599072}.

re!!!!!

ios逆向,查询字符串得到一串密文:c2a7c3b9c2acc3a5c2a2c3b6c391c295c2aac38cc28bc38ac2a6c3aec28bc28fc2a1c3aac287c382c2bfc3b6c282c38ec2b9c3a2c2a13cc28ac3adc2b1c280c2b2c384c28dc3bbc283c396c2b03dc28a3bc2b12cc287c3b0c2852bc282c39ac28432c29320c29d21c29ac392c291c3a1c296c3a06d1866396c256310c299c3946931c291c3917a2e470b632a7811730f65c385,发现密文貌似有某种规律(绝大部分都是c2xx、c3xx).
找到初始化函数:

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
id __fastcall ViewController.init(coder:)(void *obj)
{
char *receiver; // x20
char *v3; // x8
char *v4; // x8
void *obj_1; // x19
objc_super v7; // [xsp+0h] [xbp-30h] BYREF

v3 = &receiver[OBJC_IVAR____TtC9challenge14ViewController_secretKey];
*(_QWORD *)v3 = 0x616B696875726148LL;
*((_QWORD *)v3 + 1) = 0xEA00000000006567LL;
v4 = &receiver[OBJC_IVAR____TtC9challenge14ViewController_flagHash];
*(_QWORD *)v4 = 0xD000000000000126LL;
*((_QWORD *)v4 + 1) = 0x8000000100003C20LL;
*(_QWORD *)&receiver[OBJC_IVAR____TtC9challenge14ViewController_magicNumber] = 0xDEADBEEFLL;
*(_QWORD *)&receiver[OBJC_IVAR____TtC9challenge14ViewController_inputTextField] = 0;
*(_QWORD *)&receiver[OBJC_IVAR____TtC9challenge14ViewController_outputTextField] = 0;
*(_QWORD *)&receiver[OBJC_IVAR____TtC9challenge14ViewController_encryptButton] = 0;
v7.receiver = receiver;
v7.super_class = (Class)type metadata accessor for ViewController(obj);
obj_1 = objc_retainAutoreleasedReturnValue(objc_msgSendSuper2(&v7, "initWithCoder:", obj));
objc_release(obj);
if ( obj_1 )
objc_release(obj_1);
return obj_1;
}

发现这里有一个secretKey被初始化,转换端序得到密钥Haruhikage.

void ViewController.encryptButtonClicked()()中有:

1
2
3
4
5
6
7
8
9
10
11
12
v10 = ViewController.complexEncrypt(_:)(p___swiftEmptyArrayStorage, v9);
v12 = v11;
v13 = ViewController.verifyFlag(_:)(p___swiftEmptyArrayStorage, v9);
swift_bridgeObjectRelease();
if ( (v13 & 1) != 0 )
{
swift_bridgeObjectRelease();
obj_1 = *(void **)(v2 + OBJC_IVAR____TtC9challenge14ViewController_outputTextField);
if ( obj_1 ) // 恭喜你找到了flag
goto LABEL_7;
goto LABEL_12;
}

查看关键函数__int64 __fastcall ViewController.complexEncrypt(_:)(unsigned __int64 p___swiftEmptyArrayStorage, unsigned __int64 a2)

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
__int64 __fastcall ViewController.complexEncrypt(_:)(unsigned __int64 p___swiftEmptyArrayStorage, unsigned __int64 a2)
{
__int64 v2; // x20
__int64 v5; // x22
__int64 v6; // x24
char *v7; // x23
unsigned __int64 v8; // x19
__int64 v9; // x1
__int64 v10; // x21
__int64 v11; // x19
unsigned __int64 v12; // x1
unsigned __int64 v13; // x20
__int64 _countAndFlagsBits; // x0
void *_object; // x21
Swift::String v16; // kr00_16
void *v17; // x19
unsigned __int64 v18; // x1
unsigned __int64 v19; // x20
__int64 v20; // x19
__int64 v22; // [xsp+0h] [xbp-30h] BYREF

v5 = type metadata accessor for String.Encoding();
v6 = *(_QWORD *)(v5 - 8);
v7 = (char *)&v22 - ((*(_QWORD *)(v6 + 64) + 15LL) & 0xFFFFFFFFFFFFFFF0LL);
v8 = specialized ViewController.rc4Encrypt(_:key:)(
p___swiftEmptyArrayStorage,
a2,
*(_QWORD *)(v2 + OBJC_IVAR____TtC9challenge14ViewController_secretKey),
*(_QWORD *)(v2 + OBJC_IVAR____TtC9challenge14ViewController_secretKey + 8));
v10 = v9;
static String.Encoding.utf8.getter();
v11 = String.data(using:allowLossyConversion:)(v7, 0, v8, v10);
v13 = v12;
swift_bridgeObjectRelease();
(*(void (__fastcall **)(char *, __int64))(v6 + 8))(v7, v5);
if ( v13 >> 60 == 15 )
{
_countAndFlagsBits = 0;
_object = (void *)0xE000000000000000LL;
}
else
{
v16 = Data.base64EncodedString(options:)(0);
_object = v16._object;
outlined consume of Data?(v11, v13);
_countAndFlagsBits = v16._countAndFlagsBits;
}
v17 = specialized ViewController.obfuscateString(_:)(_countAndFlagsBits, (unsigned __int64)_object);
v19 = v18;
swift_bridgeObjectRelease();
v20 = specialized ViewController.transformString(_:)((__int64)v17, v19);
swift_bridgeObjectRelease();
return v20;
}

可以发现其加密逻辑为RC4(密钥为上文被初始化的密钥)→base64编码→obfuscateString→transformString,先查看最后一个加密:

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
void *__fastcall specialized ViewController.transformString(_:)(__int64 a1, unsigned __int64 a2)
{
unsigned __int64 v2; // x8
void *_countAndFlagsBits; // x0
Swift::String_optional v4; // kr00_16
void *_object; // x19
__int64 i; // x21
__int64 v7; // x20
unsigned int tmp; // w8
unsigned int res; // w8
Swift::String v10; // x0
Swift::String_optional v11; // kr10_16
int v12; // w10
int dual; // w9
int v14; // w10
int tri_1; // w13
int tri; // w10
_QWORD v17[5]; // [xsp+8h] [xbp-88h] BYREF
void *_object_1; // [xsp+30h] [xbp-60h]
unsigned __int64 v19; // [xsp+38h] [xbp-58h]

_object_1 = 0;
v19 = 0xE000000000000000LL;
v2 = HIBYTE(a2) & 0xF;
if ( (a2 & 0x2000000000000000LL) == 0 )
v2 = a1 & 0xFFFFFFFFFFFFLL;
v17[1] = a1;
v17[2] = a2;
v17[3] = 0;
v17[4] = v2;
swift_bridgeObjectRetain(a2);
v4 = String.Iterator.next()();
_countAndFlagsBits = (void *)v4.value._countAndFlagsBits;
_object = v4.value._object;
if ( v4.value._object )
{
i = 0; //索引
while ( !__OFADD__(i, 1) )
{
v7 = specialized Collection.first.getter(_countAndFlagsBits, _object);
_countAndFlagsBits = (void *)swift_bridgeObjectRelease();
if ( (i & 1) != 0 ) //奇数索引(偶数位置)异或0xBE
{
tmp = v7 ^ 0xBE;
if ( (v7 & (unsigned __int64)&_mh_execute_header) != 0 )
tmp = 0xBE;
if ( tmp >> 11 == 27 )
goto LABEL_30;
if ( tmp > 0x10FFFF )
goto LABEL_27;
}
else //偶数索引(奇数位置)异或0xEF
{
tmp = v7 ^ 0xEF;
if ( (v7 & (unsigned __int64)&_mh_execute_header) != 0 )
tmp = 0xEF;
if ( tmp >> 11 == 27 )
goto LABEL_29;
if ( tmp > 0x10FFFF )
goto LABEL_28;
}
if ( tmp > 0x7F ) //utf-8转换函数
{
v12 = (tmp & 0x3F) << 8;
dual = (tmp >> 6) + v12 + 0x81C1; //两字节表示
v14 = (v12 | (tmp >> 6) & 0x3F) << 8;
tri_1 = (tmp >> 18) + ((v14 | (tmp >> 12) & 0x3F) << 8) - 0x7E7E7E0F; //三字节表示
tri = (tmp >> 12) + v14 + 0x8181E1;
if ( HIWORD(tmp) )
tri = tri_1;
if ( tmp >= 0x800 )
res = tri;
else
res = dual;
}
else
{
res = tmp + 1;
}
v17[0] = (res + 0xFEFEFEFEFEFEFFLL) & ~(-1LL << (8 * (4 - (unsigned __int8)(__clz(res) >> 3))));
v10._countAndFlagsBits = static String._uncheckedFromUTF8(_:)(v17);
String.append(_:)(v10);
swift_bridgeObjectRelease();
v11 = String.Iterator.next()();
_countAndFlagsBits = (void *)v11.value._countAndFlagsBits;
_object = v11.value._object;
++i;
if ( !v11.value._object )
{
_object = _object_1;
goto LABEL_25;
}
}
__break(1u);
LABEL_27:
__break(1u);
LABEL_28:
__break(1u);
LABEL_29:
__break(1u);
LABEL_30:
__break(1u);
}
else
{
LABEL_25:
swift_bridgeObjectRelease();
return _object;
}
return _countAndFlagsBits;
}

发现是按奇偶索引分别异或0xBE0xEF,最后还有一个utf-8的转换函数,同构这个函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
def calc_utf8(tmp: int) -> int:
if tmp > 0x7F:
v12 = (tmp & 0x3F) << 8
v13 = ((tmp >> 6) & 0x3F) + v12 + 0x81C0
v14 = ((v12 | ((tmp >> 6) & 0x3F)) << 8)
v15 = ((tmp >> 18) & 0x3F) + (((v14 | ((tmp >> 12) & 0x3F)) << 8)) - 0x7E7E7E0F
v16 = ((tmp >> 12) & 0x3F) + v14 + 0x8181E0
if tmp > 0xFFFF:
v16 = v15
if tmp >= 0x800:
v9 = v16
else:
v9 = v13
else:
v9 = tmp
return v9

for i in range(0x0,0x100):
ui = calc_utf8(i)
if i >= 0x80:
ui = ((ui << 8) + (ui >> 8)) & 0xFFFF
print(f"{ui:04x}",end="")
else:
print(f"{ui:02x}",end="")

发现在转换结果上存在以下对应关系:

1
2
3
0x00 ~ 0x7F → 不变
0x80 ~ 0xBF → 加了一个C2的尾缀并转换端序
0xC0 ~ 0xFF → 减去0x40,加了一个C3的尾缀并转换端序

这样就知道密文中的C2和C3是哪来的了,是经过这个转换函数得到的,而这个转换函数实际上只是将每个字符切分为了三种情况,对应的实际上是utf-8的单/双/三字节编码,接下来看obfuscateString

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
void *__fastcall specialized ViewController.obfuscateString(_:)(__int64 _countAndFlagsBits, unsigned __int64 _object)
{
unsigned __int64 v2; // x8
void *_countAndFlagsBits_1; // x0
Swift::String_optional v4; // kr00_16
void *_object_1; // x19
__int64 i; // x21
__int64 v7; // x20
__int64 v8; // x8
bool v9; // vf
__int64 v10; // x8
__int64 v11; // x8
bool v12; // nf
unsigned int v13; // w8
Swift::String v14; // x0
Swift::String_optional v15; // kr10_16
_QWORD v16[5]; // [xsp+8h] [xbp-78h] BYREF
void *_object_2; // [xsp+30h] [xbp-50h]
unsigned __int64 v18; // [xsp+38h] [xbp-48h]

_object_2 = 0;
v18 = 0xE000000000000000LL;
v2 = HIBYTE(_object) & 0xF;
if ( (_object & 0x2000000000000000LL) == 0 )
v2 = _countAndFlagsBits & 0xFFFFFFFFFFFFLL;
v16[1] = _countAndFlagsBits;
v16[2] = _object;
v16[3] = 0;
v16[4] = v2;
swift_bridgeObjectRetain(_object);
v4 = String.Iterator.next()();
_countAndFlagsBits_1 = (void *)v4.value._countAndFlagsBits;
_object_1 = v4.value._object;
if ( v4.value._object )
{
i = 0; //索引
while ( !__OFADD__(i, 1) )
{
v7 = specialized Collection.first.getter(_countAndFlagsBits_1, _object_1);
_countAndFlagsBits_1 = (void *)swift_bridgeObjectRelease();
v8 = (unsigned int)v7;
if ( (v7 & (unsigned __int64)&_mh_execute_header) != 0 )
v8 = 0;
v9 = __OFADD__(v8, i);
v10 = v8 + i;
if ( v9 )
goto LABEL_20;
v9 = __OFADD__(v10, 0xDEADBEEFLL);
v11 = v10 + 0xDEADBEEFLL;
if ( v9 )
goto LABEL_21;
v12 = -v11 < 0;
v11 = (unsigned __int8)v11;
if ( !v12 )
v11 = -(__int64)(unsigned __int8)-(char)v11;
if ( v11 < 0 )
goto LABEL_22;
if ( (v11 & 0xFFFFFF80) != 0 )
v13 = (((v11 & 0x3F) << 8) | ((unsigned int)v11 >> 6)) + 0x81C1;
else
v13 = v11 + 1;
v16[0] = (v13 + 0xFEFEFEFEFEFEFFLL) & ~(-1LL << (8 * (4 - (unsigned __int8)(__clz(v13) >> 3))));
v14._countAndFlagsBits = static String._uncheckedFromUTF8(_:)(v16);
String.append(_:)(v14);
swift_bridgeObjectRelease();
v15 = String.Iterator.next()();
_countAndFlagsBits_1 = (void *)v15.value._countAndFlagsBits;
_object_1 = v15.value._object;
++i;
if ( !v15.value._object )
{
_object_1 = _object_2;
goto LABEL_18;
}
}
__break(1u);
LABEL_20:
__break(1u);
LABEL_21:
__break(1u);
LABEL_22:
__break(1u);
}
else
{
LABEL_18:
swift_bridgeObjectRelease();
return _object_1;
}
return _countAndFlagsBits_1;
}

发现也有一个转换的结构,加密方式是加上索引i和固定值0xDEADBEEF后&0xFF,逆向这两步加密:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
hex_enc = "c2a7c3b9c2acc3a5c2a2c3b6c391c295c2aac38cc28bc38ac2a6c3aec28bc28fc2a1c3aac287c382c2bfc3b6c282c38ec2b9c3a2c2a13cc28ac3adc2b1c280c2b2c384c28dc3bbc283c396c2b03dc28a3bc2b12cc287c3b0c2852bc282c39ac28432c29320c29d21c29ac392c291c3a1c296c3a06d1866396c256310c299c3946931c291c3917a2e470b632a7811730f65c385"
# hex_enc = "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f80c281c282c283c284c285c286c287c288c289c28ac28bc28cc28dc28ec28fc290c291c292c293c294c295c296c297c298c299c29ac29bc29cc29dc29ec29fc2a0c2a1c2a2c2a3c2a4c2a5c2a6c2a7c2a8c2a9c2aac2abc2acc2adc2aec2afc2b0c2b1c2b2c2b3c2b4c2b5c2b6c2b7c2b8c2b9c2bac2bbc2bcc2bdc2bec2bfc2c0c381c382c383c384c385c386c387c388c389c38ac38bc38cc38dc38ec38fc390c391c392c393c394c395c396c397c398c399c39ac39bc39cc39dc39ec39fc3a0c3a1c3a2c3a3c3a4c3a5c3a6c3a7c3a8c3a9c3aac3abc3acc3adc3aec3afc3b0c3b1c3b2c3b3c3b4c3b5c3b6c3b7c3b8c3b9c3bac3bbc3bcc3bdc3bec3bfc3c0" # 测试

def de_utf(hex_enc):
res = bytearray()
for i in range(0, len(hex_enc), 2):
idx = hex_enc[i:i+2]
# print(idx,end="")
if idx == "c2":
val = int("0x"+hex_enc[i+2:i+4], 16)
# print(hex(val), end= ", ")
res.append(val)
elif idx == "c3":
val = int("0x"+hex_enc[i+2:i+4], 16) + 0x40
# print(hex(val), end= ", ")
res.append(val)
else:
sub = hex_enc[max(i-2,0):i]
if not (sub == "c2" or sub == "c3"):
val = int("0x"+hex_enc[i:i+2], 16)
# print(hex(val), end= ", ")
res.append(val)
# print()
# print(res)
# print(len(res))
return res

dec3 = de_utf(hex_enc)
for i in range(len(dec3)):
if i % 2 == 1:
dec3[i] ^= 0xBE
else:
dec3[i] ^= 0xEF

# print(dec3.hex())

dec2 = de_utf(dec3.hex())

for i in range(len(dec2)):
dec2[i] = (dec2[i] - i - 0xDEADBEEF) & 0xFF

print(dec2.decode())

得到base64:YWRiZTI5NzkzNTg3OTgzMDhjOTExZGQ0NjQ3YTJmNmExM2MwNDJjYzMyNDU5N2UxZWRiYzA4OWE5ZTkwMTVmYmE5,解码得到一串十六进制:adbe2979358798308c911dd4647a2f6a13c042cc324597e1edbc089a9e9015fba9,RC4解密得到flag:flag{N4nd3_H4ruhik4g3_Y4tt4n0?!!}.