【困难】 猿人学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

相关文章

业务中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.模糊查找 检测输入框是否有变更,有的话调用方法,进行查找 发送检测请求,然后接收的时候设置最多显示四个类似的搜索结果

JavaWeb【day09】--(Mybatis)

1. Mybatis基础操作 学习完mybatis入门后,我们继续学习mybatis基础操作。 1.1 需求 需求说明: 根据资料中提供的《tlias智能学习辅助系统》页面原型及需求,完成员工管理的需求开发。 通过分析以上的页面原型和需求,我们确定了功能列表: 查询 根据主键ID查询 条件查询 新增 更新 删除 根据主键ID删除 根据主键ID批量删除

利用Django框架快速构建Web应用:从零到上线

随着互联网的发展,Web应用的需求日益增长,而Django作为一个高级的Python Web框架,以其强大的功能和灵活的架构,成为了众多开发者的选择。本文将指导你如何从零开始使用Django框架构建一个简单的Web应用,并将其部署到线上,让世界看到你的作品。 Django简介 Django是由Adrian Holovaty和Simon Willison于2005年开发的一个开源框架,旨在简

web群集--nginx配置文件location匹配符的优先级顺序详解及验证

文章目录 前言优先级顺序优先级顺序(详解)1. 精确匹配(Exact Match)2. 正则表达式匹配(Regex Match)3. 前缀匹配(Prefix Match) 匹配规则的综合应用验证优先级 前言 location的作用 在 NGINX 中,location 指令用于定义如何处理特定的请求 URI。由于网站往往需要不同的处理方式来适应各种请求,NGINX 提供了多种匹

构建高性能WEB之HTTP首部优化

0x00 前言 在讨论浏览器优化之前,首先我们先分析下从客户端发起一个HTTP请求到用户接收到响应之间,都发生了什么?知己知彼,才能百战不殆。这也是作为一个WEB开发者,为什么一定要深入学习TCP/IP等网络知识。 0x01 到底发生什么了? 当用户发起一个HTTP请求时,首先客户端将与服务端之间建立TCP连接,成功建立连接后,服务端将对请求进行处理,并对客户端做出响应,响应内容一般包括响应

(javaweb)mysql---DDL

一.数据模型,数据库操作 1.二维表:有行有列 2. 3.客户端连接数据库,发送sql语句给DBMS(数据库管理系统),DBMS创建--以文件夹显示 二.表结构操作--创建 database和schema含义一样。 这样就显示出了之前的内容。