瑞数6 反向工程实战:深圳大学附属医院站点绕过分析
目标站点地址:https://sghu.sz.edu.cn/HTML/News/Main/102.html
初步分析与环境准备
进入页面后直接打开开发者工具,会发现持续触发 debugger 指令,此为常规干扰手段,可忽略。清除浏览器缓存与所有 Cookie 后重新加载页面。
通过事件监听器追踪脚本执行流程,刷新页面后可定位到关键的 TypeScript 脚本文件(.ts)、启动加载脚本(tsload.js)以及动态内容注入部分(content)。这三个组件是瑞数 v6 防护体系的核心组成部分。
将上述三部分内容分别保存为独立文件,并构建一个基础运行环境:
// env.js
require('./ts.js');
require('./tsload.js');
function get_cookie() {
console.log(document.cookie);
return document.cookie;
}
get_cookie();
代理监控与全局对象模拟
为实现对核心对象的完整行为捕获,引入自定义代理机制,用于监听所有属性读写操作:
let setProxyArr = function (proxyObjArr) {
for (let i = 0; i < proxyObjArr.length; i++) {
const handler = `{
get: function(target, property, receiver) {
console.log("访问:", "get", "对象:", "${proxyObjArr[i]}", "属性:", property, "类型:", typeof property, "值:", target[property], "值类型:", typeof target[property]);
return Reflect.get(...arguments);
},
set: function(target, property, value, receiver) {
console.log("设置:", "set", "对象:", "${proxyObjArr[i]}", "属性:", property, "值:", value, "值类型:", typeof target[property]);
return Reflect.set(...arguments);
}
}`;
try {
eval(`${proxyObjArr[i]} = new Proxy(${proxyObjArr[i]}, ${handler});`);
} catch (e) {
eval(`${proxyObjArr[i]} = new Proxy({}, ${handler});`);
}
}
};
function watch(object) {
const handler = {
get: function (target, property, receiver) {
if (!['isNaN', 'encodeURI', 'Uint8Array', 'undefined', 'JSON'].includes(property)) {
console.log("获取属性:", property, "来源:", target, "值:", target[property]);
}
return Reflect.get(...arguments);
},
set: function (target, property, value, receiver) {
console.log("设置属性:", property, "值:", value, "来源:", target);
return Reflect.set(...arguments);
}
};
return new Proxy(object, handler);
}
// 安全函数封装
const safeFunction = function(func) {
Function.prototype.$call = Function.prototype.call;
const $toString = Function.toString;
const symbol = Symbol('native code');
const myToString = function() {
return typeof this === 'function' && this[symbol] || $toString.call(this);
};
const defineProp = function(obj, key, val) {
Object.defineProperty(obj, key, {
enumerable: false,
configurable: true,
writable: true,
value: val
});
};
delete Function.prototype.toString;
defineProp(Function.prototype, 'toString', myToString);
defineProp(Function.prototype.toString, symbol, 'function toString() { [native code] }');
defineProp(func, symbol, 'function () { [native code] }');
return func;
};
全局上下文重建
由于目标环境在沙箱中运行,需手动还原常见全局对象结构:
window = global;
window.Buffer = Buffer;
window.top = window;
window.self = window;
window.window = window;
// 基础定时器
window.setInterval = function setInterval() {};
window.clearInterval = function clearInterval() {};
window.setTimeout = function setTimeout() {};
// MutationObserver 补丁(谨慎使用)
window.MutationObserver = function MutationObserver() {};
window.MutationObserver.prototype.observe = function observe() {};
// 文档对象模拟
function HTMLDocument() {}
Object.setPrototypeOf(HTMLDocument.prototype, window.Document.prototype);
HTMLDocument.prototype.constructor = HTMLDocument;
document = new HTMLDocument();
window.HTMLDocument = HTMLDocument;
// Navigator、Screen、History、Location 等也需类似处理
function Navigator() {}
navigator = new Navigator();
window.Navigator = Navigator;
function Screen() {}
screen = new Screen();
window.Screen = Screen;
function History() {}
history = new History();
window.History = History;
function Location() {}
location = new Location();
window.Location = Location;
// 应用代理
setProxyArr(['window', 'document', 'location', 'history', 'screen', 'navigator']);
DOM API 伪造与检测点绕过
针对文档对象中的关键方法进行模拟,避免因缺失而引发异常:
createElement:需支持div、a、form、input等标签创建getElementById:返回一个<meta>元素实例getElementsByTagName:需识别base、script、meta标签
特别关注以下两个元素的属性检测:
- script:检查
innerText、src、getAttribute(r="m")、parentElement.removeChild - meta:检查
getAttribute、parentNode.removeChild、content属性
请求流程自动化
利用 Python 构建自动化请求流程,抓取并替换原始资源:
import requests
import execjs
from lxml import etree
session = requests.Session()
# 加载补环境脚本
with open("env.js", "r", encoding="utf-8") as f:
js_code = f.read()
# 第一次请求获取初始响应
headers = {}
response = session.get("https://sghu.sz.edu.cn/HTML/News/Main/102.html", headers=headers)
cookie = list(response.cookies)[0]
cookies = {cookie.name: cookie.value}
# 解析 HTML 提取资源
html = etree.HTML(response.text)
ts_script = html.xpath('//script')[0].text
tsload_url = 'https:' + html.xpath('//script')[1].attrib['src']
tsload_script = session.get(tsload_url, headers=headers).text
content_value = html.xpath('//meta')[1].attrib['content']
# 替换占位符
js_code = js_code.replace('ts文件', ts_script)
.replace('tsload文件', tsload_script)
.replace('content文件', content_value)
# 写入新脚本
with open("now.js", "w", encoding="utf-8") as f:
f.write(js_code)
print("脚本已生成")
# 执行环境并提取最终 Cookie
cookie_result = execjs.compile(js_code).call('get_cookie')
name, value = cookie_result.split('; path')[0].split('=', 1)
cookies[name] = value
print("最终请求参数:", cookies)
# 发起二次请求
final_response = session.get("https://sghu.sz.edu.cn/HTML/News/Main/102.html", headers=headers, cookies=cookies)
print(final_response.status_code)
print(final_response.text)
成功绕过瑞数 v6 的反爬机制,完成数据获取。