兽音译者:一次算法解析与LUA脚本移植记录

前段时间花了几天时间研究了一下兽音译者的算法,成功把它翻译成了lua脚本,记录一下学习的过程。

这个就很有意思,虽然网上现在有很多在线解密的工具,不过我还是想自己搞明白它的算法。于是在Github上找到了兽音译者(兽语翻译)SDK这个仓库,里面有Golang, Python, JavaScript, C#四种语言的算法SDK。

理解算法

我拿着仓库到处嚎大佬,终于找到了破空大佬愿意帮我研究py版本的SDK。

破空花了十五分钟看懂算法


然后花了一个小时给我讲清楚

……淦,我好菜!

总之,在破空老师的辛苦教学下,我终于啃完了py脚本,并且给每一行都加上了注释,有兴趣的可以去我fork的仓库里看看。


简单地说,兽语的加/解密各有两步:

加密

  1. 将原文字符串中的每个字符变成16进制Unicode(不含0x),然后堆在一起

    • 每个字符转换为16进制 Unicode后会变成2-4位,比如的16进制Unicode是4f60
    • 当转换后不足4位的时候,就在前面补上0使其成为4位,如a61,那就补2个0变成0061
  2. 将Unicode堆在一起的字符串毎一位进行二次加密,并转换为2个兽语,然后堆在一起输出,完成加密

    1. 首先需要一个变量n,n是字符的次序-1,比如Unicode堆中的第1个字符的n1-1=0
    2. 对 Unicode堆中的每个字符x视作16进制数字进行计算:
    3. x+(n/16的余数),然后得到个二次加密的16进制数k,如果这个数≥16,就-16使其落在0-16
    4. 对k分别做两次计算,输出两个兽语
    5. 兽语1:k除以4
    6. 兽语2:k除以4余数
    7. 得到加密完成的兽语后,在字符串的最开头加上加密字典的第4 2 1个字符,在字符串的末尾加上加密字典的第3个。比如字典{"嗷,"鸣,"啊,"~"},那就是~鸣嗷+兽语+

解密

解密就是将以上的加密过程反过来。

  1. 把兽语转换成16进制Unicode编码堆
    1. 先将兽语去掉开头结尾的字典,然后每两个字符分隔开,取出来单独算
      1. (兽语I×4)+兽语II)算出原来加密时的k
      2. 上一步得到的数减去之前二步加密时加上的n/16的余数,这里因为是两个兽语相加,所以要减去2n,最后得到k
      3. 如果k<0,就+16,使k最终落在0-16范围内
    2. 第一步得到的k就是Unicode码的其中一位了,接下来,把每一组两个的兽语都进行这样的处理,就可以得到原始字符串的16进制Unicode串
  2. 将Unicode串转换为原始字符串
    1. 将字符串每4个一组拆分
    2. 将每一组4个字符的16进制Unicode转换为字符串
    3. 将字符串组合,至此解密完成

LUA移植

为了能让QQ机器人比较方便地实现兽语的加密和解密,我花了两天时间把它翻译成了LUA脚本。不过这个算法其实还不完善,比如换行符并没有做适配,比如没有自定义字典的功能,但是我实在肝不动了,如果有哪位大佬有兴趣欢迎提交pr

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
-- 加密第一步,字符转16进制unicode的4位数字
function str2hex(str)
local ret = ""
for p, c in utf8.codes(str) do
local charHexStr = string.sub(string.format("%#x", c), 3, -1)
if string.len(charHexStr) == 3 then
charHexStr = "0" .. charHexStr
elseif string.len(charHexStr) == 2 then
charHexStr = "00" .. charHexStr
end
ret = ret .. charHexStr
end
return ret
end

-- 加密第二步,对字符串进行二次加密
function OwO(str)
-- 兽语
beast = {"嗷", "呜", "啊", "~"}
local hex = str2hex(str)
local code = ""
local n = 0
for p, c in utf8.codes(hex) do -- p是序号,c是内容
-- print("c:"..c..",n:"..n)
local k = tonumber(tostring(utf8.char(c)), 16) + math.fmod(n, 16)
if k >= 16 then
k = k - 16
end
q = math.floor(k / 4) + 1
p = math.fmod(k, 4) + 1
-- print("q="..q..",p="..p)
code = code .. beast[q] .. beast[p]
n = n + 1
---print("k="..k.." code="..code)
end
return (beast[4] .. beast[2] .. beast[1] .. code .. beast[3])
end

print(OwO("多米诺好强啊123"))

-- 解密第一步,将兽语转换为16进制unicode字符串
function human(str)
-- 兽语(没有思路怎么截取中文字符串来自定义词典)
beast = {
["嗷"] = "1",
["呜"] = "2",
["啊"] = "3",
["~"] = "4"
}
local aouwa = string.sub(str, 8, -4)
ret = ""
t = 0
i = 0
for p, c in utf8.codes(aouwa) do
if i == 0 then
pos1 = beast[utf8.char(c)] -1
i = 1
t = t + 1
else
pos2 = beast[utf8.char(c)] -1
i = 0
t = t + 1
code = (pos1 * 4 + pos2) - math.fmod((t-2) / 2, 16)
if code < 0 then
code = code + 16
end
code = string.format("%#x", math.floor(code))
if code == "0" then
ret = ret .. code
else
ret = ret .. string.sub(code, 3, 3)
end
end

end
return (ret)
end

-- 解密第二步,将unicode字符串恢复为文本
function hex2str(str)
hexArray = human(str)
local ret = ""
local i = 1
while (i <= string.len(hexArray)) do
charStr = utf8.char(tonumber(string.sub(hexArray, i, i + 3), 16))
ret = ret .. charStr
i = i + 4
end
return (ret)
end


print(OwO("多米诺好强啊123"))
print(hex2str("~呜嗷呜呜啊啊嗷~~呜啊~嗷呜~呜啊啊嗷嗷呜嗷啊呜呜呜嗷呜呜啊呜呜~嗷呜呜嗷嗷呜呜~呜啊呜啊啊啊啊嗷呜啊嗷啊呜~呜~嗷~嗷~呜嗷呜嗷呜嗷嗷嗷呜呜呜呜啊啊"))

兽音译者:一次算法解析与LUA脚本移植记录

https://blog.dominoh.com/d7f21d67.html

作者

多米诺

发布于

2022-01-02

更新于

2024-05-17

许可协议

评论