0%

古神语(Uiua)之研究

在NBCTF2023遇到了很奇怪的一坨 题目给了一个图片文件和一个.ua文件 那时候一直没搞懂.ua是什么 最近在DC群组遇到了出题人 他提示:

[uiua.org]: uiua.org “Uiua (wee-wuh 🔉) is a general purpose, stack-based, array-oriented programming language”

从此开启了 古神语之旅 一种栈机语言的研究(实际上才研究了两天) 视该语言后续在CTF中遇到的频率或者发现了什么独特的用途而更新该研究

Uiua

简介

如官网所说 这是一种基于Rust的栈机语言 但是同时也是一种功能十分强大的面向数组语言(对数组的处理能力极强且完备) 同时还内置对音频和视频的生成和处理 经过这两天的研究我发现其实用性其实非常强 只是因为使用了各种奇怪的字符让人觉得不是很正经 但是其实所有的符号都有对应的英文指令

基本特性

第一个不得不提的 构式 特性就是对不分行(该语言对格式敏感)的一条语句解释器的阅读顺序是由右到左 对多条语句的是由上至下

由于其基于栈 变量名之类的实际上不重要 但是也提供了定义变量名与相关值的相关操作 定义的变量不会储存于栈中(? 用于打印当前栈的信息):

image-20240305212820170

提供了一种类似Wasm 同时又比Wasm便捷的编程体验

同时由于所有的运算都基于栈 作为操作指令参量的栈中数据在参与完运算后如果没有特殊操作都会直接销毁(并不pop到哪些变量中 当然也可以添加pop操作)

例如:

image-20240305214009805

从这里也可以看出运算的顺序是op(value-second-top-to-stack) with value-top-to-stack

如上面所说 这是一个面向数组的语言 几乎所有功能和数组都密切相关 而Uiua提供了类似numpy库中的矩阵操作用以处理数组形成的矩阵

对于三维矩阵若其第1, 2维的长度均超过20且第3维为3或4就可以生成对应的RGB通道或RGB-Alpha通道图片 而音频 我没有研究

应用实例

[NBCTF2023/rev/wee-woo]

题目给出的古神语:

1
2
3
4
5
6
Flag ← "nbctf{redacted}"
Flag ← utf Flag
÷ⁿ:⌈η;⍢(⌊÷⁅ⁿ⌈ηπ:+◿⁅ⁿ⌈ηπ,:)(≠-×⁅ηπτ):-×⁅ηπτ⋅∘⍥(+,:)⋅∘⍥(+,:)⌊τ..⌊η-×⁅ηπτ;⍢(: ↯:⍉:↯⌊η:↯⊟.⌈√,:⧻. ☇-×⁅ηπτ↻×⁅π,◫+⁅-⌊ητ,↻-⁅π,◿ⁿ:⌈η;⍢(⌊÷⁅ⁿ⌈ηπ:+◿⁅ⁿ⌈ηπ,:)(≠-×⁅ηπτ):-×⁅ηπτ⋅∘⍥(+,:)⋅∘⍥(+,:)⌊τ..⌊η-×⁅ηπτ-:ⁿ:⌈η;⍢(⌊÷⁅ⁿ⌈ηπ:+◿⁅ⁿ⌈ηπ,:)(≠-×⁅ηπτ):-×⁅ηπτ⋅∘⍥(+,:)⋅∘⍥(+,:)⌊τ..⌊η-×⁅ηπτ:-⌊η)(>-×⁅ηπτ)⁅ₙ⋅∘⍥(+,:)⌈π.⌊η×η⧻.Flag
↯ 300_300_3
⧻ Flag
# 49

