【困难】 猿人学web第一届 第14题 备而后动-勿使有变

2024-09-02 08:36

本文主要是介绍【困难】 猿人学web第一届 第14题 备而后动-勿使有变,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

调试干扰

进入题目 打开开发者工具会进入一个无限 debugger;

向上查看堆栈,可以找到生成 debugger 的代码段在这里插入图片描述

手动解混淆后可以知道 debugger 生成的方式在这里插入图片描述

(function () {// 函数内的代码是不需要的,因为里面的代码不会执行
}['constructor']('debugger')['call']('action'));

是利用 Function.prototype.constructor 函数生成的 debugger
因为 (function(){}[‘constructor’]) === Function.prototype.constructor;
在这里插入图片描述

Hook 代码

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

请求流程

数据接口 https://match.yuanrenxue.cn/api/match/14在这里插入图片描述
请求参数为 对应的页码在这里插入图片描述
cookie 参数需要携带 m,mz 字段
mz 字段是一样的
m 字段是动态的
在这里插入图片描述
在这里插入图片描述

每次请求数据前,都会先请求 m文件
m 文件是动态的,返回的是 js 代码在这里插入图片描述> 在这里插入图片描述

Hook Cookie

请求数据时需要携带 cookie 中 m 和 mz 参数

// Hook Function
_Function = Function.prototype.constructor;
Function.prototype.constructor = function (val) {if(val.indexOf('debugger') !== -1){return _Function.call(this, '')}return _Function.call(this, val)
};
// Hook Cookie
(function(){const cookieSet = Object.getOwnPropertyDescriptor(Document.prototype, 'cookie')Object.defineProperty(document, 'cookie', {get(){return cookieSet.get.call(this);},set(val){debuggerreturn cookieSet.set.call(this, val);}})
})()

勾选脚本断点 --> 刷新页面 --> 注入 Hook 代码 --> 取消勾选脚本断点 --> 放开断点
在这里插入图片描述
在这里插入图片描述
生成 cookie 的 m.js 文件是经过混淆的,给 m.js 安排一下 AST

AST 还原 m.js文件

