逆向中的GL与着色器逆向
2024-8-7 17:49:37 Author: mp.weixin.qq.com(查看原文) 阅读量:5 收藏


着色器介绍

着色器是一种运行在图形处理单元(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爆破

球分享

球点赞

球在看

点击阅读原文查看更多


文章来源: https://mp.weixin.qq.com/s?__biz=MjM5NTc2MDYxMw==&mid=2458566271&idx=1&sn=d836b7fe83c592bd66018f5f8a87394b&chksm=b18d8ef586fa07e31609d0538c01a6a8249e415c51047cca73d133664749785f7adab2217f8e&scene=58&subscene=0#rd
如有侵权请联系:admin#unsafe.sh