// ==UserScript==
// @name Bypass verification
// @namespace http://tampermonkey.net/
// @version 0.2
// @description 绕过绿盾验证,恢复积分查看
// @author Brian
// @match http://oa.microvideo.cn/*
// @grant none
// @run-at document-start
// @license MIT
// ==/UserScript==
(function () {
"use strict";
// Store the original createElement method
const originalCreateElement = document.createElement;
// Override createElement to intercept script creation
document.createElement = function (tagName) {
// Create the element using the original method
const element = originalCreateElement.apply(document, arguments);
// Only intercept script elements
if (tagName.toLowerCase() === "script") {
// Store the original setter
const originalSrcSetter = Object.getOwnPropertyDescriptor(
HTMLScriptElement.prototype,
"src"
).set;
// Override the src property
Object.defineProperty(element, "src", {
set: function (value) {
// Check if this is the local verification request
if (value && value.includes("127.0.0.1:50018")) {
console.log(
"[Bypass] Intercepted local verification request:",
value
);
// Call the original setter with the URL to maintain script behavior
originalSrcSetter.call(this, value);
// Override the onerror handler to prevent 403 redirect
setTimeout(() => {
this.onerror = function (e) {
console.log("[Bypass] Prevented redirect to 403 page");
// Create a mock response
const mockResponse = {
result: window.be.encode(
JSON.stringify({
data: [
{
UserNo: window._t.lvdunUserNo, // Use the same UserNo from the app
},
],
})
),
};
// If there's a success handler defined, call it with our mock response
if (typeof window.localHandler === "function") {
console.log(
"[Bypass] Calling success handler with mock response"
);
window.localHandler(mockResponse);
}
// Make sure loading state is completed
if (window.z && typeof window.z.done === "function") {
window.z.done();
}
if (window.kt && window.kt.commit) {
window.kt.commit("loading/OPPEN_LOADING", { show: false });
}
return true; // Prevent the default error behavior
};
// Also override onload to ensure our mock response is used
this.onload = function () {
console.log(
"[Bypass] Script loaded, ensuring mock response is used"
);
// The script might define its own localHandler, so we'll wait a bit and then check
setTimeout(() => {
if (typeof window.localHandler === "function") {
const mockResponse = {
result: window.be.encode(
JSON.stringify({
data: [
{
UserNo: window._t.lvdunUserNo,
},
],
})
),
};
console.log(
"[Bypass] Calling success handler with mock response"
);
window.localHandler(mockResponse);
}
}, 100);
};
}, 0);
return;
}
// For all other scripts, use the original setter
originalSrcSetter.call(this, value);
},
get: function () {
return Object.getOwnPropertyDescriptor(
HTMLScriptElement.prototype,
"src"
).get.call(this);
},
});
}
return element;
};
// Create a backup method to handle the AJAX request if it's made directly
const originalAjax = window.ve && window.ve.ajax;
if (originalAjax) {
window.ve.ajax = function (options) {
if (options.url && options.url.includes("127.0.0.1:50018")) {
console.log("[Bypass] Intercepted AJAX request to local service");
// Create a mock response
const mockResponse = {
result: window.be.encode(
JSON.stringify({
data: [
{
UserNo: window._t.lvdunUserNo,
},
],
})
),
};
// Call the success callback with our mock response
if (options.success && typeof options.success === "function") {
setTimeout(() => {
options.success(mockResponse);
}, 50);
}
// Don't actually make the AJAX request
return;
}
// For all other AJAX requests, use the original method
return originalAjax.apply(this, arguments);
};
}
// Add a global error handler for the specific script
window.addEventListener(
"error",
function (event) {
if (
event.target &&
event.target.src &&
event.target.src.includes("127.0.0.1:50018")
) {
console.log("[Bypass] Caught error event for local service script");
event.preventDefault();
event.stopPropagation();
return true;
}
},
true
);
// 保存原始的 XMLHttpRequest.prototype.open 和 send 方法
const originalXhrOpen = XMLHttpRequest.prototype.open;
const originalXhrSend = XMLHttpRequest.prototype.send;
// 定义目标URL需要包含的子字符串
const targetUrlSubstring = '/prod-api/attendancePointsStatistics/getPointsByUserId?userId=';
// 重写 XMLHttpRequest.prototype.open 方法
XMLHttpRequest.prototype.open = function(method, url) {
// 在XHR对象上存储请求方法和URL,以便在 send 方法中检查
this._requestMethod = method;
this._requestUrl = url;
// console.log('[油猴脚本] XHR open:', method, url); // 用于调试
// 调用原始的 open 方法
return originalXhrOpen.apply(this, arguments);
};
// 重写 XMLHttpRequest.prototype.send 方法
XMLHttpRequest.prototype.send = function() {
// 检查当前请求是否是我们的目标请求 (GET方法且URL包含目标子字符串)
// 添加 typeof this._requestUrl === 'string' 确保 _requestUrl 是字符串类型
if (this._requestMethod && this._requestMethod.toUpperCase() === 'GET' &&
this._requestUrl && typeof this._requestUrl === 'string' && this._requestUrl.includes(targetUrlSubstring)) {
console.log('[油猴脚本] 匹配到目标XHR请求 (based on includes):', this._requestUrl);
// 保存原始的 onreadystatechange 回调函数 (如果存在)
const originalOnReadyStateChange = this.onreadystatechange;
// 设置新的 onreadystatechange 回调函数
this.onreadystatechange = function() {
// 当请求完成 (readyState === 4)
if (this.readyState === 4) {
// 再次确认URL (使用 responseURL,它会包含重定向后的最终URL)
// 并检查请求是否成功 (status 200-299)
const currentUrl = this.responseURL || this._requestUrl;
// 再次使用 String.includes() 判断 currentUrl
// 添加 typeof currentUrl === 'string' 确保 currentUrl 是字符串类型
if (this.status >= 200 && this.status < 300 &&
currentUrl && typeof currentUrl === 'string' && currentUrl.includes(targetUrlSubstring)) {
console.log('[油猴脚本] 目标XHR请求完成:', currentUrl, '状态:', this.status);
try {
// 获取原始的响应文本
let originalResponseText = this.responseText;
// 解析JSON数据
let responseData = JSON.parse(originalResponseText);
// 检查JSON结构是否符合预期,并且 data.type 字段存在
if (responseData && responseData.data && responseData.data.hasOwnProperty('type')) {
console.log('[油猴脚本] 原始 data.type:', responseData.data.type);
// 修改 data.type 的值为 "1" (字符串类型)
responseData.data.type = "1";
console.log('[油猴脚本] 修改后 data.type:', responseData.data.type);
// 将修改后的JavaScript对象转换回JSON字符串
const modifiedResponseText = JSON.stringify(responseData);
// 使用 Object.defineProperty 重新定义 responseText 和 response 属性
Object.defineProperty(this, 'responseText', {
value: modifiedResponseText,
writable: false,
configurable: true
});
if (this.responseType === 'json') {
Object.defineProperty(this, 'response', {
value: responseData,
writable: false,
configurable: true
});
} else if (this.responseType === '' || this.responseType === 'text') {
Object.defineProperty(this, 'response', {
value: modifiedResponseText,
writable: false,
configurable: true
});
} else {
Object.defineProperty(this, 'response', {
value: (this.responseType === 'json') ? responseData : modifiedResponseText,
writable: false,
configurable: true
});
}
console.log('[油猴脚本] Response 属性已为', currentUrl, '重新定义');
} else {
console.warn('[油猴脚本] 响应数据中未找到 data.type 或结构不符:', currentUrl, responseData);
}
} catch (e) {
console.error('[油猴脚本] 处理或修改响应时出错:', currentUrl, e);
}
} else {
// console.log('[油猴脚本] XHR请求完成,但非目标请求、重定向后不匹配或请求失败:', currentUrl, '状态:', this.status, '包含判断:', (currentUrl && typeof currentUrl === 'string' && currentUrl.includes(targetUrlSubstring)));
}
}
// 无论如何,都调用原始的 onreadystatechange 回调 (如果存在)
if (originalOnReadyStateChange) {
return originalOnReadyStateChange.apply(this, arguments);
}
};
}
// 调用原始的 send 方法,并传递参数
return originalXhrSend.apply(this, arguments);
};
console.log("[Bypass] Local verification bypass script initialized");
})();