先保存一份 m.js 文件到本地

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语法树// 这里插入解析规则 ==============================================================================================================================================================================// unicode, 十六进制数值
traverse(ast, {StringLiteral(path) {delete path.node.extra.raw;},NumericLiteral(path) {delete path.node.extra.raw;}
})// 字符串相加
function addStr(path) {if (path.get('left').isStringLiteral() && path.get('right').isStringLiteral()) {let left = path.node.left.value;let right = path.node.right.value;let result = left + rightpath.replaceWith(types.stringLiteral(result))} else if (path.get('left').isBinaryExpression() && path.get('right').isStringLiteral()) {path.traverse({BinaryExpression(path_) {addStr(path_)}})}
}for (let i = 0; i < 2; i++) {traverse(ast, {BinaryExpression(path) {addStr(path)}})
}// 逗号表达式
traverse(ast, {VariableDeclaration(path) {if (path.get('declarations').length > 1) {let kind = path.node.kindlet declarations = path.node.declarationslet nodeList = []for (const idx in declarations) {let newNode = types.variableDeclaration(kind, [declarations[idx]])nodeList.push(newNode)}path.replaceWithMultiple(nodeList)}},SequenceExpression(path) {if (path.get('expressions').length > 1) {let expressions = path.node.expressions;let nodeList = [];for (const idx in expressions) {nodeList.push(expressions[idx])}path.replaceWithMultiple(nodeList)path.skip()}},ReturnStatement(path) {if (path.node.argument && path.get('argument.expressions').length > 1) {let expressions = path.node.argument.expressions;let nodeList = expressions.slice(0, -1);let retNOde = types.returnStatement(expressions[expressions.length - 1])nodeList.push(retNOde)path.replaceWithMultiple(nodeList)}}
})js_code = generator(ast, {compact: false,  // 是否压缩,默认 false
}).codeast = parser.parse(js_code);
function addObj(path, newObj) {let name = path.node.id.name;let binding = path.scope.getBinding(name);if (binding) {let refPath = binding.referencePaths;for (const idx in refPath) {let grandPath = refPath[idx].parentPath.parentPath;if(grandPath.isAssignmentExpression() && grandPath.get('left').isMemberExpression() && grandPath.get('left.property').isStringLiteral()){let key = grandPath.node.left.property.value;newObj[key] = grandPath.get('right');}else if(grandPath.isVariableDeclaration()){repObj(grandPath, newObj)}}}
}
function repObj(path, newObj) {let name = path.node.declarations[0].id.name;let binding = path.scope.getBinding(name);if(binding){let refPath = binding.referencePaths.reverse();for(const idx in refPath){let parPath = refPath[idx].parentPath;if(parPath.isMemberExpression() && parPath.get('property').isStringLiteral()){let key = parPath.node.property.value;if(newObj[key].isStringLiteral()){parPath.replaceWith(newObj[key].node);}else if (newObj[key].isFunctionExpression() && newObj[key].get('body.body').length === 1){let returnStatement = newObj[key].get('body.body')[0].get('argument');let grandPath = parPath.parentPath;if(returnStatement.isBinaryExpression() || returnStatement.isLogicalExpression()){let operator = returnStatement.node.operator;let callArg = grandPath.node.arguments;let newNode = returnStatement.isBinaryExpression()? types.binaryExpression(operator, callArg[0], callArg[1]): types.logicalExpression(operator, callArg[0], callArg[1]) ;grandPath.replaceWith(newNode);}else if(returnStatement.isCallExpression()){let callArg = grandPath.node.arguments;let newNode = types.CallExpression(callArg[0], callArg.slice(1))grandPath.replaceWith(newNode);}}}}}
}
// 花指令
traverse(ast, {VariableDeclarator(path) {if (path.get('init').isObjectExpression()) {let newObj = {};addObj(path, newObj)}}
})// 字符串相加
for (let i = 0; i < 2; i++) {traverse(ast, {BinaryExpression(path) {addStr(path)}})
}js_code = generator(ast, {compact: false,  // 是否压缩,默认 false
}).code
ast = parser.parse(js_code);// switch
traverse(ast, {WhileStatement(path){if(path.inList && path.get('test').isUnaryExpression()){let idxArray = path.getSibling(path.key - 2).node.declarations[0].init.callee.object.value.split('|')let cases = path.get('body.body')[0].get('cases')let idxObj = {}for (const idx in cases){let key = cases[idx].get('test').node.value;idxObj[key] = cases[idx].get('consequent')}let nodeArr = []idxArray.forEach((objkey) => {for (const arrIdx in idxObj[objkey]){if(!idxObj[objkey][arrIdx].isContinueStatement()){nodeArr.push(idxObj[objkey][arrIdx].node)}}})for (let i=0; i<=path.key; i++){path.getSibling(0).remove()}path.replaceWithMultiple(nodeArr)}}
})// ==============================================================================================================================================================================
js_code = generator(ast, {compact: false,  // 是否压缩,默认 false
}).code  // 将ast节点转换成js代码// 写入
fs.writeFileSync('New_demo.js', js_code, {encoding: 'utf-8',
})

将 m.js 内容替换成还原后的继续 Hook cookie

Hook 到 cookie生成的位置后,向上查看堆栈在这里插入图片描述

mz 字段生成的位置在这里插入图片描述

m 字段生成的位置
在这里插入图片描述

加密流程

cookie mz 字段

Hook Cookie 后首先看到的是 mz 字段
在这里插入图片描述

向上查看堆栈,查看生成 cookie mz 字段的代码段
在这里插入图片描述
b64_zw 通过 btoa(z) 生成 z为数组,环境值

mz 字段可以固定,不需要在本地生成,每个人的电脑环境不一样,生成的值也不一样,
同一台电脑的 mz 字段是一样的

cookie m 字段

在这里插入图片描述

向上查看堆栈
在这里插入图片描述

正常情况下 Object[“defineProperty”] 不传参数执行是会报错的
在这里插入图片描述
但在 14 题的页面中执行返回的是空字符串
在这里插入图片描述

因为在文件的开头 Object[“defineProperty”] 就被重写了
(所以 hook cookie 要在脚本执行前 hook才能 hook 到)
在这里插入图片描述

所以生成 m 字段 代码会执行 false 条件的代码段

document["cookie"] = "m=" + m5(gee(aa, bb, c, d, e, b64_zw)) + "|" + b + "|" + a + "|" + window["n"] + ";path=/";

在这里插入图片描述

现在 window[‘n’], window[‘v142’], window[‘v14’] 还不知道是怎么生成的

hook window 的 ‘n’, ‘v142’, ‘v14’ 属性

勾选脚本断点,在页面脚本开始执行前 hook

function HookFunc(obj, key) {let blockVar = obj[key];Object.defineProperty(obj, key, {get() {return blockVar},set(val) {console.log(key, val)debugger;blockVar = val;}})
}
['n', 'v142', 'v14'].forEach((value) => {HookFunc(window, value)})

