Última atividade 1748306663

admin revisou este gist 1748306663. Ir para a revisão

1 file changed, 281 insertions

6.md(arquivo criado)

@@ -0,0 +1,281 @@
1 + ### 防切屏检测
2 +
3 + chrome浏览器自行安装油猴脚本
4 +
5 + ```
6 + // ==UserScript==
7 + // @name 通用阻止切屏检测
8 + // @namespace http://tampermonkey.net/
9 + // @version 0.1.0
10 + // @description 尝试阻止各类网站的切屏、焦点丢失等检测
11 + // @author xxxxxx
12 + // @match http://*/*
13 + // @match https://*/*
14 + // @run-at document-start
15 + // @grant unsafeWindow
16 + // @license GPL-3.0
17 + // ==/UserScript==
18 +
19 + (function () {
20 + 'use strict';
21 + const window = unsafeWindow; // 使用原始 window 对象
22 +
23 + // 黑名单事件,这些事件的监听器将被阻止
24 + const blackListedEvents = new Set([
25 + "visibilitychange", // 页面可见性改变
26 + "blur", // 元素或窗口失去焦点
27 + "focus", // 元素或窗口获得焦点 (某些检测可能反向利用focus)
28 + "pagehide", // 页面隐藏(例如导航到其他页面)
29 + "freeze", // 页面被冻结 (较新的事件)
30 + "resume", // 页面从冻结状态恢复 (较新的事件)
31 + "mouseleave", // 鼠标移出元素(通常是 document 或 body)
32 + "mouseout", // 鼠标移出元素(更通用的移出,但要小心副作用)
33 + // "focusout", // 元素将要失去焦点(与blur类似,但更通用,看情况添加)
34 + // "focusin", // 元素将要获得焦点(与focus类似,看情况添加)
35 + ]);
36 +
37 + // 白名单属性,这些属性在 document 对象上将被伪造
38 + const spoofedDocumentProperties = {
39 + hidden: { value: false, configurable: true },
40 + mozHidden: { value: false, configurable: true }, // Firefox (旧版)
41 + msHidden: { value: false, configurable: true }, // Internet Explorer
42 + webkitHidden: { value: false, configurable: true }, // Chrome, Safari, Opera (旧版 Blink/WebKit)
43 + visibilityState: { value: "visible", configurable: true },
44 + hasFocus: { value: () => true, configurable: true }
45 + };
46 +
47 + // 需要清空/置空的事件处理器属性 (on-event handlers)
48 + const eventHandlersToNullifyDocument = [
49 + "onvisibilitychange",
50 + "onblur",
51 + "onfocus",
52 + "onmouseleave",
53 + "onmouseout",
54 + // "onfocusout",
55 + // "onfocusin",
56 + "onpagehide",
57 + "onfreeze",
58 + "onresume"
59 + ];
60 +
61 + const eventHandlersToNullifyWindow = [
62 + "onblur",
63 + "onfocus",
64 + "onpagehide",
65 + "onpageshow", // 有些检测可能通过 pageshow 结合 persisted 属性判断
66 + "onfreeze",
67 + "onresume",
68 + "onmouseleave", // window 也有 onmouseleave
69 + "onmouseout"
70 + ];
71 +
72 + const isDebug = false; // 设置为 true 以启用调试日志
73 + const scriptPrefix = "[通用阻止切屏检测]";
74 + const log = console.log.bind(console, `%c${scriptPrefix}`, 'color: #4CAF50; font-weight: bold;');
75 + const warn = console.warn.bind(console, `%c${scriptPrefix}`, 'color: #FFC107; font-weight: bold;');
76 + const error = console.error.bind(console, `%c${scriptPrefix}`, 'color: #F44336; font-weight: bold;');
77 + const debug = isDebug ? log : () => { };
78 +
79 + /**
80 + * 伪装函数的 toString 方法,使其看起来像原始函数。
81 + * @param {Function} modifiedFunction 被修改的函数
82 + * @param {Function} originalFunction 原始函数
83 + */
84 + function patchToString(modifiedFunction, originalFunction) {
85 + if (typeof modifiedFunction !== 'function' || typeof originalFunction !== 'function') {
86 + warn("patchToString: 传入的参数不是函数。", modifiedFunction, originalFunction);
87 + return;
88 + }
89 + try {
90 + const originalToStringSource = Function.prototype.toString.call(originalFunction);
91 + modifiedFunction.toString = () => originalToStringSource;
92 +
93 + // 进一步伪装 toString.toString
94 + const originalToStringToStringSource = Function.prototype.toString.call(originalFunction.toString);
95 + Object.defineProperty(modifiedFunction.toString, 'toString', {
96 + value: () => originalToStringToStringSource,
97 + enumerable: false,
98 + configurable: true, // 保持可配置,以防万一
99 + writable: false
100 + });
101 + debug(`patchToString applied for: ${originalFunction.name || 'anonymous function'}`);
102 + } catch (e) {
103 + error("patchToString failed:", e, "for function:", originalFunction.name);
104 + }
105 + }
106 +
107 +
108 + /**
109 + * 劫持并修改对象的 addEventListener 方法。
110 + * @param {EventTarget} targetObject 要劫持的对象 (window, document, Element)
111 + * @param {string} objectName 用于日志记录的对象名称
112 + */
113 + function patchAddEventListener(targetObject, objectName) {
114 + if (!targetObject || typeof targetObject.addEventListener !== 'function') {
115 + warn(`Cannot patch addEventListener for invalid target: ${objectName}`);
116 + return;
117 + }
118 + const originalAddEventListener = targetObject.addEventListener;
119 +
120 + targetObject.addEventListener = function (type, listener, optionsOrCapture) {
121 + if (blackListedEvents.has(type.toLowerCase())) {
122 + log(`BLOCKED ${objectName}.addEventListener: ${type}`);
123 + return undefined; // 阻止添加黑名单中的事件监听器
124 + }
125 + debug(`ALLOWED ${objectName}.addEventListener: ${type}`, listener, optionsOrCapture);
126 + return originalAddEventListener.call(this, type, listener, optionsOrCapture);
127 + };
128 +
129 + patchToString(targetObject.addEventListener, originalAddEventListener);
130 + log(`${objectName}.addEventListener patched.`);
131 + }
132 +
133 + /**
134 + * 劫持并修改对象的 removeEventListener 方法 (可选,但建议一起修改)。
135 + * @param {EventTarget} targetObject 要劫持的对象
136 + * @param {string} objectName 用于日志记录的对象名称
137 + */
138 + function patchRemoveEventListener(targetObject, objectName) {
139 + if (!targetObject || typeof targetObject.removeEventListener !== 'function') {
140 + warn(`Cannot patch removeEventListener for invalid target: ${objectName}`);
141 + return;
142 + }
143 + const originalRemoveEventListener = targetObject.removeEventListener;
144 +
145 + targetObject.removeEventListener = function (type, listener, optionsOrCapture) {
146 + if (blackListedEvents.has(type.toLowerCase())) {
147 + log(`Original call to ${objectName}.removeEventListener for blacklisted event '${type}' would have been ignored by our addEventListener patch anyway. Allowing native call if needed.`);
148 + // 即使我们阻止了 addEventListener,原始的 removeEventListener 仍然应该能安全调用
149 + // 因为如果监听器从未被添加,调用 remove 也无害。
150 + }
151 + debug(`PASSTHROUGH ${objectName}.removeEventListener: ${type}`, listener, optionsOrCapture);
152 + return originalRemoveEventListener.call(this, type, listener, optionsOrCapture);
153 + };
154 + patchToString(targetObject.removeEventListener, originalRemoveEventListener);
155 + log(`${objectName}.removeEventListener patched.`);
156 + }
157 +
158 +
159 + /**
160 + * 修改对象上的属性,使其返回伪造的值。
161 + * @param {object} targetObject 目标对象 (e.g., document)
162 + * @param {object} propertiesToSpoof 属性描述对象
163 + * @param {string} objectName 对象名称
164 + */
165 + function spoofProperties(targetObject, propertiesToSpoof, objectName) {
166 + if (!targetObject) {
167 + warn(`Cannot spoof properties for invalid target: ${objectName}`);
168 + return;
169 + }
170 + for (const prop in propertiesToSpoof) {
171 + if (Object.prototype.hasOwnProperty.call(propertiesToSpoof, prop)) {
172 + try {
173 + Object.defineProperty(targetObject, prop, propertiesToSpoof[prop]);
174 + debug(`Spoofed ${objectName}.${prop}`);
175 + } catch (e) {
176 + error(`Failed to spoof ${objectName}.${prop}:`, e);
177 + }
178 + }
179 + }
180 + log(`${objectName} properties spoofed.`);
181 + }
182 +
183 + /**
184 + * 清空或置空对象上的事件处理器属性。
185 + * @param {object} targetObject 目标对象
186 + * @param {string[]} eventHandlerNames 事件处理器名称数组
187 + * @param {string} objectName 对象名称
188 + */
189 + function nullifyEventHandlers(targetObject, eventHandlerNames, objectName) {
190 + if (!targetObject) {
191 + warn(`Cannot nullify event handlers for invalid target: ${objectName}`);
192 + return;
193 + }
194 + eventHandlerNames.forEach(handlerName => {
195 + try {
196 + Object.defineProperty(targetObject, handlerName, {
197 + get: () => {
198 + debug(`Access to ${objectName}.${handlerName} (get), returning undefined.`);
199 + return undefined;
200 + },
201 + set: (newHandler) => {
202 + log(`Attempt to set ${objectName}.${handlerName} blocked.`);
203 + if (typeof newHandler === 'function') {
204 + // 可以选择性地调用 newHandler,或者完全阻止
205 + // debug(`(Blocked) Handler function was:`, newHandler);
206 + }
207 + },
208 + configurable: true // 保持可配置,以便脚本可以多次运行或被其他脚本修改
209 + });
210 + debug(`Nullified ${objectName}.${handlerName}`);
211 + } catch (e) {
212 + error(`Failed to nullify ${objectName}.${handlerName}:`, e);
213 + }
214 + });
215 + log(`${objectName} on-event handlers nullified.`);
216 + }
217 +
218 + // --- 开始执行 ---
219 +
220 + log("Script starting...");
221 +
222 + // 1. 劫持 window 和 document 的 addEventListener/removeEventListener
223 + patchAddEventListener(window, "window");
224 + patchRemoveEventListener(window, "window"); // 也 patch removeEventListener 以保持一致性
225 + patchAddEventListener(document, "document");
226 + patchRemoveEventListener(document, "document");
227 +
228 + // 2. 修改 document 的属性
229 + spoofProperties(document, spoofedDocumentProperties, "document");
230 +
231 + // 3. 置空 document 和 window 上的事件处理器
232 + nullifyEventHandlers(document, eventHandlersToNullifyDocument, "document");
233 + nullifyEventHandlers(window, eventHandlersToNullifyWindow, "window");
234 +
235 + // 4. 对于 document.body,需要等待 DOMContentLoaded
236 + // 使用 MutationObserver 确保 body 存在时立即 patch,比 DOMContentLoaded 更早且更可靠
237 + const observer = new MutationObserver((mutations, obs) => {
238 + if (document.body) {
239 + patchAddEventListener(document.body, "document.body");
240 + patchRemoveEventListener(document.body, "document.body");
241 + // 对于 document.body,也可以考虑 nullify onmouseleave, onmouseout 等
242 + nullifyEventHandlers(document.body, ["onmouseleave", "onmouseout", "onblur", "onfocus"], "document.body");
243 + log("document.body patched via MutationObserver.");
244 + obs.disconnect(); // 完成任务后断开观察者
245 + }
246 + });
247 +
248 + if (document.body) { // 如果 body 已经存在 (不太可能在 document-start,但以防万一)
249 + patchAddEventListener(document.body, "document.body");
250 + patchRemoveEventListener(document.body, "document.body");
251 + nullifyEventHandlers(document.body, ["onmouseleave", "onmouseout", "onblur", "onfocus"], "document.body");
252 + log("document.body patched directly.");
253 + } else {
254 + observer.observe(document.documentElement || document, { childList: true, subtree: true });
255 + }
256 +
257 +
258 + // 5. 调试:劫持计时器 (如果 isDebug 为 true)
259 + if (isDebug) {
260 + const originalSetInterval = window.setInterval;
261 + window.setInterval = function(...args) {
262 + const id = originalSetInterval.apply(this, args);
263 + debug("calling window.setInterval", id, args);
264 + return id;
265 + };
266 + patchToString(window.setInterval, originalSetInterval);
267 +
268 + const originalSetTimeout = window.setTimeout;
269 + window.setTimeout = function(...args) {
270 + const id = originalSetTimeout.apply(this, args);
271 + debug("calling window.setTimeout", id, args);
272 + return id;
273 + };
274 + patchToString(window.setTimeout, originalSetTimeout);
275 + log("Timer functions (setInterval, setTimeout) wrapped for debugging.");
276 + }
277 +
278 + log("Script execution finished. Monitoring active.");
279 +
280 + })();
281 + ```
Próximo Anterior