Dewu Fun110 Hook (Simple & Clean)

Simple hook to print args index and result c/d

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Dewu Fun110 Hook (Simple & Clean)
// @namespace    http://tampermonkey.net/
// @version      2.2
// @description  Simple hook to print args index and result c/d
// @author       You
// @match        *://*.dewu.com/*
// @run-at       document-start
// @grant        none
// @license      MIT
// @noframes
// ==/UserScript==

(function() {
    'use strict';

    if (window.top !== window.self) return;

    // ==========================================
    // UI 配置
    // ==========================================
    const PANEL_ID = 'dewu-v3-panel';
    const LOG_VIEW_ID = 'dewu-view-log';
    const INVOKE_VIEW_ID = 'dewu-view-invoke';
    const RPC_VIEW_ID = 'dewu-view-rpc';
    const MODAL_ID = 'dewu-v3-modal';

    // 全局 WebSocket 对象
    let ws = null;

    function createPanel() {
        if (document.getElementById(PANEL_ID)) return;
        const body = document.body || document.documentElement;
        if (!body) return setTimeout(createPanel, 100);

        const style = document.createElement('style');
        style.innerHTML = `
            #${PANEL_ID} {
                position: fixed; top: 0; left: 0; width: 100%; height: 65vh;
                background: rgba(0,0,0,0.95); color: #fff; z-index: 999999;
                display: flex; flex-direction: column;
                border-bottom: 2px solid #00ff00;
                font-family: monospace; font-size: 11px;
                box-shadow: 0 5px 15px rgba(0,0,0,0.5);
                transition: height 0.3s ease;
                -webkit-user-select: text !important; user-select: text !important;
            }
            #${PANEL_ID}.minimized { height: 40px; overflow: hidden; }
            
            .dewu-bar { height: 40px; background: #222; display: flex; align-items: center; justify-content: space-between; padding: 0 10px; border-bottom: 1px solid #444; flex-shrink: 0; }
            .dewu-title { color: #00ff00; font-weight: bold; }
            .dewu-btn { background: #444; color: #fff; border: 1px solid #555; padding: 3px 8px; border-radius: 4px; font-size: 11px; margin-left: 5px; }
            
            .dewu-tabs { display: flex; background: #1a1a1a; border-bottom: 1px solid #333; flex-shrink: 0; }
            .dewu-tab { flex: 1; text-align: center; padding: 8px 0; cursor: pointer; color: #888; border-bottom: 2px solid transparent; }
            .dewu-tab.active { color: #fff; border-bottom: 2px solid #00aaff; background: #2a2a2a; font-weight: bold; }

            .dewu-content { flex: 1; overflow-y: auto; padding: 5px; display: none; }
            .dewu-content.active { display: block; }

            /* RPC 界面样式 */
            .rpc-status { padding: 10px; text-align: center; font-size: 12px; margin-bottom: 10px; border-radius: 4px; border: 1px solid #444; }
            .rpc-status.connected { background: #004400; border-color: #00ff00; color: #00ff00; }
            .rpc-status.disconnected { background: #440000; border-color: #ff0000; color: #ff0000; }
            
            .rpc-input { width: 70%; background: #333; border: 1px solid #555; color: #fff; padding: 5px; border-radius: 3px; }
            .rpc-log { font-size: 10px; color: #aaa; margin-top: 5px; border-top: 1px dashed #444; padding-top: 5px; }

            /* 其他通用样式保持不变... */
            .log-group { border-bottom: 1px dashed #555; margin-bottom: 10px; padding: 5px; background: rgba(255,255,255,0.05); }
            .log-row { margin-top: 4px; word-break: break-all; white-space: pre-wrap; color: #ddd; }
            .dewu-copy-btn { border: 1px solid #00aaff; color: #00aaff; background: transparent; border-radius: 3px; font-size: 10px; padding: 1px 5px; }
            .inv-row { margin-bottom: 8px; }
            .inv-label { display: block; color: #aaa; margin-bottom: 2px; }
            .inv-input { width: 98%; background: #333; color: #fff; border: 1px solid #555; border-radius: 3px; padding: 4px; font-family: monospace; font-size: 11px; }
            textarea.inv-input { height: 60px; resize: vertical; }
            .inv-check-row { margin: 5px 0; display: flex; align-items: center; color: #ff9900; }
            #btn-run { width: 100%; background: #006600; color: #fff; border: none; padding: 8px; border-radius: 4px; margin-top: 5px; font-weight: bold; }
            #inv-result-box { margin-top: 10px; border-top: 1px solid #444; padding-top: 5px; }
            #${MODAL_ID} { display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.8); z-index: 1000000; justify-content: center; align-items: center; flex-direction: column; }
            #${MODAL_ID}.active { display: flex; }
            #dewu-textarea-copy { width: 90%; height: 60%; background: #fff; color: #000; padding: 10px; }
        `;
        document.head.appendChild(style);

        const div = document.createElement('div');
        div.id = PANEL_ID;
        div.innerHTML = `
            <div class="dewu-bar">
                <span class="dewu-title">Dewu Hook v3.0</span>
                <div>
                    <button id="btn-min" class="dewu-btn">最小化</button>
                    <button id="btn-cls" class="dewu-btn">清空日志</button>
                </div>
            </div>
            
            <div class="dewu-tabs">
                <div class="dewu-tab active" data-target="${LOG_VIEW_ID}">📊 监控</div>
                <div class="dewu-tab" data-target="${INVOKE_VIEW_ID}">⚡ 手动</div>
                <div class="dewu-tab" data-target="${RPC_VIEW_ID}">🌐 远程RPC</div>
            </div>

            <div id="${LOG_VIEW_ID}" class="dewu-content active">
                <div style="color:#666; text-align:center; margin-top:20px;">等待调用...</div>
            </div>

            <div id="${INVOKE_VIEW_ID}" class="dewu-content">
                <div class="inv-row"><span class="inv-label">Body:</span><textarea id="inv-body" class="inv-input" placeholder='{"spuId":...}'></textarea></div>
                <div class="inv-check-row"><input type="checkbox" id="inv-as-string"><label for="inv-as-string">Body传字符串</label></div>
                <div class="inv-row"><span class="inv-label">URL:</span><input id="inv-url" class="inv-input" type="text" /></div>
                <div class="inv-row"><span class="inv-label">Headers:</span><textarea id="inv-head" class="inv-input" placeholder='{"shumeiId":...}'></textarea></div>
                <div style="display:flex; justify-content:space-between;">
                     <button id="btn-template" class="dewu-btn" style="width:40%">填入模板</button>
                     <button id="btn-run" style="width:55%">🔥 加密</button>
                </div>
                <div id="inv-result-box"><pre id="inv-output" style="color:#ddd; white-space:pre-wrap; word-break:break-all;">...</pre></div>
            </div>

            <div id="${RPC_VIEW_ID}" class="dewu-content">
                <div id="rpc-status-box" class="rpc-status disconnected">未连接</div>
                <div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:10px;">
                    <input id="rpc-ws-url" class="rpc-input" type="text" value="ws://192.168.1.XX:8765/phone" placeholder="ws://IP:8765/phone" />
                    <button id="btn-rpc-connect" class="dewu-btn" style="background:#00aaff; border:none; padding: 6px 12px;">连接</button>
                </div>
                <div style="font-size:11px; color:#aaa; margin-bottom:5px;">RPC日志:</div>
                <div id="rpc-log-box" style="height:200px; overflow-y:auto; background:#111; padding:5px; font-family:monospace; color:#ccc;"></div>
            </div>
        `;
        body.appendChild(div);

        // 弹窗
        const modal = document.createElement('div');
        modal.id = MODAL_ID;
        modal.innerHTML = `
            <div style="color:#fff; margin-bottom:10px;">长按全选复制</div>
            <textarea id="dewu-textarea-copy" readonly></textarea>
            <button id="dewu-modal-close" style="margin-top:15px; padding:10px 30px; background:#00aaff; border:none; color:white; border-radius:5px;">关闭</button>
        `;
        body.appendChild(modal);

        bindEvents();
    }

    function addRpcLog(msg, color="#ccc") {
        const box = document.getElementById('rpc-log-box');
        if(box) {
            const time = new Date().toLocaleTimeString();
            box.innerHTML = `<div style="color:${color}">[${time}] ${msg}</div>` + box.innerHTML;
        }
    }

    function bindEvents() {
        const panel = document.getElementById(PANEL_ID);
        const btnMin = document.getElementById('btn-min');
        const btnCls = document.getElementById('btn-cls');
        const modal = document.getElementById(MODAL_ID);

        btnMin.onclick = () => { panel.classList.toggle('minimized'); btnMin.innerText = panel.classList.contains('minimized') ? "展开" : "最小化"; };
        btnCls.onclick = () => document.getElementById(LOG_VIEW_ID).innerHTML = '';
        document.getElementById('dewu-modal-close').onclick = () => modal.classList.remove('active');

        const tabs = document.querySelectorAll('.dewu-tab');
        tabs.forEach(tab => {
            tab.onclick = () => {
                document.querySelectorAll('.dewu-tab').forEach(t => t.classList.remove('active'));
                document.querySelectorAll('.dewu-content').forEach(c => c.classList.remove('active'));
                tab.classList.add('active');
                document.getElementById(tab.getAttribute('data-target')).classList.add('active');
            };
        });

        // 模板
        document.getElementById('btn-template').onclick = () => {
            document.getElementById('inv-body').value = '{"spuId":25705778,"scene":"commodityDetail"}';
            document.getElementById('inv-url').value = 'https://app.dewu.com/api/v1/h5/commodity/detail/detail';
            document.getElementById('inv-head').value = '{"shumeiId": "20251211_test"}';
            document.getElementById('inv-as-string').checked = false;
        };

        // 手动运行
        document.getElementById('btn-run').onclick = () => {
            runEncryption(
                document.getElementById('inv-body').value,
                document.getElementById('inv-url').value,
                document.getElementById('inv-head').value,
                document.getElementById('inv-as-string').checked,
                (res) => {
                    const out = document.getElementById('inv-output');
                    out.innerHTML = JSON.stringify(res, null, 2);
                    out.style.color = "#00ff00";
                },
                (err) => {
                    const out = document.getElementById('inv-output');
                    out.innerHTML = "❌ " + err;
                    out.style.color = "red";
                }
            );
        };

        // RPC 连接
        document.getElementById('btn-rpc-connect').onclick = () => {
            const url = document.getElementById('rpc-ws-url').value;
            connectRpc(url);
        };
    }

    // 核心加密封装
    function runEncryption(bodyRaw, urlStr, headRaw, isStringMode, onSuccess, onError) {
        if (!window.fun || !window.fun.Fun110) { onError("Fun110 not ready"); return; }
        try {
            let finalBody;
            if (isStringMode) finalBody = bodyRaw;
            else try { finalBody = JSON.parse(bodyRaw); } catch(e) { onError("Body JSON Error"); return; }

            let headObj;
            try { headObj = JSON.parse(headRaw); } catch(e) { onError("Headers JSON Error"); return; }

            const args = [finalBody, "post", urlStr, true, headObj];
            const res = window.fun.Fun110.apply(window.fun, args);
            onSuccess(res);
        } catch(e) { onError(e.message); }
    }

    // WebSocket 逻辑
    function connectRpc(url) {
        if (ws) { ws.close(); }
        addRpcLog(`尝试连接: ${url}...`, "#ff9900");
        
        try {
            ws = new WebSocket(url);
            ws.onopen = () => {
                document.getElementById('rpc-status-box').className = 'rpc-status connected';
                document.getElementById('rpc-status-box').innerText = '✅ 已连接服务器';
                addRpcLog("连接成功!", "#00ff00");
            };
            ws.onclose = () => {
                document.getElementById('rpc-status-box').className = 'rpc-status disconnected';
                document.getElementById('rpc-status-box').innerText = '❌ 连接断开';
                addRpcLog("连接断开", "red");
                ws = null;
            };
            ws.onerror = (e) => addRpcLog("Socket错误", "red");

            // 核心:处理来自电脑的请求
            ws.onmessage = (event) => {
                try {
                    const data = JSON.parse(event.data);
                    addRpcLog(`收到请求: ${data.url ? data.url.substring(0,20)+'...' : '未知'}`, "#00aaff");

                    // 默认配置
                    const bodyVal = data.body || "{}"; // 可以是对象或字符串
                    const urlVal = data.url || "";
                    const headVal = data.headers || {};
                    const isStr = (typeof bodyVal === 'string'); // 如果传来的是字符串,就按字符串加密

                    // 这里的 bodyRaw 和 headRaw 为了适配 runEncryption 逻辑需要转一下
                    const bodyInput = isStr ? bodyVal : JSON.stringify(bodyVal);
                    const headInput = JSON.stringify(headVal);

                    runEncryption(bodyInput, urlVal, headInput, isStr, 
                        (res) => {
                            // 成功回传
                            ws.send(JSON.stringify({ status: "ok", data: res }));
                            addRpcLog("✅ 计算完成,已回传", "#00ff00");
                        },
                        (err) => {
                            // 失败回传
                            ws.send(JSON.stringify({ status: "error", msg: err }));
                            addRpcLog("❌ 计算失败: " + err, "red");
                        }
                    );

                } catch (e) {
                    addRpcLog("消息解析失败: " + e.message, "red");
                }
            };
        } catch (e) {
            addRpcLog("创建WS失败: " + e.message, "red");
        }
    }

    createPanel();
    window.addEventListener('DOMContentLoaded', createPanel);

    // ==========================================
    // 常规 Hook 逻辑 (同 v2.2)
    // ==========================================
    function safeStr(v) { try { return typeof v === 'object' ? JSON.stringify(v, null, 2) : String(v); } catch(e) { return '[Err]'; } }
    function showCopyModal(c) { const m=document.getElementById(MODAL_ID),t=document.getElementById('dewu-textarea-copy'); if(m&&t){t.value=c;m.classList.add('active');t.focus();t.select();} }
    
    let _fun = window.fun;
    function hookFun(target) {
        if (target && target.Fun110 && !target.Fun110.__hooked) {
            const origin = target.Fun110;
            target.Fun110 = function(...args) {
                if (!document.getElementById(PANEL_ID)) createPanel();
                const logBox = document.getElementById(LOG_VIEW_ID);
                if (logBox && logBox.innerText.includes('等待')) logBox.innerHTML = '';
                
                const group = document.createElement('div');
                group.className = 'log-group';
                
                const header = document.createElement('div');
                header.style.cssText = "display:flex; justify-content:space-between; margin-bottom:5px; border-bottom:1px solid #444; padding-bottom:5px;";
                header.innerHTML = `<span style="color:#aaa;">Args: ${args.length}</span><button class="dewu-copy-btn">🔍 全屏复制</button>`;
                group.appendChild(header);
                
                // 绑定复制
                header.querySelector('button').onclick = () => showCopyModal(JSON.stringify({t:new Date().toLocaleTimeString(),args,res},null,2));

                args.forEach((arg, i) => {
                     const color = (typeof arg === 'string') ? '#ff9900' : '#ffcc00';
                     group.innerHTML += `<div class="log-row"><span style="color:${color}">[P${i+1}]</span> <span>${safeStr(arg)}</span></div>`;
                });

                let res;
                try { res = origin.apply(this, args); } catch (e) { group.innerHTML+=`<div style="color:red">Err:${e.message}</div>`; if(logBox)logBox.prepend(group); throw e;}
                
                if (res) {
                    group.innerHTML += `<div style="border-top:1px solid #444; margin-top:5px; padding-top:4px; color:#0f0;">[Result]</div>`;
                    group.innerHTML += `<div class="log-row">c: ${res.c||'-'}</div><div class="log-row">d: ${res.d||'-'}</div>`;
                }
                
                if (logBox) logBox.prepend(group);
                return res;
            };
            target.Fun110.__hooked = true;
            console.log("Hook Ready v3.0 RPC");
        }
        return target;
    }
    if (_fun) hookFun(_fun);
    Object.defineProperty(window, 'fun', { get:()=>_fun, set:(v)=>_fun=hookFun(v), configurable:true });
})();