文章目录
- 请求流程
- 请求参数
- cookie信息
- 加密参数定位
- Hook Cookie
- AST 还原混淆代码
- 解密函数还原字符串
- 还原数组引用
- 还原浏览器内置对象 / 变量值引用
- 还原逗号表达式
- 还原 unicode, 16进制数值
- 字符串相加
- AST 解混淆完整代码
- 加密参数还原
- cookie m字段
- m字段坑点
- cookie RM4hZBv0dDon443M 字段
- hook window\["_$ss"\]
- Hook window\['_$qF'\]
- 请求参数
- 请求代码
请求流程
打开 调试工具,查看数据接口 https://match.yuanrenxue.cn/api/match/5
请求参数
请求参数携带了 page, m, f 3个字段,
page为页数,
m 为时间戳 像是 new Date().getTIme() 生成的
f 为时间戳 像是 Date.parse(new Date()) 生成的
cookie信息
请求的 cookie 携带了两个字段
RM4hZBv0dDon443M字段 和 m字段
加密参数定位
Hook Cookie
cookie 中有两个加密字段 m, RM4hZBv0dDon443M
Hook Cookie 找到生成这两个字段信息的关键代码
// Hook Cookie
(function () {let cookie_func = Object.getOwnPropertyDescriptor(Document.prototype, 'cookie')Object.defineProperty(document, 'cookie', {get() {return cookie_func.get.call(this);},set(val) {console.log(val);debugger;return cookie_func.set.call(this, val);}})
})();
勾选事件监听断点中的脚本断点并刷新页面
刷新页面之后将 脚本断点取消 将 Hook Cookie 的代码在控制台注入之后放开断点
断点断住之后查看对应生成 cookie 的位置,对应的 js 文件是做了混淆的
解个混淆先
这段代码是在 eval 函数里执行的,向上查看堆栈,是在 5.html 文件中生成的代码,然后放入 eval 函数中执行
这段代码是经过混淆的,解混淆以后会容易看很多
AST 还原混淆代码
解密函数还原字符串
文件开头定义了一个数组 _0x34bd
后定义了解密函数,解密函数依赖 _0x34bd
后又将解密函数 _0x34bd 赋值给了 _0x1383f7
调用方式就为 _0x1383f7(…)
将解密函数和依赖一起放入 AST 文件中
遍历 CallExpression 节点
且 callee 类型为 Identifier name属性值为 _0x1383f7
arguments长度为1,且获取 arguments 参数中的 value 值
// 解密函数还原
var _0x34bd = ["_$zr", ...数组太长了,剩下的省略了];
var _0x54e9 = function (_0x1b88e5, _0x1abb3d) {
_0x1b88e5 = _0x1b88e5 - 229;
var _0x34bd3d = _0x34bd[_0x1b88e5];
return _0x34bd3d;
};
var _0x1383f7 = _0x54e9;
(function (_0x5340a0, _0x2aca3b) {
var _0x595372 = _0x54e9;
while (!![]) {try {var _0x133a0b = parseInt(_0x595372(572)) + parseInt(_0x595372(418)) * -parseInt(_0x595372(834)) + -parseInt(_0x595372(350)) * -parseInt(_0x595372(483)) + parseInt(_0x595372(823)) + -parseInt(_0x595372(744)) + parseInt(_0x595372(382)) * parseInt(_0x595372(710)) + -parseInt(_0x595372(847)) * -parseInt(_0x595372(496));if (_0x133a0b === _0x2aca3b) break;else _0x5340a0["push"](_0x5340a0["shift"]());} catch (_0x28ba71) {_0x5340a0["push"](_0x5340a0["shift"]());}
}
})(_0x34bd, 997352)
traverse(ast, {CallExpression(path){// callee 类型为 Identifier name属性值为 _0x1383f7// arguments长度为1,且获取 arguments 参数中的 value 值if(path.get('callee').isIdentifier({name: '_0x1383f7'}) && path.node.arguments.length === 1){console.log('解密函数还原前: ', path + '')let Arg = path.node.arguments[0].value; // 获取参数值// 将节点替换path.replaceWith(types.valueToNode(_0x1383f7(Arg)))console.log('解密函数还原后: ', path + '')console.log('==============================')}}
})
还原数组引用
文件中有很多引用到 _0xceb4b2 数组的地方
可以看到 _0xceb4b2 数组是通过 _0xac9d20() 方法执行后生成的
在浏览器中 copy(_0xceb4b2 ) 数组拿到本地就可以
_$UH 的值也是 _0xceb4b2 在文件中也有很多引用到的地方
注意
_0xceb4b2 在 _0xac9d20() 方法执行后生成了大数组后又赋值给了 _$UH
$UH 在随后的又进行了一系列操作,导致数组又变化了
copy() 大数组时在文件最后的 return 处 copy()
单单 copy() 是行不通的,$UH 后续赋值了 内置对象 window,导致 copy 之后只得到一串字符串
copy 代码
_$UH_copy = []
for(const index in _$UH){if (window === _$UH[index]){_$UH_copy.push('window')}else{_$UH_copy.push(_$UH[index])}
}
copy(_$UH_copy)
将取值代码放入ast网站中
遍历 MemberExpression 节点
object 为 Identifier 类型 且 name 属性值为 _0xceb4b2
或
object 为 Identifier 类型 且 name 属性值为 _$UH
property 为 NumericLiteral 类型,后续拿到对应的 value 值
// 数组引用还原
let _0xceb4b2 = ["name",// ... 数组太长了剩下的省略了
];
traverse(ast, {MemberExpression(path) {// object 为 Identifier 类型 且 name 属性值为字符串 _0xceb4b2// property 为 NumericLiteral 类型if (path.get('object').isIdentifier({name: '_0xceb4b2'}) || path.get('object').isIdentifier({name: '_$UH'}) && path.get('property').isNumericLiteral()) {// 拿到 property 节点中的 value 值let value = path.node.property.value;// 节点替换console.log('数组引用还原前: ', path.parentPath + '');if (_0xceb4b2[value] === 'window') {path.replaceWith(types.identifier('window'));} else {path.replaceWith(types.valueToNode(_0xceb4b2[value]));}console.log('数组引用还原后: ', path.parentPath + '');console.log('==============================')}}
})
还原浏览器内置对象 / 变量值引用
文件中有很多类似这样的操作
将一个浏览器内置对象赋值给一个变量,后续操作这个变量赋值给下一个变量
遍历 VariableDeclarator 节点
init 节点中 name 属性值为对应的 浏览器内置对象的值
后续获取节点中 id 属性中对应的 name 值
拿到 name 值后 获取对应的引用,将 name 值替换成对应的浏览器内置对象
function reductionVar(init, name, type) {traverse(ast, {Identifier(identPath) {if (identPath.node.name === name) {// _0x4e96b4 = window 这样的节点是不需要修改的// _0x4e96b4['$_zw'] 需要修改这样的 MemberExpression 节点if (type === 'CallExpression' && identPath.parentPath.isCallExpression()) {// identPath_.replaceWith(types.identifier(init))console.log('内置对象引用还原前: ', identPath.parentPath + '');identPath.replaceWith(init);console.log('内置对象引用还原后: ', identPath.parentPath + '');console.log('==============================')}if (type === 'MemberExpression' && identPath.parentPath.isMemberExpression()) {// identPath_.replaceWith(types.identifier(init))console.log('内置对象引用还原前: ', identPath.parentPath + '');identPath.replaceWith(types.identifier(init));console.log('内置对象引用还原后: ', identPath.parentPath + '');console.log('==============================')}}identPath.skip()}})
}
// 浏览器内置对象引用
let nativeArray = ['window','String','Error','Array','Math','parseInt','Date','document','Object','unescape','encodeURIComponent','Function','CryptoJS'
].join('...') + '...';
traverse(ast, {VariableDeclarator(path) {let init = path.get('init').isIdentifier() && path.node.init.name;if (init && nativeArray.indexOf(init + '...') !== -1) {let name = path.get('id').isIdentifier() && path.node.id.name;reductionVar(init, name, 'MemberExpression')}},AssignmentExpression(path) {if (path.get('left').isIdentifier() && path.get('right').isIdentifier()) {let init = path.node.right.name;if (init && nativeArray.indexOf(init + '...') !== -1) {let name = path.node.left.name;reductionVar(init, name, 'MemberExpression')}}}
})
// 变量值引用
traverse(ast, {VariableDeclarator(path) {if (path.get('id').isIdentifier() && path.get('init').isMemberExpression()) {let init = path.node.init;let init_name = path.node.init.object;for (let i=0; i < 10; i++) {if (types.isIdentifier(init_name)) {init_name = init_name.name;}if (types.isMemberExpression(init_name)) {init_name = init_name.object;}}let name = path.node.id.name;if (init && nativeArray.indexOf(init_name + '...') !== -1) {console.log('内置对象: ', generator(init).code, ' 引用对象: ', name);reductionVar(init, name, 'CallExpression')}}}
})
还原逗号表达式
遍历 VariableDeclaration 节点,且改节点中 declarations 的长度大于 1
获取 declarations 数组,declarations数组中每个元素都创建新的 VariableDeclaration 节点
将当前节点替换成新创建的 VariableDeclaration 节点
遍历 SequenceExpression 节点,且 expressions 数组的元素大于1
获取 expressions 数组
将数组中的每个元素都创建为新的 ExpressionStatement 节点
将当前节点替换成新创建的 ExpressionStatement 节点
// 还原逗号表达式
// 逗号表达式
traverse(ast, {VariableDeclaration(path) {if (path.get('declarations').length > 1) {// 例如: var a, b, c// 拿到声明符号 varlet kind = path.node.kind;// 拿到 a, b, c 变量let Arg = path.get('declarations')// 创建的节点元素let nodeList = []for (const index in Arg) {// 创建一个新的 VariableDeclaration 节点// var a// var b// var clet varTionNode = types.variableDeclaration(kind, [Arg[index].node]);nodeList.push(varTionNode);console.log('逗号表达式: ', generator(varTionNode).code.slice(0, 50).replaceAll('\n', ''));console.log('============================================================');}// 替换当前节点path.replaceWithMultiple(nodeList);}},SequenceExpression(path) {if (path.get('expressions').length > 1) {let Arg = path.get('expressions');let nodeList = []for (const index in Arg) {// 创建新的 ExpressionStatement 节点let experNode = types.expressionStatement(Arg[index].node);nodeList.push(experNode)console.log('逗号表达式: ', generator(experNode).code.slice(0, 50).replaceAll('\n', ''));console.log('============================================================');}// 替换当前节点path.replaceWithMultiple(nodeList);path.skip();}}
})
还原 unicode, 16进制数值
// 还原 unicode, 16进制数值
traverse(ast, {"StringLiteral|NumericLiteral"(path) {if(path.node.extra){console.log('数值,unicode还原前: ', path.node.extra.raw);delete path.node.extra.raw;console.log('数值,unicode还原后: ', path.node.extra.rawValue);console.log('==============================')}}
});
字符串相加
// 字符串相加
function strConcat(path) {for (let i = 0; i <= 2; 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)}
})
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语法树// 这里插入解析规则
///
// 解密函数还原
var _0x34bd = ["_$zr", "_$d5", ..];
var _0x54e9 = function (_0x1b88e5, _0x1abb3d) {_0x1b88e5 = _0x1b88e5 - 229;var _0x34bd3d = _0x34bd[_0x1b88e5];return _0x34bd3d;
};
var _0x1383f7 = _0x54e9;
(function (_0x5340a0, _0x2aca3b) {var _0x595372 = _0x54e9;while (!![]) {try {var _0x133a0b = parseInt(_0x595372(572)) + parseInt(_0x595372(418)) * -parseInt(_0x595372(834)) + -parseInt(_0x595372(350)) * -parseInt(_0x595372(483)) + parseInt(_0x595372(823)) + -parseInt(_0x595372(744)) + parseInt(_0x595372(382)) * parseInt(_0x595372(710)) + -parseInt(_0x595372(847)) * -parseInt(_0x595372(496));if (_0x133a0b === _0x2aca3b) break; else _0x5340a0["push"](_0x5340a0["shift"]());} catch (_0x28ba71) {_0x5340a0["push"](_0x5340a0["shift"]());}}
})(_0x34bd, 997352);
traverse(ast, {CallExpression(path) {// callee 类型为 Identifier name属性值为 _0x1383f7// arguments长度为1,且获取 arguments 参数中的 value 值if (path.get('callee').isIdentifier({name: '_0x1383f7'}) && path.node.arguments.length === 1) {console.log('解密函数还原前: ', path + '')let Arg = path.node.arguments[0].value; // 获取参数值// 将节点替换path.replaceWith(types.valueToNode(_0x1383f7(Arg)))console.log('解密函数还原后: ', path + '')console.log('==============================')}}
});// 数组引用还原
let _0xceb4b2 = ["name","setAttribute",...
];
traverse(ast, {MemberExpression(path) {// object 为 Identifier 类型 且 name 属性值为字符串 _0xceb4b2// property 为 NumericLiteral 类型if (path.get('object').isIdentifier({name: '_0xceb4b2'}) || path.get('object').isIdentifier({name: '_$UH'}) && path.get('property').isNumericLiteral()) {// 拿到 property 节点中的 value 值let value = path.node.property.value;// 节点替换console.log('数组引用还原前: ', path.parentPath + '');if (_0xceb4b2[value] === 'window') {path.replaceWith(types.identifier('window'));} else {path.replaceWith(types.valueToNode(_0xceb4b2[value]));}console.log('数组引用还原后: ', path.parentPath + '');console.log('==============================')}}
})function reductionVar(init, name, type) {traverse(ast, {Identifier(identPath) {if (identPath.node.name === name) {// _0x4e96b4 = window 这样的节点是不需要修改的// _0x4e96b4['$_zw'] 需要修改这样的 MemberExpression 节点if (type === 'CallExpression' && identPath.parentPath.isCallExpression()) {// identPath_.replaceWith(types.identifier(init))console.log('内置对象引用还原前: ', identPath.parentPath + '');identPath.replaceWith(init);console.log('内置对象引用还原后: ', identPath.parentPath + '');console.log('==============================')}if (type === 'MemberExpression' && identPath.parentPath.isMemberExpression()) {// identPath_.replaceWith(types.identifier(init))console.log('内置对象引用还原前: ', identPath.parentPath + '');identPath.replaceWith(types.identifier(init));console.log('内置对象引用还原后: ', identPath.parentPath + '');console.log('==============================')}}identPath.skip()}})
}
// 浏览器内置对象引用
let nativeArray = ['window','String','Error','Array','Math','parseInt','Date','document','Object','unescape','encodeURIComponent','Function','CryptoJS'
].join('...') + '...';
traverse(ast, {VariableDeclarator(path) {let init = path.get('init').isIdentifier() && path.node.init.name;if (init && nativeArray.indexOf(init + '...') !== -1) {let name = path.get('id').isIdentifier() && path.node.id.name;reductionVar(init, name, 'MemberExpression')}},AssignmentExpression(path) {if (path.get('left').isIdentifier() && path.get('right').isIdentifier()) {let init = path.node.right.name;if (init && nativeArray.indexOf(init + '...') !== -1) {let name = path.node.left.name;reductionVar(init, name, 'MemberExpression')}}}
})
// 变量值引用
traverse(ast, {VariableDeclarator(path) {if (path.get('id').isIdentifier() && path.get('init').isMemberExpression()) {let init = path.node.init;let init_name = path.node.init.object;for (let i=0; i < 10; i++) {if (types.isIdentifier(init_name)) {init_name = init_name.name;}if (types.isMemberExpression(init_name)) {init_name = init_name.object;}}let name = path.node.id.name;if (init && nativeArray.indexOf(init_name + '...') !== -1) {console.log('内置对象: ', generator(init).code, ' 引用对象: ', name);reductionVar(init, name, 'CallExpression')}}}
})// 逗号表达式
traverse(ast, {VariableDeclaration(path) {if (path.get('declarations').length > 1) {// 例如: var a, b, c// 拿到声明符号 varlet kind = path.node.kind;// 拿到 a, b, c 变量let Arg = path.get('declarations')// 创建的节点元素let nodeList = []for (const index in Arg) {// 创建一个新的 VariableDeclaration 节点// var a// var b// var clet varTionNode = types.variableDeclaration(kind, [Arg[index].node]);nodeList.push(varTionNode);console.log('逗号表达式: ', generator(varTionNode).code.slice(0, 50).replaceAll('\n', ''));console.log('============================================================');}// 替换当前节点path.replaceWithMultiple(nodeList);}},SequenceExpression(path) {if (path.get('expressions').length > 1) {let Arg = path.get('expressions');let nodeList = []for (const index in Arg) {// 创建新的 ExpressionStatement 节点let experNode = types.expressionStatement(Arg[index].node);nodeList.push(experNode)console.log('逗号表达式: ', generator(experNode).code.slice(0, 50).replaceAll('\n', ''));console.log('============================================================');}// 替换当前节点path.replaceWithMultiple(nodeList);path.skip();}}
})// 还原 unicode, 16进制数值
traverse(ast, {"StringLiteral|NumericLiteral|DirectiveLiteral"(path) {if(path.node.extra){console.log('数值,unicode还原前: ', path.node.extra.raw);delete path.node.extra.raw;console.log('数值,unicode还原后: ', path.node.extra.rawValue);console.log('==============================')}}
});// 字符串相加
function strConcat(path) {for (let i = 0; i <= 2; 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)}
})
///let js_code = generator(ast, {compact: true, // 是否压缩,默认 false
}).code // 将ast节点转换成js代码// 写入(路径记得改)
fs.writeFileSync('New_demo.js', js_code, {encoding: 'utf-8',
})
加密参数还原
将 5.html 文件开启替换,并将解混淆后的 js 代码替换到对应的 Script 标签
全局搜索 function _$KS() {
将这个函数内的代码替换成 解混淆后的js代码
开启脚本断点
刷新页面 Hook Cookie 后取消勾选脚本断点,开始调试
Hook Cookie
(function () {let cookie_func = Object.getOwnPropertyDescriptor(Document.prototype, 'cookie')Object.defineProperty(document, 'cookie', {get() {return cookie_func.get.call(this);},set(val) {if (val.indexOf('m') !== -1 || val.indexOf('RM4hZBv0dDon443M') !== -1){debuggerconsole.log(val);}return cookie_func.set.call(this, val);}})
})();
在 m 字段设置第 5 次之后 RM4 才会赋值
cookie m字段
m字段一共会生成 5 次
前 4 次生成的位置
_$Wa = _0x12eaf3(); // _0x12eaf3() 是 Date["parse"](new Date())
document[_$Fe] = "m=" + _0x474032(_$Wa) + "; path=/";
window["_$pr"]["push"](_0x474032(_$Wa)); // _$pr 刚开始是空数组
/*
_$tT 和 _$Jy 初始的定义为
window["_$tT"] = -172015004;
window["_$Jy"] = 461512024;
*/
delete window["_$Jy"];
delete window["_$tT"];
window["_$Jy"] = _0x2d5f5b(); // new Date()["valueOf"]()
window["_$tT"] = _0x2d5f5b() - _0x12eaf3(); // new Date()["valueOf"]() - Date["parse"](new Date())
_0x474032() 方法时只需要扣 return 最后一个方法,因为传进去的参数始终就只有一个
function _0x474032(_0x233f82, _0xe2ed33, _0x3229f9) {return _0x37614a(_0x233f82);
}
_0x11a7a2() 方法里有检测
try {// window["XMLHttpRequest"]["prototype"]["DONE"] 的值始终为 4var _0x42fb36 = window["XMLHttpRequest"]["prototype"]["DONE"] * 4;
} catch (_0x1b1b35) {var _0x42fb36 = 1;
}
// 改写为
var _0x42fb36 = 4 * 4;
try {window["$_z2"][0] = "Q";
} catch (_0x4c574d) {try {// window["$_zw"]["length"] 的值为 27op = window["$_zw"]["length"];} catch (_0x58af26) {var _0x3b7935 = 0;for (var _0x1badc3 = 0; _0x1badc3 < 1000000; _0x1badc3++) {_0x3b7935 = _0x3b7935 + _0x1badc3["toString"]();history["pushState"](0, 0, _0x3b7935);}}if (op > 20) {// 执行后 b64pad 的值为 1eval("b64pad = _0x4e96b4['$_zw'][9]['length'];");} else if (op < 10) {window["$_zw"] = [1, 8, 2, 4, 23, 45, 8, 15, 81, 68, 13, 72, 70];}
}
// 改写
op = 27;
b64pad = 1;
var _0x3e0c38 = _0x1171c8;
var _0xdb4d2c = _0x4dae05;
var _0x1724c5 = _0x183a1d;
var _0x257ec6 = _0xcfa373;
// 全局搜索, _0x1171c8 可以找到定义位置
// 定义在文件开头处
var _0x1171c8 = 1732584193;
var _0x4dae05 = -271733879;
var _0x183a1d = -1732584194;
var _0xcfa373 = 271733878;
try {if (window["_$6_"]) {} else {window["_$6_"] = 8821003647;}
} catch (_0x15bf3f) {window["_$6_"] = 37885443;
}
// 控制台查看对应的值为 8821003647
// 改写
window["_$6_"] = 8821003647;
第 5 次生成的位置
_$yw = _0x2d5f5b()["toString"](); // new Date()["valueOf"]();
document[_$Fe] = "m=" + _0x474032(_$yw) + "; path=/";
window["_$is"] = _$yw; // 这个在后面加密 rm4 字段时有用
window["_$pr"]["push"](_0x474032(_$yw));
m字段坑点
加密时依赖的几个变量
这几个变量都需要在 _0x11a7a2() 方法中用到
window["_$Jy"], window["_$tT"], _0x42fb36
b64pad, _0x1171c8, _0x4dae05, _0x183a1d, _0xcfa373, window["_$6_"]其实只要关注
window["_$Jy"], window["_$tT"], window["_$6_"]
在 _0x11a7a2() 方法中插上条件断点
console.log("_$Jy ", window["_$Jy"], '\n',"_$tT ", window["_$tT"], '\n',"_$6_ ", window["_$6_"], '\n',
)
for (_0x1badc3 = 0; 处
重新 hook Cookie 直到 RM4hZBv0dDon443M 字段有值为止
第 1-4 次
第 5 次(重点)
刷新页面第 5 次的值还是固定的
cookie RM4hZBv0dDon443M 字段
开启脚本断点,刷新页面注入 Hook 代码
在 Hook 到 RM4 字段并且有值的时候查看上一层堆栈
(前2次的值是 undefined 不用理)
(后两次的值是一样的)
RM4 生成位置
// _$Fe 是 ’cookie‘
document[_$Fe] = "RM4hZBv0dDon443M=" + window["_$ss"] + "; path=/";
hook window[“_$ss”]
Hook 代码
// 老样子开启脚本断点,刷新页面,注入 Hook 代码 + Cookie 代码// Hook Cookie
(function () {let cookie_func = Object.getOwnPropertyDescriptor(Document.prototype, 'cookie')Object.defineProperty(document, 'cookie', {get() {return cookie_func.get.call(this);},set(val) {console.log(val);debugger;return cookie_func.set.call(this, val);}})
})();// Hook window['_$ss']
(function () {let __$ss = window['_$ss'];Object.defineProperty(window, '_$ss', {get() {return __$ss},set(val) {debugger__$ss = val;}})
}
)()
在断点断住时查看上一层堆栈
_$ss 生成位置
// window["_$pr"] 在前5次cookie生成的时候就有值了
_$Ww = CryptoJS["enc"]["Utf8"]["parse"](window["_$pr"]["toString"]());// _0xc77418("0x6", "OCbs") 执行之后是 '_$qF'
_0x29dd83 = CryptoJS["AES"]["encrypt"](_$Ww, window[_0xc77418("0x6", "OCbs")], {"mode": CryptoJS["mode"]["ECB"],"padding": CryptoJS["pad"]["Pkcs7"]
});// _$ss 生成位置
window["_$" + "cs7"[1] + "cs7"[1]] = _0x29dd83["toString"]();
CryptoJS 在 nodejs 中直接导包就可以了 let CryptoJs = require(‘crypto-js’);
window[0xc77418(“0x6”, “OCbs”)] 也就是 window['$qF’] 生成的位置要找
Hook window[‘_$qF’]
// 和 Hook _$ss 的方式一样 开启脚本断点,注入代码后放开
// Hook Cookie
(function () {let cookie_func = Object.getOwnPropertyDescriptor(Document.prototype, 'cookie')Object.defineProperty(document, 'cookie', {get() {return cookie_func.get.call(this);},set(val) {console.log(val);debugger;return cookie_func.set.call(this, val);}})
})();// Hook window._$qF
(function () {let __$qF = window['_$qF'];Object.defineProperty(window, '_$qF', {get() {return __$qF},set(val) {debugger__$qF = val;}})
}
)()
_$qf 生成位置
window["_$qF"] = CryptoJS["enc"]["Utf8"]["parse"](window["btoa"](window["_$is"])["slice"](0, 16));// window["_$is"] 在 m 第五次生成的时候有赋值
// window["_$is"] = _$yw;
这段代码会执行很多次,插个日志断点查看值即可
console.log(window["_$is"])
// 全局搜索 _0x456805 < 213
在 m 生成的时候会给 _$yw 赋值,在赋值的位置也打上日志断点,确认是否引用了这个值
console.log(_$yw)
// 全局搜索
// _$aA["_$ji"] = _$aA[_$aA["_$ji"]](_0x2566fa, _0x56717d);
插好日志断点以后,还是刷新页面 Hook Cookie
第 5 次设置 m字段后 _$ym 有值了
window[“_$is”] 的值与 _$ym 一致
所以可以确定 $ym 就是 window["$is"]的值
请求参数
查看请求堆栈中 requests 栈
var list = {"page": window.page,"m": window._$is, // window["_$is"] 在 cookie 设置第五次时就有赋值了"f": window.$_zw[23]};
全局搜索 $_zw[23]
let $_t1 = Date.parse(new Date());
验证
在整个代码逻辑中,$_t1 是最先赋值的 (let $_t1 = Date.parse(new Date());)
在 node 也是最先定义
请求代码
完整的 js 代码
window = global;
window["_$tT"] = -172015004;
window["_$Jy"] = 461512024;
window["_$pr"] = [];
var _0x1171c8 = 1732584193;
var _0x4dae05 = -271733879;
var _0x183a1d = -1732584194;
var _0xcfa373 = 271733878;
window["_$6_"] = 8821003647;var CryptoJS = require('crypto-js');
var window = global;function _0x3180ec(_0x401705, _0x240e6a, _0x56b131, _0x5a5c20, _0x1f2a72, _0x2bfc1, _0x19741a) {return _0xaaef84(_0x240e6a & _0x5a5c20 | _0x56b131 & ~_0x5a5c20, _0x401705, _0x240e6a, _0x1f2a72, _0x2bfc1, _0x19741a);
}
function _0x32032f(_0x520fdf, _0x13921d, _0x1af9d5, _0x4a2311, _0xb6d40a, _0x1d58da, _0x361df0) {return _0xaaef84(_0x13921d ^ _0x1af9d5 ^ _0x4a2311, _0x520fdf, _0x13921d, _0xb6d40a, _0x1d58da, _0x361df0);
}
function _0x4b459d(_0x8d8f2a, _0x406d34, _0x53e7d7, _0x26c827, _0xec41ea, _0x52dead, _0x3f66e7) {return _0xaaef84(_0x53e7d7 ^ (_0x406d34 | ~_0x26c827), _0x8d8f2a, _0x406d34, _0xec41ea, _0x52dead, _0x3f66e7);
}
function _0x3634fc(_0x5803ba, _0x1ce5b2) {return _0x5803ba << _0x1ce5b2 | _0x5803ba >>> 32 - _0x1ce5b2;
}
function _0x12e4a8(_0x7542c8, _0x5eada0) {var _0x41f81f = (65535 & _0x7542c8) + (65535 & _0x5eada0);return (_0x7542c8 >> 16) + (_0x5eada0 >> 16) + (_0x41f81f >> 16) << 16 | 65535 & _0x41f81f;
}
function _0xaaef84(_0xaf3112, _0x2a165a, _0x532fb4, _0x10aa40, _0x41c4e7, _0x1cb4da) {return _0x12e4a8(_0x3634fc(_0x12e4a8(_0x12e4a8(_0x2a165a, _0xaf3112), _0x12e4a8(_0x10aa40, _0x1cb4da)), _0x41c4e7), _0x532fb4);
}
function _0x48d200(_0x4b706e, _0x3c3a85, _0x111154, _0x311f9f, _0x5439cf, _0x38cac7, _0x26bd2e) {return _0xaaef84(_0x3c3a85 & _0x111154 | ~_0x3c3a85 & _0x311f9f, _0x4b706e, _0x3c3a85, _0x5439cf, _0x38cac7, _0x26bd2e);
}
function _0x35f5f2(_0x243853) {var _0x139b8b;var _0xa791a1 = [];for (_0xa791a1[(_0x243853["length"] >> 2) - 1] = void 0,_0x139b8b = 0; _0x139b8b < _0xa791a1["length"]; _0x139b8b += 1)_0xa791a1[_0x139b8b] = 0;var _0x41a533 = 8 * _0x243853["length"];for (_0x139b8b = 0; _0x139b8b < _0x41a533; _0x139b8b += 8)_0xa791a1[_0x139b8b >> 5] |= (255 & _0x243853["charCodeAt"](_0x139b8b / 8)) << _0x139b8b % 32;return _0xa791a1;
}
function _0x474032(_0x233f82, _0xe2ed33, _0x3229f9) {return _0x37614a(_0x233f82);
}
function _0x37614a(_0x32e7c1) {return _0x499969(_0x41873d(_0x32e7c1));
}
function _0x41873d(_0x5a6962) {return _0x1ee7ec(_0x2b8a17(_0x5a6962));
}
function _0x2b8a17(_0x36f847) {return unescape(encodeURIComponent(_0x36f847));
}
function _0x1ee7ec(_0x206333) {return _0x12b47d(_0x11a7a2(_0x35f5f2(_0x206333), 8 * _0x206333["length"]));
}
function _0x499969(_0x82fe7e) {var _0x5bdda4;var _0x322a73;var _0xd0b5cd = "0123456789abcdef";var _0x21f411 = "";for (_0x322a73 = 0; _0x322a73 < _0x82fe7e["length"]; _0x322a73 += 1) {_0x5bdda4 = _0x82fe7e["charCodeAt"](_0x322a73);_0x21f411 += _0xd0b5cd["charAt"](_0x5bdda4 >>> 4 & 15) + _0xd0b5cd["charAt"](15 & _0x5bdda4);}return _0x21f411;
}
function _0x12b47d(_0x149183) {var _0xabbcb3;var _0x1145c3 = "";var _0x4fce58 = 32 * _0x149183["length"];for (_0xabbcb3 = 0; _0xabbcb3 < _0x4fce58; _0xabbcb3 += 8)_0x1145c3 += String["fromCharCode"](_0x149183[_0xabbcb3 >> 5] >>> _0xabbcb3 % 32 & 255);return _0x1145c3;
}
function _0x11a7a2(_0x193f00, _0x1cfe89) {_0x193f00[_0x1cfe89 >> 5] |= 128 << _0x1cfe89 % 32;_0x193f00[14 + (_0x1cfe89 + 64 >>> 9 << 4)] = _0x1cfe89;var _0x42fb36 = 4 * 4;op = 27;b64pad = 1;var _0x1badc3;var _0x38ca59;var _0x431764;var _0x43f1b4;var _0x5722c0;var _0x3e0c38 = _0x1171c8;var _0xdb4d2c = _0x4dae05;var _0x1724c5 = _0x183a1d;var _0x257ec6 = _0xcfa373;for (_0x1badc3 = 0; _0x1badc3 < _0x193f00["length"]; _0x1badc3 += _0x42fb36) {_0x38ca59 = _0x3e0c38;_0x431764 = _0xdb4d2c;_0x43f1b4 = _0x1724c5;_0x5722c0 = _0x257ec6;_0x3e0c38 = _0x48d200(_0x3e0c38, _0xdb4d2c, _0x1724c5, _0x257ec6, _0x193f00[_0x1badc3], 7, 513548);_0x257ec6 = _0x48d200(_0x257ec6, _0x3e0c38, _0xdb4d2c, _0x1724c5, _0x193f00[_0x1badc3 + 1], 12, window["_$6_"]);_0x1724c5 = _0x48d200(_0x1724c5, _0x257ec6, _0x3e0c38, _0xdb4d2c, _0x193f00[_0x1badc3 + 2], 17, 606105819);_0xdb4d2c = _0x48d200(_0xdb4d2c, _0x1724c5, _0x257ec6, _0x3e0c38, _0x193f00[_0x1badc3 + 3], 22, -1044525330);_0x3e0c38 = _0x48d200(_0x3e0c38, _0xdb4d2c, _0x1724c5, _0x257ec6, _0x193f00[_0x1badc3 + 4], 7, -176418897);_0x257ec6 = _0x48d200(_0x257ec6, _0x3e0c38, _0xdb4d2c, _0x1724c5, _0x193f00[_0x1badc3 + 5], 12, 1200080426);_0x1724c5 = _0x48d200(_0x1724c5, _0x257ec6, _0x3e0c38, _0xdb4d2c, _0x193f00[_0x1badc3 + 6], 17, -1473231341);_0xdb4d2c = _0x48d200(_0xdb4d2c, _0x1724c5, _0x257ec6, _0x3e0c38, _0x193f00[_0x1badc3 + 7], 22, -45705983);_0x3e0c38 = _0x48d200(_0x3e0c38, _0xdb4d2c, _0x1724c5, _0x257ec6, _0x193f00[_0x1badc3 + 8], 7, 1770035416);_0x257ec6 = _0x48d200(_0x257ec6, _0x3e0c38, _0xdb4d2c, _0x1724c5, _0x193f00[_0x1badc3 + 9], 12, -1958414417);_0x1724c5 = _0x48d200(_0x1724c5, _0x257ec6, _0x3e0c38, _0xdb4d2c, _0x193f00[_0x1badc3 + 10], 17, -42063);_0xdb4d2c = _0x48d200(_0xdb4d2c, _0x1724c5, _0x257ec6, _0x3e0c38, _0x193f00[_0x1badc3 + 11], 22, -1990404162);_0x3e0c38 = _0x48d200(_0x3e0c38, _0xdb4d2c, _0x1724c5, _0x257ec6, _0x193f00[_0x1badc3 + 12], 7, 1804603682);_0x257ec6 = _0x48d200(_0x257ec6, _0x3e0c38, _0xdb4d2c, _0x1724c5, _0x193f00[_0x1badc3 + 13], 12, -40341101);_0x1724c5 = _0x48d200(_0x1724c5, _0x257ec6, _0x3e0c38, _0xdb4d2c, _0x193f00[_0x1badc3 + 14], 17, -1502002290);_0xdb4d2c = _0x48d200(_0xdb4d2c, _0x1724c5, _0x257ec6, _0x3e0c38, _0x193f00[_0x1badc3 + 15], 22, 1236535329);_0x3e0c38 = _0x3180ec(_0x3e0c38, _0xdb4d2c, _0x1724c5, _0x257ec6, _0x193f00[_0x1badc3 + 1], 5, -165796510);_0x257ec6 = _0x3180ec(_0x257ec6, _0x3e0c38, _0xdb4d2c, _0x1724c5, _0x193f00[_0x1badc3 + 6], 9, -1069501632);_0x1724c5 = _0x3180ec(_0x1724c5, _0x257ec6, _0x3e0c38, _0xdb4d2c, _0x193f00[_0x1badc3 + 11], 14, 643717713);_0xdb4d2c = _0x3180ec(_0xdb4d2c, _0x1724c5, _0x257ec6, _0x3e0c38, _0x193f00[_0x1badc3], 20, -373897302);_0x3e0c38 = _0x3180ec(_0x3e0c38, _0xdb4d2c, _0x1724c5, _0x257ec6, _0x193f00[_0x1badc3 + 5], 5, -701558691);_0x257ec6 = _0x3180ec(_0x257ec6, _0x3e0c38, _0xdb4d2c, _0x1724c5, _0x193f00[_0x1badc3 + 10], 9, 38016083);_0x1724c5 = _0x3180ec(_0x1724c5, _0x257ec6, _0x3e0c38, _0xdb4d2c, _0x193f00[_0x1badc3 + 15], 14, window["_$tT"]);_0xdb4d2c = _0x3180ec(_0xdb4d2c, _0x1724c5, _0x257ec6, _0x3e0c38, _0x193f00[_0x1badc3 + 4], 20, window["_$Jy"]);_0x3e0c38 = _0x3180ec(_0x3e0c38, _0xdb4d2c, _0x1724c5, _0x257ec6, _0x193f00[_0x1badc3 + 9], 5, 568446438);_0x257ec6 = _0x3180ec(_0x257ec6, _0x3e0c38, _0xdb4d2c, _0x1724c5, _0x193f00[_0x1badc3 + 14], 9, -1019783690);_0x1724c5 = _0x3180ec(_0x1724c5, _0x257ec6, _0x3e0c38, _0xdb4d2c, _0x193f00[_0x1badc3 + 3], 14, -187363961);_0xdb4d2c = _0x3180ec(_0xdb4d2c, _0x1724c5, _0x257ec6, _0x3e0c38, _0x193f00[_0x1badc3 + 8], 20, 1163531501);_0x3e0c38 = _0x3180ec(_0x3e0c38, _0xdb4d2c, _0x1724c5, _0x257ec6, _0x193f00[_0x1badc3 + 13], 5, -1554681467);_0x257ec6 = _0x3180ec(_0x257ec6, _0x3e0c38, _0xdb4d2c, _0x1724c5, _0x193f00[_0x1badc3 + 2], 9, -51403784);_0x1724c5 = _0x3180ec(_0x1724c5, _0x257ec6, _0x3e0c38, _0xdb4d2c, _0x193f00[_0x1badc3 + 7], 14, 1735328473);_0xdb4d2c = _0x3180ec(_0xdb4d2c, _0x1724c5, _0x257ec6, _0x3e0c38, _0x193f00[_0x1badc3 + 12], 20, -1926607734);_0x3e0c38 = _0x32032f(_0x3e0c38, _0xdb4d2c, _0x1724c5, _0x257ec6, _0x193f00[_0x1badc3 + 5], 4, -37824558);_0x257ec6 = _0x32032f(_0x257ec6, _0x3e0c38, _0xdb4d2c, _0x1724c5, _0x193f00[_0x1badc3 + 8], 11, -2022574463);_0x1724c5 = _0x32032f(_0x1724c5, _0x257ec6, _0x3e0c38, _0xdb4d2c, _0x193f00[_0x1badc3 + 11], 16, 1839030562);_0xdb4d2c = _0x32032f(_0xdb4d2c, _0x1724c5, _0x257ec6, _0x3e0c38, _0x193f00[_0x1badc3 + 14], 23, -35309556);_0x3e0c38 = _0x32032f(_0x3e0c38, _0xdb4d2c, _0x1724c5, _0x257ec6, _0x193f00[_0x1badc3 + 1], 4, -1530992060 * b64pad);_0x257ec6 = _0x32032f(_0x257ec6, _0x3e0c38, _0xdb4d2c, _0x1724c5, _0x193f00[_0x1badc3 + 4], 11, 1272893353);_0x1724c5 = _0x32032f(_0x1724c5, _0x257ec6, _0x3e0c38, _0xdb4d2c, _0x193f00[_0x1badc3 + 7], 16, -155497632);_0xdb4d2c = _0x32032f(_0xdb4d2c, _0x1724c5, _0x257ec6, _0x3e0c38, _0x193f00[_0x1badc3 + 10], 23, -1094730640);_0x3e0c38 = _0x32032f(_0x3e0c38, _0xdb4d2c, _0x1724c5, _0x257ec6, _0x193f00[_0x1badc3 + 13], 4, 681279174);_0x257ec6 = _0x32032f(_0x257ec6, _0x3e0c38, _0xdb4d2c, _0x1724c5, _0x193f00[_0x1badc3], 11, -358537222);_0x1724c5 = _0x32032f(_0x1724c5, _0x257ec6, _0x3e0c38, _0xdb4d2c, _0x193f00[_0x1badc3 + 3], 16, -722521979);_0xdb4d2c = _0x32032f(_0xdb4d2c, _0x1724c5, _0x257ec6, _0x3e0c38, _0x193f00[_0x1badc3 + 6], 23, 760291289);_0x3e0c38 = _0x32032f(_0x3e0c38, _0xdb4d2c, _0x1724c5, _0x257ec6, _0x193f00[_0x1badc3 + 9], 4, -64036887);_0x257ec6 = _0x32032f(_0x257ec6, _0x3e0c38, _0xdb4d2c, _0x1724c5, _0x193f00[_0x1badc3 + 12], 11, -421815835);_0x1724c5 = _0x32032f(_0x1724c5, _0x257ec6, _0x3e0c38, _0xdb4d2c, _0x193f00[_0x1badc3 + 15], 16, 530742520);_0xdb4d2c = _0x32032f(_0xdb4d2c, _0x1724c5, _0x257ec6, _0x3e0c38, _0x193f00[_0x1badc3 + 2], 23, -995338651);_0x3e0c38 = _0x4b459d(_0x3e0c38, _0xdb4d2c, _0x1724c5, _0x257ec6, _0x193f00[_0x1badc3], 6, -198630844);_0x257ec6 = _0x4b459d(_0x257ec6, _0x3e0c38, _0xdb4d2c, _0x1724c5, _0x193f00[_0x1badc3 + 7], 10, 1126891415);_0x1724c5 = _0x4b459d(_0x1724c5, _0x257ec6, _0x3e0c38, _0xdb4d2c, _0x193f00[_0x1badc3 + 14], 15, -1416354905);_0xdb4d2c = _0x4b459d(_0xdb4d2c, _0x1724c5, _0x257ec6, _0x3e0c38, _0x193f00[_0x1badc3 + 5], 21, -57434055);_0x3e0c38 = _0x4b459d(_0x3e0c38, _0xdb4d2c, _0x1724c5, _0x257ec6, _0x193f00[_0x1badc3 + 12], 6, 1700485571);_0x257ec6 = _0x4b459d(_0x257ec6, _0x3e0c38, _0xdb4d2c, _0x1724c5, _0x193f00[_0x1badc3 + 3], 10, -1894746606);_0x1724c5 = _0x4b459d(_0x1724c5, _0x257ec6, _0x3e0c38, _0xdb4d2c, _0x193f00[_0x1badc3 + 10], 15, -105181523);_0xdb4d2c = _0x4b459d(_0xdb4d2c, _0x1724c5, _0x257ec6, _0x3e0c38, _0x193f00[_0x1badc3 + 1], 21, -2054922799);_0x3e0c38 = _0x4b459d(_0x3e0c38, _0xdb4d2c, _0x1724c5, _0x257ec6, _0x193f00[_0x1badc3 + 8], 6, 1873313359);_0x257ec6 = _0x4b459d(_0x257ec6, _0x3e0c38, _0xdb4d2c, _0x1724c5, _0x193f00[_0x1badc3 + 15], 10, -30611744);_0x1724c5 = _0x4b459d(_0x1724c5, _0x257ec6, _0x3e0c38, _0xdb4d2c, _0x193f00[_0x1badc3 + 6], 15, -1560198380);_0xdb4d2c = _0x4b459d(_0xdb4d2c, _0x1724c5, _0x257ec6, _0x3e0c38, _0x193f00[_0x1badc3 + 13], 21, 1309151649);_0x3e0c38 = _0x4b459d(_0x3e0c38, _0xdb4d2c, _0x1724c5, _0x257ec6, _0x193f00[_0x1badc3 + 4], 6, -145523070);_0x257ec6 = _0x4b459d(_0x257ec6, _0x3e0c38, _0xdb4d2c, _0x1724c5, _0x193f00[_0x1badc3 + 11], 10, -1120211379);_0x1724c5 = _0x4b459d(_0x1724c5, _0x257ec6, _0x3e0c38, _0xdb4d2c, _0x193f00[_0x1badc3 + 2], 15, 718787259);_0xdb4d2c = _0x4b459d(_0xdb4d2c, _0x1724c5, _0x257ec6, _0x3e0c38, _0x193f00[_0x1badc3 + 9], 21, -343485441);_0x3e0c38 = _0x12e4a8(_0x3e0c38, _0x38ca59);_0xdb4d2c = _0x12e4a8(_0xdb4d2c, _0x431764);_0x1724c5 = _0x12e4a8(_0x1724c5, _0x43f1b4);_0x257ec6 = _0x12e4a8(_0x257ec6, _0x5722c0);}return [_0x3e0c38, _0xdb4d2c, _0x1724c5, _0x257ec6];
}function sdk() {let $_t1 = Date.parse(new Date());// cookie_m 1-4 次for (let i = 0; i < 4; i++) {let _$Wa = Date["parse"](new Date());cookie_m = _0x474032(_$Wa);window["_$pr"].push(_0x474032(_$Wa));delete window["_$Jy"];delete window["_$tT"];window["_$Jy"] = new Date()["valueOf"]();window["_$tT"] = new Date()["valueOf"]() - Date["parse"](new Date());}// cookie_m 第五次window["_$Jy"] = -405537848;window["_$tT"] = -660478335;window["_$6_"] = -389564586;let _$yw = new Date()["valueOf"]();cookie_m = _0x474032(_$yw);window["_$is"] = _$yw;window["_$pr"]["push"](_0x474032(_$yw));// console.log(cookie_m);// console.log(_$pr);window["_$qF"] = CryptoJS["enc"]["Utf8"]["parse"](window["btoa"](window["_$is"])["slice"](0, 16));let _$Ww = CryptoJS["enc"]["Utf8"]["parse"](window["_$pr"]["toString"]());let cookie_rm4 = CryptoJS["AES"]["encrypt"](_$Ww, window['_$qF'], {"mode": CryptoJS["mode"]["ECB"],"padding": CryptoJS["pad"]["Pkcs7"]}).toString();// console.log(cookie_m);// console.log(cookie_rm4);return {cookie_m,cookie_rm4,params_m: window["_$is"],params_f: $_t1}
}
// console.log(sdk());
完整的 python 代码
import requests
import execjs
import timeheaders = {"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","m": "","RM4hZBv0dDon443M": ""
}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_match5(page, param_dict):url = "https://match.yuanrenxue.cn/api/match/5"params = {"page": page,"m": param_dict['params_m'],"f": param_dict['params_f']}response = requests.get(url, headers=headers, cookies=cookies, params=params)cookies["m"] = param_dict['cookie_m']cookies["RM4hZBv0dDon443M"] = param_dict['cookie_rm4']print(params)print(cookies)print(response.json())print('==================================================')if __name__ == '__main__':time_ = int(str(int(time.time() * 1000))[:10] + '000')send_params = call_js('5.js', 'sdk') # params_m, cookie_m, cookie_rm4send_params['params_f'] = time_for page in range(1, 3):send_match5(page, send_params)