提前说一句: 所有版本均可rpc通杀,一键注入、高并发采集;但是吃机器性能、不太方便,不过是一个很好的临时方案。
这里主要说明补环境方案
Rs5
网址 : aHR0cHM6Ly9iaWRkaW5nLnN5c3UuZWR1LmNuL2luZm9ybWF0aW9uL25vdGljZQ==
Rs5 Cookie分析
ZMRhjlxmTSDe443S
: 第一次请求返回。
ghDuiPNsEBSVMaMDUj1PvfwD5Mque8iERsLq96oHPxWAF6P0w79j2_mJj.mLFgiH
ZMRhjlxmTSDe443T
:第一次请求返回来的content、自执行函数、和ts.js文件生成,瑞数核心算法、环境检测,全在这里头了。
5l_Zh6CRXjrzYnAPT8.zBxJDG8x4QNmVP5QxNzVbvNSdn_ALUFyqlPqgpo6UpYHYnemQNsm6B9iKxIHk6bMQOq_eqA7PTmdW87OH4FjPVprz0bxrsdoz0Fsls_SEkVsdAaXoFQMo3OuPyipFiN7HW4v3BXexJxkCBkbW_NqckT46BgVnAx1L1pvVRb2T6aKal47UFV3YUMLbsCMXBWtW4rICVpO79DWW_zBqB.v.KkMdRBvzXl.VEM.9rnZ9dv1bjQq4WM7XR9yUMnQzsC03J9XyATeNU8TvtWTQHcmKkPEyXZLI75euA.8C54_CnOslc5.R62X5CRar55FGdcgYWN9UR
请求流程
第一次请求
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| url = 'https://bidding.sysu.edu.cn/information/notice' headers = { 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7', 'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8', 'Cache-Control': 'no-cache', 'Connection': 'keep-alive', 'Pragma': 'no-cache', 'Referer': 'https://bidding.sysu.edu.cn/information/notice?page=1', 'Sec-Fetch-Dest': 'document', 'Sec-Fetch-Mode': 'navigate', 'Sec-Fetch-Site': 'same-origin', 'Sec-Fetch-User': '?1', 'Upgrade-Insecure-Requests': '1', 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36', 'sec-ch-ua': '"Chromium";v="118", "Google Chrome";v="118", "Not=A?Brand";v="99"', 'sec-ch-ua-mobile': '?0', 'sec-ch-ua-platform': '"macOS"', }
response = session.get( url, cookies=cookies, headers=headers
) response.encoding = response.apparent_encoding print('第一次请求状态码---->',response.status_code) cookies = response.cookies.get_dict() Str = response.text with open('固定.html','w',encoding='utf8') as f: f.write(Str)
|
返回状态码是412
返回的文本如下
我们提取出 content
,auto_js
,ts_url
1 2 3 4 5 6
| content = re.findall('<meta content="(.*?)"', Str)[0] auto_js = re.findall('r="m">(\(function\(\).*?)</script>', Str,re.S|re.I)[0] ts_url = re.findall('src="(.*?js)" r=',Str,re.S|re.I)[0] ts_url = f'https://bidding.sysu.edu.cn{ts_url}' logger.debug(f'ts_url:{ts_url}')
|
下一步就是结合这三个条件,生成我们要的ZMRhjlxmTSDe443T
打出第二次请求
补环境结构
env就是我们要补的环境,nodejs 执行这段代码后,会对document.cookie赋值,也就是ZMRhjlxmTSDe443T,我们导出即可
补环境过程
首先把content、自执行函数、ts文件放入js代码中,这步很简单,会复制粘贴就行
我们只需要在补环境那块,填入我们的环境代码即可。
那么,现在开始正式补环境!
首先,最基本的,window
location
document
navigator
先补进去
location我们把最常见的那几个维度的字段,先提前补进去
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| setInterval = function(){}; setTimeout = function(){}; window = global; window.top = window; window.self = window; location = { href: 'https://bidding.sysu.edu.cn/information/notice?page=1', protocol: "https:", origin: "https://bidding.sysu.edu.cn", hostname: "bidding.sysu.edu.cn", port: "", host: "bidding.sysu.edu.cn", search: "?page=1", hash: "", pathname: "/information/notice", }; navigator = { userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36', webdriver: false, platform: "MacIntel", languages: ["zh-CN", "zh", "en"], appVersion: "5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36", webkitPersistentStorage: {}, getBattery: function(){console.log('navigator.getBattery--->',arguments);}, connection: { downlink: 1.3, effectiveType: "3g", rtt: 550, saveData: false, }, }; document = { charset: "UTF-8", characterSet: "UTF-8", };
|
不管哪个版本的瑞数,我们都能这样先补一下
然后上代理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| window = new Proxy(window, { set(target, property, value, receiver) {
console.log("设置属性set window", property, typeof value); return Reflect.set(...arguments); }, get(target, property, receiver) {
console.log("获取属性get window", property, typeof target[property]); if (target[property]==undefined){ } return target[property] } });
function proxy(proxy_array) { for (let i = 0; i < proxy_array.length; i++) { eval( proxy_array[i] + `= new Proxy(` + proxy_array[i] + `, { get(target, key) { if(key == 'parentElement'){ // debugger; } console.log('----------------------') console.log( '【` + proxy_array[i] + `】取属性 ' + key + ' 值: ' + target[key] + ', 详细位置debugger查看'); console.log('----------------------') if (target[key]==clearInterval){ debugger; } return target[key]; } });` ); } }
var proxy_array = [ "document", "navigator", "location", ]; proxy(proxy_array);
|
代理也是固定的,放到补的环境下面即可
下一步,开始调试,mac用户按 fn+F5开始调试即可
提前约定一下,遇到如下图这种windows 某个属性 undefind的,如果是函数,我们就暂时补充一个空函数,打印入参即可,如果是字段,先去官网调试下是什么类型的,一般就是对象类型或者字符串,直接补空的即可。
如果是$开头的属性呢,直接跳过就好啦,因为正常来说windows 不会有这些属性
例如上图,我们就可以这么补
1 2 3
| window.execScript = eval; window.XMLHttpRequest = function(){console.log('window.XMLHttpRequest--->',arguments);}; window.ActiveXObject = function(){console.log('window.ActiveXObject--->',arguments);};
|
后面会有一堆这种的,我就不在一个个说了
补完继续调试
很明显了,需要补document.createElement
我们先定义一个createElement,然后打印参数,看看需要补那些参数
1 2 3 4
| var createElement = function(){ console.log('createElement传入参数--->',arguments); } document.createElement = createElement;
|
是不是很明显了,传入了div,那我们就要模拟出来一个在传入div时候的返回值
然后注意到缺失了getElementsByTagName
我来说下怎么回事:
正常流程是:createElement会创建一个div对象,然后div对象又执行了它的getElementsByTagName函数
现在是:createElement什么都没返回,默认返回了undefind,就导致拿不到getElementsByTagName这个函数了
我们可以这样做
1 2 3 4 5 6 7 8 9
| var div = { } var createElement = function(){ console.log('createElement传入参数--->',arguments); switch(arguments[0]){ case 'div': return div } }
|
同时,我们把div加入监控数组
1 2 3 4 5 6 7 8
| var proxy_array = [ "document", "navigator", "location", "div" ]; proxy(proxy_array);
|
再次执行
对吧,让我们直接补一个getElementsByTagName,再让它是div的一个属性即可
1 2 3 4
| var getElementsByTagName = function(){ console.log('getElementsByTagName传入参数--->',arguments); } div.getElementsByTagName = getElementsByTagName;
|
再次执行
我们可以看到getElementsByTagName原本是传入了一个i参数,然后返回了一个列表对象(getElementsByTagName,注意带s,返回的是列表)
后续在取对象第0个元素时候,取不到,于是就报了这个reading ‘0’的错误~
很简单,我们直接补一个返回值即可
1 2 3 4 5 6 7
| var getElementsByTagName = function(){ console.log('getElementsByTagName传入参数--->',arguments); switch(arguments[0]){ case 'i': return []; } }
|
那,我们应该怎么补返回值呢,列表里到底要写什么,这个暂时不清楚。
遇到这种情况,我们可以写一份hook代码,看看浏览器的真实环境是怎么执行的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| const originalGetElementsByTagName = document.getElementsByTagName;
document.getElementsByTagName = function(tagName) {
const elements = originalGetElementsByTagName.apply(this, arguments);
if (tagName=='i'){ console.log('调用getElementsByTagName,参数:', tagName); console.log('获取的元素:', elements); }
return elements; };
const divElements = document.getElementsByTagName('div');
|
这个hook代码到后面还有用,我们后面要常用的
那么刷新浏览器之后什么都没输出,那么我们直接让它返回[]就好了,然后继续下一步
前面说过了,直接补
1 2 3
| window.addEventListener = function(){console.log('window.addEventListener--->',arguments);}; window.attachEvent = function(){console.log('window.attachEvent--->',arguments);}; window.localStorage = {};
|
直接补
1
| document.getElementsByTagName = getElementsByTagName;
|
这里的meta,参照div的补法即可
补content,parentNode即可
1 2 3 4 5 6
| var meta = { content:content,
parentNode:parentNode
}
|
这里看到对parentNode取了什么值,我们调试看一下是什么,这里的debugger打开
很明显,我们补一个removeChild即可
1 2 3 4 5
| var parentNode = { removeChild:function(){ console.log('removeChild传入参数--->',arguments); } }
|
直接补
1 2 3
| document.addEventListener = function(){console.log('document.addEventListener--->',arguments);}; document.attachEvent = function(){console.log('document.attachEvent--->',arguments);}; document.documentElement= {};
|
按照之前的方式补
直接补
先看第一个: base的href属性到底是什么,这里我们浏览器hook调试一下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| const originalGetElementsByTagName = document.getElementsByTagName;
document.getElementsByTagName = function(tagName) {
const elements = originalGetElementsByTagName.apply(this, arguments);
if (tagName=='base'){ console.log('调用getElementsByTagName,参数:', tagName); console.log('获取的元素:', elements[0].getAttribute('href')); }
return elements; };
const divElements = document.getElementsByTagName('div');
|
找不到,那就不补了
补第二个
1 2 3 4 5
| var getElementById= function(){ console.log('getElementById传入参数--->',arguments); } document.getElementById = getElementById;
|
补第三个
直接补
1 2 3 4 5 6
| var script = { getAttribute:function(){ console.log('getAttribute传入参数--->',arguments); }
}
|
出值了
中间其实也有一些undefind的,比如
我们hook一下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| const originalGetElementsByTagName = document.getElementsByTagName;
document.getElementsByTagName = function(tagName) {
const elements = originalGetElementsByTagName.apply(this, arguments);
if (tagName=='script'){ console.log('调用getElementsByTagName,参数:', tagName); console.log('获取的元素:', elements[0].getAttribute('r')); }
return elements; };
const divElements = document.getElementsByTagName('div');
|
直接补
1 2 3 4 5 6 7
| var script = { getAttribute:function(){ console.log('getAttribute传入参数--->',arguments); return 'm' }
}
|
直接补
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| var script = { getAttribute:function(){ console.log('getAttribute传入参数--->',arguments); switch(arguments[0]){ case 'r': return 'm' } }, parentElement:{ removeChild:function(){ console.log('removeChild传入参数--->',arguments); } }
}
|
再次出值
我们拿着环境测试一下
很不幸,还是400
这时候我们回头把window那些空的属性再过一遍就好了,比如
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| window.name = ''; window.globalStorage = {};
window.indexedDB = {}; window.mozIndexedDB = {}; window.webkitIndexedDB = {}; window.msIndexedDB = {}; window.PointerEvent = function(){console.log('window.PointerEvent--->',arguments);}; window.jesion = {}; window.chrome = { "app": { "isInstalled": false, "InstallState": { "DISABLED": "disabled", "INSTALLED": "installed", "NOT_INSTALLED": "not_installed" }, "RunningState": { "CANNOT_RUN": "cannot_run", "READY_TO_RUN": "ready_to_run", "RUNNING": "running" } } }; window.Gamepad = function(){console.log('window.Gamepad--->',arguments);}; window.webkitRequestFileSystem = function(){console.log('window.webkitRequestFileSystem--->',arguments);}; window.openDatabase = function(){console.log('window.openDatabase--->',arguments);};
|
第二次返回200,成功~
至此,Rs5补环境结束
补充
Rs6补环境和Rs vmp补环境就不会再说这么多细节了,会侧重一些特殊的环境检测。
未经允许禁止转载哦
作者:Nohup