window[‘n’]
在这里插入图片描述

window[‘v142’]
在这里插入图片描述
在这里插入图片描述

window[‘‘v14’’]
在这里插入图片描述
在这里插入图片描述

注意

m 文件是动态的,n值是固定的0,但 v14, v142 的值是动态变化的

生成 v14, v142 属性的代码段是在 eval 函数里生成的 Y 是请求
在这里插入图片描述
Y 是请求 “/api/match/14/m” 后返回的数据
在这里插入图片描述
在这里插入图片描述

将 “/api/match/14/m” 接口里的代码还原后进行替换
在这里插入图片描述

m 代码反混淆

先将将 m 文件替换
在这里插入图片描述
将代码保存到本地 demo.js

解密函数

前三列是解密函数和对应的依赖,还原时是需要用到的在这里插入图片描述
解密函数内部是有格式化检测的,这里将 REgExp.prototype.test 方法重写即可在这里插入图片描述

代码
// 安装 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语法树// 这里插入解析规则 ==============================================================================================================================================================================
// unicode, 十六进制数值
traverse(ast, {StringLiteral(path) {delete path.node.extra.raw;},NumericLiteral(path) {delete path.node.extra.raw;}
})RegExp.prototype.test = function(){return true}
var $_0x5109 = [解密函数数组];
(function (_0x3608d7, _0x510963) {// 自执行数组乱序
}($_0x5109, 0x147));
var $_0x5d3c = function (_0x3608d7, _0x510963) {// 解密函数return _0x5d3c4d;
};
// 解密函数还原字符串
traverse(ast, {CallExpression(path){if(path.get('callee').isIdentifier({name: $_0x5d3c.name})){let callArg = path.node.arguments;let result = $_0x5d3c(callArg[0].value, callArg[1].value);path.replaceWith(types.StringLiteral(result))}}
})// 字符串相加
function addStr(path) {if (path.get('left').isStringLiteral() && path.get('right').isStringLiteral()) {let left = path.node.left.value;let right = path.node.right.value;let result = left + rightpath.replaceWith(types.stringLiteral(result))} else if (path.get('left').isBinaryExpression() && path.get('right').isStringLiteral()) {path.traverse({BinaryExpression(path_) {addStr(path_)}})}
}
for (let i = 0; i < 2; i++) {traverse(ast, {BinaryExpression(path) {addStr(path)}})
}// 解决正则字符串报错
traverse(ast, {StringLiteral(path){let result = path.node.value;if(result.indexOf('"') !== -1 || result.indexOf("'") !== -1 || result.indexOf("\\") !== -1){path.replaceWith(types.templateLiteral([types.templateElement({raw: result})],[]))}}
})// 逗号表达式
traverse(ast, {VariableDeclaration(path) {if (path.get('declarations').length > 1) {let kind = path.node.kindlet declarations = path.node.declarationslet nodeList = []for (const idx in declarations) {let newNode = types.variableDeclaration(kind, [declarations[idx]])nodeList.push(newNode)}path.replaceWithMultiple(nodeList)}},SequenceExpression(path) {if (path.get('expressions').length > 1) {let expressions = path.node.expressions;let nodeList = [];for (const idx in expressions) {nodeList.push(expressions[idx])}path.replaceWithMultiple(nodeList)path.skip()}},ReturnStatement(path) {if (path.node.argument && path.get('argument.expressions').length > 1) {let expressions = path.node.argument.expressions;let nodeList = expressions.slice(0, -1);let retNOde = types.returnStatement(expressions[expressions.length - 1])nodeList.push(retNOde)path.replaceWithMultiple(nodeList)}}
})js_code = generator(ast, {compact: false,  // 是否压缩,默认 false
}).code
ast = parser.parse(js_code);// 花指令
function addObj(path, newObj) {let name = path.node.id.name;let binding = path.scope.getBinding(name);if (binding) {let refPath = binding.referencePaths;for (const idx in refPath) {let grandPath = refPath[idx].parentPath.parentPath;if(grandPath.isAssignmentExpression() && grandPath.get('left').isMemberExpression() && grandPath.get('left.property').isStringLiteral()){let key = grandPath.node.left.property.value;newObj[key] = grandPath.get('right');}else if(grandPath.isVariableDeclaration()){repObj(grandPath, newObj)}}}
}
function repObj(path, newObj) {let name = path.node.declarations[0].id.name;let binding = path.scope.getBinding(name);if(binding){let refPath = binding.referencePaths.reverse();for(const idx in refPath){let parPath = refPath[idx].parentPath;if(parPath.isMemberExpression() && parPath.get('property').isStringLiteral()){let key = parPath.node.property.value;if(newObj[key].isStringLiteral() || newObj[key].isTemplateLiteral()){parPath.replaceWith(newObj[key].node);}else if (newObj[key].isFunctionExpression() && newObj[key].get('body.body').length === 1){let returnStatement = newObj[key].get('body.body')[0].get('argument');let grandPath = parPath.parentPath;if(returnStatement.isBinaryExpression() || returnStatement.isLogicalExpression()){let operator = returnStatement.node.operator;let callArg = grandPath.node.arguments;let newNode = returnStatement.isBinaryExpression()? types.binaryExpression(operator, callArg[0], callArg[1]): types.logicalExpression(operator, callArg[0], callArg[1]) ;grandPath.replaceWith(newNode);}else if(returnStatement.isCallExpression()){let callArg = grandPath.node.arguments;let newNode = types.CallExpression(callArg[0], callArg.slice(1))grandPath.replaceWith(newNode);}}else{console.log('未处理的类型', newObj[key].type)console.log(newObj[key].toString())}}}}
}
traverse(ast, {VariableDeclarator(path) {if (path.get('init').isObjectExpression()) {let newObj = {};addObj(path, newObj)}}
})// 字符串相加
for (let i = 0; i < 2; i++) {traverse(ast, {BinaryExpression(path) {addStr(path)}})
}// switch
traverse(ast, {WhileStatement(path){if(path.inList && path.get('test').isUnaryExpression()){let idxArray = path.getSibling(path.key - 2).node.declarations[0].init.callee.object.value.split('|')let cases = path.get('body.body')[0].get('cases')let idxObj = {}for (const idx in cases){let key = cases[idx].get('test').node.value;idxObj[key] = cases[idx].get('consequent')}let nodeArr = []idxArray.forEach((objkey) => {for (const arrIdx in idxObj[objkey]){if(!idxObj[objkey][arrIdx].isContinueStatement()){nodeArr.push(idxObj[objkey][arrIdx].node)}}})for (let i=0; i<=path.key; i++){path.getSibling(0).remove()}path.replaceWithMultiple(nodeArr)}}
})// 无用 if
traverse(ast, {"IfStatement"(path){if(path.get('test').isStringLiteral()){let test = path.node.test.value;let ifStatement = path.node.consequent;let elseStatement = path.node.alternate;let result = Boolean(test);result ? path.replaceWith(ifStatement) : path.replaceWith(elseStatement)}}
})// ==============================================================================================================================================================================
js_code = generator(ast, {compact: false,  // 是否压缩,默认 false
}).code  // 将ast节点转换成js代码// 写入(路径记得改)
fs.writeFileSync('New_demo.js', js_code, {encoding: 'utf-8',
})

