Greasy Fork is available in English.

Bypass verification

绕过绿盾验证,恢复积分查看

// ==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");
})();