防盗链_防克隆跳转机制 (preventCheat)
防克隆 / 防自部署跳转机制是商业站(如 toolonline.net)用 obfuscator.io 混淆前端时植入的多层自我保护,核心是 domainLock(域名锁):检测当前域名是否属于白名单,否则把页面重定向回原站。这类逻辑的字符串被加密在字符串数组里,直接 grep "toolonline.net" 永远搜不到,必须先解码。
domainLock(域名锁)——"域名统一检查"的真相
机制(从 format-html.js 反混淆后还原):
// _0x4d0156 = document.domain || location.hostname
// _0x458122 = 允许域名列表(加密串按字符类 .replace 解码后 split(";"))
for (每个允许域名 d) {
if (hostname 以 d 结尾) { allow = true; break; } // 后缀匹配
}
if (!allow) {
// 动态拼出 "https://toolonline.net",然后:
document.location = "https://toolonline.net"; // 跳回原站
}- 允许域名:
toolonline.net(后缀匹配,localhost不匹配 → 必跳) - 跳转 URL:
https://toolonline.net - 字符串是加密的,例如原始串
htSvTtIps:B//AtoWcolSbWgoQMnline.mnReNtWaSPAcDgrNu...用字符类正则.replace(/[rOLAX...]/g,"")解码才得到 URL。所以"解密"不是解码数组,而是.replace(字符类,"")这一步。
出现位置
- common.js:域名锁藏在
_0x52cc81()函数里(函数名只是混淆标识符) - 96 个工具脚本:各自有一个
_0x21a116()式的独立函数(同一版本 obfuscator.io 生成,结构确定性一致) - 内联脚本、纯资源页面通常没有
其余保护层
| 层 | 位置 | 作用 |
|---|---|---|
| debugProtection | 同域名锁函数 | 动态构造 debugger 字符串 + 定时器陷阱,防调试 |
| preventCheat | common.js | 屏蔽 F12 / 右键 / 复制 / 选中;冻结 setTimeout/setInterval 定义 |
| cookie 域名锁 | common.js setToken/removeToken | 非 localhost 时 cookie domain 写死 toolonline.net,自部署后登录态失效 |
| 防 iframe | common.js init() | window.top !== window.self 时强制 top.location 跳走 |
| 后端依赖 | 全局 webApiSiteHost/staticSiteHost | API/静态资源指向原站域名 |
变体与坑(实战记录)
- 声明关键字变体:域名锁块的变量声明不一定是
var,也可能是const/let。strip 正则写成if(!x){var \w+=new RegExp(...)会漏掉const版本(实战中HistoryDialog.js、digital-huarong-road.js等 5 个文件因此漏网,页面仍跳转)。正则要写(?:var|const|let)\s+\w+=。 - 延迟跳转变体:部分"排行榜游戏"页(2048、n-back、数字/序列记忆)有延迟 2-3 秒的硬编码跳转
location.href = "https://toolonline.net/",埋在 javascript-obfuscator 高配变体(_0x5e98a8={hex:hex}控制流扁平化 + 多参解码器)里,webcrack 也解不出导航语句。本质是登录/排行榜会话检查:未登录 + 非原域 → 延时跳回首页。这类属于后端鉴权依赖,前端无法彻底剥离。 - 诊断技巧:延迟跳转用
Network.requestWillBeSent的 initiator 栈归因不可靠;用 Playwright 反向隔离(stub 其它脚本、只放行一个真实文件)能准确找到跳转所在文件。
万能零改动方案:欺骗 document.domain(已实测)
所有跳转都是 document.domain || location.hostname 后缀匹配 toolonline.net。location.hostname 不可篡改,但 Document.prototype.domain 的 getter 是可覆写的。注入一段守卫,让 document.domain 恒返回 toolonline.net,域名检查全部通过——任何域名/localhost 都能跑,完全不用 hosts 别名,包括反混淆不了的复杂变体(javascript-obfuscator 高配排行榜游戏)。
// 注入到每个页面 <head> 最前(先于站点脚本运行)
<script>try{Object.defineProperty(Document.prototype,"domain",{get:function(){return "toolonline.net"},configurable:true});}catch(e){}</script>实测:2048 / n-back / 序列记忆 / 数字记忆 / 对称积木 5 个"排行榜游戏"页在纯 localhost 下全部不再跳转(此前必须靠 hosts 别名)。注意:Location.prototype.href 和 hostname 在 Chromium 里都不在原型上(各自实例的非可配置属性),无法覆写,所以打 href/hostname 主意的路子都不通。
hosts 别名 = 零代码完整方案(已实测验证)
所有跳转(域名锁 + 延迟登录检查)都是 hostname 条件触发(不匹配 toolonline.net 才跳)。所以把自托管站点跑在 toolonline.net 后缀的主机名上,任何代码都不用改就能通过全部检查,cookie 域名逻辑也自洽。
- 本地验证:
127.0.0.1 toolonline.net加入 hosts,访问http://toolonline.net:8080/ - 无需改 hosts 的测试法:Chromium 启动参数
--host-resolver-rules=MAP toolonline.net 127.0.0.1,Playwright 里直接访问http://toolonline.net:8091/... - 实测:alias 域名下 17/17 页面全通过(含 2048/n-back 等此前跳转的页面),唯一外部请求只剩评论 API
中和方式(三种,按侵入性排序)
- hosts 别名(零代码改动):在 hosts 加
127.0.0.1 toolonline.net,用http://toolonline.net:8080访问。因为检查是后缀匹配,本地自托管直接通过,cookie 域名逻辑也自洽。适合本地学习/复现。 - 删域名锁块(真正部署):删除
if(!X){... document.location=<url>;}这个确定性结构(正则/if\(!\w+\)\{var \w+=new RegExp\([^,\n]+,\s*['"]g['"]\)[^}]*?\w+\[\w+\]\[\w+\]=\w+;\}/g全站批量命中)。项目脚本:toolonline.net/_scripts/strip_domainlock.py。 - 拆调用点(脆弱):只注释掉
_0x52cc81();这类调用。函数名随站点更新会变,且工具脚本的调用点容易被漏掉。
相关
- JavaScript 混淆逆向与反混淆 (JavaScript Obfuscation Reverse Engineering)
- 正则反混淆破坏旋转器 (Regex Deobfuscation Breaks the Rotator)
本文归档于个人知识库 · 领域:CS & AI · 模块:04 Project_CS_AI/Web