全局搜索 test, 将对应的 Regexp 注释,将 test 改为 true (格式化检测)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

将 m 文件替换成还原后的代码
在这里插入图片描述

补环境

使用反混淆后的 m.js / m文件补环境会轻松的多(能看得到操作浏览器哪些对象的属性)

在本地执行文件,首先报错 window is not defined在这里插入图片描述
在文件头部声明 window = global;

在文件头部声明 document = {};
在文件尾部打印 console.log(document.cookie)
再次运行就没有报错了,document.cookie 打印出来的值是 undefined
在这里插入图片描述

观察浏览器对应的页面,是执行了 sp() 方法后才进入流程的,在本地也执行 sp() 方法
在这里插入图片描述
执行后报错了
在这里插入图片描述
看看这个代码段 和 cookie mz字段的生成有关系
在这里插入图片描述

这里拿的都是自己的环境值

Object.prototype.toString = function(){return `[object ${this.constructor.name}]`
}
function NetworkInformation(){}
function Geolocation(){}
function MediaCapabilities(){}
function MediaSession(){}
function MimeTypeArray(){}
function Permissions(){}
function PluginArray(){}
function UserActivation(){}
function DeprecatedStorageQuota(){}
navigator = {};
navigator["appCodeName"] = 拿你自己的;
navigator["appName"] = 拿你自己的;
navigator["appVersion"] = '拿你自己的
navigator["connection"] = new NetworkInformation();
navigator["cookieEnabled"] = true
navigator["doNotTrack"] = null
navigator["geolocation"] = new Geolocation();
navigator["hardwareConcurrency"] = 拿你自己的;
navigator["language"] = '拿你自己的';
navigator["languages"] = [拿你自己的];
navigator["maxTouchPoints"] = 0
navigator["mediaCapabilities"] = new MediaCapabilities();
navigator["mediaSession"] = new MediaSession();
navigator["mimeTypes"] = new MimeTypeArray();
navigator["onLine"] = true
navigator["permissions"] = new Permissions();
navigator["platform"] = 'Win32';
navigator["plugins"] = new PluginArray();
navigator["product"] = 'Gecko';
navigator["productSub"] = '20030107';
navigator["userActivation"] = new UserActivation();
navigator["userAgent"] = '拿你自己的';
navigator["vendor"] = '拿你自己的';
navigator["vendorSub"] = '';
navigator["webkitPersistentStorage"] = new DeprecatedStorageQuota();
navigator["webkitTemporaryStorage"] = new DeprecatedStorageQuota();screen = {}
function ScreenOrientation(){}
screen["availHeight"] = 1040
screen["availLeft"] = 0
screen["availTop"] = 0
screen["availWidth"] = 1920
screen["colorDepth"] = 24
screen["height"] = 1080
screen["orientation"] = new ScreenOrientation();
screen["pixelDepth"] = 24
screen["width"] = 1920;function DOMStringList(){}
document = {};
document["location"] = {};
document["location"]["ancestorOrigins"] = new DOMStringList();
document["location"]["hash"] = ''
document["location"]["host"] = 'match.yuanrenxue.cn'
document["location"]["hostname"] = 'match.yuanrenxue.cn'
document["location"]["href"] = 'https://match.yuanrenxue.cn/match/14'
document["location"]["origin"] = 'https://match.yuanrenxue.cn'
document["location"]["pathname"] = '/match/14'
document["location"]["port"] = ''
document["location"]["protocol"] = 'https:'
document["location"]["search"] = ''document["location"]["assign"] = function assign(){}
document["location"]["assign"].toString = function (){return 'function assign() { [native code] }'}document["location"]["reload"] = function reload(){}
document["location"]["reload"].toString = function (){return 'function reload() { [native code] }'}document["location"]["replace"] = function replace(){}
document["location"]["replace"].toString = function (){return 'function replace() { [native code] }'}document["location"]["toString"] = function toString(){}
document["location"]["toString"].toString = function (){return 'function toString() { [native code] }'}document["location"]["valueOf"] =  function valueOf(){}
document["location"]["valueOf"].toString = function (){return 'function valueOf() { [native code] }'}

