李恒道 发表于 2023-5-28 22:02

超星考试pos参数破译

本帖最后由 李恒道 于 2023-5-28 22:25 编辑

FBI WARNING
这里的超星考试指本人的原创单机游戏【超级星星V.666】单词大考验关卡
并非通俗意义超星
请勿联想
感谢

感谢@涛之雨 大佬在我失业之后为我积极推荐工作,经过大佬的内推成功进入了美团送外卖
正文
点击考试预览可以追到AST部分这里可以看到是ob混淆+字符串转义

我们先解决字符串转义部分
直接利用babel的最小化就可以将字符串转义压缩成正常字符串const parser = require("@babel/parser");
const generator = require("@babel/generator").default;
const code_js = fs.readFileSync("./chaoxing_exam.js", {
encoding: "utf-8",
});
const ast = parser.parse(code_js)
let code = generator(ast, {
minified: true,
jsescOption: { minimal: true },
}).code;
fs.writeFile("./convert1.js", code, (err) => {});
然后查看一下结果

然后解字符串,这里是_0x56bc
直接抬手一个转义,因为没有多层,所以只处理_0x56bc即可
老规矩,复制_0x56bc到nodejs作为解密函数
首先测试一下,发现卡死了,多半是正则爆破,搜索Reg找了
      _0x4337b4["prototype"]["RvZSqA"] = function () {
      var _0x403f16 = new RegExp(this["MTxJBI"] + this["FMdsHI"]);
      var _0x55e9af = _0x403f16["test"](this["ZxBleI"]["toString"]())
          ? --this["pNCgln"]
          : --this["pNCgln"];
      return this["AfmIcA"](_0x55e9af);
      };
      _0x4337b4["prototype"]["AfmIcA"] = function (_0x172353) {
      if (!Boolean(~_0x172353)) {
          return _0x172353;
      }
      return this["ebdBfN"](this["GRlANK"]);
      };
      _0x4337b4["prototype"]["ebdBfN"] = function (_0xa7fe72) {
      for (
          var _0x5f3bbf = 0, _0x1ed88a = this["pNCgln"]["length"];
          _0x5f3bbf < _0x1ed88a;
          _0x5f3bbf++
      ) {
          this["pNCgln"]["push"](Math["round"](Math["random"]()));
          _0x1ed88a = this["pNCgln"]["length"];
      }
      return _0xa7fe72(this["pNCgln"]);
      };
应该是正则下面的三元运算符在捣乱,我们跑一下发现是false,所以直接强制改成true内的--this["pNCgln"]
   _0x4337b4["prototype"]["RvZSqA"] = function () {
      var _0x403f16 = new RegExp(this["MTxJBI"] + this["FMdsHI"]);
      var _0x55e9af = --this["pNCgln"]
      return this["AfmIcA"](_0x55e9af);
      };
在运行发现还是存在内存爆破
向上找到了字符串混淆内存在分支污染和内存爆破

直接将第一个设为false,第二个设为true
再运行crp函数

解密成功
然后直接开始解字符串混淆
const fuck_js = fs.readFileSync("./convert2.js", {
encoding: "utf-8",
});
const ast = parser.parse(fuck_js);
traverse(ast, {
CallExpression(path) {
    if (path.node.callee.name === "_0x56bc") {
      let loc1 = path.node.arguments.value;
      let loc2 = path.node.arguments?.value;
      let str_node = types.stringLiteral(_0x56bc(loc1, loc2));
      path.replaceInline(str_node);
    }
},
});
let code = generator(ast, {
minified: true,
jsescOption: { minimal: true },
}).code;
fs.writeFile("./convert3.js", code, (err) => {});
已经具备基本的可读了

接下来就是硬干了
我们目标比较简单

分析这些数据吧
value像是鼠标的数据,我们先看value,确实是鼠标的相对页面键值
    var scrollLeft =
      document["documentElement"]["scrollLeft"] ||
      document["body"]["scrollLeft"],
      scrollTop =
      document["documentElement"]["scrollTop"] ||
      document["body"]["scrollTop"];
    (mouseMap["x"] =
      winEvent["pageX"] || winEvent["clientX"] + scrollLeft),
      (mouseMap["y"] =
      winEvent["pageY"] || winEvent["clientY"] + scrollTop),
    var value =
      "(" +
      Math["ceil"](mouseMap["x"]) +
      "|" +
      Math["ceil"](mouseMap["y"]) +
      ")";
