当前位置: 首页 > news >正文

网站流量指数百度识图 上传图片

网站流量指数,百度识图 上传图片,产品广告视频制作,wordpress 建站 pdf文章目录 页面流程请求参数cookie 调试干扰替换 utc.js 文件替换 9.html 文件 Hook CookieAST 反混淆9.html文件 反混淆字符串解密函数 $b$a$b 解密函数检测点验证结果AST 代码 完整的 AST 还原代码分析反混淆后的代码代码 udc.js文件 反混淆字符串解密函数 _0x56ae完整的 AST …

文章目录

  • 页面流程
    • 请求参数
    • cookie
  • 调试干扰
    • 替换 utc.js 文件
    • 替换 9.html 文件
  • Hook Cookie
  • AST 反混淆
    • 9.html文件 反混淆
      • 字符串解密函数 $b
        • $a
        • $b 解密函数检测点
        • 验证结果
        • AST 代码
      • 完整的 AST 还原代码
      • 分析反混淆后的代码代码
    • udc.js文件 反混淆
      • 字符串解密函数 _0x56ae
      • 完整的 AST 还原代码
      • 格式化检测
  • 还原加密函数 decrypt
    • 测试
  • 9.html 请求处理
  • python 代码

页面流程

打开 调试工具,查看数据接口 https://match.yuanrenxue.cn/api/match/9
在这里插入图片描述

请求参数

请求参数只携带了 page 对应的页码,没有加密参数
在这里插入图片描述在这里插入图片描述

cookie

请求数据接口时是需要携带 m 加密字段的
m字段失效后会弹窗提示 cookie 信息失效并刷新页面重新请求
在这里插入图片描述

调试干扰

删除 cookie m字段并刷新页面会出现 debugger
在这里插入图片描述

这段debugger是在 if 条件中生成的,可以在对应的代码段选择一律不在此处暂停,但会很卡
在这里插入图片描述

替换 utc.js 文件

比较建议替换源文件将对应的 bugger 字段删除
在这里插入图片描述

代码里有格式化检测,替换时应取消美观输出

在这里插入图片描述

删除 udc.js 文件里对应的 debugger
将 udc.js 文件取消美观输出后保存到本地
ctrl + f 搜索 debugger 关键字 将对应的 debugger 删除(一共有 3 处)
在这里插入图片描述
将 删除后的代码替换到浏览器中

替换 9.html 文件

9.html文件是动态的,每一次的返回的代码段都不一样(先固定,把debugger调试过掉)

第 2 次生成的 debugger 是在 9.html 文件中出现的
在这里插入图片描述
查看上一层堆栈, 这是一段利用 Function.prototype.constructor 生成的 debugger 代码
在这里插入图片描述

Hook Function 代码

let _Function = Function.prototype.constructor;
Function.prototype.constructor = function(val){if(val.indexOf('debugger') !== -1){return _Function.call(this, '');}return _Function.call(this, val);
}

在本地创建一个 html 文件
继续将生成 debugger 的字段替换(也需要保存取消了美观输出的代码,代码中有格式化检测)
在这里插入图片描述

需要删除的代码段

9.html 文件有两个debugger需要删除的
文件是动态的,需要自己找出生成 debugger 的代码这是我这份文件的debugger代码,可以做个参考
第一处
d[$b('\x30\x78\x31\x38\x38','\x47\x37\x39\x46')+'\x45\x47']($b('\x30\x78\x35\x66','\x69\x55\x6a\x45')+'\x75',$b('\x30\x78\x31\x31\x62','\x39\x6d\x23\x48')+'\x72')第二处
$b('\x30\x78\x31\x30\x62','\x44\x65\x29\x74')+'\x75'+d['\x65\x4f\x46'+'\x64\x67']

替换完成之后右键选择替换内容即可

替换完成之后会页面补显示数据,反而一直刷新,查看请求列表
在这里插入图片描述
替换好页面后就可以正常调试了,接下来就是找到 cookie 生成的位置

Hook Cookie

// Hook Cookie
(function(){let cookieFunc = Object.getOwnPropertyDescriptor(Document.prototype, 'cookie')Object.defineProperty(document, 'cookie', {get(){return cookieFunc.get.call(this)},set(val){if(val.indexOf('m') !== -1){debugger;}return cookieFunc.set.call(this, val)}})
})()

