NodeLoc(网页版机型标签伪造)

油猴脚本 手机和电脑都可以使用
目前看来是被暗改了 只有鼠标放到名字后面的图标上才显示出来

// ==UserScript==
// @name       NodeLoc(机型标签伪造)
// @namespace    http://tampermonkey.net/
// @version      1.0
// @author       CC1359
// @description  支持发帖时伪造/自定义机型标签
// @match        https://*.nodeloc.com/*
// @grant        none
// @run-at       document-start
// @license      MIT
// ==/UserScript==

(function () {
    'use strict';

    // ==================== 机型标签伪造配置 ====================
    // 是否启用伪造
    const ENABLED = true;

    // 当前选择的伪造机型(可修改为预设名称或自定义英文/数字)
    let CURRENT_MODEL = "iPhone 4S";

    const DEVICES = [
        { name: "iPhone 15 Pro Max", brand: "Apple", model: "iPhone 15 Pro Max", platform: "ios" },
        { name: "iPhone SE (3rd generation)", brand: "Apple", model: "iPhone SE (3rd generation)", platform: "ios" },
        { name: "iPad Pro 12.9", brand: "Apple", model: "iPad Pro", platform: "ios" },
        { name: "Google Pixel 8 Pro", brand: "Google", model: "Pixel 8 Pro", platform: "android" },
        { name: "Samsung Galaxy S24 Ultra", brand: "Samsung", model: "Samsung Galaxy S24 Ultra", platform: "android" },
        { name: "Xiaomi 14", brand: "Xiaomi", model: "Xiaomi 14", platform: "android" },
        { name: "Redmi Note 13 Pro", brand: "Redmi", model: "Redmi Note 13 Pro", platform: "android" },
        { name: "HUAWEI Mate 60 Pro", brand: "HUAWEI", model: "HUAWEI Mate 60 Pro", platform: "android" },
        { name: "OPPO Find X7 Ultra", brand: "OPPO", model: "OPPO Find X7 Ultra", platform: "android" },
        { name: "vivo X100 Pro", brand: "vivo", model: "vivo X100 Pro", platform: "android" },
        { name: "OnePlus 12", brand: "OnePlus", model: "OnePlus 12", platform: "android" },
        { name: "NodeLoc", brand: "NodeLoc", model: "NodeLoc", platform: "android" }
    ];

    function getDeviceConfig() {
        if (!ENABLED) return null;
        let found = DEVICES.find(d => d.model.toLowerCase() === CURRENT_MODEL.toLowerCase());
        if (found) {
            return {
                platform: found.platform,
                brand: found.brand,
                model: found.model
            };
        }
        let cleanModel = String(CURRENT_MODEL || "").replace(/[^\x20-\x7E]/g, "").trim();
        if (!cleanModel) return null;
        return {
            platform: "android",
            brand: "NodeLoc",
            model: cleanModel
        };
    }

    const DEVICE_CONFIG = getDeviceConfig();

    const DeviceInject = (() => {
        function applyDevice(p, d) {
            if (!d) return p;
            if (d.platform) p.mobile_source_platform = d.platform;
            if (d.brand) p.mobile_source_brand = d.brand;
            if (d.model) p.mobile_source_model = d.model;
            return p;
        }
        function parseUrlencoded(str) {
            const out = {};
            if (typeof str !== "string" || !str) return out;
            const sp = new URLSearchParams(str);
            for (const [k, v] of sp.entries()) out[k] = v;
            return out;
        }
        function serializeUrlencoded(p) {
            const sp = new URLSearchParams();
            for (const k of Object.keys(p)) {
                if (p[k] === undefined || p[k] === null) continue;
                sp.append(k, String(p[k]));
            }
            return sp.toString();
        }
        function parseJson(str) {
            try { return str && typeof str === "string" && str.trim() ? JSON.parse(str) : {}; }
            catch { return {}; }
        }
        function isFormData(b) { return typeof FormData !== "undefined" && b instanceof FormData; }
        function isURLSearchParams(b) { return typeof URLSearchParams !== "undefined" && b instanceof URLSearchParams; }

        function injectBody(body, contentType, device) {
            if (!device) return { body, contentType: contentType || "" };
            if (isFormData(body) || isURLSearchParams(body)) {
                if (device.platform) body.append("mobile_source_platform", device.platform);
                if (device.brand) body.append("mobile_source_brand", device.brand);
                if (device.model) body.append("mobile_source_model", device.model);
                return { body, contentType: contentType || "" };
            }
            const ct = (contentType || "").toLowerCase();
            const forceJson = ct.includes("json");
            if (typeof body === "string") {
                if (forceJson || body.trim().startsWith("{") || body.trim().startsWith("[")) {
                    const obj = parseJson(body);
                    applyDevice(obj, device);
                    return { body: JSON.stringify(obj), contentType: ct || "application/json" };
                }
                const p = parseUrlencoded(body);
                applyDevice(p, device);
                return { body: serializeUrlencoded(p), contentType: ct || "application/x-www-form-urlencoded" };
            }
            if (body && typeof body === "object") {
                const obj = forceJson ? body : parseUrlencoded(String(body));
                applyDevice(obj, device);
                if (forceJson) return { body: JSON.stringify(obj), contentType: ct };
                return { body: serializeUrlencoded(obj), contentType: "application/x-www-form-urlencoded" };
            }
            const p = {};
            applyDevice(p, device);
            return { body: serializeUrlencoded(p), contentType: "application/x-www-form-urlencoded" };
        }
        return { injectBody };
    })();

    const TARGET_RE = /\/posts(\.json)?(\?|$)/;

    function isTarget(url) {
        if (!url) return false;
        try {
            const u = new URL(url, location.href);
            return u.hostname.endsWith("nodeloc.com") && TARGET_RE.test(u.pathname);
        } catch { return TARGET_RE.test(url); }
    }
    function looksJson(s) {
        return typeof s === "string" && (s.trim().startsWith("{") || s.trim().startsWith("["));
    }
    function getContentType(headers) {
        if (!headers) return "";
        if (typeof headers.get === "function") return headers.get("Content-Type") || "";
        if (headers instanceof Object) return headers["Content-Type"] || headers["content-type"] || "";
        return "";
    }
    function setContentType(headers, value) {
        if (!headers) return;
        if (typeof headers.set === "function") headers.set("Content-Type", value);
        else if (headers instanceof Object) headers["Content-Type"] = value;
    }

    // 拦截 fetch
    const origFetch = window.fetch.bind(window);
    window.fetch = function (input, init = {}) {
        const url = typeof input === "string" ? input : input && input.url || "";
        const method = (init && init.method) || (input instanceof Request ? input.method : "POST");
        if (ENABLED && DEVICE_CONFIG && isTarget(url) && /^post$/i.test(method || "POST")) {
            const ct = getContentType(init.headers);
            const useJson = /json/i.test(ct) || looksJson(init.body);
            const res = DeviceInject.injectBody(
                init.body,
                useJson ? "application/json" : "application/x-www-form-urlencoded",
                DEVICE_CONFIG
            );
            console.log("[NodeLoc] 已拦截 fetch 发帖请求,注入机型:", DEVICE_CONFIG);
            const newInit = { ...init, body: res.body };
            setContentType(newInit.headers || (newInit.headers = new Headers()), res.contentType);
            if (input instanceof Request) input = new Request(input, newInit);
            else init = newInit;
        }
        return origFetch(input, init);
    };

    // 拦截 XMLHttpRequest
    const origXHROpen = XMLHttpRequest.prototype.open;
    const origXHRSend = XMLHttpRequest.prototype.send;
    XMLHttpRequest.prototype.open = function (method, url, ...rest) {
        this.__nl_method = method;
        this.__nl_url = url;
        return origXHROpen.apply(this, arguments);
    };
    XMLHttpRequest.prototype.send = function (body) {
        if (ENABLED && DEVICE_CONFIG && isTarget(this.__nl_url) && /^post$/i.test(this.__nl_method || "POST")) {
            const useJson = looksJson(body);
            const res = DeviceInject.injectBody(
                body,
                useJson ? "application/json" : "application/x-www-form-urlencoded",
                DEVICE_CONFIG
            );
            console.log("[NodeLoc] 已拦截 XHR 发帖请求,注入机型:", DEVICE_CONFIG);
            if (res.contentType) {
                try { this.setRequestHeader("Content-Type", res.contentType); } catch (e) {}
            }
            body = res.body;
        }
        return origXHRSend.call(this, body);
    };

    console.log("[NodeLoc] 机型标签伪造已加载,当前伪造机型:", CURRENT_MODEL);

})();
4 个赞

测试

二次测试

1 个赞

看看有没有用(骗子,大家可以撤了)

三次测试,脚本部分修改

改请求体? :xhj001:

测试一下


测试了一下好像确实改变了

:xhj09: 修复了吗

嗯 估计是被修复了 早知道就自己用不发出来了

其实…我很早就发了一篇这个了自定义机型,现在看应该是修成隐形隐藏了,能用是能用,但是外显的只有苹果,安卓等图标了

1 个赞

目前看是被暗改了 只有鼠标放到名字后面的图标上才显示出来

测试

测试1

好的脚本

膜拜大佬

就是把发送是上交的数据改了嘛