长亭百川云 - 文章详情

逆向中的GL与着色器逆向

看雪学苑

64

2024-08-08

一  

  

着色器介绍

着色器是一种运行在图形处理单元(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(https://github.com/yixinBC/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函数中。

分析代码,着色器在程序的resource里,可以使用resource hacker等工具提取出来。发现是二进制格式的,需要反编译。根据我们前置介绍里讲的,Vulkan平台通常使用SPIR-V作为中间表示,那么现在我们需要找一个SPIR-V的反编译器。经过一番搜索,发现百度只能找到spirv-dis,是反编译成字节码,逆向难度仍然较大。GitHub能找到spirv-cross,直接把我们提取出来的中间表示还原成GLSL:

#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加密的特征就很明显了,我们写出对应的解密代码:

#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爆破一下。

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:

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(三个数构成的数组),前两个被用来确定顶点的位置,第三个作为一个可以在着色器间传递的变量,进入了片段着色器:

attribute vec3 position;  
varying   float owO;  
void main(void){      
    gl\_Position = vec4(position.xy, 0.0, 1.0);  
    owO = position.z;  
}

片段着色器代码如下:

#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  
}

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

#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  
}

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

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。


\_0x5c13fb += abs(  
        0.21054889940828397 - calc\_canvas.gtfo(\_0x52d3ed, \_0x3cef24, \_0x2ee445, 8, 2)  
    )

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

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。

参考链接

https://learnopengl-cn.github.io/01%20Getting%20started/04%20Hello%20Triangle/

https://r3kapig-not1on.notion.site/HITCON-CTF-2024-Quals-Writeup-by-RePokemonedCollections-69217608fb3a4a71b7882469f8b0ad30?pvs=97#5178437c29284342b4f7a54c28b48e3b

看雪ID:yixinBC

https://bbs.kanxue.com/user-home-966257.htm

*本文为看雪论坛优秀文章,由 yixinBC 原创,转载请注明来自看雪社区



# 往期推荐

1、记录一次秀动APP的逆向

2、DASCTF 2024赛题解析:Reverse-BabyAndroid

3、告别RegisterNatives获取JNI函数绑定的地址,迎接最底层的方式获取(3个案例)

4、内核漏洞学习记录(CVE-2021-22555)

5、corCTF 2024:位运算虚拟机及gpu hash爆破

球分享

球点赞

球在看

点击阅读原文查看更多

相关推荐
关注或联系我们
添加百川云公众号,移动管理云安全产品
咨询热线:
4000-327-707
百川公众号
百川公众号
百川云客服
百川云客服

Copyright ©2024 北京长亭科技有限公司
icon
京ICP备 2024055124号-2