一
着色器介绍
着色器是一种运行在图形处理单元(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/
看雪ID:yixinBC
https://bbs.kanxue.com/user-home-966257.htm
*本文为看雪论坛优秀文章,由 yixinBC 原创,转载请注明来自看雪社区
# 往期推荐
2、DASCTF 2024赛题解析:Reverse-BabyAndroid
3、告别RegisterNatives获取JNI函数绑定的地址,迎接最底层的方式获取(3个案例)
5、corCTF 2024:位运算虚拟机及gpu hash爆破
球分享
球点赞
球在看
点击阅读原文查看更多