着色器介绍

着色器是一种运行在图形处理单元(GPU)上的小程序,用于对图形渲染管线的特定部分进行处理。着色器的主要作用是将输入数据(如顶点位置、颜色等)转化为输出数据(如像素颜色),从而实现对图像的渲染和处理。

着色器的分类

  • 顶点着色器(Vertex Shader):主要负责处理顶点的几何关系、位置变换等。在图形渲染过程中,顶点着色器首先接收输入的顶点数据,然后对这些数据进行坐标变换、光照计算等处理,最终输出处理后的顶点数据。
  • 片段着色器(Fragment Shader):主要负责处理像素或片段的颜色计算。像素着色器接收顶点着色器输出的顶点数据,然后根据这些数据计算每个像素的颜色值,最终生成渲染图像。
  • 几何着色器(Geometry Shader):它位于顶点着色器和片段着色器之间,是一个可选的着色器阶段。几何着色器的主要作用是对顶点着色器输出的顶点数据进行进一步的处理,生成新的顶点或图元,或者修改现有图元的属性。
  • 计算着色器(Compute Shader):主要用于在GPU上执行各种通用计算任务,而不仅仅是图形渲染。
  • ……

着色器语言

  • OpenGL着色语言(GLSL):GLSL是OpenGL(Open Graphics Library)的着色器语言,用于OpenGL图形渲染管线的顶点着色器和片段着色器。
  • DirectX高级着色器语言(HLSL):HLSL是DirectX图形API的着色器语言,主要用于Windows平台的游戏开发和图形应用。
  • ……

着色器的执行

从着色器代码到GPU可以理解的着色器程序,一般需要经过编译。
针对较低版本的OpenGL,这一般意味着从文件或者data段读取源代码直接编译。这给了我们修改shader以控制最终渲染表现的机会。例如笔者在VNCTF2024中的题目LearnOpenGL,就可以通过直接修改着色器的方法得到flag。
更为细致的,这里的”编译“其实包含了编译与链接两步。
一般而言,如果最后需要着色器生成一个图像,那么顶点着色器与片段着色器是不可缺的。因为图形的渲染必然涉及到以下两步:第一步把3D坐标转换为2D坐标,第二步是把2D坐标转变为实际的有颜色的像素。
可以大致理解成顶点着色器负责第一步,片段着色器负责第二步。那么两步加在一起才是一个完整的过程。反映到代码层面,就是在编译好顶点与片段着色器后,需要把两者链接起来,变成一个program,后续调用会直接调用这个program进行渲染。
一个着色器的一般流程
而对于OpenGL之外的其它环境,着色器代码可能会被预编译为中间表示,如Vulkan平台通常使用SPIR-V作为中间表示,而DirectX平台则使用DXBC。

DubheCTF - ezVK

根据题目名,以及调用的动态链接库,我们初步怀疑是Vulkan平台的shader逆向。运行程序,发现没有图形界面,那么看来它调用vulkan不是用来绘图的。直接猜测一手,核心的加密逻辑在vulkan调用的着色器里。
ida分析main_0函数,很容易发现dword_140021000里存的是加密后的密文。往前翻哪里读入了着色器,因为着色器读入后需要平台进行加载,所以在vkCreateShaderModule函数之前的sub_14001135C函数中
sub_14001135C函数
分析代码,着色器在程序的resource里,可以使用resource hacker等工具提取出来。发现是二进制格式的,需要反编译。根据我们前置介绍里讲的,Vulkan平台通常使用SPIR-V作为中间表示,那么现在我们需要找一个SPIR-V的反编译器。经过一番搜索,发现百度只能找到spirv-dis,是反编译成字节码,逆向难度仍然较大。GitHub能找到spirv-cross,直接把我们提取出来的中间表示还原成GLSL:

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
#version 450
layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
 
const uint _80[5] = uint[](1214346853u, 558265710u, 559376756u, 1747010677u, 1651008801u);
 
layout(binding = 0, std430) buffer V
{
    uint v[];
} _23;
 
void main()
{
    uint cnt = gl_GlobalInvocationID.x * 2u;
    uint sum = 0u;
    uint l = _23.v[cnt];
    uint r = _23.v[cnt + 1u];
    for (int i = 1; i <= 40; i++)
    {
        l += ((((((~(r << uint(3))) & (r >> uint(5))) | ((r << uint(3)) & (~(r >> uint(5))))) ^ (~r)) & ((r << uint(3)) ^ (r >> uint(5)))) ^ ((~((~(sum + _80[sum & 4u])) | (~((r >> uint(3)) & (r << uint(2))))))));
        sum += 1932555628u;
        r += ((((((~(l << uint(3))) & (l >> uint(5))) | ((l << uint(3)) & (~(l >> uint(5))))) ^ (~l)) & ((l << uint(3)) ^ (l >> uint(5)))) ^ ((~((~(sum + _80[(sum >> uint(11)) & 4u])) | (~((l >> uint(3)) & (l << uint(2))))))));
    }
    _23.v[cnt] = l;
    _23.v[cnt + 1u] = r;
}

到了这步,xtea加密的特征就很明显了,我们写出对应的解密代码:

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
#include <stdint.h>
#include <stdio.h>
 
// 解密函数
void decrypt(uint32_t *v, uint32_t *k) {
  uint32_t v0 = v[0], v1 = v[1], sum = 0, i; /* set up */
  uint32_t delta = 1932555628u;              /* a key schedule constant */
  for (i = 0; i < 40; i++) {
    sum += delta;
  }
  for (i = 0; i < 40; i++) { /* basic cycle start */
    v1 -=
        ((((((~(v0 << 3u)) & (v0 >> 5u)) | ((v0 << 3u) & (~(v0 >> 5u)))) ^
           (~v0)) &
          ((v0 << 3u) ^ (v0 >> 5u))) ^
         ((~((~(sum + k[(sum >> 11u) & 4u])) | (~((v0 >> 3u) & (v0 << 2u)))))));
    sum -= delta;
    v0 -= ((((((~(v1 << 3u)) & (v1 >> 5u)) | ((v1 << 3u) & (~(v1 >> 5u)))) ^
             (~v1)) &
            ((v1 << 3u) ^ (v1 >> 5u))) ^
           ((~((~(sum + k[sum & 4u])) | (~((v1 >> 3u) & (v1 << 2u)))))));
  } /* end cycle */
  v[0] = v0;
  v[1] = v1;
}
 
int main() {
  uint32_t ida_chars[] = {0x185B72AF, 0X631D2C6,  0XDE8B33CC, 0X31EBCD9F,
                          0X5DB8B33,  0XA8D77D0,  0X865C6111, 0XBF032335,
                          0X722228A5, 0XAD833A57, 0XB7C3456F};
  uint32_t key[] = {1214346853u, 558265710u, 559376756u, 1747010677u,
                    1651008801u};
  decrypt(ida_chars, key);
  decrypt(ida_chars + 2, key);
  decrypt(ida_chars + 4, key);
  decrypt(ida_chars + 6, key);
  decrypt(ida_chars + 8, key);
  decrypt(ida_chars + 10, key);
  decrypt(ida_chars + 12, key);
  decrypt(ida_chars + 14, key);
  printf("%s", ida_chars);
  return 0;
}
// :8?eCTF{Go0Od!!!You_4re_Vu1k@N_Mast3r^^??

头尾有些问题。头可以确定是DubheCTF,根据flag规则与tea加密原理,尾部只有一位未知,即^^*},在本地用python的subprocess爆破一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import string
import subprocess
 
known_chars = "DubheCTF{Go0Od!!!You_4re_Vu1k@N_Mast3r^^"  # 已知的40位字符
 
