尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

雨课堂脚本原理与排查:篡改猴、心跳拦截及防挂机检测

雨课堂脚本原理与排查:篡改猴、心跳拦截及防挂机检测 1. 从“刷课”需求说起雨课堂学习场景的真实痛点1.1 为什么有人会想到用脚本处理雨课堂雨课堂这类在线学习平台本质上是一个把课件、视频、习题、考勤打包在一起的网页应用。它的课程进度统计逻辑通常是靠前端定时向后端发送心跳包来记录“你还在看”。只要页面处于激活状态、视频在播放、心跳包按时发出系统就认为你在正常学习。问题就出在这里。很多同学会遇到几种典型场景一门课的视频总时长好几个小时但内容自己已经掌握了不想再花时间从头看到尾或者多门课的时间窗口撞在一起顾此失彼又或者视频必须保持页面在前台切到别的标签页就暂停计时。这些场景催生了“能不能让浏览器自动帮我完成进度”的想法于是脚本方案就进入了视野。需要先把话说在前面任何试图绕过平台正常学习记录机制的行为都可能违反学校的课程管理规定和平台的服务条款。我写这篇东西是把这类脚本的技术原理、浏览器扩展的工作机制、以及实际操作中会遇到的一堆坑讲清楚供你在合规前提下理解前端自动化的思路。至于用不用、怎么用取决于你自己对规则的判断。技术本身是中性的但使用技术的人要为自己的选择负责。1.2 这类脚本到底在做什么很多人以为“刷课脚本”是什么高深的东西其实拆开看它的核心逻辑非常朴素。雨课堂的网页在播放视频时会周期性地执行一段 JavaScript把当前播放位置、课程 ID、时间戳等信息发给服务器。脚本要做的就是接管或模拟这个过程。具体来说脚本通常做三件事第一找到页面里的 video 元素直接设置播放速度或者跳过等待第二拦截或重放那个定时发送的心跳请求让服务器持续收到“正在学习”的信号第三处理页面上的弹题、暂停、防挂机检测等干扰。听起来简单但真正落地时会发现平台的前端逻辑经常更新选择器会变、请求参数会加签名、还会检测页面是否真的在前台。这就是为什么很多人装了脚本却发现“篡改猴脚本已启用但是没有运行”。1.3 适合谁来读这篇内容如果你是完全不懂前端的小白这篇内容能让你明白浏览器扩展、用户脚本、开发者工具这些概念是怎么回事至少知道别人说的“篡改猴”“油猴”是什么东西。如果你有一定基础想理解用户脚本的注入机制、DOM 操作、网络请求拦截那这里面的实操细节和排查思路会对你有用。如果你只是好奇为什么自己的脚本不生效第 4 章的排查表基本能覆盖大部分情况。我不会给你一个“复制粘贴就能用”的成品脚本因为那既不负责也不安全。我会把原理、工具链、调试方法、常见故障讲透让你具备自己判断和解决问题的能力。2. 工具链拆解篡改猴、浏览器与开发者模式2.1 篡改猴到底是什么为什么它成了用户脚本的代名词篡改猴Tampermonkey是一个浏览器扩展它的作用只有一个让你安装的第三方 JavaScript 脚本在指定网页加载时自动执行。你可以把它理解成一个“脚本容器”。浏览器本身不允许随便往网页里注入代码但扩展有权限篡改猴就是利用扩展的权限把用户写的脚本注入到页面上下文里。它的工作流程大致是这样你在篡改猴里新建一个脚本脚本头部有一段元数据注释比如// match https://*.yuketang.cn/*这行告诉篡改猴“这个脚本只在雨课堂的域名下运行”。当浏览器打开匹配的页面时篡改猴会把脚本内容包装一下注入到页面中执行。脚本里就能访问页面的 DOM、window 对象甚至拦截 fetch 和 XMLHttpRequest。很多人搜索“篡改猴没用啊”“篡改猴脚本并没有运行”八成是元数据写错了或者脚本被浏览器的安全策略挡了。后面会细说。2.2 Chrome 与 Edge 的开发者模式扩展安装的必经之路正常情况下Chrome 应用商店里的扩展是一键安装的。但用户脚本类工具、以及一些没上架的扩展就需要手动加载。这时候就要用到开发者模式。在 Chrome 里地址栏输入chrome://extensions/回车右上角有一个“开发者模式”的开关打开它就会出现“加载已解压的扩展程序”“打包扩展程序”等按钮。Edge 对应的是edge://extensions/逻辑一样。这个模式的作用是允许你加载本地文件夹里的扩展绕过商店审核。这里有个高频坑很多人下载了一个扩展的压缩包直接选压缩包加载结果报错。开发者模式加载的是解压后的文件夹不是 zip 包。你得先解压选中里面包含manifest.json的那个目录才行。另外Chrome 从某个版本开始对 Manifest V2 扩展逐步限制如果你拿到的扩展还是 V2 的可能会提示“该扩展程序未列在 Chrome 应用商店中并可能是在您不知情的情况下添加的”甚至直接无法启用。这时候要么找 V3 版本要么换支持 V2 的浏览器版本。2.3 用户脚本的元数据块脚本能不能跑起来的第一道关一个用户脚本的开头通常长这样// UserScript // name 雨课堂辅助 // namespace http://tampermonkey.net/ // version 0.1 // description 尝试处理课程进度 // author you // match https://*.yuketang.cn/* // grant none // /UserScriptmatch是最关键的。它决定了脚本在哪些 URL 上生效。雨课堂的域名可能有多个比如yuketang.cn、*.yuketang.cn不同学校可能还有定制域名。如果match写窄了脚本根本不会注入你在控制台里当然看不到任何效果。grant决定脚本能使用哪些篡改猴提供的 API写none表示不申请特殊权限直接用原生浏览器 API。我见过太多人拿着一个脚本改都不改就装上去然后说“没用”。先检查match再检查脚本是否真的在目标页面被注入了——打开开发者工具的 Console看看有没有脚本里console.log的输出。这一步能筛掉一半的问题。2.4 浏览器版本与系统环境的隐性影响热词里出现了“chrome 109 win7”“chrome 109”这类词说明有不少人还在用较老的系统或浏览器版本。Chrome 109 是最后一个支持 Windows 7 的版本之后的版本就不再兼容了。老版本浏览器对新扩展的支持有限有些用 Manifest V3 写的扩展在 109 上可能装不上或者装上了但部分 API 不可用。如果你在 Win7 上用 Chrome 109能用的扩展生态是受限的。这时候要么升级系统要么接受功能残缺。另外某些国产浏览器套壳 Chromium版本号可能停留在很老的阶段chrome://extensions/的界面和功能也可能被改过开发者模式的入口位置不一样。遇到这种情况建议直接用原版 Chrome 或 Edge减少变量。3. 脚本核心逻辑与实操要点3.1 视频进度控制从 video 元素入手雨课堂的课程视频在网页里就是一个标准的video标签。脚本控制进度的第一步就是拿到这个元素。常见做法是const video document.querySelector(video); if (video) { video.playbackRate 2.0; // 二倍速 video.muted true; // 静音避免自动播放被拦 video.play(); }playbackRate可以直接改播放速度但很多平台会检测这个值发现异常就暂停或者上报。所以有些脚本不会直接改速度而是用currentTime跳转比如每隔几秒把currentTime往前推一大段。但这样做风险更高因为服务器端可能记录了你实际观看的时长和进度曲线跳变太明显会被标记。更隐蔽的做法是保持正常速度播放但通过拦截心跳请求让服务器以为你一直在看。这就涉及到下一节的网络请求处理。注意直接操作video元素时要等元素加载出来再执行。页面可能是异步渲染的脚本跑的时候 video 还不存在。用MutationObserver监听 DOM 变化或者用setInterval轮询是比较稳妥的方式。3.2 心跳请求拦截fetch 与 XHR 的钩子雨课堂记录进度靠的是前端定时发请求。这个请求可能用fetch也可能用XMLHttpRequest。脚本要拦截它就得在页面上下文里重写这两个方法。以fetch为例基本思路是const originalFetch window.fetch; window.fetch function(...args) { const url args[0]; if (typeof url string url.includes(heartbeat)) { // 可以在这里修改请求参数或者直接返回一个假的成功响应 console.log(拦截到心跳请求, args); } return originalFetch.apply(this, args); };但这里有个关键问题篡改猴脚本默认运行在沙箱环境里window不是页面的真实window。你重写的fetch可能只影响脚本自己的作用域页面本身的代码用的还是原来的fetch。要解决这个问题需要在元数据里加grant none并且用unsafeWindow来访问页面的真实全局对象。不同版本的篡改猴对unsafeWindow的支持策略不一样这也是“脚本已启用但是没有运行”的常见原因之一。XHR 的拦截类似重写XMLHttpRequest.prototype.open和send在send里检查 URL 和请求体。但要注意重写原型方法影响面很大写不好会把页面其他功能搞崩。3.3 防挂机检测的应对思路平台不是傻子它们会加各种检测。常见的几种页面可见性检测用document.visibilityState或visibilitychange事件判断你是不是切走了标签页。切走就暂停计时。鼠标/键盘活动检测监听mousemove、keydown长时间没动静就弹窗或者暂停。弹题检测视频播放到某个时间点弹出题目不答就不继续。多标签检测同一账号在多个地方打开可能触发风控。脚本要应对这些就得模拟这些事件。比如定时派发mousemove事件或者用Object.defineProperty把document.visibilityState固定成visible。弹题的话要么自动选一个答案要么直接隐藏弹窗元素。但这些操作都有风险平台更新一次检测逻辑脚本就可能失效甚至触发账号异常。实操心得我见过有人为了省事直接用一个setInterval每秒钟派发一次鼠标移动事件。结果页面上的其他交互被干扰比如下拉菜单一直闪。正确做法是只在检测到页面失焦时才模拟活动而且事件坐标要随机化别每次都一样。3.4 脚本注入时机与执行环境的选择篡改猴的元数据里有一个run-at可以控制脚本在页面加载的哪个阶段执行。常见值有document-start、document-end、document-idle。默认是document-idle也就是页面基本加载完了才跑。如果你要拦截网络请求必须用document-start因为fetch和 XHR 可能在页面早期就被调用。如果只是操作 DOMdocument-end或document-idle更安全避免元素还没生成就去找。另外有些脚本需要等待特定的元素出现。这时候可以用MutationObserverconst observer new MutationObserver((mutations, obs) { const video document.querySelector(video); if (video) { // 找到 video 了开始处理 obs.disconnect(); } }); observer.observe(document.body, { childList: true, subtree: true });这种方式比无脑setInterval轮询更高效也不会因为轮询间隔太长而错过元素。4. 常见问题与排查技巧实录4.1 脚本装了但完全没反应怎么一步步定位这是最高频的问题。排查顺序应该是这样的排查项检查方法常见问题扩展是否启用chrome://extensions/看篡改猴开关被禁用或崩溃脚本是否启用篡改猴面板里看脚本开关装完忘了开match是否匹配对比当前 URL 和脚本里的 match域名写错、路径不匹配脚本是否注入Console 里看有没有脚本的 log被 CSP 拦截、沙箱问题是否有报错Console 的 Errors 标签语法错误、API 不存在如果 Console 里连脚本的console.log都没有说明脚本根本没注入。这时候重点查match和扩展权限。如果注入了但报错看错误信息通常是某个 API 在当前浏览器版本不支持或者unsafeWindow访问被拒。4.2 “篡改猴脚本已启用但是没有运行”的几种真实原因这个提示意味着篡改猴知道有这个脚本但脚本没有在页面上执行。除了上面说的match问题还有几种情况第一种页面有Content Security PolicyCSP禁止内联脚本执行。篡改猴注入脚本的方式如果被 CSP 挡住脚本就跑不起来。解决办法是在元数据里加grant申请权限让篡改猴用更底层的方式注入。第二种脚本用了require加载外部库但那个库的地址挂了或者被墙了。脚本会卡在加载依赖那一步后面的代码不执行。第三种脚本里有语法错误篡改猴解析失败直接跳过。这种情况 Console 里通常会有红色报错。第四种浏览器开了“增强保护”或者某些安全扩展把用户脚本当成了可疑行为拦截。可以试试在无痕窗口里测试排除其他扩展的干扰。4.3 网络请求拦截失效的排查如果你写了fetch钩子但发现没生效先确认脚本是不是运行在页面上下文。在 Console 里输入window.fetch.toString()看看输出的是不是原生代码。如果显示function fetch() { [native code] }说明你的重写没生效。原因通常是沙箱隔离。篡改猴默认把脚本放在一个隔离环境里window是代理对象。要访问真实window用unsafeWindow。但unsafeWindow在 Manifest V3 的扩展里支持有限可能需要改用grant none加inject-into page之类的配置。另一个原因是拦截时机太晚。页面在脚本执行之前就已经调用了fetch你重写的时候人家已经用完了。这时候要把run-at改成document-start。4.4 账号安全与风控的边界必须单独说这一点。平台的风控系统会记录异常行为比如学习速度明显快于正常人类几小时的课几分钟完成心跳请求的时间间隔过于规律同一 IP 下多个账号行为一致页面事件鼠标、键盘完全缺失或过于机械一旦被标记轻则进度清零重则账号受限。我个人的建议是如果只是偶尔需要补进度手动调整播放速度比跑脚本更安全。脚本的自动化程度越高行为特征越明显被检测的概率越大。注意不要在同一时间对多门课同时跑脚本也不要把播放速度调到离谱的值。任何“看起来不像人”的行为都是风控的重点关注对象。5. 从脚本到工程化更稳妥的替代思路5.1 用浏览器原生功能减少对脚本的依赖很多人不知道Chrome 和 Edge 本身就支持一些自动化能力。比如开发者工具里的 Snippets可以保存一段 JS 代码在 Console 里随时调用。虽然不能自动执行但至少不用装扩展。再比如书签栏的 bookmarklet把一段 JS 存成书签点一下就在当前页面执行。这些方式比装用户脚本更轻量也不容易被平台检测为“扩展注入”。另外浏览器的画中画模式Picture-in-Picture可以让视频浮在其他窗口上面这样你切到别的应用时视频仍然在播放页面可见性检测也可能不会触发。这算是一个合规的取巧方式至少没有篡改任何请求。5.2 理解平台检测逻辑后的合规选择如果你真的想减少在已掌握内容上的时间消耗更稳妥的做法是用高倍速播放平台允许的范围内比如 1.5x 或 2x把视频下载到本地用本地播放器看如果平台提供下载和老师沟通看能否申请免听或替代任务利用碎片时间把视频当背景音过一遍这些方式不涉及篡改请求风险低得多。脚本方案的本质是欺骗服务器而欺骗行为一旦被识别后果由你自己承担。5.3 脚本调试的通用方法论抛开雨课堂这个具体场景用户脚本的调试有一套通用方法。首先在篡改猴的编辑器里写代码利用它自带的语法检查。其次善用console.log和debugger语句在开发者工具里单步执行。再次用try...catch包裹可能出错的代码把错误信息打出来而不是让脚本静默失败。还有一个小技巧在脚本开头加一行console.log(脚本已注入, location.href)这样你一眼就能看出脚本有没有跑、在哪个页面跑的。很多问题在这一步就暴露了。5.4 关于“nyquist脚本”“via脚本”等热词的说明热词里出现了“nyquist脚本”“via脚本”这些词它们和雨课堂脚本没有直接关系。Nyquist 是一种音频编程语言常用于 Audacity 的音频处理。Via 可能指某个浏览器或者某个工具。这些词出现在热词列表里大概率是搜索联想的结果不是雨课堂脚本生态的一部分。如果你在找相关资源时看到这些词不用被带偏核心还是用户脚本和浏览器扩展那一套。同样“shell脚本for循环”“npm 无法识别”“powershell开机自启脚本”这些属于另一个技术领域和浏览器里的用户脚本不是一回事。用户脚本跑在浏览器沙箱里用的是 JavaScript不是 shell也不是 PowerShell。搞清楚这个边界能避免很多无效搜索。6. 我踩过的坑和最后几句实在话最早接触这类东西的时候我也以为装个脚本就万事大吉。实际折腾下来发现最耗时间的不是写代码而是搞明白为什么代码不跑。match写错、run-at时机不对、unsafeWindow访问不了、CSP 拦截、扩展冲突每一个都能让你卡半天。后来我养成了一个习惯任何脚本先写一行console.log确认注入成功再往下做。这一步花十秒钟能省后面半小时的瞎猜。另一个体会是平台的前端逻辑一直在变。今天能用的选择器明天可能就改了。今天没签名的请求明天可能就加了校验。所以指望一个脚本长期稳定运行本身就不现实。你得有自己调试和更新的能力否则就是被脚本作者牵着走。最后说句掏心窝的话。技术手段能帮你省时间但省下来的时间用来干什么才是更值得想的问题。如果一门课的内容你确实已经掌握和老师坦诚沟通往往比偷偷跑脚本更有效。如果只是懒得看那脚本也只是把问题往后拖。工具是死的怎么用、用在哪还是看人。
返回列表