具体含义如下(根据官网自带的编译器以及符号含义得出 其实看着非常复杂的代码有很大一部分是混淆 即用代数运算结果来代替一些数值):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
⍢(
:-1
:-×⁅ηπτ⋅∘⍥(+,:)⋅∘⍥(+,:)⌊τ..⌊η-×⁅ηπτ
⍢(⌊÷⁅ⁿ⌈ηπ:+◿⁅ⁿ⌈ηπ,:)(≠-×⁅ηπτ)
:-×⁅ηπτ⋅∘⍥(+,:)⋅∘⍥(+,:)⌊τ..⌊η-×⁅ηπτ-:ⁿ:⌈η◌ # 256 - Flag
⍢(⌊÷⁅ⁿ⌈ηπ:+◿⁅ⁿ⌈ηπ,:)(≠-×⁅ηπτ)
↻-⁅π,◿ⁿ:⌈η◌ # 右移3 - n行 n = (2, 1, 0)
◫+⁅-⌊ητ, # 构造窗口 列数为7 - n
-×⁅ηπτ↻×⁅π, # 上移 3n 行
☇ # 铺平为1维
⊟.⌈√,:⧻. # 长度入栈交换和矩阵的位置 压入shape = [., ceil(√len)]
:↯ # 改变矩阵形状为shape 取尽后未填满从头再取
:↯⌊η
:⍉ # 矩阵转置
:↯ # 取原来的矩阵大小摊平为1维
)(>0)
:-×⁅ηπτ⋅∘⍥(+,:)⋅∘⍥(+,:)⌊τ..⌊η-×⁅ηπτ
⍢(
⌊÷⁅ⁿ⌈ηπ:+◿⁅ⁿ⌈ηπ,:
)(≠-×⁅ηπτ)
÷ⁿ:⌈η◌
↯ 300_300_3

根据以上的分析 题目的古神语并没有对Flag进行任何特殊加密 只是单纯的将256与每个值相减然后进过一系列矩阵变化打乱顺序再构成RGB三通道来生成图片 同时最后输出了Flag的实际长度49

那么我们可以先处理一段长49 且处理完后的值为[0~48]的顺序列表来当作下标标记处理后的值本来应该存放在flag的什么位置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
[256 255 254 253 252 251 250 249 248 247 246 245 244 243 242 241 240 239 238 237 236 235 234 233 232 231 230 229 228 227 226 225 224 223 222 221 220 219 218 217 216 215 214 213 212 211 210 209 208]
3
⍢(
:-1
:-×⁅ηπτ⋅∘⍥(+,:)⋅∘⍥(+,:)⌊τ..⌊η-×⁅ηπτ
⍢(⌊÷⁅ⁿ⌈ηπ:+◿⁅ⁿ⌈ηπ,:)(≠-×⁅ηπτ)
:-×⁅ηπτ⋅∘⍥(+,:)⋅∘⍥(+,:)⌊τ..⌊η-×⁅ηπτ-:ⁿ:⌈η◌ # 256 - Flag
⍢(⌊÷⁅ⁿ⌈ηπ:+◿⁅ⁿ⌈ηπ,:)(≠-×⁅ηπτ)
↻-⁅π,◿ⁿ:⌈η◌ # 右移3 - n行 n = (2, 1, 0)
◫+⁅-⌊ητ, # 构造窗口 列数为7 - n
-×⁅ηπτ↻×⁅π, # 上移 3n 行
☇ # 铺平为1维
⊟.⌈√,:⧻. # 长度入栈交换和矩阵的位置 压入shape = [., ceil(√len)]
:↯ # 改变矩阵形状为shape 取尽后未填满从头再取
:↯⌊η
:⍉ # 矩阵转置
:↯ # 取原来的矩阵大小摊平为1维
)(>0)
:-×⁅ηπτ⋅∘⍥(+,:)⋅∘⍥(+,:)⌊τ..⌊η-×⁅ηπτ
⍢(
⌊÷⁅ⁿ⌈ηπ:+◿⁅ⁿ⌈ηπ,:
)(≠-×⁅ηπτ)
÷ⁿ:⌈η◌
↯ 300_300_3
☇ 0 . # 转化回原来1维方便处理

结果太长不展示

然后利用题目给的图片解密:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from PIL import Image
import numpy as np

# key = []
# for i in range(49):
# key.append(256 - i)
# print(str(key).replace(", ", " " ))

ind = [...]
ind = [ind[3 * i:3 * i + 3] for i in range(len(ind) // 3)]
flag = np.array(Image.open("flag.png"))
weiht, height = flag.shape[0], flag.shape[1]
F = [257] * 49
for i in range(weiht):
for j in range(height):
for k in range(3):
F[ind[300 * i + j][k]] = flag[i][j][k]
if 257 not in F:
F = [chr(255 - i) for i in F]
print("".join(F))
exit(0)
# nbctf{TBH_1_h4t3d_m4kin6_7h1s_ch4ll_6d1a32fe482b}

后记

这几天的古神语研究真的让我产生了一种”这是一种非常安全且功能强大的语言 如果能在某些语言中内联安全性一定很高”的感觉 而且官方目前依然保持高频率更新 只能说希望以后真的能实现古神语保护吧