接下来看qid,取自页面的questionId数据
_0x210627 = document["getElementById"]("questionId"),
(qid = _0x210627["value"])
看rd,随机数
var rd = Math["random"](),
接下来只剩enc和_edt了,先看enc
里面是一个闭包
看大概应该是一个转义字符代码,懒得搞了,直接抽离

function getEncData(posData, userId, questionId, randomNum, rd) {
let userIdAndQuestionIdAndRandom =
    userId + "_" + questionId + "|" + randomNum;
if (
    null == userIdAndQuestionIdAndRandom ||
    userIdAndQuestionIdAndRandom["length"] <= 0
)
    return null;
for (
    var _0x469e62 = "", _0x388a1f = 0;
    _0x388a1f < userIdAndQuestionIdAndRandom["length"];
    _0x388a1f++
)
    _0x469e62 +=
      userIdAndQuestionIdAndRandom["charCodeAt"](_0x388a1f)["toString"]();
var _0x210627 = Math["floor"](_0x469e62["length"] / 5),
    _0x215858 = parseInt(
      _0x469e62["charAt"](_0x210627) +
      _0x469e62["charAt"](2 * _0x210627) +
      _0x469e62["charAt"](3 * _0x210627) +
      _0x469e62["charAt"](4 * _0x210627)
    ),
    _0x408603 = Math["ceil"](userIdAndQuestionIdAndRandom["length"] / 2),
    _0x3ed445 = Math["pow"](2, 31) - 1;
if (_0x215858 < 2) return null;
var rd = rd,
    _0x58d995 = Math["round"](1000000000 * rd) % 100000000;
10 < (_0x469e62 += _0x58d995)["length"] &&
    (_0x469e62 = parseInt(_0x469e62["substring"](0, 10))["toString"]()),
    (_0x469e62 = (_0x215858 * _0x469e62 + _0x408603) % _0x3ed445);
var _0x3c1f52 = "",
    _0x435dce = "";
for (_0x388a1f = 0; _0x388a1f < posData["length"]; _0x388a1f++)
    (_0x435dce +=
      (_0x3c1f52 = parseInt(
      posData["charCodeAt"](_0x388a1f) ^
          Math["floor"]((_0x469e62 / _0x3ed445) * 255)
      )) < 16
      ? "0" + _0x3c1f52["toString"](16)
      : _0x3c1f52["toString"](16)),
      (_0x469e62 = (_0x215858 * _0x469e62 + _0x408603) % _0x3ed445);
for (_0x58d995 = _0x58d995["toString"](16); _0x58d995["length"] < 8; )
    _0x58d995 = "0" + _0x58d995;
return (_0x435dce += _0x58d995);
}
console.log(
'getEncData("(1890|22)", userId, questionId, 265, 0.7836752714778081)',
getEncData("(1890|22)", 用户id, 试卷id, 265, 0.7836752714778081)
);

官方数据

抽离代码

代码生成成功,然后看最后的_edt,来源是      "&_edt=" +
(_0x408603 + randomNumA),其中randomNumA是一个生成的随机数
function _0xe7b063() {
    if ("ySLJF" !== "ySLJF") {
      (function () {
      return ![];
      })
      ["constructor"]("debu" + "gger")
      ["apply"]("stateObject");
    } else {
      return Math["floor"](10 * Math["random"]());
    }
}
而另外一个603的变量则是时间
_0x408603 = "" + new Date()["getTime"](),
至此全部破译完毕
结语
撒花










Rangon 发表于 2023-5-28 22:16

Stubborn6 发表于 2023-5-28 22:25

Asra 发表于 2023-5-28 23:01

xskn 发表于 2023-5-29 01:18

zhanghaollpp 发表于 2023-5-29 03:25

FCGkitty 发表于 2023-5-29 08:17

看到混淆加密头就疼,头皮发麻

1c3z 发表于 2023-5-29 08:42

LuckyClover 发表于 2023-5-29 08:50

参数破译,进来学习一下

Yangzaipython 发表于 2023-5-29 09:01

页: [1] 2 3 4
查看完整版本: 超星考试pos参数破译