for ch in string.printable:
    flag = known_chars + ch + "}"
    with subprocess.Popen(
        ["./ezVK.exe"], stdin=subprocess.PIPE, stdout=subprocess.PIPE
    ) as p:
        stdout, _ = p.communicate(input=flag.encode())
        if "You Are GENIUS!!!" in stdout.decode():
            print("flag found:", flag)
            exit(0)
# flag found: DubheCTF{Go0Od!!!You_4re_Vu1k@N_Mast3r^^_}

hitcon - revisual

js先上下面这个网站去一点混淆:https://deobfuscate.relative.im/
flag解密部分的代码如下,记该函数为get_flag:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function _0x52fd86(parm) {
    let salt = CryptoJS.enc.Hex.parse(
        CryptoJS.SHA256(parm).toString(CryptoJS.enc.Hex)
    ),
        iv_ = CryptoJS.enc.Hex.parse('fd3cb6c1be89457ba82919a33f02707c'),
        enc = CryptoJS.enc.Hex.parse(
            '4f6b9161b29e59e2d94fa90529d745601473cb4203c02d9549eea6e322908d71e0472241d86f3821b3c96dd82937b04dcef80b9f68b23dd2371d2a56ef873ce857563eefc6f9057aa0cc5b41ff87477256f6b56ef342da815099d1217d301d03b76e4fae675d27bf95ca43154015b964'
        ),
        flag = CryptoJS.AES.decrypt({ ciphertext: enc }, salt, {
            iv: iv_,
            padding: CryptoJS.pad.Pkcs7,
            mode: CryptoJS.mode.CBC,
            hasher: CryptoJS.algo.SHA256,
        })
    return flag.toString(CryptoJS.enc.Utf8)
}

现在我们要求的变成了这个函数的parm
与检查flag相关的顶点着色器代码如下,传入vec3(三个数构成的数组),前两个被用来确定顶点的位置,第三个作为一个可以在着色器间传递的变量,进入了片段着色器:

1
2
3
4
5
6
attribute vec3 position;
varying   float owO;
void main(void){   
    gl_Position = vec4(position.xy, 0.0, 1.0);
    owO = position.z;
}