勾选事件监听器断点中的脚本断点,刷新页面后注入 Hook cookie 的代码
在这里插入图片描述

取消勾选脚本断点,在控制台运行 hook Cookie 的代码后 放开断点,直到在 Hook 代码中断住
在这里插入图片描述
查看上一层堆栈
在这里插入图片描述

对应的 js 代码是混淆过的,AST 小小解个混淆先

AST 反混淆

9.html文件 反混淆

将 9.html 中第一个 script 标签中的代码解混淆
在这里插入图片描述

字符串解密函数 $b

9.html 文件是动态的,但是检测的点都差不多,跟着浏览器对着本地进行调试即可

代码中常用到解密函数 $b(‘…’, ‘…’)
在这里插入图片描述$b() 解密函数在文件的开头中有定义

$b() 解密函数 依赖 $a 数组
$a 数组在文件的开头定义了,而后又经过一个自执行函数打乱了排序
在这里插入图片描述

$a

拿到打乱排序后的 $a 数组(在浏览器中 copy 即可)
断点下在 经过自执行函数打乱排序的代码段后,在这里插入图片描述
断住之后 copy($a) 到本地即可
在这里插入图片描述

$b 解密函数检测点

断点打在 $b 解密函数中引用到 $a 数组的地方, 刷新页面后 在这里插入图片描述单步调试
在这里插入图片描述

f 经过 h()方法执行后赋值成了 window
后又通过 f 取了[‘atob’] 方法
atob 在浏览器环境是有的,在 node 环境中并没有 在这里插入图片描述

在 node 中的处理

global['atob'] = require('atob') // npm install atob

继续往下跟,有一处格式化检测
在浏览器中返回值是 true在这里插入图片描述
在 node 中返回的值是 false
在这里插入图片描述
直接将这段格式化检测的代码写死成 true 即可
g[‘test’](this[‘BBYicw’][‘toString’]()) 改成 true
在这里插入图片描述
以上都处理好后,解密函数就可以正常执行了

验证结果

在这里插入图片描述
在这里插入图片描述

AST 代码

将代码放入 AST explorer站点解析
在这里插入图片描述
在这里插入图片描述

遍历 CallExpression 节点
callee 为 Identifier 类型,且 name 属性为 $b
获取 arguments 列表 里的字符串内容(实参)
执行解密函数 $b() 并传入实参拿到解密后的字符
对该节点进行替换