再次执行后报错 $ is not defined
在这里插入图片描述
看看这段代码
在这里插入图片描述
再看看浏览器,浏览器里是执行了 success 方法,并且用 eval 执行了接口返回的代码
k 是接口返回的内容
在这里插入图片描述
将这个方法补上

$ = {'ajax': function(){// 替换成 eval 内的代码段}
}

继续执行后报错 ASN1 is not defined
在这里插入图片描述
ASN1 是有给 window 赋值的
在这里插入图片描述
单独调用 ASN1 会报错 undefined
在这里插入图片描述
因为 window 并不是全局变量 global 才是
如果赋值的代码为 global[‘ASN1’] = … 再单独调用 ASN1 是找得到值的
设置 window 的 属性的描述符

// 全局对象有两个,globalTHis, global
// 可以通过 globalThis 查看 global(globalThis.global)
// 可以通过 global 查看 globalTHis(global.globalThis)
Object.defineProperty(globalThis, 'window', {get(){return globalThis;}
})

在这里插入图片描述

调试 调用 ASN1 就可以找到了
在这里插入图片描述

继续执行后进入死循环(这个代码段如果按照前面的方法做了,其实已经被删除了)
在这里插入图片描述
条件判断的代码
if (m5"toString"“indexOf” != -(552 + 71 * -103 + -156 * -53 + -(4814 + -2 * -3371 + -11245) * -(-6316 + -8443 * 1 + -1055 * -14) + -(-1333 + 1468 + 2 * 2396))) while (!![]) {
在这里插入图片描述
判断通过后会进入死循环
这行代码可以直接删掉,里面没有业务逻辑(调试的时候 格式化的代码会被检测到)

继续执行会报错 alert is not defined
在这里插入图片描述

这是在一个 try catch 块中的代码
调试看看 try 块中的代码报了什么错(或者可以将 try catch 块删除只保留框选的那一段代码)
只用执行 框选的那一句,因为 definedProperty 被重写了 执行后只返回 空字符串
在这里插入图片描述
报错 undefined(reading ‘length’) undefined 无法读取 ‘length’ 属性
逐个看 gee() 方法传入进去的参数 aa, bb, c, d, e, b64_zw
c 和 e 为 defined, c 和 e 各自为 window.v14, window.v142 属性的值
在这里插入图片描述
window.v14, window.v142 是在 m接口返回的代码段设置的
m接口 的代码是通过 ajax success 方法执行的
在这里插入图片描述
将 ajax 方法重写
在这里插入图片描述

$ = {'ajax': function(){// 替换成还原好的 m 的代码}
}

再次运行 代码会一直阻塞,不打印结果
将 setTimeout, setInterval 置空
setTimeout = function(){}
setInterval = function(){}

成功运行结束了,但是没有输出结果在这里插入图片描述
console.log 被重写了在这里插入图片描述

在文件开头声明一个变量 接收console.log 方法
_log = console.log;
顺利输出了在这里插入图片描述

验证结果的时候,加密出来的值不一致在这里插入图片描述
将浏览器和本地加密的值都写死,一起单步调试,排查问题在这里插入图片描述

不报错的环境检测

搜索 eval 这 4 个操作对浏览器是无效的,这两个内置对象不能被删除,不能被设置值
在这里插入图片描述
在本地修改这两个对象的属性描述符,设置为不可删除,不可修改

// window
Object.defineProperty(globalThis, 'window', {get(){return globalThis;},configurable: false,  // 对象不可删除
})// navigator
Object.defineProperty(globalThis, 'navigator', {configurable: false,  // 不可删除writable: false  // 不可修改
})// document
Object.defineProperty(globalThis, 'document', {configurable: false,  // 不可删除writable: false  // 不可修改
})

全局搜索 try

补 CanvasCaptureMediaStreamTrack 方法
在这里插入图片描述

CanvasCaptureMediaStreamTrack = function CanvasCaptureMediaStreamTrack(){};

浏览器中查看 gloabl 会报错,nodejs不会,设置 nodejs 中 global 的属性描述符在这里插入图片描述

Object.defineProperty(globalThis, 'global', {get(){// 只要一访问 global 就会报错throw Error('')},
})

Python 代码

js 模板

还有一个坑
如果页码为 1 那么 m字段是加密一次生成的
如果页码为 2 那么 m字段是加密两次生成的
所以模板最后应该改为
for(let i=1; i<=// 页码; i++){
sp()
}

Object.defineProperty(globalThis, 'global', {get(){throw Error('')},
})
_log = console.log;
window = {}
Object.defineProperty(globalThis, 'window', {get(){return globalThis;},configurable: false,  // 对象不可删除
})
setTimeout = function setTimeout(){};
setInterval = function setInterval(){};
CanvasCaptureMediaStreamTrack = function CanvasCaptureMediaStreamTrack(){};
Object.prototype.toString = function(){return `[object ${this.constructor.name}]`
}
function NetworkInformation(){}
function Geolocation(){}
function MediaCapabilities(){}
function MediaSession(){}
function MimeTypeArray(){}
function Permissions(){}
function PluginArray(){}
function UserActivation(){}
function DeprecatedStorageQuota(){}
navigator = {};
Object.defineProperty(globalThis, 'navigator', {configurable: false,  // 不可删除writable: false  // 不可修改
})
navigator["appCodeName"] = 'Mozilla';
navigator["appName"] = 'Netscape';
navigator["appVersion"] = '5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36'
navigator["connection"] = new NetworkInformation();
navigator["cookieEnabled"] = true
navigator["doNotTrack"] = null
navigator["geolocation"] = new Geolocation();
navigator["hardwareConcurrency"] = 12;
navigator["language"] = 'zh-CN';
navigator["languages"] = ['zh-CN', 'zh'];
navigator["maxTouchPoints"] = 0
navigator["mediaCapabilities"] = new MediaCapabilities();
navigator["mediaSession"] = new MediaSession();
navigator["mimeTypes"] = new MimeTypeArray();
navigator["onLine"] = true
navigator["permissions"] = new Permissions();
navigator["platform"] = 'Win32';
navigator["plugins"] = new PluginArray();
navigator["product"] = 'Gecko';
navigator["productSub"] = '20030107';
navigator["userActivation"] = new UserActivation();
navigator["userAgent"] = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36';
navigator["vendor"] = 'Google Inc.';
navigator["vendorSub"] = '';
navigator["webkitPersistentStorage"] = new DeprecatedStorageQuota();
navigator["webkitTemporaryStorage"] = new DeprecatedStorageQuota();screen = {}
function ScreenOrientation(){}
screen["availHeight"] = 1040
screen["availLeft"] = 0
screen["availTop"] = 0
screen["availWidth"] = 1920
screen["colorDepth"] = 24
screen["height"] = 1080
screen["orientation"] = new ScreenOrientation();
screen["pixelDepth"] = 24
screen["width"] = 1920;function DOMStringList(){}
document = {};
Object.defineProperty(globalThis, 'document', {configurable: false,  // 不可删除writable: false  // 不可修改
})
document["location"] = {};
document["location"]["ancestorOrigins"] = new DOMStringList();
document["location"]["hash"] = ''
document["location"]["host"] = 'match.yuanrenxue.cn'
document["location"]["hostname"] = 'match.yuanrenxue.cn'
document["location"]["href"] = 'https://match.yuanrenxue.cn/match/14'
document["location"]["origin"] = 'https://match.yuanrenxue.cn'
document["location"]["pathname"] = '/match/14'
document["location"]["port"] = ''
document["location"]["protocol"] = 'https:'
document["location"]["search"] = ''document["location"]["assign"] = function assign(){}
document["location"]["assign"].toString = function (){return 'function assign() { [native code] }'}document["location"]["reload"] = function reload(){}
document["location"]["reload"].toString = function (){return 'function reload() { [native code] }'}document["location"]["replace"] = function replace(){}
document["location"]["replace"].toString = function (){return 'function replace() { [native code] }'}document["location"]["toString"] = function toString(){}
document["location"]["toString"].toString = function (){return 'function toString() { [native code] }'}document["location"]["valueOf"] =  function valueOf(){}
document["location"]["valueOf"].toString = function (){return 'function valueOf() { [native code] }'}$ = {'ajax': function() {// 替换成还原好的 m的代码}
}// m.js 代码for(let i=1; i<=// 页码; i++){sp()
}
_log(document.cookie)

页面执行流程

第一次请求 m.js --> m --> 数据接口
在这里插入图片描述
后续请求 m --> 数据接口
在这里插入图片描述

python 代码

替换 m.js

首先请求 m.js 代码,替换到 js模板代码内

def get_mJs():url = 'https://match.yuanrenxue.cn/static/match/match14/m.js'response = requests.get(url, headers=headers, cookies=cookies)with open('14.js', mode='r', encoding='utf-8') as f:js_demo = f.read()js_demo = js_demo.replace('// m.js 代码', response.text)return js_demo

替换 m 接口代码

请求接口前都会请求 m 接口的代码(设置 window 的 v12 v142 属性值 后再给 cookie m字段赋值

def get_m():url = 'https://match.yuanrenxue.cn/api/match/14/m'response = requests.get(url, headers=headers, cookies=cookies)return response.text

请求接口前会先设置 cookie m的字段
mz 字段可以写死,同一台电脑的环境值就是固定的

def get_cookie(js_demo, page_):js_demo = js_demo.replace('// 替换成还原好的 m的代码', get_m())js_demo = js_demo.replace('// 页码', str(page_))with open('new_14.js', mode='w', encoding='utf-8') as f:f.write(js_demo)output = subprocess.check_output("node new_14.js")return output.strip().split('=')[1].split(';')[0]

完整的 python 代码

import subprocess
import requestsheaders = {"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 = {'mz': '你浏览器生成的 mz 值,是可以固定的',"sessionid": "你的SessionId",
}def get_mJs():url = 'https://match.yuanrenxue.cn/static/match/match14/m.js'response = requests.get(url, headers=headers, cookies=cookies)with open('14.js', mode='r', encoding='utf-8') as f:js_demo = f.read()js_demo = js_demo.replace('// m.js 代码', response.text)return js_demodef get_m():url = 'https://match.yuanrenxue.cn/api/match/14/m'response = requests.get(url, headers=headers, cookies=cookies)return response.textdef get_cookie(js_demo, page_):js_demo = js_demo.replace('// 替换成还原好的 m的代码', get_m())js_demo = js_demo.replace('// 页码', str(page_))with open('new_14.js', mode='w', encoding='utf-8') as f:f.write(js_demo)output = subprocess.check_output("node new_14.js")return output.strip().split('=')[1].split(';')[0]def get_match14(js_demo, page_):url = "https://match.yuanrenxue.cn/api/match/14"params = {'page': f'{page_}'}cookies['m'] = get_cookie(js_demo, page_)[0:-1] + f'{page_}'print(cookies['m'])response = requests.get(url, headers=headers, cookies=cookies, params=params)print(response.json())return response.json()['data']if __name__ == '__main__':js_code = get_mJs()  # m.js 文件nums = 0for page in range(1, 6):nums_list = get_match14(js_code, page)for num in nums_list:nums += num['value']print('page: ', page, 'nums: ', nums)

这篇关于【困难】 猿人学web第一届 第14题 备而后动-勿使有变的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1129494

相关文章

解决JavaWeb-file.isDirectory()遇到的坑问题

《解决JavaWeb-file.isDirectory()遇到的坑问题》JavaWeb开发中,使用`file.isDirectory()`判断路径是否为文件夹时,需要特别注意:该方法只能判断已存在的文... 目录Jahttp://www.chinasem.cnvaWeb-file.isDirectory()遇

JavaWeb-WebSocket浏览器服务器双向通信方式

《JavaWeb-WebSocket浏览器服务器双向通信方式》文章介绍了WebSocket协议的工作原理和应用场景,包括与HTTP的对比,接着,详细介绍了如何在Java中使用WebSocket,包括配... 目录一、概述二、入门2.1 POM依赖2.2 编写配置类2.3 编写WebSocket服务2.4 浏

Spring常见错误之Web嵌套对象校验失效解决办法

《Spring常见错误之Web嵌套对象校验失效解决办法》:本文主要介绍Spring常见错误之Web嵌套对象校验失效解决的相关资料,通过在Phone对象上添加@Valid注解,问题得以解决,需要的朋... 目录问题复现案例解析问题修正总结  问题复现当开发一个学籍管理系统时,我们会提供了一个 API 接口去

使用IntelliJ IDEA创建简单的Java Web项目完整步骤

《使用IntelliJIDEA创建简单的JavaWeb项目完整步骤》:本文主要介绍如何使用IntelliJIDEA创建一个简单的JavaWeb项目,实现登录、注册和查看用户列表功能,使用Se... 目录前置准备项目功能实现步骤1. 创建项目2. 配置 Tomcat3. 项目文件结构4. 创建数据库和表5.

手把手教你idea中创建一个javaweb(webapp)项目详细图文教程

《手把手教你idea中创建一个javaweb(webapp)项目详细图文教程》:本文主要介绍如何使用IntelliJIDEA创建一个Maven项目,并配置Tomcat服务器进行运行,过程包括创建... 1.启动idea2.创建项目模板点击项目-新建项目-选择maven,显示如下页面输入项目名称,选择

业务中14个需要进行A/B测试的时刻[信息图]

在本指南中,我们将全面了解有关 A/B测试 的所有内容。 我们将介绍不同类型的A/B测试,如何有效地规划和启动测试,如何评估测试是否成功,您应该关注哪些指标,多年来我们发现的常见错误等等。 什么是A/B测试? A/B测试(有时称为“分割测试”)是一种实验类型,其中您创建两种或多种内容变体——如登录页面、电子邮件或广告——并将它们显示给不同的受众群体,以查看哪一种效果最好。 本质上,A/B测

Java Web指的是什么

Java Web指的是使用Java技术进行Web开发的一种方式。Java在Web开发领域有着广泛的应用,主要通过Java EE(Enterprise Edition)平台来实现。  主要特点和技术包括: 1. Servlets和JSP:     Servlets 是Java编写的服务器端程序,用于处理客户端请求和生成动态网页内容。     JSP(JavaServer Pages)

BUUCTF靶场[web][极客大挑战 2019]Http、[HCTF 2018]admin

目录   [web][极客大挑战 2019]Http 考点:Referer协议、UA协议、X-Forwarded-For协议 [web][HCTF 2018]admin 考点:弱密码字典爆破 四种方法:   [web][极客大挑战 2019]Http 考点:Referer协议、UA协议、X-Forwarded-For协议 访问环境 老规矩,我们先查看源代码

EasyPlayer.js网页H5 Web js播放器能力合集

最近遇到一个需求,要求做一款播放器,发现能力上跟EasyPlayer.js基本一致,满足要求: 需求 功性能 分类 需求描述 功能 预览 分屏模式 单分屏(单屏/全屏) 多分屏(2*2) 多分屏(3*3) 多分屏(4*4) 播放控制 播放(单个或全部) 暂停(暂停时展示最后一帧画面) 停止(单个或全部) 声音控制(开关/音量调节) 主辅码流切换 辅助功能 屏

9.8javaweb项目总结

1.主界面用户信息显示 登录成功后,将用户信息存储在记录在 localStorage中,然后进入界面之前通过js来渲染主界面 存储用户信息 将用户信息渲染在主界面上,并且头像设置跳转,到个人资料界面 这里数据库中还没有设置相关信息 2.模糊查找 检测输入框是否有变更,有的话调用方法,进行查找 发送检测请求,然后接收的时候设置最多显示四个类似的搜索结果