片段着色器代码如下:

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
#ifdef GL_ES
precision highp float//设置为高精度浮点数
#endif
varying float owO;  //顶点着色器传入
#define OvO 255.0
#define Ovo 128.0
#define OVO 23.0
float OwO (float Owo, float OWO, float owO) {
    OWO = floor(OWO + 0.5);
    owO = floor(owO + 0.5);
    return mod(floor((floor(Owo) + 0.5) / exp2(OWO)), floor(1.0*exp2(owO - OWO) + 0.5));
}
vec4 oWo (float Ow0) {
    if (Ow0 == 0.0) return vec4(0.0);   
    float Owo = Ow0 > 0.0 ? 0.0 : 1.0;   
    Ow0 = abs(Ow0);  
    float OWO = floor(log2(Ow0));  
    float oWo = OWO + OvO - Ovo;
    OWO = ((Ow0 / exp2(OWO)) - 1.0) * pow(2.0, OVO);  
    float owO = oWo / 2.0
    oWo = fract(owO) + fract(owO); 
    float oWO = floor(owO);
    owO = OwO(OWO, 0.0, 8.0) / OvO;   
    Ow0 = OwO(OWO, 8.0, 16.0) / OvO;   
    OWO = (oWo * Ovo + OwO(OWO, 16.0, OVO)) / OvO;  
    Owo = (Owo * Ovo + oWO) / OvO; 
    return vec4(owO, Ow0, OWO, Owo);
}
void main(){
    gl_FragColor = oWo(owO); //RGBA

稍微去一下名称混淆如下:

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
#ifdef GL_ES
precision highp float//设置为高精度浮点数
#endif
varying float vert_in;  //顶点着色器传入,即owO
#define val_255 255.0
#define val_128 128.0
#define val_23 23.0
float func1 (float parm1, float parm2, float vert_in) {
    parm2 = floor(parm2 + 0.5);
    vert_in = floor(vert_in + 0.5);
    return mod(floor((floor(parm1) + 0.5) / exp2(parm2)), floor(1.0*exp2(vert_in - parm2) + 0.5));
}
vec4 func2 (float parm1) {
    if (parm1 == 0.0) return vec4(0.0);   
    float temp1 = parm1 > 0.0 ? 0.0 : 1.0;   
    parm1 = abs(parm1);  
    float temp2 = floor(log2(parm1));  
    float temp3 = temp2 + val_255 - val_128;
    temp2 = ((parm1 / exp2(temp2)) - 1.0) * pow(2.0, val_23);  
    float vert_in = temp3 / 2.0
    temp3 = fract(vert_in) + fract(vert_in); 
    float temp4 = floor(vert_in);
    vert_in = func1(temp2, 0.0, 8.0) / val_255;   
    parm1 = func1(temp2, 8.0, 16.0) / val_255;   
    temp2 = (temp3 * val_128 + func1(temp2, 16.0, val_23)) / val_255;  
    temp1 = (temp1 * val_128 + temp4) / val_255; 
    return vec4(vert_in, parm1, temp2, temp1);
}
void main(){
    gl_FragColor = func2(vert_in); //RGBA

后面就是根据功能恢复一下代码里的函数与变量名:

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
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
const _0x464c09 = (function () {
    let _0x451913 = true
    return function (_0x258426, _0x4b7b8d) {
        const _0x1bb18e = _0x451913
            ? function () {
                if (_0x4b7b8d) {
                    const _0x2d8886 = _0x4b7b8d.apply(_0x258426, arguments)
                    return (_0x4b7b8d = null), _0x2d8886
                }
            }
            : function () { }
        return (_0x451913 = false), _0x1bb18e
    }
})(),
    _0x4c494b = _0x464c09(this, function () {
        return _0x4c494b
            .toString()
            .search(_0x9bd70f.dnYkL)
            .toString()
            .constructor(_0x4c494b)
            .search(_0x9bd70f.dnYkL)
    })
_0x4c494b()
window.addEventListener('load', load)
var draw_canvas, calc_canvas
function load() {
    let draw_canvas_vert =
        '\n  attribute vec3 position;\n  uniform   mat4 mvpMatrix;\n  varying   vec2 vPosition;\n\n  void main(void){\n      gl_Position = mvpMatrix * vec4(position, 1.0);\n      vPosition = position.xy;\n  }\n  ',
        draw_canvas_frag =
            '\n#ifdef GL_ES\nprecision mediump float;\n#endif            \n\nuniform float u_time;\nuniform vec2 u_resolution;\nvarying vec2 vPosition;\n\nvec3 hash(vec2 seed){\n    vec3 p3 = fract(float(seed.x + seed.y*86.) * vec3(.1051, .1020, .0983));\n\tp3 += dot(p3, p3.yzx + 33.33);\n    return fract(p3);\n}\n\nvec3 layer(float scale, vec2 uv, float time){\n    // uv coord in cell\n    vec2 scaled_uv = uv * scale - 0.5;\n    vec2 uv0 = fract( scaled_uv ) - 0.5;\n    // cell id\n    vec2 cell_id = scaled_uv - fract(scaled_uv);\n    \n    \n    vec3 col = vec3(0);\n    float speed = 1.5;\n    // distance to a spinning random point in the cell (also surrounding cells)\n    vec3 seed = hash(cell_id);\n\n    float radiance = seed.x + time * seed.y;\n    vec2 center_of_star = vec2(sin(radiance), cos(radiance))*0.3;\n\n    // radial distort effect for star shine\n    vec2 v_to_star = uv0 - center_of_star;\n    float star_radiance = atan(v_to_star.x/v_to_star.y);\n    float star_spark_1 = sin(star_radiance*14.+radiance*6.);\n    float star_spark_2 = sin(star_radiance*8.-radiance*2.);\n    float stars = length(v_to_star) * (5.+star_spark_1+star_spark_2) * 0.03;\n    col += smoothstep(length(seed) * 0.01, 0., stars);\n    return col;\n}\nvoid main()\n{    // center global uv from -1 to 1\n    vec2 virtual_resolution = vec2(2.0, 2.0);\n    vec2 uv = (vPosition * 2. - virtual_resolution.xy) / virtual_resolution.y;\n    vec3 col = vec3(0.);//vColor.xyz;\n    \n    const float layer_count = 6.5;\n    for(float i = 0.0; i < layer_count; i+=1.){\n        float rotate_speed = u_time*0.4;\n        float scale = mod(i - rotate_speed, layer_count)*1.5;\n        vec2 offseted_uv = uv + vec2(sin(rotate_speed), cos(rotate_speed));\n        vec3 layer_col = layer(scale, offseted_uv, u_time + i*1.5);\n        \n        // we want the star to smoothly show up\n        float max_scale = layer_count * 1.5;\n        float color_amp = smoothstep(0., 1., smoothstep(max_scale, 0., scale));\n        col += layer_col * color_amp;\n    }\n    // blue background\n    col += vec3(0., 0., -0.15) * (uv.y - 0.7) * pow(length(uv), 0.5);\n    gl_FragColor = vec4(col, 1.);\n}\n  ',
        calc_canvas_vert =
            `\nattribute vec3 position;
            varying   float owO;\n  \n 
            void main(void){\n     
            gl_Position = vec4(position.xy, 0.0, 1.0);\n     
            owO = position.z;\n  }\n  `,
        calc_canvas_frag =
            `\n#ifdef GL_ES\n
            precision highp float;\n
            #endif            \n
            varying float owO;\n
            #define OvO 255.0\n
            #define Ovo 128.0\n
            #define OVO 23.0\n\n
            float OwO (float Owo, float OWO, float owO) {
             \n    OWO = floor(OWO + 0.5);
             owO = floor(owO + 0.5);
             \n    return mod(floor((floor(Owo) + 0.5) / exp2(OWO)), floor(1.0*exp2(owO - OWO) + 0.5));
              \n}\n
            vec4 oWo (float Ow0) {
            \n    if (Ow0 == 0.0) return vec4(0.0); \n   
            float Owo = Ow0 > 0.0 ? 0.0 : 1.0; \n   
            Ow0 = abs(Ow0); \n   
            float OWO = floor(log2(Ow0)); \n   
            float oWo = OWO + OvO - Ovo; \n   
            OWO = ((Ow0 / exp2(OWO)) - 1.0) * pow(2.0, OVO);\n   
            float owO = oWo / 2.0; \n   
            oWo = fract(owO) + fract(owO); \n   
            float oWO = floor(owO); \n   
            owO = OwO(OWO, 0.0, 8.0) / OvO; \n   
            Ow0 = OwO(OWO, 8.0, 16.0) / OvO; \n   
            OWO = (oWo * Ovo + OwO(OWO, 16.0, OVO)) / OvO; \n   
            Owo = (Owo * Ovo + oWO) / OvO; \n  
             return vec4(owO, Ow0, OWO, Owo); \n
             }\n\nvoid main()\n{
             \n    gl_FragColor = oWo(owO);\n
             }\n  `
    draw_canvas = new ShaderManager('canvas', 0, 0, draw_canvas_vert, draw_canvas_frag, true)
    draw_canvas.render()
    calc_canvas = new ShaderManager(
        'canvas-calc',
        650,
        650,
        calc_canvas_vert,
        calc_canvas_frag,
        false
    )
    calc_canvas.render()
    let pattern_container = document.getElementById('pattern-container'),
        lines = document.getElementById('lines'),
        latest_selected_dot = null,
        line = null,
        selected = false,
        selected_dot = []
    pattern_container.childNodes.forEach((dot) => {
        if (!dot.classList) {
            return
        }
        if (!dot.classList.contains('dot')) {
            return
        }
        dot.addEventListener('mousedown', (_0x5be741) => {
            selected_dot.forEach((dot) => {
                dot.classList.remove('selected')
                dot.classList.remove('select')
                dot.classList.remove('lose')
                dot.classList.remove('win')
            })
            lines.innerHTML = ''
            selected_dot = []
            selected = true
            dot.classList.add('select')
            line = document.createElementNS('http://www.w3.org/2000/svg', 'line')
            line.setAttribute(
                'x1',
                dot.offsetLeft + dot.offsetWidth / 2
            )
            line.setAttribute(
                'y1',
                dot.offsetTop + dot.offsetHeight / 2
            )
            line.setAttribute(
                'x2',
                dot.offsetLeft + dot.offsetWidth / 2
            )
            line.setAttribute(
                'y2',
                dot.offsetTop + dot.offsetHeight / 2
            )
            line.setAttribute('stroke', 'white')
            line.setAttribute('stroke-width', '5')
            lines.appendChild(line)  //画线,但是头尾是同一个点
            selected_dot.push(dot)
            latest_selected_dot = dot
        })
        dot.addEventListener('mouseover', (_0x1cae46) => {  //鼠标移入
            if (!selected) {
                return
            }
            if (dot.classList.contains('selected')) {
                return
            }
            if (dot.classList.contains('select')) {
                return
            }
            line &&
                (line.setAttribute(
                    'x2',
                    dot.offsetLeft + dot.offsetWidth / 2
                ),
                    line.setAttribute(
                        'y2',
                        dot.offsetTop + dot.offsetHeight / 2
                    ),
                    latest_selected_dot.classList.add('selected'),
                    latest_selected_dot.classList.remove('select'))  // 把线从上一个点连到当前点
            dot.classList.add('select')
            line = document.createElementNS('http://www.w3.org/2000/svg', 'line')
            line.setAttribute(
                'x1',
                dot.offsetLeft + dot.offsetWidth / 2
            )
            line.setAttribute(
                'y1',
                dot.offsetTop + dot.offsetHeight / 2
            )
            line.setAttribute(
                'x2',
                dot.offsetLeft + dot.offsetWidth / 2
            )
            line.setAttribute(
                'y2',
                dot.offsetTop + dot.offsetHeight / 2
            )
            line.setAttribute('stroke', 'white')
            line.setAttribute('stroke-width', '5')
            lines.appendChild(line)
            selected_dot.push(dot)
            latest_selected_dot = dot
        })
    })
    pattern_container.addEventListener('mousemove', (pos) => {
        if (!selected) {
            return
        }
        latest_selected_dot &&
            line &&
            (line.setAttribute(
                'x2',
                pos.clientX - pattern_container.getBoundingClientRect().left
            ),
                line.setAttribute(
                    'y2',
                    pos.clientY - pattern_container.getBoundingClientRect().top
                ))
    })  // 连线跟随鼠标
    pattern_container.addEventListener('mouseup', (_0x3584c1) => {  //鼠标松开
        if (latest_selected_dot && line) {
            line.setAttribute(
                'x2',
                latest_selected_dot.offsetLeft + latest_selected_dot.offsetWidth / 2
            )
            line.setAttribute(
                'y2',
                latest_selected_dot.offsetTop + latest_selected_dot.offsetHeight / 2
            )
            latest_selected_dot.classList.add('selected')
            latest_selected_dot = null
            let flag = check_flag(
                selected_dot.map((dot) => parseInt(dot.dataset.number)) // 传入选中的点的顺序
            )
            if (flag !== null) {
                selected_dot.forEach((dot) => {
                    dot.classList.add('win')
                })
                let flag_p = document.getElementById('flag')
                flag_p.innerText = flag
            } else {
                selected_dot.forEach((dot) => {
                    dot.classList.add('lose')
                })
            }
        }
        selected = false
    })
}
function abs(num) {
    return Math.abs(num)
}
const _0xdf21a4 = async (_0x5c1638) => {
    const _0x4f0cab = new TextEncoder().encode(_0x5c1638),
        _0x336245 = await window.crypto.subtle.digest(_0x37cc4d.tNPCS, _0x4f0cab),
        _0x587059 = Array.from(new Uint8Array(_0x336245)),
        _0x49519a = _0x587059
            .map((_0x29f99c) => _0x29f99c.toString(16).padStart(2, '0'))
            .join('')
    return _0x49519a
}
function check_flag(index_list) {
    let _0x526465 = calc_canvas.wtf(index_list[19], index_list[3], index_list[5]) * 25,
        _0x27d483 = calc_canvas.wtf(index_list[7], index_list[20], index_list[18]) * 25,
        _0x47edd7 = calc_canvas.wtf(index_list[11], index_list[22], index_list[18]) * 25,
        _0x3c8060 = calc_canvas.wtf(index_list[5], index_list[17], index_list[2]) * 25,
        _0x315313 = calc_canvas.wtf(index_list[20], index_list[13], index_list[5]) * 25,
        _0x3cef24 = calc_canvas.wtf(index_list[11], index_list[1], index_list[21]) * 25,
        _0x2ee445 = calc_canvas.wtf(index_list[8], index_list[11], index_list[1]) * 25,
        _0x5e280a = calc_canvas.wtf(index_list[9], index_list[5], index_list[4]) * 25,
        _0x5f6c26 = calc_canvas.wtf(index_list[17], index_list[9], index_list[21]) * 25,
        _0x13e7aa = calc_canvas.wtf(index_list[23], index_list[9], index_list[20]) * 25,
        _0x9d682e = calc_canvas.wtf(index_list[16], index_list[5], index_list[4]) * 25,
        _0x277f3c = calc_canvas.wtf(index_list[16], index_list[14], index_list[13]) * 25,
        _0x2f58be = calc_canvas.wtf(index_list[5], index_list[6], index_list[10]) * 25,
        _0x5a6698 = calc_canvas.wtf(index_list[2], index_list[11], index_list[5]) * 25,
        _0x52d3ed = calc_canvas.wtf(index_list[11], index_list[3], index_list[1]) * 25,
        _0x4320e6 = calc_canvas.wtf(index_list[12], index_list[3], index_list[10]) * 25,
        _0xf9ef4b = calc_canvas.wtf(index_list[14], index_list[1], index_list[9]) * 25,
        _0x429aaf = calc_canvas.wtf(index_list[18], index_list[11], index_list[17]) * 25,
        _0x1a4487 = calc_canvas.wtf(index_list[12], index_list[15], index_list[2]) * 25,
        _0x4c135d = calc_canvas.wtf(index_list[22], index_list[0], index_list[19]) * 25,
        _0x5c13fb = 0
    _0x5c13fb += abs(
        0.3837876686390533 - calc_canvas.gtfo(_0x3cef24, _0xf9ef4b, _0x5f6c26, 16, 21)
    )
    _0x5c13fb += abs(
        0.21054889940828397 - calc_canvas.gtfo(_0x52d3ed, _0x3cef24, _0x2ee445, 8, 2)
    )
    _0x5c13fb += abs(
        0.475323349112426 - calc_canvas.gtfo(_0x3cef24, _0x429aaf, _0x2f58be, 0, 20)
    )
    _0x5c13fb += abs(
        0.6338370887573964 - calc_canvas.gtfo(_0x3c8060, _0x27d483, _0x2f58be, 8, 4)
    )
    _0x5c13fb += abs(
        0.4111607928994082 - calc_canvas.gtfo(_0x47edd7, _0x52d3ed, _0x4320e6, 23, 1)
    )
    _0x5c13fb += abs(
        0.7707577751479291 - calc_canvas.gtfo(_0x429aaf, _0x3c8060, _0x277f3c, 20, 6)
    )
    _0x5c13fb += abs(
        0.7743081420118344 - calc_canvas.gtfo(_0x13e7aa, _0x5a6698, _0x3c8060, 9, 10)
    )
    _0x5c13fb += abs(
        0.36471487573964495 - calc_canvas.gtfo(_0x5f6c26, _0x526465, _0x315313, 18, 8)
    )
    _0x5c13fb += abs(
        0.312678449704142 - calc_canvas.gtfo(_0x4320e6, _0x13e7aa, _0x429aaf, 0, 17)
    )
    _0x5c13fb += abs(
        0.9502808165680473 - calc_canvas.gtfo(_0x1a4487, _0x13e7aa, _0x3c8060, 22, 10)
    )
    _0x5c13fb += abs(
        0.5869052899408282 - calc_canvas.gtfo(_0x2f58be, _0x5e280a, _0x47edd7, 14, 10)
    )
    _0x5c13fb += abs(
        0.9323389467455623 - calc_canvas.gtfo(_0x429aaf, _0x47edd7, _0x2f58be, 12, 7)
    )
    _0x5c13fb += abs(
        0.4587118106508875 - calc_canvas.gtfo(_0x2ee445, _0x5a6698, _0x47edd7, 4, 21)
    )
    _0x5c13fb += abs(
        0.14484472189349107 - calc_canvas.gtfo(_0x4320e6, _0x13e7aa, _0x52d3ed, 7, 15)
    )
    _0x5c13fb += abs(
        0.7255550059171598 - calc_canvas.gtfo(_0x3cef24, _0x429aaf, _0x1a4487, 9, 23)
    )
    _0x5c13fb += abs(
        0.5031261301775147 - calc_canvas.gtfo(_0x3c8060, _0x47edd7, _0x52d3ed, 7, 1)
    )
    _0x5c13fb += abs(
        0.1417352189349112 - calc_canvas.gtfo(_0x2ee445, _0x52d3ed, _0x5f6c26, 16, 14)
    )
    _0x5c13fb += abs(
        0.5579334437869822 - calc_canvas.gtfo(_0x52d3ed, _0x47edd7, _0x1a4487, 19, 11)
    )
    _0x5c13fb += abs(
        0.48502262721893485 -
        calc_canvas.gtfo(_0x9d682e, _0x315313, _0x5e280a, 23, 18)
    )
    _0x5c13fb += abs(
        0.5920916568047336 - calc_canvas.gtfo(_0x5e280a, _0x5f6c26, _0x27d483, 19, 6)
    )
    _0x5c13fb += abs(
        0.7222713017751479 - calc_canvas.gtfo(_0xf9ef4b, _0x47edd7, _0x315313, 8, 16)
    )
    _0x5c13fb += abs(
        0.12367382248520711 - calc_canvas.gtfo(_0x9d682e, _0x4320e6, _0x2f58be, 9, 5)
    )
    _0x5c13fb += abs(
        0.4558028402366864 - calc_canvas.gtfo(_0x277f3c, _0x9d682e, _0x47edd7, 10, 2)
    )
    _0x5c13fb += abs(
        0.8537692426035504 - calc_canvas.gtfo(_0x429aaf, _0x13e7aa, _0x5a6698, 4, 11)
    )
    _0x5c13fb += abs(
        0.9618170650887574 - calc_canvas.gtfo(_0x2f58be, _0x1a4487, _0x429aaf, 15, 2)
    )
    _0x5c13fb += abs(
        0.22088933727810647 - calc_canvas.gtfo(_0x526465, _0x5e280a, _0xf9ef4b, 10, 5)
    )
    _0x5c13fb += abs(
        0.4302783550295858 - calc_canvas.gtfo(_0xf9ef4b, _0x277f3c, _0x3cef24, 14, 2)
    )
    _0x5c13fb += abs(
        0.6262803313609467 - calc_canvas.gtfo(_0x4c135d, _0x52d3ed, _0x47edd7, 17, 22)
    )
    if (_0x5c13fb > 0.00001) {
        return null
    }
    s = ''
    s += Math.round(
        calc_canvas.wtf(index_list[4], index_list[2], index_list[22]) * 100000
    ).toString()
    s += Math.round(
        calc_canvas.wtf(index_list[17], index_list[9], index_list[14]) * 100000
    ).toString()
    s += Math.round(
        calc_canvas.wtf(index_list[4], index_list[13], index_list[7]) * 100000
    ).toString()
    s += Math.round(
        calc_canvas.wtf(index_list[4], index_list[20], index_list[23]) * 100000
    ).toString()
    s += Math.round(
        calc_canvas.wtf(index_list[5], index_list[7], index_list[12]) * 100000
    ).toString()
    s += Math.round(
        calc_canvas.wtf(index_list[20], index_list[19], index_list[4]) * 100000
    ).toString()
    s += Math.round(
        calc_canvas.wtf(index_list[17], index_list[6], index_list[19]) * 100000
    ).toString()
    s += Math.round(
        calc_canvas.wtf(index_list[6], index_list[21], index_list[18]) * 100000
    ).toString()
    s += Math.round(
        calc_canvas.wtf(index_list[4], index_list[3], index_list[8]) * 100000
    ).toString()
    s += Math.round(
        calc_canvas.wtf(index_list[11], index_list[7], index_list[14]) * 100000
    ).toString()
    s += Math.round(
        calc_canvas.wtf(index_list[9], index_list[2], index_list[13]) * 100000
    ).toString()
    s += Math.round(
        calc_canvas.wtf(index_list[22], index_list[10], index_list[3]) * 100000
    ).toString()
    s += Math.round(
        calc_canvas.wtf(index_list[15], index_list[22], index_list[13]) * 100000
    ).toString()
    s += Math.round(
        calc_canvas.wtf(index_list[16], index_list[12], index_list[9]) * 100000
    ).toString()
    s += Math.round(
        calc_canvas.wtf(index_list[14], index_list[8], index_list[17]) * 100000
    ).toString()
    s += Math.round(
        calc_canvas.wtf(index_list[1], index_list[18], index_list[6]) * 100000
    ).toString()
    s += Math.round(
        calc_canvas.wtf(index_list[10], index_list[11], index_list[3]) * 100000
    ).toString()
    s += Math.round(
        calc_canvas.wtf(index_list[8], index_list[12], index_list[5]) * 100000
    ).toString()
    s += Math.round(
        calc_canvas.wtf(index_list[1], index_list[3], index_list[12]) * 100000
    ).toString()
    s += Math.round(
        calc_canvas.wtf(index_list[9], index_list[13], index_list[7]) * 100000
    ).toString()
    s += Math.round(
        calc_canvas.gtfo(
            index_list[22],
            index_list[13],
            index_list[5],
            index_list[4],
            index_list[7]
        ) * 100000
    ).toString()
    s += Math.round(
        calc_canvas.gtfo(
            index_list[10],
            index_list[14],
            index_list[17],
            index_list[23],
            index_list[11]
        ) * 100000
    ).toString()
    s += Math.round(
        calc_canvas.gtfo(
            index_list[23],
            index_list[20],
            index_list[6],
            index_list[1],
            index_list[3]
        ) * 100000
    ).toString()
    s += Math.round(
        calc_canvas.gtfo(
            index_list[15],
            index_list[12],
            index_list[2],
            index_list[13],
            index_list[9]
        ) * 100000
    ).toString()
    s += Math.round(
        calc_canvas.gtfo(
            index_list[16],
            index_list[20],
            index_list[6],
            index_list[5],
            index_list[18]
        ) * 100000
    ).toString()
    s += Math.round(
        calc_canvas.gtfo(
            index_list[3],
            index_list[6],
            index_list[7],
            index_list[8],
            index_list[23]
        ) * 100000
    ).toString()
    s += Math.round(
        calc_canvas.gtfo(
            index_list[21],
            index_list[9],
            index_list[10],
            index_list[3],
            index_list[22]
        ) * 100000
    ).toString()
    s += Math.round(
        calc_canvas.gtfo(
            index_list[14],
            index_list[6],
            index_list[15],
            index_list[12],
            index_list[19]
        ) * 100000
    ).toString()
    s += Math.round(
        calc_canvas.gtfo(
            index_list[13],
            index_list[19],
            index_list[22],
            index_list[23],
            index_list[1]
        ) * 100000
    ).toString()
    s += Math.round(
        calc_canvas.gtfo(
            index_list[21],
            index_list[2],
            index_list[9],
            index_list[0],
            index_list[19]
        ) * 100000
    ).toString()
    s += Math.round(
        calc_canvas.gtfo(
            index_list[5],
            index_list[19],
            index_list[21],
            index_list[14],
            index_list[6]
        ) * 100000
    ).toString()
    s += Math.round(
        calc_canvas.gtfo(
            index_list[16],
            index_list[15],
            index_list[20],
            index_list[13],
            index_list[3]
        ) * 100000
    ).toString()
    s += Math.round(
        calc_canvas.gtfo(
            index_list[20],
            index_list[15],
            index_list[10],
            index_list[21],
            index_list[6]
        ) * 100000
    ).toString()
    s += Math.round(
        calc_canvas.gtfo(
            index_list[7],
            index_list[1],
            index_list[21],
            index_list[20],
            index_list[3]
        ) * 100000
    ).toString()
    s += Math.round(
        calc_canvas.gtfo(
            index_list[9],
            index_list[20],
            index_list[1],
            index_list[10],
            index_list[6]
        ) * 100000
    ).toString()
    s += Math.round(
        calc_canvas.gtfo(
            index_list[10],
            index_list[2],
            index_list[1],
            index_list[16],
            index_list[4]
        ) * 100000
    ).toString()
    s += Math.round(
        calc_canvas.gtfo(
            index_list[15],
            index_list[5],
            index_list[20],
            index_list[19],
            index_list[8]
        ) * 100000
    ).toString()
    s += Math.round(
        calc_canvas.gtfo(
            index_list[20],
            index_list[8],
            index_list[21],
            index_list[10],
            index_list[12]
        ) * 100000
    ).toString()
    s += Math.round(
        calc_canvas.gtfo(
            index_list[19],
            index_list[5],
            index_list[4],
            index_list[2],
            index_list[22]
        ) * 100000
    ).toString()
    s += Math.round(
        calc_canvas.gtfo(
            index_list[10],
            index_list[20],
            index_list[14],
            index_list[9],
            index_list[7]
        ) * 100000
    ).toString()
    let flag = get_flag(s)
    return (
        (s += Math.round(
            calc_canvas.gtfo(
                index_list[5],
                index_list[15],
                index_list[9],
                index_list[13],
                index_list[16]
            ) * 100000
        ).toString()),
        (s += Math.round(
            calc_canvas.gtfo(
                index_list[20],
                index_list[8],
                index_list[11],
                index_list[22],
                index_list[23]
            ) * 100000
        ).toString()),
        (s += Math.round(
            calc_canvas.gtfo(
                index_list[22],
                index_list[3],
                index_list[1],
                index_list[17],
                index_list[15]
            ) * 100000
        ).toString()),
        (s += Math.round(
            calc_canvas.gtfo(
                index_list[4],
                index_list[8],
                index_list[14],
                index_list[3],
                index_list[17]
            ) * 100000
        ).toString()),
        (s += Math.round(
            calc_canvas.gtfo(
                index_list[12],
                index_list[6],
                index_list[11],
                index_list[10],
                index_list[15]
            ) * 100000
        ).toString()),
        (s += Math.round(
            calc_canvas.gtfo(
                index_list[13],
                index_list[5],
                index_list[2],
                index_list[4],
                index_list[9]
            ) * 100000
        ).toString()),
        (s += Math.round(
            calc_canvas.gtfo(
                index_list[21],
                index_list[12],
                index_list[19],
                index_list[11],
                index_list[20]
            ) * 100000
        ).toString()),
        (s += Math.round(
            calc_canvas.gtfo(
                index_list[13],
                index_list[11],
                index_list[18],
                index_list[12],
                index_list[20]
            ) * 100000
        ).toString()),
        (s += Math.round(
            calc_canvas.gtfo(
                index_list[11],
                index_list[2],
                index_list[8],
                index_list[3],
                index_list[16]
            ) * 100000
        ).toString()),
        (s += Math.round(
            calc_canvas.gtfo(
                index_list[16],
                index_list[1],
                index_list[5],
                index_list[4],
                index_list[22]
            ) * 100000
        ).toString()),
        (s += Math.round(
            calc_canvas.gtfo(
                index_list[0],
                index_list[3],
                index_list[12],
                index_list[10],
                index_list[1]
            ) * 100000
        ).toString()),
        (s += Math.round(
            calc_canvas.gtfo(
                index_list[19],
                index_list[22],
                index_list[17],
                index_list[14],
                index_list[13]
            ) * 100000
        ).toString()),
        (s += Math.round(
            calc_canvas.gtfo(
                index_list[14],
                index_list[2],
                index_list[10],
                index_list[18],
                index_list[16]
            ) * 100000
        ).toString()),
        (s += Math.round(
            calc_canvas.gtfo(
                index_list[21],
                index_list[0],
                index_list[18],
                index_list[19],
                index_list[4]
            ) * 100000
        ).toString()),
        (s += Math.round(
            calc_canvas.gtfo(
                index_list[22],
                index_list[12],
                index_list[9],
                index_list[16],
                index_list[17]
            ) * 100000
        ).toString()),
        (s += Math.round(
            calc_canvas.gtfo(
                index_list[4],
                index_list[18],
                index_list[15],
                index_list[0],
                index_list[14]
            ) * 100000
        ).toString()),
        (s += Math.round(
            calc_canvas.gtfo(
                index_list[9],
                index_list[5],
                index_list[19],
                index_list[20],
                index_list[12]
            ) * 100000
        ).toString()),
        (s += Math.round(
            calc_canvas.gtfo(
                index_list[10],
                index_list[6],
                index_list[20],
                index_list[11],
                index_list[5]
            ) * 100000
        ).toString()),
        (s += Math.round(
            calc_canvas.gtfo(
                index_list[1],
                index_list[11],
                index_list[22],
                index_list[13],
                index_list[9]
            ) * 100000
        ).toString()),
        (s += Math.round(
            calc_canvas.gtfo(
                index_list[1],
                index_list[19],
                index_list[10],
                index_list[0],
                index_list[18]
            ) * 100000
        ).toString()),
        flag
    )
}
function get_flag(parm) {
    let salt = CryptoJS.enc.Hex.parse(
        CryptoJS.SHA256(parm).toString(CryptoJS.enc.Hex)
    ),
        iv_ = CryptoJS.enc.Hex.parse('fd3cb6c1be89457ba82919a33f02707c'),
        enc = CryptoJS.enc.Hex.parse(
            '4f6b9161b29e59e2d94fa90529d745601473cb4203c02d9549eea6e322908d71e0472241d86f3821b3c96dd82937b04dcef80b9f68b23dd2371d2a56ef873ce857563eefc6f9057aa0cc5b41ff87477256f6b56ef342da815099d1217d301d03b76e4fae675d27bf95ca43154015b964'
        ),
        flag = CryptoJS.AES.decrypt({ ciphertext: enc }, salt, {
            iv: iv_,
            padding: CryptoJS.pad.Pkcs7,
            mode: CryptoJS.mode.CBC,
            hasher: CryptoJS.algo.SHA256,
        })
    return flag.toString(CryptoJS.enc.Utf8)
}
class ShaderManager {
    constructor(
        canvas_id,
        _width,
        _height,
        vert,
        frag,
        render_now
    ) {
        this.canvas = document.getElementById(canvas_id)
        _width != 0 && _height != 0
            ? ((this.canvas.width = _width), (this.canvas.height = _height))
            : ((this.canvas.width = window.innerWidth),
                (this.canvas.height = window.innerHeight))
        this.w = this.canvas.width
        this.h = this.canvas.height
        this.d = [
            4, 20, 23, 13, 11, 0, 15, 1, 14, 21, 9, 19, 8, 3, 17, 24, 16, 6, 22, 10,
            7, 18, 2, 5, 12,
        ]
        this.timeLoad = performance.now()
        this.gl = this.canvas.getContext('webgl2')
        this.gl.getExtension('EXT_color_buffer_float')
        this.v_shader = this.create_shader(vert, 'OuO')
        this.f_shader = this.create_shader(frag, '>w<')
        this.prg = this.create_program(this.v_shader, this.f_shader)
        let _this = this
        function _0x52ad9c() {
            _this.render()
            _this.animationFrameRequest = window.requestAnimationFrame(_0x52ad9c)
        }
        return render_now && _0x52ad9c(), this
    }
    ['wtf'](parm1, parm2, parm3) {
        this.gl.clearColor(0, 0, 0, 1)
        this.gl.clearDepth(1)
        this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT)
        const _0x4b856b = this.gl.getAttribLocation(this.prg, 'position'),
            position_array = [
                -1,
                -1,
                ((parm1 % 1) + this.d[~~parm1]) / 25,
                -1,
                1,
                ((parm2 % 1) + this.d[~~parm2]) / 25,
                1,
                1,
                ((parm2 % 1) + this.d[~~parm2]) / 25,
                -1,
                -1,
                ((parm1 % 1) + this.d[~~parm1]) / 25,
                1,
                1,
                ((parm2 % 1) + this.d[~~parm2]) / 25,
                1,
                -1,
                ((parm1 % 1) + this.d[~~parm1]) / 25,
            ],
            _0x3e2e26 = this.create_vbo(position_array)
        this.gl.bindBuffer(this.gl.ARRAY_BUFFER, _0x3e2e26)
        this.gl.enableVertexAttribArray(_0x4b856b)
        this.gl.vertexAttribPointer(_0x4b856b, 3, this.gl.FLOAT, false, 0, 0)
        this.gl.useProgram(this.prg)
        this.gl.drawArrays(this.gl.TRIANGLES, 0, 6)
        this.gl.flush()
        const _0x2fa9a7 = new Uint8Array(4)
        this.gl.readPixels(
            this.w / 2,
            (((parm3 % 1) + this.d[~~parm3]) * this.h) / 25,
            1,
            1,
            this.gl.RGBA,
            this.gl.UNSIGNED_BYTE,
            _0x2fa9a7
        )
        let _0x511406 = new Float32Array(_0x2fa9a7.buffer)
        return _0x511406[0].toFixed(15)
    }
    ['gtfo'](parm1, parm2, parm3, parm4, parm5) {
        this.gl.clearColor(0, 0, 0, 1)
        this.gl.clearDepth(1)
        this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT)
        const _0x16760a = this.gl.getAttribLocation(this.prg, 'position'),
            _0x13e5e0 = [
                -1,
                -1,
                ((parm1 % 1) + this.d[~~parm1]) / 25,
                3,
                -1,
                ((parm2 % 1) + this.d[~~parm2]) / 25,
                -1,
                3,
                ((parm3 % 1) + this.d[~~parm3]) / 25,
            ],
            _0x49be08 = this.create_vbo(_0x13e5e0)
        this.gl.bindBuffer(this.gl.ARRAY_BUFFER, _0x49be08)
        this.gl.enableVertexAttribArray(_0x16760a)
        this.gl.vertexAttribPointer(_0x16760a, 3, this.gl.FLOAT, false, 0, 0)
        this.gl.useProgram(this.prg)
        this.gl.drawArrays(this.gl.TRIANGLES, 0, 3)
        this.gl.flush()
        const _0x3da8ae = new Uint8Array(4)
        this.gl.readPixels(
            (((parm4 % 1) + this.d[~~parm4]) * this.w) / 25,
            (((parm5 % 1) + this.d[~~parm5]) * this.h) / 25,
            1,
            1,
            this.gl.RGBA,
            this.gl.UNSIGNED_BYTE,
            _0x3da8ae
        )
        let _0x2e76ac = new Float32Array(_0x3da8ae.buffer)
        return _0x2e76ac[0].toFixed(15)
    }
    ['render']() {
        this.gl.clearColor(0, 0, 0, 1)
        this.gl.clearDepth(1)
        this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT)
        let _0x39f658 = performance.now()
        this.timeDelta = (_0x39f658 - this.timePrev) / 1000
        this.timePrev = _0x39f658
        const _0x18111e = new Array(2)
        _0x18111e[0] = this.gl.getAttribLocation(this.prg, 'position')
        const _0x15ddf2 = new Array(2)
        _0x15ddf2[0] = 3
        _0x15ddf2[1] = 4
        const _0x2626af = [
            3, 8, 0, 7, -3, 5, 3, -8, 0, 3, 8, 0, 7, -3, 5, 7, 3, 5, 3, 8, 0, -3,
            -8, 0, 3, -8, 0, 3, 8, 0, -3, -8, 0, -3, 8, 0, -3, 8, 0, -7, -3, 5, -3,
            -8, 0, -3, 8, 0, -7, -3, 5, -7, 3, 5,
        ],
            _0x1ebfe3 = this.create_vbo(_0x2626af)
        this.gl.bindBuffer(this.gl.ARRAY_BUFFER, _0x1ebfe3)
        this.gl.enableVertexAttribArray(_0x18111e[0])
        this.gl.vertexAttribPointer(
            _0x18111e[0],
            _0x15ddf2[0],
            this.gl.FLOAT,
            false,
            0,
            0
        )
        const _0x570653 = new matIV(),
            _0x5a4b24 = _0x570653.identity(_0x570653.create()),
            _0x5f594b = _0x570653.identity(_0x570653.create()),
            _0x15df66 = _0x570653.identity(_0x570653.create()),
            _0x28d80f = _0x570653.identity(_0x570653.create()),
            _0xf54bc7 = (_0x39f658 - this.timeLoad) / 1000,
            _0x404188 = [
                Math.sin(Math.sin(_0xf54bc7) / 3),
                Math.cos(Math.sin(_0xf54bc7) / 3),
                0,
            ]
        _0x570653.lookAt([0, 0, 5], [0, 0, 0], _0x404188, _0x5f594b)
        _0x570653.perspective(
            90,
            this.canvas.width / this.canvas.height,
            0.1,
            100,
            _0x15df66
        )
        _0x570653.multiply(_0x15df66, _0x5f594b, _0x28d80f)
        _0x570653.multiply(_0x28d80f, _0x5a4b24, _0x28d80f)
        const _0x4d0a27 = this.gl.getUniformLocation(this.prg, 'mvpMatrix')
        this.gl.uniformMatrix4fv(_0x4d0a27, false, _0x28d80f)
        const _0x504e76 = this.gl.getUniformLocation(this.prg, 'u_time')
        this.gl.uniform1f(_0x504e76, _0xf54bc7)
        const _0x15e050 = this.gl.getUniformLocation(this.prg, 'u_resolution')
        this.gl.uniform2f(_0x15e050, this.canvas.width, this.canvas.height)
        this.gl.useProgram(this.prg)
        this.gl.drawArrays(this.gl.TRIANGLES, 0, 18)
        this.gl.flush()
    }
    ['create_shader'](_0x217f95, _0x50b1bf) {
        let _0x333f8e
        switch (_0x50b1bf) {
            case 'OuO':
                _0x333f8e = this.gl.createShader(this.gl.VERTEX_SHADER)
                break
            case '>w<':
                _0x333f8e = this.gl.createShader(this.gl.FRAGMENT_SHADER)
                break
            default:
                return
        }
        this.gl.shaderSource(_0x333f8e, _0x217f95)
        this.gl.compileShader(_0x333f8e)
        if (this.gl.getShaderParameter(_0x333f8e, this.gl.COMPILE_STATUS)) {
            return _0x333f8e
        } else {
            alert(this.gl.getShaderInfoLog(_0x333f8e))
        }
    }
    ['create_program'](_0x4d159e, _0x135a44) {
        const _0x171a80 = this.gl.createProgram()
        this.gl.attachShader(_0x171a80, _0x4d159e)
        this.gl.attachShader(_0x171a80, _0x135a44)
        this.gl.linkProgram(_0x171a80)
        if (this.gl.getProgramParameter(_0x171a80, this.gl.LINK_STATUS)) {
            return this.gl.useProgram(_0x171a80), _0x171a80
        } else {
            alert(this.gl.getProgramInfoLog(_0x171a80))
        }
    }
    ['create_vbo'](_0xbcae1c) {
        const _0x9500e2 = this.gl.createBuffer()
        return (
            this.gl.bindBuffer(this.gl.ARRAY_BUFFER, _0x9500e2),
            this.gl.bufferData(
                this.gl.ARRAY_BUFFER,
                new Float32Array(_0xbcae1c),
                this.gl.STATIC_DRAW
            ),
            this.gl.bindBuffer(this.gl.ARRAY_BUFFER, null),
            _0x9500e2
        )
    }
}