// 将解密函数与对应的依赖拿到 AST 文件中
global['atob'] = require('atob')
var $a = ["QsOkw7g=",...数组太长了这里忽略了
];
var $b = function (a, b) {...解密函数太长了这里忽略了return c;
};
traverse(ast, {CallExpression(path){// callee 为 Identifier 类型,且 name 属性为 $bif(path.get('callee').isIdentifier({'name': '$b'})){// 通过判断之后获取的节点都为// $b('\x30\x78\x31\x37\x36', '\x21\x5d\x44\x2a')// 获取 arguments列表 里的字符串内容(实参)// '\x30\x78\x31\x37\x36', '\x21\x5d\x44\x2a'let Arg = path.node.arguments;// 执行解密函数并传入对应的参数// $b('\x30\x78\x31\x37\x36', '\x21\x5d\x44\x2a')let result = $b(Arg[0].value, Arg[1].value)// 替换节点console.log('解密函数还原前的代码:  ', path + '')path.replaceWith(types.valueToNode(result))console.log('解密函数还原前的代码:  ', path + '')console.log('============================================================')}}
});

还原前
在这里插入图片描述

还原后
在这里插入图片描述

完整的 AST 还原代码

// 安装 babel 库:  npm install @babel/core
const fs = require('fs');const traverse = require('@babel/traverse').default; // 用于遍历 AST 节点
const types = require('@babel/types');  // 用于判断, 生成 AST 节点const parser = require('@babel/parser');  // 将js代码解析成ast节点
const generator = require('@babel/generator').default;  // 将ast节点转换成js代码// 读取(路径记得改)
const ast_code = fs.readFileSync('demo.js', {encoding: 'utf-8'
});let ast = parser.parse(ast_code);  // 将js代码解析成ast语法树// 这里插入解析规则
///// 将解密函数与对应的依赖拿到 AST 文件中
global['atob'] = require('atob')
var $a = ["VsKwwrc=",...数组太长,剩下的省略了
];
var $b = function (a, b) {...解密函数太长,剩下的省略了return c;
};
traverse(ast, {CallExpression(path) {// callee 为 Identifier 类型,且 name 属性为 $bif (path.get('callee').isIdentifier({'name': '$b'})) {// 通过判断之后获取的节点都为// $b('\x30\x78\x31\x37\x36', '\x21\x5d\x44\x2a')// 获取 arguments列表 里的字符串内容(实参)// '\x30\x78\x31\x37\x36', '\x21\x5d\x44\x2a'let Arg = path.node.arguments;// 执行解密函数并传入对应的参数// $b('\x30\x78\x31\x37\x36', '\x21\x5d\x44\x2a')let result = $b(Arg[0].value, Arg[1].value)// 替换节点console.log('解密函数还原前的代码:  ', path + '')path.replaceWith(types.valueToNode(result))console.log('解密函数还原前的代码:  ', path + '')console.log('============================================================')}}
});// ASCII码/16进制 字符/数值还原
traverse(ast, {"StringLiteral|NumericLiteral"(path) {if (path.node.extra) {if (path.isStringLiteral()) {console.log('ASCII码替换前: ', path.node.extra);delete path.node.extra.raw// path.node.extra.raw = `'${path.node.extra.rawValue}'`console.log('ASCII码替换后: ', path.node.extra);console.log('============================================================');} else if (path.isNumericLiteral()) {console.log('十六进制数值还原前: ', path.node.extra);path.node.extra.raw = `${path.node.extra.rawValue}`console.log('十六进制数值还原后: ', path.node.extra);console.log('============================================================');}}}
})// 字符串相加
function strConcat(path) {for (let i = 0; i <= 3; i++) {let node = path.node// left 节点为 StringLiteral 类型// right 节点为 StringLiteral 类型// operator 操作符属性为字符串 +if (types.isStringLiteral(node.left) && types.isStringLiteral(node.right) && node.operator === '+') {// 例 'e' + 'f'console.log('字符串相加前: ', path + '');// 例 'e' + 'f'let result = path.node.left.value + path.node.right.value;// 例: 'e' + 'f'// 替换成: 'ef'path.replaceWith(types.valueToNode(result));console.log('字符串相加后:  ', path + '');console.log('============================================================');} else {// 递归是针对多个字符串相加的// 例当前遍历到的节点: 'a' + 'b' + 'c' + 'd'// 这个节点在上面是不会处理的// path 对象的 traverse 方法是从当前节点继续遍历// 传入 strConcat 方法的节点就为:  'a' + 'b' + 'c' + 'd'// 调用过后还是会再次进入到 path.traverse 因为还是会有多个字符串相加// 上一次传入的节点被处理过了// 所以这一次传入的节点代码就为 'ab' + 'c' + 'd'// 一直递归到节点为 'abc' + 'd' 就会停止递归// 'abc' + 'd' 在 for 循环中会二次处理 (可以试着for循环只遍历一次看看效果path.traverse({BinaryExpression(path_) {strConcat(path_)}})}}
}traverse(ast, {BinaryExpression(path) {  // 遍历 BinaryExpression 节点strConcat(path)}
})// 花指令
traverse(ast, {VariableDeclarator(path) {if (path.get('init').isObjectExpression()) {let name = path.node.id.name;let binding = path.scope.getOwnBinding(name);let newObj = {}if (binding) {let refPath = binding.referencePaths;for (const index in refPath) {let parPath = refPath[index].parentPath.parentPath;// console.log(parPath + '')if (parPath.isAssignmentExpression() && parPath.get('left').isMemberExpression()) {let key = parPath.node.left.property.value;newObj[key] = parPath.node.right;// console.log('isAssignmentExpression:  ', (generator(newObj[key]).code).replaceAll('\n', ''));}}for (const index in refPath) {let parPath = refPath[index].parentPath.parentPath;if (parPath.isVariableDeclaration()) {if (parPath.get('declarations').length === 1) {let refName = parPath.node.declarations[0].id.name;let refBinding = parPath.scope.getBinding(refName);if (refBinding) {let refRefPath = refBinding.referencePaths.reverse();for (const ref in refRefPath) {let refParPath = refRefPath[ref].parentPath;if (refParPath.isMemberExpression() && refParPath.get('property').isStringLiteral()) {let refKey = refParPath.node.property.value;// console.log('isVariableDeclaration:  ', refParPath.toString().replaceAll('\n', ''));// console.log(refKey)if (types.isStringLiteral(newObj[refKey])) {// console.log(generator(newObj[refKey]).code)// console.log('字符串花指令还原前: ', refParPath.parentPath.toString())refParPath.replaceWith(newObj[refKey])// console.log('字符串花指令还原后: ', refParPath.parentPath.toString())// console.log('============================================================')}if (types.isFunctionExpression(newObj[refKey])) {let objRet = newObj[refKey].body.body[0].argument;// BinaryExpression 二项式if (types.isBinaryExpression(objRet)) {let grandPath = refParPath.parentPathlet operator = objRet.operator;  // 操作符let left = grandPath.node.arguments[0];let right = grandPath.node.arguments[1];// console.log('二项式花指令还原前', grandPath.toString())grandPath.replaceWith(types.binaryExpression(operator, left, right))// console.log('二项式花指令还原后', grandPath.toString())// console.log('============================================================')// CallExpression 调用} else if (types.isCallExpression(objRet)) {let grandPath = refParPath.parentPathlet callee = grandPath.node.arguments[0];let argument = grandPath.node.arguments.slice(1);console.log('函数调用花指令还原前', grandPath.toString().replaceAll('\n', '').slice(0, 100))grandPath.replaceWith(types.callExpression(callee, argument))console.log('函数调用花指令还原后', grandPath.toString().replaceAll('\n', '').slice(0, 100))console.log('============================================================')// console.log('isCallExpression:  ', grandPath.toString().replaceAll('\n', ''))}}}}}}}}// path.stop();// console.log('============================================================')}}}
})// Switch
traverse(ast, {WhileStatement(path) {if (path.get('test').isUnaryExpression() && path.inList) {let splitPath = path.getSibling(0).node.declarations[0].init.callee.object.value.split('|');let casesArg = path.node.body.body[0].cases;let indexObj = {}for (const index in casesArg) {let key = casesArg[index].test.value;indexObj[key] = casesArg[index].consequent[0]}let pathArray = []for (const index in casesArg) {pathArray.push(indexObj[casesArg[index].test.value]);}console.log('还原的Switch语句: ', path.toString().replaceAll('\n', '').slice(0, 50));path.replaceWithMultiple(pathArray)console.log('============================================================')for (let i = path.key; i >= 0; i--) {path.getSibling(i).remove();}}}
})js_code = generator(ast, {// compact: true,  // 是否压缩,默认 false
}).code
ast = parser.parse(js_code)
// 删除无引用代码段
// 无引用标识符
traverse(ast, {Identifier(path) {let name = path.node.name;let binding = path.scope.getBinding(name);let refPath = binding && binding.referencePaths;if (refPath && refPath.length === 0) {let grandPath = path.parentPath.parentPathif (grandPath.isVariableDeclaration()) {console.log(path.parentPath.parentPath.toString());grandPath.remove();console.log('============================================================')}}}
})// 无引用对象
traverse(ast, {VariableDeclarator(path) {if (path.get('init').isObjectExpression()) {let name = path.node.id.name;let binding = path.scope.getOwnBinding(name);if (path.get('init.properties').length === 0) {console.log('删除的无引用对象代码段: ', path + '')path.remove()}if (binding) {let refPath = binding.referencePaths;for (const index in refPath) {let parPath = refPath[index].parentPath.parentPath;if (parPath.isAssignmentExpression()  && !parPath.get('right').isIdentifier()) {console.log('删除的无引用对象parPath代码段: ', parPath.toString().replaceAll('\n', '').slice(0, 50));parPath.remove()}}}console.log('============================================================')}}
})// 虚假 if
traverse(ast, {// IfStatement 为if判断语句// ConditionalExpression 为三元表达式"IfStatement|ConditionalExpression"(path) {// 该节点中的判断条件应该为 二元表达式// left 节点应该为 StringLiteral 类型// right 节点应该为 StringLiteral 类型if (path.get('test').isBinaryExpression() && path.get('test.left').isStringLiteral() && path.get('test.right').isStringLiteral()) {// 取出判断条件中对应的值// 例:  if ('a' === 'a') {  console.log("'a' === 'a' true");} else {  console.log("'a' === 'a' false");}let operator = path.node.test.operator;  // 取出操作符 例: ===let leftString = path.node.test.left.value;  // 左边的字符串 例: 'a'let rightString = path.node.test.right.value;  // 右边的字符串 例: === 'a'// 生成 eval 可判断的字符串let vmRun = `"${leftString}"  ${operator} "${rightString}"`;  // 例: 'a' === 'a'let result = eval(vmRun);console.log('虚假 if:  ' + (path + '').replaceAll('\n', '').slice(0, 50))// 取出 if 与 else 中的代码块// path.node.consequent.body 为 if(){}else{}; 形式取值// path.node.consequent.arguments 为 statement ? true : false; 形式取值let ifTrue = path.node.consequent.body || path.node.consequent.arguments;let elFalse = path.node.alternate.body || path.node.alternate.arguments;// 判断 result 的执行结果,替换对应的代码块 if 或 elseresult ? path.replaceWithMultiple(ifTrue) : path.replaceWithMultiple(elFalse)}}
})///
js_code = generator(ast, {compact: true,  // 是否压缩,默认 false
}).code  // 将ast节点转换成js代码// 写入(路径记得改)
fs.writeFileSync('New_demo.js', js_code, {encoding: 'utf-8',
})

分析反混淆后的代码代码

还原后继续 hook
可以看到 cookie m 的生成 (m-1)[‘toString’]() + res
m 为 3 (经过循环后m为4,减去1后就为3)
res是经过 decrpyt 方法生成的
在这里插入图片描述

decrypt() 函数在 udc.js 文件中, udc.js 文件也是混淆过的,再将 udc.js 还原
图片的函数是decrypt() 函数,已经用 AST 还原好了在这里插入图片描述

udc.js文件 反混淆

字符串解密函数 _0x56ae

函数依赖一个大数组,在浏览器 copy 即可
在这里插入图片描述

继续向下调试
window[‘atob’]
在node中导包即可
const atob = require(‘atob’); // npm install atob
在这里插入图片描述

格式化检测
在 node 中将这行代码改为 true 即可在这里插入图片描述

以上都处理好后,验证下结果
在这里插入图片描述
接下来写好对应的解析规则,就可以开始还原字符串了,思路与 9.html 文件的解密函数还原一样

完整的 AST 还原代码

// 安装 babel 库:  npm install @babel/core
const fs = require('fs');const traverse = require('@babel/traverse').default; // 用于遍历 AST 节点
const types = require('@babel/types');  // 用于判断, 生成 AST 节点const parser = require('@babel/parser');  // 将js代码解析成ast节点
const generator = require('@babel/generator').default;  // 将ast节点转换成js代码// 读取(路径记得改)
const ast_code = fs.readFileSync('demo.js', {encoding: 'utf-8'
});let ast = parser.parse(ast_code);  // 将js代码解析成ast语法树// 这里插入解析规则
///let atob = require('atob')
let __0x9a4eb = ["RcOJesKLGcO7",// 数组太长了,按照上面去浏览器 copy 即可
]
var _0x56ae = function (_0x4f4e67, _0x43c602) {// 剩余的内容 按照上面去浏览器 copy 即可return _0x223635;
};
traverse(ast, {CallExpression(path) {// callee 为 Identifier 类型,且 name 属性为 $bif (path.get('callee').isIdentifier({'name': '_0x56ae'})) {// 通过判断之后获取的节点都为// $b('\x30\x78\x31\x37\x36', '\x21\x5d\x44\x2a')// 获取 arguments列表 里的字符串内容(实参)// '\x30\x78\x31\x37\x36', '\x21\x5d\x44\x2a'let Arg = path.node.arguments;// 执行解密函数并传入对应的参数// $b('\x30\x78\x31\x37\x36', '\x21\x5d\x44\x2a')let result = _0x56ae(Arg[0].value, Arg[1].value)// 替换节点console.log('解密函数还原前的代码:  ', path + '')path.replaceWith(types.valueToNode(result))console.log('解密函数还原前的代码:  ', path + '')console.log('============================================================')}}
});// ASCII码/16进制 字符/数值还原
traverse(ast, {"StringLiteral|NumericLiteral"(path) {if (path.node.extra) {if (path.isStringLiteral()) {console.log('ASCII码替换前: ', path.node.extra);delete path.node.extra.raw;console.log('ASCII码替换后: ', path.node.extra);console.log('============================================================');} else if (path.isNumericLiteral()) {console.log('十六进制数值还原前: ', path.node.extra);delete path.node.extra.raw;console.log('十六进制数值还原后: ', path.node.extra);console.log('============================================================');}}}
})// 花指令
traverse(ast, {VariableDeclarator(path) {if (path.get('init').isObjectExpression()) {let property = path.node.init.properties;let newObj = {}for (const index in property) {let key = property[index].key.value;newObj[key] = property[index].value;}let name = path.node.id.name;let binding = path.scope.getBinding(name);if (binding) {let refPath = binding.referencePaths.reverse();for (const index in refPath) {let parPath = refPath[index].parentPathif (parPath.isMemberExpression() && parPath.get('property').isStringLiteral()) {let key = parPath.node.property.value;// 字符串if (types.isStringLiteral(newObj[key])) {// console.log('字符串花指令前: ', parPath + '')parPath.replaceWith(newObj[key])// console.log('字符串花指令后: ', parPath + '')// console.log('============================================================');} else if (types.isFunctionExpression(newObj[key])) {let retState = newObj[key].body.body[0].argument;let grandPath = parPath.parentPath;// 二项式表达式if (types.isBinaryExpression(retState)) {let operator = retState.operator;let binArg = grandPath.node.arguments;// console.log('二项式花指令前: ', grandPath + '');grandPath.replaceWith(types.binaryExpression(operator, binArg[0], binArg[1]));// console.log('二项式花指令后: ', grandPath + '');// console.log('============================================================');} else if (types.isLogicalExpression(retState)) {let operator = retState.operator;let logArg = grandPath.node.arguments;let newLogical = types.logicalExpression(operator, logArg[0], logArg[1])// console.log('二项式花指令前: ', grandPath + '');grandPath.replaceWith(newLogical)// console.log('二项式花指令后: ', grandPath + '');// console.log('============================================================');// 调用花指令} else if (types.isCallExpression(retState)) {let binArg = grandPath.node.arguments;// console.log('调用花指令前: ', grandPath + '');let newCallExp = types.callExpression(binArg[0], binArg.slice(1))grandPath.replaceWith(newCallExp)// console.log('调用花指令后: ', grandPath + '');// console.log('============================================================');}}}}}}}
})js_code = generator(ast, {// compact: true,  // 是否压缩,默认 false
}).code
ast = parser.parse(js_code);
// 无引用对象
traverse(ast, {Identifier(path) {let name = path.node.name;let binding = path.scope.getBinding(name);if (binding) {let refPath = binding.referencePaths;let parPath = path.parentPath;if (refPath.length === 0 && parPath.isVariableDeclarator() && parPath.get('init').isObjectExpression()) {console.log('已删除: ', parPath.parentPath.toString().replaceAll('\n', '').slice(0, 50));parPath.parentPath.remove();console.log('============================================================');}}}
})
// 虚假 if
traverse(ast, {// IfStatement 为if判断语句// ConditionalExpression 为三元表达式"IfStatement|ConditionalExpression"(path) {// 该节点中的判断条件应该为 二元表达式// left 节点应该为 StringLiteral 类型// right 节点应该为 StringLiteral 类型if (path.get('test').isBinaryExpression() && path.get('test.left').isStringLiteral() && path.get('test.right').isStringLiteral()) {// 取出判断条件中对应的值// 例:  if ('a' === 'a') {  console.log("'a' === 'a' true");} else {  console.log("'a' === 'a' false");}let operator = path.node.test.operator;  // 取出操作符 例: ===let leftString = path.node.test.left.value;  // 左边的字符串 例: 'a'let rightString = path.node.test.right.value;  // 右边的字符串 例: === 'a'// 生成 eval 可判断的字符串let vmRun = `"${leftString}"  ${operator} "${rightString}"`;  // 例: 'a' === 'a'let result = eval(vmRun);console.log('虚假 if:  ' + (path + '').replaceAll('\n', '').slice(0, 50))// 取出 if 与 else 中的代码块// path.node.consequent.body 为 if(){}else{}; 形式取值// path.node.consequent.arguments 为 statement ? true : false; 形式取值let ifTrue = path.node.consequent.body || path.node.consequent.arguments;let elFalse = path.node.alternate.body || path.node.alternate.arguments;// 判断 result 的执行结果,替换对应的代码块 if 或 elseresult ? path.replaceWithMultiple(ifTrue) : path.replaceWithMultiple(elFalse)}}
})// switch
traverse(ast, {WhileStatement(path) {if (path.inList && path.key === 1) {let sibLin = path.getSibling(0);let splitStr = sibLin.node.declarations[0].init.callee.object.value.split('|');let idxObj = {}let cases = path.node.body.body[0].cases;for (const index in cases){let key = cases[index].test.value;idxObj[key] = cases[index].consequent[0]}let newArray = []for (const index in splitStr){newArray.push(idxObj[splitStr[index]]);}console.log('switch:', path.toString().replaceAll('\n', '').slice(0, 50));path.replaceWithMultiple(newArray)sibLin.remove();console.log('============================================================');}}
})
/
js_code = generator(ast, {compact: false,  // 是否压缩,默认 false
}).code  // 将ast节点转换成js代码// 写入(路径记得改)
fs.writeFileSync('New_demo.js', js_code, {encoding: 'utf-8',
})

格式化检测

在将还原好的 utc.js 文件替换之前,还需要将里面的格式化检测过掉

第一处

var _0x5ea72e = new RegExp("\\w+ *\\(\\) *{\\w+ *['|\"].+['|\"];? *}");
return !_0x5ea72e["test"](_0x3b5e10["toString"]());// 改成
var _0x5ea72e = new RegExp("\\w+ *\\(\\) *{\\w+ *['|\"].+['|\"];? *}");
return !true;

第二处

var _0x2dc31f = new RegExp("(\\\\[x|u](\\w){2,4})+");
return _0x2dc31f["test"](_0x3b2471["toString"]());// 改成
var _0x2dc31f = new RegExp("(\\\\[x|u](\\w){2,4})+");
return true;

第三处

if (!_0x4d1b87["test"](_0x4818e0 + "chain") || !_0x1dda0b["test"](_0x4818e0 + "input"))// 改成
if (!true || !true)

顺便全局搜搜是否存在 debugger 关键字,如果有的话将 debugger 替换成空即可

这两段代码也是不需要的,直接删除即可
第一段为解密函数部分
第二段为延时执行
在这里插入图片描述

还原加密函数 decrypt

将 utc.js 代码全部 copy 到本地

有两个地方的环境是需要补的

"Microsoft Internet Explorer" == navigator["appName"]// 在文件头部声明
navigator = {appName: 'Netscape'
};
if (window["crypto"] && window["crypto"]["getRandomValues"])// 在文件头部声明
window = global;

测试

function sdk(nums, timeStrap){for (var m = 1; m <= nums; m++) {res = decrypt(timeStrap) + "r";}return (m - 1)["toString"]() + res;
}console.log(sdk(3, "1723805794"));

会发现 console.log 打印不出来,是因为 console 里面的所有方法都被重置为空函数了

文件中重置 console.log 的代码

// 将这里的代码注释或删除即可
_0xe77b28["console"]["log"] = _0xaf0f8f;
_0xe77b28["console"]["warn"] = _0xaf0f8f;
_0xe77b28["console"]["debug"] = _0xaf0f8f;
_0xe77b28["console"]["info"] = _0xaf0f8f;
_0xe77b28["console"]["error"] = _0xaf0f8f;
_0xe77b28["console"]["exception"] = _0xaf0f8f;
_0xe77b28["console"]["trace"] = _0xaf0f8f;

处理好以上的各个点之后,就可以顺利输出了

9.html 请求处理

9.html 文件是动态的, 关键的点是 m 循环的次数,还有 加密对应的字符串
例如 循环的次数: m <= 3, 加密的点:decrypt(“1723805794”)

用这则将这两个点匹配好就行

时间戳的正则
re.findall(“(decrypt,‘(\d+)’)”, response.text)[0]

循环次数的正则
try:
nums = re.findall(r’window=new Array();for(var m=0x1;m<=(\d);', response.text)[0]
except:
nums = re.findall(r"window=new Array();for(var m=0x1;.*?(m,(\d));", response.text)[0]

python 代码

import re
import requests
import execjsheaders = {"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36",
}
cookies = {"sessionid": "你的SessionID",
}def call_js(file_name, func_name, *args):with open(file_name, mode='r', encoding='utf-8') as f:js_code = execjs.compile(f.read())return js_code.call(func_name, *args)def send_index():url = "https://match.yuanrenxue.cn/match/9"response = requests.get(url, headers=headers, cookies=cookies)time_strap_ = re.findall("\(decrypt,'(\d+)'\)", response.text)[0]try:nums = re.findall(r'window=new Array\(\);for\(var m=0x1;m<=(\d);', response.text)[0]except:nums = re.findall(r"window=new Array\(\);for\(var m=0x1;.*?\(m,(\d)\);", response.text)[0]return time_strap_, numsdef send_math9(page_):url = "https://match.yuanrenxue.cn/api/match/9"params = {"page": f"{page_}"}response = requests.get(url, headers=headers, params=params, cookies=cookies)print(response.json())return response.json()['data']if __name__ == '__main__':time_strap, num = send_index()cookies['m'] = call_js('9.js', 'sdk', num, time_strap)nums = 0division_nums = 0for page in range(1, 6):math9_data = send_math9(page)for dataDict in math9_data:nums += int(dataDict['value'])division_nums += 1print('页数: ', page, '总和', nums, '被除数: ', division_nums)print(nums / division_nums)
http://www.rdtb.cn/news/19908.html

相关文章:

  • 个人制作网站工具最新网络营销方式
  • 怎么申请公司网址seo关键词优化外包公司
  • 做微商做什么网站比较好seo咨询师招聘
  • 网站代理怎么做推广下载app拿佣金
  • 公司想推广做网站有用手机怎么建立网站
  • 做坑网站需要官方推广平台
  • 专门做隐形眼镜的网站网站免费发布与推广
  • 做招聘网站需要什么资质百度推广怎么做效果好
  • 现在最流行的网站开发工具aso100官网
  • 山东三强建设咨询有限公司网站百度推广优化技巧
  • wordpress主机和域名绑定域名网站seo李守洪排名大师
  • 外国风格网站建设费用网络推广员的工作内容
  • 做网站需要自己上传产品吗廊坊百度快照优化哪家服务好
  • 网站架构设计师有哪些学校可以报考互联网广告公司
  • 那些网站布局好看百度付费问答平台
  • 网站优化图片济南做网站公司哪家好
  • 南宁网站建设产品介绍网站模版
  • 八年级信息上册如何做网站竞价托管服务多少钱
  • 网络营销推广策略包括哪些排名优化软件点击
  • WordPress可以做政府网站吗中国seo网站
  • 做任务给佣金的网站有哪些免费网站外链推广
  • 网站下载织梦模板促销式软文案例
  • 济南自适应网站建设百度收录提交网站后多久收录
  • 全运会网站建设方案小程序开发流程详细
  • 网站自助制作sem账户托管公司
  • 沧州做网站的大公司网络营销方案设计范文
  • 做网站电商湖南网站建设效果
  • 科技 杭州 网站建设微指数官网
  • 做字画的网站热狗seo外包
  • 宁夏网站制作网络营销与直播电商就业前景