猿人学修仙之旅——js混淆,源码乱码
js混淆——源码乱码之m参数
前言
本题使用了一定的AI辅助,在AI当道的世俗下,快来和我一起在猿人学修仙吧!
网站链接
https://match.yuanrenxue.cn/
抓包
进来之后会遇到第一个敌人,setInterval!

直接never pause here

也是直接看到了接口

找参数,反混淆
如图并且如题所示,本题的加密就是m

那么好办了,大脑飞速运转,如果我全局搜索m,那叫m的也太多了,并且不一定能找到。聪明的你想到这题是乱码题。好啊好啊,让我看看乱在哪里。如图,咱们看到了花指令,十六进制等等。诶!这个eval里的怎么这么长?机智的你很快想到,密文不会就藏在这里吧。

可是这个也太恶心了,这不就是被多重转义的巨型字符串吗!但是这可难不倒咱们的大佬,机智的你很快就又想到了,诶?!!!AST!!!
const parse = require("@babel/parser").parse;const generate = require("@babel/generator").default;const traverse = require("@babel/traverse").default;const types = require("@babel/types");const fs = require('fs');var code = 'window[\'\\x75\\x72\\x6c\'] = \'\\x2f\\x61\\x70\\x69\\x2f\' + \'\\x6d\\x61\\x74\\x63\\x68\' + \'\\x2f\\x31\', request = function () {\n var _0x2268f9 = Date[\'\\x70\\x61\\x72\\x73\\x65\'](new Date()) + (16798545 + -72936737 + 156138192), 'let js_code = codelet ast = parse(js_code);//通用的十六进制转十进制插件const transform_literal = { NumericLiteral({node}) { // 检查 node.extra 是否存在 // 并且使用正则 /^0[obx]/i 检查原始文本 (raw) 是否以 0x, 0b 或 0o 开头 if (node.extra && /^0[obx]/i.test(node.extra.raw)) { // 当你把 extra 删掉,Babel 生成器由于找不到“原始样子”,就会被迫按照 node.value 输出最干净的十进制。 node.extra = undefined; // console.log(node.value) } }, StringLiteral({node}) { // 检查 node.extra 是否存在 // 并使用正则 /\\[ux]/gi 检查原始文本中是否有反斜杠加 u (Unicode) 或 x (Hex) if (node.extra && /\\[ux]/gi.test(node.extra.raw)) { node.extra = undefined; // console.log(node.value) } },}traverse(ast, transform_literal)//十六进制转十进制,本插件为蔡老板公众号提供//https://mp.weixin.qq.com/s/PGn2Wqz4S5fjHPW6fLX0Wgconst visitor = { // 识别形如 "a" + "b" 的表达式 BinaryExpression(path) { const { left, operator, right } = path.node; // 1. 只处理加法运算符 if (operator != '+') return; // 2. 判断左右两边是否都是字符串字面量 // 注意:如果是 'a' + 'b' + 'c',AST 结构是 (('a'+'b') + 'c') // 所以这个插件会通过递归或多次遍历把它们全合并 if (types.isStringLiteral(left) && types.isStringLiteral(right)) { const newValue = left.value + right.value; // 3. 用新的字符串节点替换掉原来的加法节点 path.replaceWith(types.stringLiteral(newValue)); // 4. 【关键】由于合并后可能产生新的合并机会(比如嵌套加法), // 我们跳回父节点重新检查 path.parentPath.requeue(); } }};traverse(ast, visitor);console.log("正在生成最终代码...");const {code: output} = generate(ast, { jsescOption: {minimal: true}, // 保证中文不乱码 retainLines: false, // 允许重新格式化});fs.writeFileSync('output.js', output);console.log("反混淆完成!请查看 output.js");
那么具体AST的写法我就不做赘述了,大家可以自行查找或者蔡老板公众号。本处常量折叠是AI写的,还是那句话,拥抱AI,提高速度。
反混淆效果图展示

分析逻辑
机智的你很快就找到了m参数的生成逻辑,首先看_0x2268f9,是个修改后的时间戳Date.parse(new Date()) + 100000000,然后咱们再去看那个_0x57feae,它是由加密函数oo0O0和f参数构成的。那么开始扣代码吧。
逻辑说明
一
oo0O0内包含了个类似于ob混淆的函数

二
oo0O0函数最后返回了个”(空),难道这个函数什么都没干吗,其实不是,看第三点

三
最后在返回前进行了eval(),其实是将window[‘b’]给base64解码了,至于window[‘b’]怎么来的,自然是window.a转换过来的。不过不用担心,这是个定值,断点打到位,copy(atob(window[‘b’]))拿到加密算法。此处还需要注意,最后将mwqqppz换成了mw



四
可以看到f的由来了,那么m参数就算是大功告成了,接下来完善请求逻辑,咱们进行模拟请求



模拟请求


搞定
记得改UA
总结
猿人学的题目越来越好玩,越来越刁钻了,真是让人意想不到欲罢不能。
感谢蔡老板公众号强而有力的ast插件。
由于爬虫逆向反混淆技术博大精深,本人水平有限,文中若有疏漏或理解偏差之处,恳请各位前辈大佬不吝赐教,我们评论区见。
最后,愿我们在高处相见,亦或在深处重逢。
夜雨聆风