本来的打算是使用z3,对输入进行规约,但是存在两个难点:

  • 着色器中存在log与exp,SMT对这种非线性运算的求解效果不好
  • 片段着色器为顶点所连起来的图形上色,但是由于存在颜色渐变等情况,像素点对应的颜色需要对于片段着色器理解深刻才能最终确定

赛后参考其它队伍的做法,发现能通过重放爆破的方式,把输入逐渐爆破出来,感觉这题有点过于偏技巧了
以下均来自r3kapig的wp


1
2
3
_0x5c13fb += abs(
        0.21054889940828397 - calc_canvas.gtfo(_0x52d3ed, _0x3cef24, _0x2ee445, 8, 2)
    )

只涉及到输入里的5个变量(input[0xb], input[0x3], input[0x1], input[0x15], input[0x8]),可以考虑爆破出来

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
function test() {
    let eps = 0.00000035714285714285714285714285714286;
    let now_dif = 10000000000;
    let MAX = 25;
    let table = [];
    for(i = 0;i < MAX;i++) {
        table[i] = [];
        for(j = 0; j < MAX;j++) {
            table[i][j] = [];
            if(i != j) for(k = 0;k < MAX;k++) {
                table[i][j][k] = webglHandler2.wtf(i, j, k) * 0x19;
            }
        }
    }
    // 先对wtf的结果进行打表
     
    // for(i = 0;i < MAX;i++) {
    //     for(j = 0; j < MAX;j++) {
    //         console.log(i, j);
    //         if(i != j) for(k = 0;k < MAX;k++) {
    //             if(k != i && k != j) for(l = 0;l < MAX;l++) {
    //                 if(l != i && l != j && l != k) for(m = 0;m < MAX;m++) {
    //                     if(m != i && m != j && m != k && m != l) {
    //                         tmp = Math.abs(0.21054889940828397 - webglHandler2.gtfo(table[i][j][k], table[i][k][l], table[m][i][k], 0x8, 0x2));
    //                         if(tmp < now_dif) {
    //                             console.log("input[0xb], input[0x3], input[0x1], input[0x15], input[0x8]", i, j, k, l, m, now_dif);
    //                             now_dif = tmp;
    //                         }
    //                         if(tmp < eps) {
    //                             console.log("input[0xb], input[0x3], input[0x1], input[0x15], input[0x8]", i, j, k, l, m);
    //                         }
    //                     }
    //                 }
    //             }
    //         }
    //     }
    // }
    // input[0xb], input[0x3], input[0x1], input[0x15], input[0x8]
    // 6 15 9 1 23
 
    // for(i = 0;i < MAX;i++) {
    //     for(j = 0; j < MAX;j++) {
    //         if(i != j) {
    //             tmp = Math.abs(0.1417352189349112 - webglHandler2.gtfo(table[23][6][9], table[6][15][9], table[i][j][1], 0x10, 0xe));
    //             if(tmp < now_dif) {
    //                 now_dif = tmp;
    //                 console.log("input[0x11], input[0x9]", i, j, now_dif);
    //             }
    //             if(tmp < eps) {
    //                 console.log("input[0x11], input[0x9]", i, j);
    //             }
    //         }
    //     }
    // }
    // input[0x11], input[0x9] 13 5
 
    // for(i = 0;i < MAX;i++) {
    //     console.log("i", i);
    //     for(j = 0; j < MAX;j++) {
    //         if(i != j) {
    //             for(k = 0;k < MAX;k++) {
    //                 if(k != i && k != j){
    //                     tmp = Math.abs(0.4302783550295858 - webglHandler2.gtfo(table[j][9][5], table[i][j][k], table[6][9][1], 0xe, 0x2));
    //                     if(tmp < now_dif) {
    //                         now_dif = tmp;
    //                         console.log("input[0x10], input[0xe], input[0xd]", i, j, k, now_dif);
    //                     }
    //                     if(tmp < eps) {
    //                         console.log("input[0x10], input[0xe], input[0xd]", i, j, k);
    //                     }
    //                 }
    //             }
    //         }
    //     }
    // }
    // input[0x10], input[0xe], input[0xd] 2 12 14
 
    // for(i = 0;i < MAX;i++) {
    //     console.log("i", i);
    //     for(j = 0; j < MAX;j++) {
    //         if(i != j) {
    //             for(k = 0;k < MAX;k++) {
    //                 if(k != i && k != j){
    //                     tmp = Math.abs(0.7707577751479291 - webglHandler2.gtfo(table[i][6][13], table[j][13][k], table[2][12][14], 0x14, 0x6));
    //                     if(tmp < now_dif) {
    //                         now_dif = tmp;
    //                         console.log("input[0x12], input[0x5], input[0x2]", i, j, k, now_dif);
    //                     }
    //                     if(tmp < eps) {
    //                         console.log("input[0x12], input[0x5], input[0x2]", i, j, k);
    //                     }
    //                 }
    //             }
    //         }
    //     }
    // }
    // input[0x12], input[0x5], input[0x2] 16 18 8
 
    // for(i = 0;i < MAX;i++) {
    //     console.log("i", i);
    //     for(j = 0; j < MAX;j++) {
    //         if(i != j) {
    //             for(k = 0;k < MAX;k++) {
    //                 if(k != i && k != j){
    //                     tmp = Math.abs(0.4111607928994082 - webglHandler2.gtfo(table[6][i][16], table[6][15][9], table[j][15][k], 0x17, 0x1));
    //                     if(tmp < now_dif) {
    //                         now_dif = tmp;
    //                         console.log("input[0x16], input[0xc], input[0xa]", i, j, k, now_dif);
    //                     }
    //                     if(tmp < eps) {
    //                         console.log("input[0x16], input[0xc], input[0xa]", i, j, k);
    //                     }
    //                 }
    //             }
    //         }
    //     }
    // }
    // input[0x16], input[0xc], input[0xa] 21 24 0
     
    // for(i = 0;i < MAX;i++) {
    //     console.log("i", i);
    //     for(j = 0; j < MAX;j++) {
    //         if(i != j) {
    //             for(k = 0;k < MAX;k++) {
    //                 if(k != i && k != j){
    //                     tmp = Math.abs(0.6338370887573964 - webglHandler2.gtfo(table[18][13][8], table[i][j][16], table[18][k][0], 0x8, 0x4));
    //                     if(tmp < now_dif) {
    //                         now_dif = tmp;
    //                         console.log("input[0x7], input[0x14], input[0x6]", i, j, k, now_dif);
    //                     }
    //                     if(tmp < eps) {
    //                         console.log("input[0x7], input[0x14], input[0x6]", i, j, k);
    //                     }
    //                 }
    //             }
    //         }
    //     }
    // }
    // input[0x7], input[0x14], input[0x6] 10 7 17
     
    // for(i = 0;i < MAX;i++) {
    //     for(j = 0; j < MAX;j++) {
    //         if(i != j) {
    //             tmp = Math.abs(0.6262803313609467 - webglHandler2.gtfo(table[21][i][j], table[6][15][9], table[6][21][16], 0x11, 0x16));
    //             if(tmp < now_dif) {
    //                 now_dif = tmp;
    //                 console.log("input[0x0], input[0x13]", i, j, now_dif);
    //             }
    //             if(tmp < eps) {
    //                 console.log("input[0x0], input[0x13]", i, j);
    //             }
    //         }
    //     }
    // }
    // input[0x0], input[0x13] 19 4
 
    // for(i = 0;i < MAX;i++) {
    //     for(j = 0; j < MAX;j++) {
    //         if(i != j) {
    //             tmp = Math.abs(0.9502808165680473 - webglHandler2.gtfo(table[24][i][8], table[j][5][7], table[18][13][8], 0x16, 0xa));
    //             if(tmp < now_dif) {
    //                 now_dif = tmp;
    //                 console.log("input[0xf], input[0x17]", i, j, now_dif);
    //             }
    //             if(tmp < eps) {
    //                 console.log("input[0xf], input[0x17]", i, j);
    //             }
    //         }
    //     }
    // }
    // input[0xf], input[0x17] 11 22
 
    // for(i = 0;i < MAX;i++) {
    //     tmp = Math.abs(0.5869052899408282 - webglHandler2.gtfo(table[18][17][0], table[5][18][i], table[6][21][16], 0xe, 0xa));
    //     if(tmp < now_dif) {
    //         now_dif = tmp;
    //         console.log("input[0x4]", i, now_dif);
    //     }
    //     if(tmp < eps) {
    //         console.log("input[0x4]", i);
    //     }
    // }
    // input[0x4] 3
    return;
     
    // input[0xb], input[0x3], input[0x1], input[0x15], input[0x8] 6 15 9 1 23
    // input[0x11], input[0x9] 13 5
    // input[0x10], input[0xe], input[0xd] 2 12 14
    // input[0x12], input[0x5], input[0x2] 16 18 8
    // input[0x16], input[0xc], input[0xa] 21 24 0
    // input[0x7], input[0x14], input[0x6] 10 7 17
    // input[0x0], input[0x13] 19 4
    // input[0xf], input[0x17] 11 22
    // input[0x4] 3
 
}
 
function check(input) {
    // test();
    ...

后续进行结果的组装
input = [19, 9, 8, 15, 3, 18, 17, 10, 23, 5, 0, 6, 24, 14, 12, 11, 2, 13, 16, 4, 7, 1, 21, 22, 20];
手动执行check,即可拿到flag
参考链接

上传的附件 revisual-8acf90164f9aed0ce5e4018b3e9ea66a203022e5.tar.gz
_media_file_task_cc971aa3-b8c6-4d8f-8793-38048cdb1bf8.zip

最新回复:
yixinBC 2024-07-29 12:59:58
视频版参见:https://www.bilibili.com/video/BV1Pb421J7nG