Featured image of post TPCTF 2023

TPCTF 2023

UKFC 2023 TPCTF Writeup

Maze

1
2
3
4
5
6
import maze
help(maze)
print('EqdU3uQNCi=',maze.EqdU3uQNCi)
print('UJ9mxXxeoS=',maze.UJ9mxXxeoS)
print('c2VjcmV0=',maze.c2VjcmV0)
print('regexes=',maze.regexes)

给定一个没有混淆的 api 和函数模块 debug 功能,解 flag 也不是什么难事了

  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
NAME
    maze

CLASSES
    builtins.object
        Q2Fy
        Q2VsbA
        TWF6ZUxhbmc
  
    class Q2Fy(builtins.object)    # Car
     |  Q2Fy(value, x, y)
     |  
     |  Methods defined here:
     |  
     |  __init__(self, value, x, y)
     |  
     |  __repr__(self)
     |  
     |  ----------------------------------------------------------------------
     |  Data descriptors defined here:
     |  
     |  __dict__
     |      dictionary for instance variables (if defined)
     |  
     |  __weakref__
     |      list of weak references to the object (if defined)
  
    class Q2VsbA(builtins.object)    # Cell
     |  Q2VsbA(name, value)
     |  
     |  Methods defined here:
     |  
     |  __init__(self, name, value)
     |  
     |  __repr__(self)
     |  
     |  ----------------------------------------------------------------------
     |  Data descriptors defined here:
     |  
     |  __dict__
     |      dictionary for instance variables (if defined)
     |  
     |  __weakref__
     |      list of weak references to the object (if defined)
  
    class TWF6ZUxhbmc(builtins.object)    # MazeLang
     |  TWF6ZUxhbmc(code)
     |  
     |  Methods defined here:
     |  
     |  YWRkX2NlbGw(self, code)    # add_cell
     |  
     |  YWRkX2Z1bmN0aW9u(self, code)    # add_function
     |  
     |  Z2V0X2NlbGw(self, code)    # get_cell
     |  
     |  Z2V0X3Bvcw(self, pos, direction)    # get_pos
     |  
     |  __init__(self, code)
     |  
     |  aW5pdA(self)    # init
     |  
     |  b3Bw(self, direction)    # opp
     |  
     |  c3RlcA(self)    # step
     |  
     |  cnVuX3RpbGxfb3V0cHV0(self)    # run_till_output
     |  
     |  ----------------------------------------------------------------------
     |  Data descriptors defined here:
     |  
     |  __dict__
     |      dictionary for instance variables (if defined)
     |  
     |  __weakref__
     |      list of weak references to the object (if defined)

FUNCTIONS
    aW5pdF9zZWNyZXQ()    # init_secret
  
    c29sdmU(SvL6VEBRwx) -> 'int'    # solve
  
    exit(status=None, /)
        Exit the interpreter by raising SystemExit(status).
      
        If the status is omitted or None, it defaults to zero (i.e., success).
        If the status is an integer, it will be used as the system exit status.
        If it is another kind of object, it will be printed and the system
        exit status will be one (i.e., failure).
  
    run()

DATA
    EqdU3uQNCi= [18, 17, 15, 0, 27, 31, 10, 19, 14, 21, 25, 22, 6, 3, 30, 8, 24, 5, 7, 4, 13, 29, 9, 26, 1, 2, 28, 16, 20, 32, 12, 23, 11]
    UJ9mxXxeoS= 'IyMgIyMgIyMgIyMgIyMgIyMgIyMKIyMgIyMgIyMgXl4gIyMgXl4gIyMKIyMgIyMgIyMgLi4gIyMgSVogIyMgIyMgIyMgIyMKIyMgJVIgLi4gJUQgIyMgJUQgLi4gLi4gJUwgIyMKIyMgPj4gIyMgLi4gIyMgRUEgKiogUFAgJVUgIyMKIyMgJVUgSUEgVEEgIyMgRUIgKiogUFAgJVUgIyMKIyMgJVUgSUIgVEIgIyMgRUMgKiogUFAgJVUgIyMKIyMgJVUgSUMgVEMgIyMgRUQgKiogUFAgJVUgIyMKIyMgJVUgSUQgVEQgIyMgRUUgKiogUFAgJVUgIyMKIyMgJVUgSUUgVEUgIyMgRUYgKiogUFAgJVUgIyMKIyMgJVUgSUYgVEYgIyMgJVIgKiogSVogJVUgIyMKIyMgJVUgSUcgJUwgIyMgIyMgIyMgIyMgIyMgIyMKIyMgIyMgIyMgIyMgIyMgIyMKClBQIC0+ICs9MQpNTSAtPiAtPTEKSVogLT4gPTAKRUEgLT4gSUYgPT0wIFRIRU4gJVIgRUxTRSAlRApFQiAtPiBJRiA9PTEgVEhFTiAlUiBFTFNFICVECkVDIC0+IElGID09MiBUSEVOICVSIEVMU0UgJUQKRUQgLT4gSUYgPT0zIFRIRU4gJVIgRUxTRSAlRApFRSAtPiBJRiA9PTQgVEhFTiAlUiBFTFNFICVECkVGIC0+IElGID09NSBUSEVOICVSIEVMU0UgJUQKVEEgLT4gSUYgKiogVEhFTiAlTCBFTFNFICVECklBIC0+ID03MgpUQiAtPiBJRiAqKiBUSEVOICVMIEVMU0UgJUQKSUIgLT4gPTczClRDIC0+IElGICoqIFRIRU4gJUwgRUxTRSAlRApJQyAtPiA9ODQKVEQgLT4gSUYgKiogVEhFTiAlTCBFTFNFICVECklEIC0+ID04MApURSAtPiBJRiAqKiBUSEVOICVMIEVMU0UgJUQKSUUgLT4gPTY3ClRGIC0+IElGICoqIFRIRU4gJUwgRUxTRSAlRApJRiAtPiA9ODQKSUcgLT4gPTcwCkxUIC0+IElGID09NiBUSEVOICVEIEVMU0UgJUwK'
# secret
    c2VjcmV0= [7, 47, 60, 28, 39, 11, 23, 5, 49, 49, 26, 11, 63, 4, 9, 2, 25, 61, 36, 112, 25, 15, 62, 25, 3, 16, 102, 38, 14, 7, 37, 4, 40]
    regexes= {'wall': '##|``', 'path': '\\.\\.', 'splitter': '<>', 'pause': '[0-9]{2}', 'start': '\\^\\^', 'hole': '\\(\\)', 'out': '>>', 'in': '<<', 'one-use': '--', 'direction': '%[LRUDNlrudn]', 'signal': '(?<=\\*)[\\*A-Za-z0-9]', 'function': '[A-Za-z][A-Za-z0-9]'}
FILE
    /root/Desktop/maze.so
1
2
3
4
5
6
7
8
import maze
maze.aW5pdF9zZWNyZXQ()
flag = []
x0 = maze.TWF6ZUxhbmc(maze.base64.b64decode(maze.UJ9mxXxeoS).decode())
for i in range(33):
    flag.append(x0.cnVuX3RpbGxfb3V0cHV0() ^ maze.c2VjcmV0[i])
print(bytes(flag))
# TPCTF{yOu_@re_m@sT3r_OF_mAZElaN6}

Laogong

reverse

apple

