兽音译者

7,630 阅读4分钟

简介

兽音加密为对称加密
明文为字符串的Unicode码 秘钥为字符位置索引
得到密文后简单的变形再去查表得到具体的字符
通常表设置为['嗷', '呜', '啊', '~']这四个字符
简单的说你只需要~!@# 再 $%^& 再 *()_ 就得到结果了


加密过程

  • 原字符串→ 原神启动
    • 查Unicode→ u539f\u795e\u542f\u52a8
    • 转数组→ [539f, 795e, 542f, 52a8]
      • 可以用字符串存储也可以用数字存储
    • 第一次拆→ [{5,3,9,f}, {7,9,5,e}, {5,4,2,f}, {5,2,a,8}]
      • 可以按字符串位置拆也可以用位运算得到4个位置上的数字
    • 扁平化→ [5, 3, 9, f, 7, 9, 5, e, 5, 4, 2, f, 5, 2, a, 8]
    • 加上索引→ [5+0, 3+1, 9+2, f+3, 7+4, 9+5, 5+6, e+7, 5+8, 4+9, 2+a, f+b, 5+c, 2+d, a+e, 8+f]
      • 示例中使用16进制数据
    • 计算结果→ [5, 4, b, 12, b, e, b, 15, d, d, c, 1a, 11, f, 18, 17]
    • 去掉溢出→ [5, 4, b, 2, b, e, b, 5, d, d, c, a, 1, f, 8, 7]
    • 第二次拆→ [{1,1}, {1,0}, {2,3}, {0,2}, {2,3}, {3,2}, {2,3}, {1,1}, {3,1}, {3,1}, {3,0}, {2,2}, {0,1}, {3,3}, {2,0}, {1,3}]
      • 将1个16进制数拆成2个4进制数
    • 扁平化→ 11102302233223113131302201332013
    • 加上头尾→ \310111023022332231131313022013320132
      • 开头固定310 结尾固定2
    • 查表→ ~呜嗷呜呜呜嗷啊~嗷啊啊~~啊啊~呜呜~呜~呜~嗷啊啊嗷呜~~啊嗷呜~啊
      • 一般用['嗷', '呜', '啊', '~']作为字典 将上面数字映射为字典里对应的字符
  • 最终结果→ ~呜嗷呜呜呜嗷啊~嗷啊啊~~啊啊~呜呜~呜~呜~嗷啊啊嗷呜~~啊嗷呜~啊

解密过程

  • 原字符串→ ~呜嗷呜呜呜嗷啊~嗷啊啊~~啊啊~呜呜~呜~呜~嗷啊啊嗷呜~~啊嗷呜~啊
    • 拆分字符→ ['~呜嗷', '呜呜呜嗷啊~嗷啊啊~~啊啊~呜呜~呜~呜~嗷啊啊嗷呜~~啊嗷呜~', '啊']
      • 把原字符串索引为0、1、2和最后一位单独拆出来
    • 取字典→ ['嗷呜啊~', '呜呜呜嗷啊~嗷啊啊~~啊啊~呜呜~呜~呜~嗷啊啊嗷呜~~啊嗷呜~']
      • 根据规则前三位实际上是字典表中的310 最后一位是2 重新排个序直观一点
      • 并非所有人用默认表加密 取表只需固定取前三位和最后一位 就能知道原来是什么了
    • 字典映射→ 11102302233223113131302201332013
      • 把除字典以外的字符通过字典映射为字典索引数字
    • 两两一组→ [{1,1}, {1,0}, {2,3}, {0,2}, {2,3}, {3,2}, {2,3}, {1,1}, {3,1}, {3,1}, {3,0}, {2,2}, {0,1}, {3,3}, {2,0}, {1,3}]
      • 由于加密特性长度一定是偶数 放心大胆的拆
    • 合并→ [5, 4, b, 2, b, e, b, 5, d, d, c, a, 1, f, 8, 7]
      • 第一个数字是高位 第二个数字是低位 新数=高位x进制+低位
    • 减去索引→ [5-0, 4-1, b-2, 2-3, b-4, e-5, b-6, 5-7, d-8, d-9, c-a, a-b, 1-c, f-d, 8-e, 7-f]
    • 计算结果→ [5, 3, 9, f, 7, 9, 5, -2, 5, 4, 2, -1, -b, 2, -6, -8]
    • 去掉溢出→ [5, 3, 9, f, 7, 9, 5, e, 5, 4, 2, f, 5, 2, a, 8]
      • 计算机存储负数原理不多赘述
    • 四个一组→ [{5,3,9,f}, {7,9,5,e}, {5,4,2,f}, {5,2,a,8}]
    • 合并→ [539f, 795e, 542f, 52a8]
    • Unicode→ \u539f\u795e\u542f\u52a8
    • 原文→ 原神启动
  • 最终结果→ 原神启动

代码实现

// 默认加密字典
const defaultMap = ['嗷', '呜', '啊', '~']
// 加密
const enc = (data, map = defaultMap) => {
  const nums = [...data]
    // 获取unicode码
    .map(value => value.codePointAt(0))
    // 拆分为4位
    .map(num => [(num >> 12) & 0xf, (num >> 8) & 0xf, (num >> 4) & 0xf, (num >> 0) & 0xf])
    .flatMap(i => i)
    // 变形一下
    .map((num, index) => index + num)
    // 拆分为2位
    .map(num => [((num & 0xf) >> 2) & 0x3, ((num & 0xf) >> 0) & 0x3])
    .flatMap(i => i)
    
  return [3, 1, 0, ...nums, 2]
    // 取字典
    .map(index => map[index])
    // 拼接
    .join('')
}

// 解密
const dec = (data) => {
  const map = {
    [data.at(0)]: 3,
    [data.at(1)]: 1,
    [data.at(2)]: 0,
    [data.at(-1)]: 2
  }
  return [...data.slice(3, -1)]
    // 还原
    .map(value => map[value])
    // 两个一组
    .reduce((acc, cur, index) => {
      const i = index % 2
      i === 0 && acc.push([])
      acc.at(-1)[i] = cur
      return acc
    }, [])
    // 合并为16进制
    .map(([a, b]) => (parseInt(a) << 2) + parseInt(b))
    // 变形
    .map((num, index) => (num - index) & 0xf)
    // 四个一组
    .reduce((acc, cur, index) => {
      const i = index % 4
      i === 0 && acc.push([])
      acc.at(-1)[i] = cur
      return acc
    }, [])
    // 合并
    .map(([a, b, c, d]) => (a << 12) + (b << 8) + (c << 4) + (d << 0))
    // 还原
    .map(value => String.fromCodePoint(value))
    // 拼接
    .join('')
    
}