先花一晚上时间 git clone ,第二天醒来之后 make ,运行 apl ,将题目中的代码拆成各个部分喂给 apl ,抄写得到逻辑:

 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
# print("Flag: ")
def _f(arg): # _
    # assert all(i in [0, 1] for i in arg)
    s = [[0] * i + arg + [0] * (4 - i) for i in [4, 3, 1, 0]]
    return [s[0][i] ^ s[1][i] ^ s[2][i] ^ s[3][i] for i in range(len(s[0]))]

def check(a):
    # a = input()
    a = ''.join(j[-1] for j in sorted([a[i: ] + a[: i] for i in range(len(a))]))
    # print(a)
    a += '\x00' * ((8 - len(a)) % 8)
    b = []
    for i in a:
        b += [int(j) for j in bin(ord(i))[2: ].rjust(8, '0')]
  
    c = []
    for i in range(0, len(b), 64):
        c.append([])
        for j in range(8):
            c[-1] += [1 ^ k for k in b[i + 8 * j: i + 8 * j + 8][::-1]]
  
    d = [_f(i) for i in c]
  
    e = []
    for k in d:
        e.append([1 ^ (i ^ j) for i, j in zip([0] * 56 + _f(k[: -64]), k[-64: ])][::-1])
  
    f = [0] * 64
    for i in e:
        f = [2 * f[j] + i[j] for j in range(64)]
  
    g = ''.join(chr(48 + i) for i in f)[4:]
    # print(g)
    return g == "T]OZ7E7UG59[G[456^Tb6=>B`QGhfLBTUQ2hRYFTT7XdDg__?Z9cVZ17ZR02"

最终比较的 g 少了 4 字节,需要暴破。不过给出的字节减 48 都是小于 64 的,爆破空间只有 64 ** 4 。

从 c 到 e 是 64 bit 到 64 bit 的变换,从 e 没办法计算得到 d 的,只能直接从 e 计算得到 c 。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
'''
from z3 import *
def solve_group(r):
    ci = [BitVec('i%d' % i, 1) for i in range(64)]
    di = _f(ci)
    ei = [simplify(1 ^ (i ^ j)) for i, j in zip([0] * 56 + _f(di[: -64]), di[-64: ])][::-1]
    s = Solver()
    for i in range(64):
        s.add(ei[i] == r[i])
    assert s.check() == sat
    model = s.model()
    return [model[ci[i]].as_long() for i in range(64)]
'''

def solve_group(r):
    t = [i ^ 1 for i in r]
    s = [None] * 64

    s[59] = t[2] ^ t[3] ^ t[4]
    s[58] = t[3] ^ t[4] ^ t[5]
    s[57] = t[0] ^ t[4] ^ t[5] ^ t[6]
    s[56] = t[0] ^ t[1] ^ t[5] ^ t[6] ^ t[7]
    for i in range(55, -1, -1):
        s[i] = s[i + 1] ^ s[i + 3] ^ s[i + 4] ^ t[63 - i]
    s[60] = t[7] ^ s[0] ^ s[56] ^ s[57] ^ s[59]
    s[61] = t[6] ^ s[1] ^ s[57] ^ s[58] ^ s[60]
    s[62] = t[2] ^ s[0] ^ s[2] ^ s[61]
    s[63] = t[0] ^ s[0] ^ s[2] ^ s[3]
    return s

def recover_str(s):
    sorted_s = sorted(s)
    t = s[0]
    for i in range(len(s) - 1):
        t += sorted_s[s.index(t[-1])]
    return t

from tqdm import trange

def solve():
    g = "T]OZ7E7UG59[G[456^Tb6=>B`QGhfLBTUQ2hRYFTT7XdDg__?Z9cVZ17ZR02"
    t = [ord(i) - 48 for i in g]
    for _i in trange(64 ** 4):
        f = [_i // (64 ** 3) % 64, _i // (64 ** 2) % 64, _i // 64 % 64, _i % 64] + t
        e = []
        for _j in range(5, -1, -1):
            e.append([(f[_k] >> _j) & 1 for _k in range(64)])

        c = [solve_group(i) for i in e]

        b = []
        for ci in c:
            for _j in range(8):
                b += [1 ^ _k for _k in ci[8 * _j: 8 * _j + 8][::-1]]

        # print(b)

        a = ''
        for _j in range(0, len(b), 8):
            a += chr(int(''.join(str(_k) for _k in b[_j: _j + 8]), 2))

        a = a.rstrip('\x00')
        if all(chr(0x21) <= i <= chr(0x7e) for i in a):
            print(a)
            # a = recover_str(a)
            # print(a)

暴破得到第一步变换后的串为 s5vrn6__3C{_R_PTPTACC}-43LrhelWerr4e0ruwBv4oF3

还剩下第一步,不知道怎么逆,写个 z3 慢慢暴。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
from z3 import Int, Solver, And, Or, sat, If

s = b's5vrn6__3C{_R_PTPTACC}-43LrhelWerr4e0ruwBv4oF3'
sorted_s = sorted(s)
d = {}
l = len(s)
for i in range(l):
    if s[i] not in d:
        d[s[i]] = []
    d[s[i]].append(sorted_s[i])

# print(d)

t = [Int('t%d' % i) for i in range(l)]
s = Solver()

for i in range(l):
    cond = False
    for k in d:
        for v in d[k]:
            cond = Or(cond, And(t[i] == k, t[(i + 1) % l] == v))
    s.add(cond)

for i in range(6):
    s.add(t[i] == b'TPCTF{'[i])

s.add(t[-1] == ord('}'))

def Count(arr, v):
    c = 0
    for i in arr:
        c += If(i == v, 1, 0)
    return c

for i in set(sorted_s):
    s.add(Count(t, i) == sorted_s.count(i))

while s.check() == sat:
    model = s.model()
    result = [model[t[i]].as_long() for i in range(l)]
    a = bytes(result).decode()
    print(a, end='  ')
    a = ''.join(j[-1] for j in sorted([a[i: ] + a[: i] for i in range(len(a))]))
    print(a)
    if a == 's5vrn6__3C{_R_PTPTACC}-43LrhelWerr4e0ruwBv4oF3':
        input('Found')
    cond = False
    for i in range(l):
        cond = Or(cond, t[i] != result[i])
    s.add(cond)

# TPCTF{APL_Burrows-Wheeler_CRC64_4r3_4vv350rn3}

funky puzzle

计算机中浮点数的存储与整数不一样, +0.0 和 -0.0 是不同的,并且可以用二者进行计算得到 +0.0 或者 -0.0 ,于是可以用二者表示 1 和 0 进行基本运算。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
p = 0.0
n = -0.0

def is_p(x):
    return str(x) == str(p)

def is_n(x):
    return str(x) == str(n)

is_0 = is_n
is_1 = is_p

def to_bit(x):
    return int(is_1(x))

syms = [n, p]

def to_sym(v, size=8):
    r = []
    while v:
        r.append(syms[v & 1])
        v >>= 1
    assert len(r) <= size
    while len(r) < size:
        r.append(syms[0])
    return r

def expr_table(expr):
    print('%s:' % expr)
    for x in syms:
        for y in syms:
            print('\t(%d, %d) -> %d' % (to_bit(x), to_bit(y), to_bit(eval(expr))))


expr_table('-(x - y) - (y - x)') # xor
expr_table('-(-x - y)') # and
expr_table('-(-(-(x - y) - (y - x)) - n) - (-x - y)') # carry
expr_table('-((-(x - y) - (y - x)) - n) - (n - (-(x - y) - (y - x)))') # add

'''
-(x - y) - (y - x):
    (0, 0) -> 0
    (0, 1) -> 1
    (1, 0) -> 1
    (1, 1) -> 0
-(-x - y):
    (0, 0) -> 0
    (0, 1) -> 0
    (1, 0) -> 0
    (1, 1) -> 1
-(-(-(x - y) - (y - x)) - n) - (-x - y):
    (0, 0) -> 0
    (0, 1) -> 0
    (1, 0) -> 0
    (1, 1) -> 1
-((-(x - y) - (y - x)) - n) - (n - (-(x - y) - (y - x))):
    (0, 0) -> 0
    (0, 1) -> 1
    (1, 0) -> 1
    (1, 1) -> 0
'''

逆向:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 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
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
int __cdecl main(int argc, const char **argv, const char **envp)
{
  char *v3; // rbp
  bbyte *_flag_bits; // rbx
  bbyte *_flag_bits_tmp; // rcx
  char *flag_ptr; // rdx
  char v7; // si
  bbyte *_flag_bits_ptr; // rax
  char *flag_end; // r8
  float v10; // edx
  char v11; // al
  bbyte flag_bits_tmp; // [rsp+0h] [rbp-48h] BYREF
  unsigned __int64 v14; // [rsp+28h] [rbp-20h]

  v14 = __readfsqword(0x28u);
  __printf_chk(1, "Input the flag: ");
  __isoc99_scanf("%s", flag);
  if ( strlen(flag) != 39 )
    goto LABEL_3;
  if ( *(_DWORD *)flag != 'TCPT' )
    goto LABEL_3;
  if ( *(_WORD *)&flag[4] != '{F' )
    goto LABEL_3;
  if ( flag[38] != '}' )
    goto LABEL_3;
  v3 = &flag[6];
  _flag_bits = flag_bits;
  _flag_bits_tmp = &flag_bits_tmp;
  flag_ptr = &flag[6];
  do
  {
    v7 = *flag_ptr;
    *(_QWORD *)flag_bits_tmp.data = 0LL;
    *(_QWORD *)&flag_bits_tmp.data[2] = 0LL;
    *(_QWORD *)&flag_bits_tmp.data[4] = 0LL;
    *(_QWORD *)&flag_bits_tmp.data[6] = 0LL;
    bbyte_from(_flag_bits_tmp, v7);
    _flag_bits_ptr[-1] = flag_bits_tmp;
  }
  while ( flag_end != flag_ptr );
  encrypt1();
  encrypt2();
  encrypt3();
  do
  {
    v10 = _flag_bits->data[0];
    v11 = 2
        * ((2
          * ((2
            * ((2
              * ((2
                * ((2 * ((2 * (_flag_bits->data[7] >= 0.0)) | (_flag_bits->data[6] >= 0.0))) | (_flag_bits->data[5] >= 0.0))) | (_flag_bits->data[4] >= 0.0))) | (_flag_bits->data[3] >= 0.0))) | (_flag_bits->data[2] >= 0.0))) | (_flag_bits->data[1] >= 0.0));
    ++_flag_bits;
    *v3++ = (v10 >= 0.0) | v11;
  }
  while ( flag != (char *)_flag_bits );
  if ( *(_QWORD *)&flag[6] ^ 0x69DA6110F30513BELL | *(_QWORD *)&flag[14] ^ 0xB564B4894A8B7B39LL
    || *(_QWORD *)&flag[22] ^ 0xD94585C18415AE32LL | *(_QWORD *)&flag[30] ^ 0xF68893F67CB61DA9LL )
  {
LABEL_3:
    fail();
  }
  puts("Correct flag!");
  return 0;
}

void __fastcall encrypt1()
{
  // [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-"+" TO EXPAND]

  v0 = flag_bits[1].data;
  v23 = __readfsqword(0x28u);
  *(_OWORD *)_63.data = 0LL;
  *(_OWORD *)&_63.data[4] = 0LL;
  bbyte_from(&_63, 0x63);
  *(_OWORD *)t1.data.data = 0LL;
  *(_OWORD *)&t1.data.data[4] = 0LL;
  bbyte_from(&t1.data, 0x34);
  _34.mod = __63;
  _34.data.data[0] = t1.data.data[0];
  _34.data.data[1] = t1.data.data[1];
  _34.data.data[2] = t1.data.data[2];
  v2 = t1.data.data[3];
  *(_OWORD *)t1.data.data = 0LL;
  _34.data.data[3] = v2;
  _34.data.data[4] = t1.data.data[4];
  _34.data.data[5] = t1.data.data[5];
  _34.data.data[6] = t1.data.data[6];
  v3 = t1.data.data[7];
  *(_OWORD *)&t1.data.data[4] = 0LL;
  _34.data.data[7] = v3;
  bbyte_from(&t1.data, 0xE3);
  t0.mod = __63;
  t0.data.data[0] = t1.data.data[0];
  t0.data.data[1] = t1.data.data[1];
  t0.data.data[2] = t1.data.data[2];
  t0.data.data[3] = t1.data.data[3];
  t0.data.data[4] = t1.data.data[4];
  t0.data.data[5] = t1.data.data[5];
  t0.data.data[6] = t1.data.data[6];
  t0.data.data[7] = t1.data.data[7];
  do
  {
    *(_QWORD *)t1.data.data = 0LL;
    t1_ptr = (float *)&t1;
    flag_bits_ptr = v0 - 8;
    v7 = 8LL;
    *(_QWORD *)&t1.data.data[2] = 0LL;
    _flag_bits_ptr = v0 - 8;
    *(_QWORD *)&t1.data.data[4] = 0LL;
    *(_QWORD *)&t1.data.data[6] = 0LL;
    do
    {
      v9 = *_flag_bits_ptr;
      ++t1_ptr;
      ++_flag_bits_ptr;
      *(t1_ptr - 1) = v9;
      --v7;
    }
    while ( v7 );
    v0 += 8;
    t2.mod = &_63;
    t2.data.data[0] = t1.data.data[0];
    t2.data.data[1] = t1.data.data[1];
    t2.data.data[2] = t1.data.data[2];
    t2.data.data[3] = t1.data.data[3];
    t2.data.data[4] = t1.data.data[4];
    t2.data.data[5] = t1.data.data[5];
    t2.data.data[6] = t1.data.data[6];
    t2.data.data[7] = t1.data.data[7];
    sbyte_mul(&t3, &t2, &_34.data);
    sbyte_xor(&t1, &t3, &t0.data);
    v10 = t1.data.data[1];
    v11 = t1.data.data[2];
    v12 = t1.data.data[3];
    v13 = t1.data.data[4];
    v14 = t1.data.data[5];
    *(_QWORD *)t2.data.data = *(_QWORD *)t1.data.data;
    v15 = t1.data.data[6];
    v16 = t1.data.data[7];
    t2.data.data[2] = t1.data.data[2];
    t2.data.data[3] = t1.data.data[3];
    t2.data.data[4] = t1.data.data[4];
    t2.data.data[5] = t1.data.data[5];
    t2.data.data[6] = t1.data.data[6];
    t2.data.data[7] = t1.data.data[7];
    *flag_bits_ptr = t1.data.data[0];
    flag_bits_ptr[1] = v10;
    flag_bits_ptr[2] = v11;
    flag_bits_ptr[3] = v12;
    flag_bits_ptr[4] = v13;
    flag_bits_ptr[5] = v14;
    flag_bits_ptr[6] = v15;
    flag_bits_ptr[7] = v16;
  }
  while ( &flag[32] != (char *)v0 );
}

void __fastcall encrypt2()
{
  // [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-"+" TO EXPAND]

  i = 32;
  v89 = __readfsqword(0x28u);
  t1_ptr = &t1;
  t0 = flag_bits[8];
  do
  {
    flag_bits_ptr = flag_bits;
    do
    {
      *(_QWORD *)t1.data = 0LL;
      *(_OWORD *)&t1.data[2] = 0uLL;
      *(_QWORD *)&t1.data[6] = 0LL;
      bbyte_add(t1_ptr, &t0, flag_bits_ptr);
      t0 = t1;
      v3 = LODWORD(t1.data[4]);
      v4 = t1.data[7] >= 0.0;
      *(_OWORD *)&t1.data[2] = 0uLL;
      v5 = t1.data[6] >= 0.0;
      *(_QWORD *)&t1.data[6] = 0LL;
      v6 = (2
          * ((2 * ((2 * ((2 * ((2 * ((2 * v4) | v5)) | (t0.data[5] >= 0.0))) | (v3 >= 0))) | (t0.data[3] >= 0.0))) | (t0.data[2] >= 0.0))) | (t1.data[1] >= 0.0);
      v7 = LODWORD(t1.data[0]);
      *(_QWORD *)t1.data = 0LL;
      bbyte_from(t1_ptr, sbox_ptr[(unsigned __int8)((2 * v6) | (v7 >= 0))]);
      *(_QWORD *)t2.data = 0LL;
      *(_QWORD *)&t2.data[2] = 0LL;
      *(_QWORD *)&t2.data[4] = 0LL;
      *(_QWORD *)&t2.data[6] = 0LL;
      bbyte_add(t2_ptr, t1_ptr, v10);
      v13 = t2.data[0];
      v14 = t2.data[2];
      v15 = t2.data[3];
      v16 = t2.data[4];
      v17 = t2.data[5];
      v18 = t2.data[6];
      v19 = t2.data[1];
      *(_QWORD *)&t0.data[6] = *(_QWORD *)&t2.data[5];
      *(_QWORD *)&t0.data[4] = *(_QWORD *)&t2.data[3];
      *(_QWORD *)&t0.data[2] = *(_QWORD *)&t2.data[1];
      t0.data[0] = t2.data[7];
      t0.data[1] = t2.data[0];
      *v20 = t2.data[7];
      v20[1] = v13;
      v20[2] = v19;
      v20[3] = v14;
      v20[4] = v15;
      v20[5] = v16;
      v20[6] = v17;
      v20[7] = v18;
    }
    while ( j != 8 );
    --i;
  }
  while ( i );
  v22 = 32;
  t0 = flag_bits[16];
  do
  {
    v23 = &flag_bits[8];
    do
    {
      *(_QWORD *)t1.data = 0LL;
      *(_OWORD *)&t1.data[2] = 0uLL;
      *(_QWORD *)&t1.data[6] = 0LL;
      bbyte_add(t1_ptr, &t0, v23);
      t0 = t1;
      v24 = LODWORD(t1.data[4]);
      v25 = t1.data[7] >= 0.0;
      *(_OWORD *)&t1.data[2] = 0uLL;
      v26 = t1.data[6] >= 0.0;
      *(_QWORD *)&t1.data[6] = 0LL;
      v27 = (2
           * ((2 * ((2 * ((2 * ((2 * ((2 * v25) | v26)) | (t0.data[5] >= 0.0))) | (v24 >= 0))) | (t0.data[3] >= 0.0))) | (t0.data[2] >= 0.0))) | (t1.data[1] >= 0.0);
      v28 = LODWORD(t1.data[0]);
      *(_QWORD *)t1.data = 0LL;
      bbyte_from(v30, *(_BYTE *)(v29 + (unsigned __int8)((2 * v27) | (v28 >= 0))));
      *(_QWORD *)t2.data = 0LL;
      *(_QWORD *)&t2.data[2] = 0LL;
      *(_QWORD *)&t2.data[4] = 0LL;
      *(_QWORD *)&t2.data[6] = 0LL;
      bbyte_add(v33, v32, v31);
      v34 = t2.data[0];
      v35 = t2.data[2];
      v36 = t2.data[3];
      v37 = t2.data[4];
      v38 = t2.data[5];
      v39 = t2.data[6];
      v40 = t2.data[1];
      *(_QWORD *)&t0.data[6] = *(_QWORD *)&t2.data[5];
      *(_QWORD *)&t0.data[4] = *(_QWORD *)&t2.data[3];
      *(_QWORD *)&t0.data[2] = *(_QWORD *)&t2.data[1];
      t0.data[0] = t2.data[7];
      t0.data[1] = t2.data[0];
      *v41 = t2.data[7];
      v41[1] = v34;
      v41[2] = v40;
      v41[3] = v35;
      v41[4] = v36;
      v41[5] = v37;
      v41[6] = v38;
      v41[7] = v39;
    }
    while ( v42 != 8 );
    --v22;
  }
  while ( v22 );
  v43 = &t0;
  t0_ptr = 32;
  t0 = flag_bits[24];
  do
  {
    v44 = &flag_bits[16];
    do
    {
      *(_QWORD *)t1.data = 0LL;
      *(_OWORD *)&t1.data[2] = 0uLL;
      *(_QWORD *)&t1.data[6] = 0LL;
      bbyte_add(t1_ptr, v43, v44);
      v45 = LODWORD(t1.data[1]);
      t0.data[0] = t1.data[0];
      v46 = LODWORD(t1.data[3]);
      *(_QWORD *)t1.data = 0LL;
      LODWORD(t0.data[1]) = v45;
      v47 = LODWORD(t1.data[4]);
      t0.data[2] = t1.data[2];
      *(_QWORD *)&t0.data[3] = *(_QWORD *)&t1.data[3];
      t0.data[5] = t1.data[5];
      *(_QWORD *)&t0.data[6] = *(_QWORD *)&t1.data[6];
      v48 = t1.data[7] >= 0.0;
      *(_OWORD *)&t1.data[2] = 0uLL;
      v49 = t1.data[6] >= 0.0;
      *(_QWORD *)&t1.data[6] = 0LL;
      ++v44;
      bbyte_from(
        v51,
        *(_BYTE *)(v50
                 + (unsigned __int8)((2
                                    * ((2
                                      * ((2
                                        * ((2 * ((2 * ((2 * ((2 * v48) | v49)) | (t0.data[5] >= 0.0))) | (v47 >= 0))) | (v46 >= 0))) | (t0.data[2] >= 0.0))) | (v45 >= 0))) | (t0.data[0] >= 0.0))));
      *(_QWORD *)t2.data = 0LL;
      *(_QWORD *)&t2.data[2] = 0LL;
      *(_QWORD *)&t2.data[4] = 0LL;
      *(_QWORD *)&t2.data[6] = 0LL;
      bbyte_add(v54, v53, v52);
      v55 = t2.data[0];
      v56 = t2.data[2];
      v57 = t2.data[3];
      v58 = t2.data[4];
      v59 = t2.data[5];
      v60 = t2.data[6];
      v61 = t2.data[1];
      *(_QWORD *)&t0.data[6] = *(_QWORD *)&t2.data[5];
      *(_QWORD *)&t0.data[4] = *(_QWORD *)&t2.data[3];
      *(_QWORD *)&t0.data[2] = *(_QWORD *)&t2.data[1];
      t0.data[0] = t2.data[7];
      t0.data[1] = t2.data[0];
      *v62 = t2.data[7];
      v62[1] = v55;
      v62[2] = v61;
      v62[3] = v56;
      v62[4] = v57;
      v62[5] = v58;
      v62[6] = v59;
      v62[7] = v60;
    }
    while ( v63 != 8 );
    --t0_ptr;
  }
  while ( t0_ptr );
  xa = 32;
  t0 = flag_bits[0];
  do
  {
    v64 = &flag_bits[24];
    do
    {
      *(_QWORD *)t1.data = 0LL;
      *(_OWORD *)&t1.data[2] = 0uLL;
      *(_QWORD *)&t1.data[6] = 0LL;
      bbyte_add(t1_ptr, v43, v64);
      v65 = LODWORD(t1.data[1]);
      t0.data[0] = t1.data[0];
      v66 = LODWORD(t1.data[3]);
      *(_QWORD *)t1.data = 0LL;
      LODWORD(t0.data[1]) = v65;
      v67 = LODWORD(t1.data[4]);
      t0.data[2] = t1.data[2];
      *(_QWORD *)&t0.data[3] = *(_QWORD *)&t1.data[3];
      t0.data[5] = t1.data[5];
      *(_QWORD *)&t0.data[6] = *(_QWORD *)&t1.data[6];
      v68 = t1.data[7] >= 0.0;
      *(_OWORD *)&t1.data[2] = 0uLL;
      v69 = t1.data[6] >= 0.0;
      *(_QWORD *)&t1.data[6] = 0LL;
      ++v64;
      bbyte_from(
        v71,
        *(_BYTE *)(v70
                 + (unsigned __int8)((2
                                    * ((2
                                      * ((2
                                        * ((2 * ((2 * ((2 * ((2 * v68) | v69)) | (t0.data[5] >= 0.0))) | (v67 >= 0))) | (v66 >= 0))) | (t0.data[2] >= 0.0))) | (v65 >= 0))) | (t0.data[0] >= 0.0))));
      *(_QWORD *)t2.data = 0LL;
      *(_QWORD *)&t2.data[2] = 0LL;
      *(_QWORD *)&t2.data[4] = 0LL;
      *(_QWORD *)&t2.data[6] = 0LL;
      bbyte_add(v74, v73, v72);
      v75 = t2.data[0];
      v76 = t2.data[2];
      v77 = t2.data[3];
      v78 = t2.data[4];
      v79 = t2.data[5];
      v80 = t2.data[6];
      v81 = t2.data[1];
      *(_QWORD *)&t0.data[6] = *(_QWORD *)&t2.data[5];
      *(_QWORD *)&t0.data[4] = *(_QWORD *)&t2.data[3];
      *(_QWORD *)&t0.data[2] = *(_QWORD *)&t2.data[1];
      t0.data[0] = t2.data[7];
      t0.data[1] = t2.data[0];
      *v82 = t2.data[7];
      v82[1] = v75;
      v82[2] = v81;
      v82[3] = v76;
      v82[4] = v77;
      v82[5] = v78;
      v82[6] = v79;
      v82[7] = v80;
    }
    while ( v83 != 8 );
    --xa;
  }
  while ( xa );
}

void __fastcall encrypt3()
{
  float *flag_bits_ptr; // rbp
  bword *__7481; // rcx MAPDST
  float v3; // xmm0_4
  float v4; // xmm0_4
  float v5; // xmm14_4
  float v6; // xmm13_4
  float v7; // xmm12_4
  float v8; // xmm11_4
  float v9; // xmm10_4
  float v10; // xmm9_4
  float v11; // xmm8_4
  float v12; // xmm7_4
  float v13; // xmm6_4
  float v14; // xmm5_4
  float v15; // xmm4_4
  float v16; // xmm3_4
  float v17; // xmm2_4
  float v18; // xmm1_4
  float v19; // xmm0_4
  bword _7481; // [rsp+10h] [rbp-208h] BYREF
  sword _6f08; // [rsp+50h] [rbp-1C8h] BYREF
  sword _5edd; // [rsp+A0h] [rbp-178h] BYREF
  sword t0; // [rsp+F0h] [rbp-128h] BYREF
  sword t2; // [rsp+140h] [rbp-D8h] BYREF
  sword t1; // [rsp+190h] [rbp-88h] BYREF
  unsigned __int64 v26; // [rsp+1D8h] [rbp-40h]

  flag_bits_ptr = (float *)flag_bits;
  v26 = __readfsqword(0x28u);
  *(_OWORD *)_7481.data = 0LL;
  *(_OWORD *)&_7481.data[4] = 0LL;
  *(_OWORD *)&_7481.data[8] = 0LL;
  *(_OWORD *)&_7481.data[12] = 0LL;
  bword_from(&_7481, 0x7481);
  *(_OWORD *)t1.data.data = 0LL;
  *(_OWORD *)&t1.data.data[4] = 0LL;
  *(_OWORD *)&t1.data.data[8] = 0LL;
  *(_OWORD *)&t1.data.data[12] = 0LL;
  bword_from(&t1.data, 0x6F08);
  _6f08.data.data[0] = t1.data.data[0];
  _6f08.data.data[1] = t1.data.data[1];
  _6f08.data.data[2] = t1.data.data[2];
  _6f08.data.data[3] = t1.data.data[3];
  _6f08.data.data[4] = t1.data.data[4];
  _6f08.data.data[5] = t1.data.data[5];
  _6f08.data.data[6] = t1.data.data[6];
  _6f08.data.data[7] = t1.data.data[7];
  _6f08.data.data[8] = t1.data.data[8];
  _6f08.data.data[9] = t1.data.data[9];
  _6f08.data.data[10] = t1.data.data[10];
  _6f08.data.data[11] = t1.data.data[11];
  _6f08.data.data[12] = t1.data.data[12];
  _6f08.data.data[13] = t1.data.data[13];
  _6f08.data.data[14] = t1.data.data[14];
  _6f08.data.data[15] = t1.data.data[15];
  _6f08.mod = __7481;
  *(_OWORD *)t1.data.data = 0LL;
  *(_OWORD *)&t1.data.data[4] = 0LL;
  *(_OWORD *)&t1.data.data[8] = 0LL;
  *(_OWORD *)&t1.data.data[12] = 0LL;
  bword_from(&t1.data, 0x5EDD);
  _5edd.data.data[0] = t1.data.data[0];
  _5edd.data.data[1] = t1.data.data[1];
  _5edd.data.data[2] = t1.data.data[2];
  _5edd.data.data[3] = t1.data.data[3];
  _5edd.data.data[4] = t1.data.data[4];
  _5edd.data.data[5] = t1.data.data[5];
  _5edd.data.data[6] = t1.data.data[6];
  _5edd.data.data[7] = t1.data.data[7];
  _5edd.data.data[8] = t1.data.data[8];
  _5edd.data.data[9] = t1.data.data[9];
  _5edd.data.data[10] = t1.data.data[10];
  _5edd.data.data[11] = t1.data.data[11];
  _5edd.data.data[12] = t1.data.data[12];
  _5edd.data.data[13] = t1.data.data[13];
  _5edd.data.data[14] = t1.data.data[14];
  _5edd.data.data[15] = t1.data.data[15];
  _5edd.mod = __7481;
  do
  {
    v3 = *flag_bits_ptr;
    flag_bits_ptr += 16;
    t0.data.data[1] = 0.0;
    t0.data.data[2] = 0.0;
    t0.data.data[3] = 0.0;
    t0.data.data[4] = 0.0;
    t0.data.data[5] = 0.0;
    t0.data.data[6] = 0.0;
    t0.data.data[7] = 0.0;
    t0.data.data[8] = 0.0;
    t0.data.data[9] = 0.0;
    t0.data.data[10] = 0.0;
    t0.data.data[11] = 0.0;
    t0.data.data[12] = 0.0;
    t0.data.data[13] = 0.0;
    t0.data.data[14] = 0.0;
    t0.data.data[15] = 0.0;
    t0.data.data[0] = v3;
    v4 = *(flag_bits_ptr - 15);
    t0.mod = &_7481;
    t0.data.data[1] = v4;
    t0.data.data[2] = *(flag_bits_ptr - 14);
    t0.data.data[3] = *(flag_bits_ptr - 13);
    t0.data.data[4] = *(flag_bits_ptr - 12);
    t0.data.data[5] = *(flag_bits_ptr - 11);
    t0.data.data[6] = *(flag_bits_ptr - 10);
    t0.data.data[7] = *(flag_bits_ptr - 9);
    t0.data.data[8] = *(flag_bits_ptr - 8);
    t0.data.data[9] = *(flag_bits_ptr - 7);
    t0.data.data[10] = *(flag_bits_ptr - 6);
    t0.data.data[11] = *(flag_bits_ptr - 5);
    t0.data.data[12] = *(flag_bits_ptr - 4);
    t0.data.data[13] = *(flag_bits_ptr - 3);
    t0.data.data[14] = *(flag_bits_ptr - 2);
    t0.data.data[15] = *(flag_bits_ptr - 1);
    sword_mul(&t2, &t0, &_6f08.data);
    sword_xor(&t1, &t2, &_5edd.data);
    v5 = t1.data.data[1];
    v6 = t1.data.data[2];
    v7 = t1.data.data[3];
    v8 = t1.data.data[4];
    v9 = t1.data.data[5];
    v10 = t1.data.data[6];
    v11 = t1.data.data[7];
    v12 = t1.data.data[8];
    v13 = t1.data.data[9];
    v14 = t1.data.data[10];
    v15 = t1.data.data[11];
    v16 = t1.data.data[12];
    v17 = t1.data.data[13];
    v18 = t1.data.data[14];
    v19 = t1.data.data[15];
    *(_QWORD *)t0.data.data = *(_QWORD *)t1.data.data;
    t0.data.data[2] = t1.data.data[2];
    t0.data.data[3] = t1.data.data[3];
    t0.data.data[4] = t1.data.data[4];
    t0.data.data[5] = t1.data.data[5];
    t0.data.data[6] = t1.data.data[6];
    t0.data.data[7] = t1.data.data[7];
    t0.data.data[8] = t1.data.data[8];
    t0.data.data[9] = t1.data.data[9];
    t0.data.data[10] = t1.data.data[10];
    t0.data.data[11] = t1.data.data[11];
    t0.data.data[12] = t1.data.data[12];
    t0.data.data[13] = t1.data.data[13];
    t0.data.data[14] = t1.data.data[14];
    t0.data.data[15] = t1.data.data[15];
    *(flag_bits_ptr - 16) = t1.data.data[0];
    *(flag_bits_ptr - 15) = v5;
    *(flag_bits_ptr - 14) = v6;
    *(flag_bits_ptr - 13) = v7;
    *(flag_bits_ptr - 12) = v8;
    *(flag_bits_ptr - 11) = v9;
    *(flag_bits_ptr - 10) = v10;
    *(flag_bits_ptr - 9) = v11;
    *(flag_bits_ptr - 8) = v12;
    *(flag_bits_ptr - 7) = v13;
    *(flag_bits_ptr - 6) = v14;
    *(flag_bits_ptr - 5) = v15;
    *(flag_bits_ptr - 4) = v16;
    *(flag_bits_ptr - 3) = v17;
    *(flag_bits_ptr - 2) = v18;
    *(flag_bits_ptr - 1) = v19;
  }
  while ( flag_bits_ptr != (float *)flag );
}

前两个函数将每 8 bit 组合成一个 byte 进行计算,第三个函数将每 16 bit 组合成一个 word 计算。理解逻辑之后就很简单了。

  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
def bbyte_mul(x, y, p):
    p |= 0x100
    r = 0
    while y:
        if (y & 1):
            r ^= x
        y >>= 1
        x <<= 1
        if (x >> 8) & 1:
            x ^= p
    return r

def invert8(x, n):
    for i in range(0x100):
        if bbyte_mul(x, i, n) == 1:
            return i
    else:
        assert False, 'invert(0x%x, 0x%x) not found' % (x, n)

def encrypt1(data):
    out = []
    for i in range(32):
        out.append(bbyte_mul(data[i], 0x34, 0x163) ^ 0xe3)
    return bytes(out)

def decrypt1(data):
    inv1 = invert8(0x34, 0x163)
    out = []
    for i in range(32):
        out.append(bbyte_mul(data[i] ^ 0xe3, inv1, 0x163))
    return bytes(out)

sbox = bytes.fromhex('637c777bf26b6fc53001672bfed7ab76ca82c97dfa5947f0add4a2af9ca472c0b7fd9326363ff7cc34a5e5f171d8311504c723c31896059a071280e2eb27b27509832c1a1b6e5aa0523bd6b329e32f8453d100ed20fcb15b6acbbe394a4c58cfd0efaafb434d338545f9027f503c9fa851a3408f929d38f5bcb6da2110fff3d2cd0c13ec5f974417c4a77e3d645d197360814fdc222a908846eeb814de5e0bdbe0323a0a4906245cc2d3ac629195e479e7c8376d8dd54ea96c56f4ea657aae08ba78252e1ca6b4c6e8dd741f4bbd8b8a703eb5664803f60e613557b986c11d9ee1f8981169d98e949b1e87e9ce5528df8ca1890dbfe6426841992d0fb054bb16')

def ror1(x, n):
    x &= 0xff
    return (x >> n) | (x << (8 - n)) & 0xff

def rol1(x, n):
    return ror1(x, 8 - n)

def encrypt2(data):
    out = list(data)
    for k in range(4):
        for i in range(32):
            for j in range(8):
                out[8 + 8 * k + ((j + 1) & 7) & 0x1f] = rol1(sbox[out[8 + 8 * k + (j & 7) & 0x1f] + out[j + 8 * k] & 0xff] + out[8 + 8 * k + ((j + 1) & 7) & 0x1f] & 0xff, 1)
    return bytes(out)

def decrypt2(data):
    out = list(data)
    for k in range(3, -1, -1):
        for i in range(32):
            for j in range(7, -1, -1):
                out[8 + 8 * k + ((j + 1) & 7) & 0x1f] = ror1(out[8 + 8 * k + ((j + 1) & 7) & 0x1f], 1) - sbox[out[8 + 8 * k + (j & 7) & 0x1f] + out[j + 8 * k] & 0xff] & 0xff
    return bytes(out)

def bword_mul(x, y, p):
    p |= 0x10000
    r = 0
    while y:
        if (y & 1):
            r ^= x
        y >>= 1
        x <<= 1
        if (x >> 16) & 1:
            x ^= p
    return r

def invert16(x, n):
    for i in range(0x10000):
        if bword_mul(x, i, n) == 1:
            return i
    else:
        assert False, 'invert(0x%x, 0x%x) not found' % (x, n)

def encrypt3(data):
    out = []
    for i in range(0, 32, 2):
        t = bword_mul(data[i] | (data[i + 1] << 8), 0x6f08, 0x17481) ^ 0x5edd
        out += [t & 0xff, (t >> 8) & 0xff]
    return bytes(out)

def decrypt3(data):
    inv3 = invert16(0x6f08, 0x17481)
    out = []
    for i in range(0, 32, 2):
        t = bword_mul((data[i] | (data[i + 1] << 8)) ^ 0x5edd, inv3, 0x17481)
        out += [t & 0xff, (t >> 8) & 0xff]
    return bytes(out)

'''
flag = b'0123456789abcdef0123456789abcdef'
enc1 = encrypt1(flag)
enc2 = encrypt2(enc1)
enc3 = encrypt3(enc2)


print(to_sym(enc2[0]))
print(to_sym(enc2[1]))
print()
print(to_sym(enc3[0]))
print(to_sym(enc3[1]))
'''

enc3 = b''.join(i.to_bytes(8, 'little') for i in [0x69da6110f30513be, 0xb564b4894a8b7b39, 0xd94585c18415ae32, 0xf68893f67cb61da9])

dec = decrypt1(decrypt2(decrypt3(enc3)))
print(b'TPCTF{%s}' % dec)

# b'TPCTF{fP_nuMb3R_5Ub_!s_Tur1ng_C0mPLEtE}'

maze

pyinstaller 解包, chal.pyc 反编译得到

1
2
from maze import run
run()

有个 maze.so ,应该是 python 编译成 native 库了。

通过 python 终端( python 3.8 )执行 import maze 就能直接导入这个库, help(maze) 能看到很多有用的信息( class 结构、变量、函数名),不过具体的函数行为还需要手动去逆向。不过题目设计得不难,简单逆向之后就能求解:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
def aW5pdF9zZWNyZXQ():
    for i in range(33):
        c2VjcmV0[i], c2VjcmV0[EqdU3uQNCi[i]] = c2VjcmV0[EqdU3uQNCi[i]], c2VjcmV0[i]

def c29sdmU(SvL6VEBRwx) -> int:
    x0 = TWF6ZUxhbmc(base64.b64decode(UJ9mxXxeoS).decode())
    if len(SvL6VEBRwx) != 33:
        return 1
    aW5pdF9zZWNyZXQ()
    for i in range(33):
        if ord(SvL6VEBRwx[i]) ^ x0.cnVuX3RpbGxfb3V0cHV0() != c2VjcmV0[i]:
            return 1
    return 0

def run():
    print("Welcome to the world of Maze!")
    print("Please input the flag:")
    if c29sdmU(input()):
        print("Failed, please try again!")
    else:
        print("Congratulations! You got the flag!")

上面三个(准确说是后两个)函数逆出来就可以直接求解了

1
2
3
4
5
6
7
8
import maze
maze.aW5pdF9zZWNyZXQ()
flag = []
x0 = maze.TWF6ZUxhbmc(maze.base64.b64decode(maze.UJ9mxXxeoS).decode())
for i in range(33):
    flag.append(x0.cnVuX3RpbGxfb3V0cHV0() ^ maze.c2VjcmV0[i])
print(bytes(flag))
# TPCTF{yOu_@re_m@sT3r_OF_mAZElaN6}

polynomial

逆向 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
int __cdecl main(int argc, const char **argv, const char **envp)
{
  void *v3; // rax
  void *v4; // rax
  int v5; // ebx
  size_t v6; // rbx
  char *v7; // rax
  void *v8; // rax
  void *v9; // rax
  int value; // [rsp+10h] [rbp-70h] BYREF
  int i; // [rsp+14h] [rbp-6Ch]
  int j; // [rsp+18h] [rbp-68h]
  int k; // [rsp+1Ch] [rbp-64h]
  int l; // [rsp+20h] [rbp-60h]
  int v16; // [rsp+24h] [rbp-5Ch]
  int v17; // [rsp+28h] [rbp-58h]
  int size; // [rsp+2Ch] [rbp-54h]
  int *tmp0; // [rsp+30h] [rbp-50h]
  int *tmp1; // [rsp+38h] [rbp-48h]
  std::string input; // [rsp+40h] [rbp-40h] BYREF
  unsigned __int64 v22; // [rsp+68h] [rbp-18h]

  v22 = __readfsqword(0x28u);
  v3 = std::ostream::write(&std::cout, "Welcome to the world of polynomial!");
  std::ostream::write_0(v3, std::endl);
  std::string::ctor(&input);
  std::ostream::write(&std::cout, "Please input the flag: ");
  std::istream::read(&std::cin, &input);
  v16 = std::string::size(&input);
  if ( v16 == 41 )
  {
    v17 = 42;
    size = 2 << bsr(43u);
    tmp0 = (int *)malloc(4LL * size);
    tmp1 = (int *)malloc(4LL * size);
    memset(tmp0, 0, 4LL * size);
    memset(tmp1, 0, 4LL * size);
    myint::set(&value, 1);
    *tmp0 = value;
    for ( i = 1; ; ++i )
    {
      v6 = i;
      if ( v6 > std::string::size(&input) )
        break;
      v7 = std::string::at(&input, i - 1);
      myint::set(&value, *v7);
      tmp0[i] = value;
    }
    poly::inverse_sqrt(tmp0, tmp1, size);
    poly::integral(tmp1, tmp0, size);
    f1(tmp0, tmp1, size);
    for ( j = 0; j < size; ++j )
    {
      myint::set(&value, dword_5DA140[j]);
      myint::add_eq(&tmp1[j], value);
    }
    f2(tmp1, tmp0, size);
    myint::set(&value, 1);
    *tmp0 = value;
    f2(tmp0, tmp1, size);
    for ( k = 0; k < size; ++k )
      myint::mul_eq(&tmp1[k], 0x125E591);
    f1(tmp1, tmp0, size);
    poly::derivative(tmp0, tmp1, size);
    for ( l = 0; l < size; ++l )
    {
      if ( tmp1[l] != dst[l] )
      {
        v8 = std::ostream::write(&std::cout, "Failed, please try again!");
        std::ostream::write_0(v8, std::endl);
        v5 = 1;
        goto LABEL_18;
      }
    }
    v9 = std::ostream::write(&std::cout, "Congratulations! You got the flag!");
    std::ostream::write_0(v9, std::endl);
    free(tmp0);
    free(tmp1);
    v5 = 0;
  }
  else
  {
    v4 = std::ostream::write(&std::cout, "Failed, please try again!");
    std::ostream::write_0(v4, std::endl);
    v5 = 1;
  }
LABEL_18:
  std::string::dtor(&input);
  return v5;
}

f1 和 f2 两个函数不知道是什么,不过看到有求导和积分互逆的函数,就调试试了一下发现 f1 和 f2 正好也是互逆的,那么不用知道这两个函数是什么了,直接 gdb 断在 main 跑 gdbpython 得到 flag

 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
#!/usr/bin/env python3

def read(addr: int, length: int = 0x10) -> bytes:
    """Return a `length` long byte array with the copy of the process memory at `addr`."""
    return gdb.selected_inferior().read_memory(addr, length).tobytes()

def execute(command):
    return gdb.execute(command, to_string=True)

from Crypto.Util.number import inverse
import struct

n = 0x3b800001

def run():
    execute("set $malloc = (void* (*)(unsigned int)) 0x4df5d0")
    execute("set $memset = (void* (*)(void*, unsigned int, unsigned int)) 0x4010a0")
    execute("set $mult = (void* (*) (void*, void*, void*, unsigned int)) 0x4052fd")
    execute("set $inverse = (void* (*) (void*, void*, unsigned int)) 0x405038")
    execute("set $integral = (void* (*) (void*, void*, unsigned int)) 0x404f7a")
    execute("set $derivative = (void* (*) (void*, void*, unsigned int)) 0x404eaf")
    execute("set $f1 = (void* (*) (void*, void*, unsigned int)) 0x40561c")
    execute("set $f2 = (void* (*) (void*, void*, unsigned int)) 0x4054ec")

    execute("set $size = 0x40")
    execute("set $buf0 = 0x6da140")
    execute("set $buf1 = $malloc($size * 4)")
    execute("set $buf2 = $malloc($size * 4)")
    execute("set $buf3 = $malloc($size * 4)")

    execute("p/x $memset($buf1, 0, $size * 4)")
    execute("p/x $memset($buf2, 0, $size * 4)")
    execute("p/x $memset($buf3, 0, $size * 4)")

    execute("p/x $integral($buf0, $buf1, $size)")
    execute("set ((int*) $buf1) [0] = 1")
    execute("p/x $f2($buf1, $buf2, $size)")
    inv = inverse(0x125e591, n)
    for i in range(0x40):
        execute("set ((int*) $buf2) [%d] = ((int*) $buf2) [%d] * 0x%xl %% 0x%xl" % (i, i, inv, n))
    execute("p/x $f1($buf2, $buf1, $size)")
    execute("set ((int*) $buf1) [0] = 0")
    execute("p/x $f1($buf1, $buf2, $size)")

    for i in range(0x40):
        execute("set ((int*) $buf2) [%d] = (((int*) $buf2) [%d] - ((int*) 0x5da140) [%d]) %% 0x%xl" % (i, i, i, n))
    execute("p/x $f2($buf2, $buf1, $size)")

    execute("p/x $derivative($buf1, $buf2, $size)")
    execute("p/x $inverse($buf2, $buf1, $size)")
    execute("p/x $mult($buf1, $buf1, $buf2, $size)")

    '''
    # execute("p/x $f1($buf1, $buf3, $size)")

    buf0 = int(execute("p/x $buf0").strip().split(' = ')[1], 16)
    buf1 = int(execute("p/x $buf1").strip().split(' = ')[1], 16)
    buf2 = int(execute("p/x $buf2").strip().split(' = ')[1], 16)
    buf3 = int(execute("p/x $buf3").strip().split(' = ')[1], 16)

    print(read(buf2, 0x40 * 4) == read(buf3, 0x40 * 4))
    '''

    buf2 = int(execute("p/x $buf2").strip().split(' = ')[1], 16)
    print(bytes(struct.unpack('<42i', read(buf2, 42 * 4)))[1: ])

run()
# TPCTF{wELCoME_T0_thE_W0r1D_0f_poLynoM141}

nanoPyEnc

pyinstaller 解包之后反编译 run.pyc:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
from secret import key, enc
from Crypto.Cipher import AES
from Crypto.Util.number import *
from Crypto.Util.Padding import pad
key = key.encode()
message = input('Enter your message: ').strip()
if not message.startswith('TPCTF{') or message.endswith('}'):
    raise AssertionError

def encrypt_message(key = None, message = None):
    cipher = AES.new(key, AES.MODE_ECB)
    ciphertext = cipher.encrypt(pad(message, AES.block_size))
    return ciphertext

encrypted = list(encrypt_message(key, message.encode()))
for x, y in zip(encrypted, enc):
    if x != y:
        print('Wrong!')
        return None

print('Right!')
return None

从 secret 拿 key, enc 。反编译 secret.pyc:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
key = '2033-05-18_03:33'
enc = [
    213,
    231,
    201,
    213,
    9,
    197,
    233,
    81,
    111,
    223,
    34,
    166,
    103,
    225,
    175,
    180]

解出来是假的 flag ,看到 run 有这样一行 from Crypto.Util.number import * ,那么可能在 number 里覆盖了 enc 或者 key ,反编译之后在最后可以看到:

1
2
if time.time() % 64 < 1:
    enc = (153, 240, 237, 199, 63, 44, 237, 45, 25, 47, 97, 154, 158, 112, 46, 176, 219, 247, 44, 115, 169, 124, 64, 63, 121, 253, 250, 137, 34, 144, 33, 17, 182, 9, 16, 247, 249, 41, 165, 114, 87, 231, 222, 242, 126, 30, 124, 237)

解 enc 得到的是乱码,说明还有东西。 number 里导入有这行 from Crypto.Util.py3compat import * ,所以继续在 py3compat 里找,看到对 list 做了修改:

1
2
3
def list(s):
    _x = time.time() % 64 < 1
    return [x ^ _x for x in s]

再异或 1 就能解密得到 flag

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
#!/usr/bin/env python3

from Crypto.Cipher import AES

key = b'2033-05-18_03:33'
# dec = b'flag{test}\x06\x06\x06\x06\x06\x06'
enc = [213, 231, 201, 213, 9, 197, 233, 81, 111, 223, 34, 166, 103, 225, 175, 180]
enc = (153, 240, 237, 199, 63, 44, 237, 45, 25, 47, 97, 154, 158, 112, 46, 176, 219, 247, 44, 115, 169, 124, 64, 63, 121, 253, 250, 137, 34, 144, 33, 17, 182, 9, 16, 247, 249, 41, 165, 114, 87, 231, 222, 242, 126, 30, 124, 237)

enc = [i ^ 1 for i in enc]

print(AES.new(key, AES.MODE_ECB).decrypt(bytes(enc)))

# TPCTF{83_C4u710U5_0F_PY7hON_k3YW0Rd_sHadOWIN9}

比赛时没做出来,当时看 py3compat 没仔细看,以为也会把修改的放到最后,就错过了中间的 list 。但是这是逆向题, time.time() % 64 < 1 就是纯纯恶心人,程序本身就不能判断 flag 。题出得很好,下次放 misc 吧。

Licensed under CC BY-NC-SA 4.0
使用 Hugo 构建
主题 StackJimmy 设计