/* ============================================================================ * 自动抓包系统 v6.0 - 纯 Cocos 原生(无任何 HTML) * 修复:多行显示 / 单行格式 / Cocos 状态弹窗 * ============================================================================ */ ;(function () { 'use strict'; var BALL_R = 34; var ball = null; var mainPanel = null; var fullPanel = null; var startToast = null; var statusToast = null; function getVS() { var w = 720, h = 1280; try { var vs = cc.view.getVisibleSize(); if (vs && vs.width) { w = vs.width; h = vs.height; } } catch (e) {} return { width: w, height: h }; } function currentScene() { try { if (cc.director.getScene) return cc.director.getScene(); } catch (e) {} try { return cc.director.getRunningScene(); } catch (e2) {} return null; } function ev(base, dft) { try { var t = cc.Node.EventType; if (t && t[base]) return t[base]; } catch (e) {} return dft; } // 创建 label,支持指定高度 function mkLabel(parent, text, size, rgba, x, y, w, h, align) { var n = new cc.Node('Lbl'); n.parent = parent; var H = h && h > 0 ? h : (size + 12); n.setContentSize(Math.max(10, w), H); try { n.anchorX = (align === 'left' ? 0 : 0.5); n.anchorY = 0.5; } catch (e0) {} n.setPosition(x, y); var l = n.addComponent(cc.Label); l.string = text || ''; l.fontSize = size; l.lineHeight = Math.round(size * 1.35); try { l.horizontalAlign = align === 'left' ? cc.Label.HorizontalAlign.LEFT : cc.Label.HorizontalAlign.CENTER; } catch (e1) {} try { l.verticalAlign = cc.Label.VerticalAlign.CENTER; } catch (e2) {} try { n.color = new cc.Color(rgba[0], rgba[1], rgba[2], rgba[3]); } catch (e3) {} return l; } // 多行顶部对齐 label(列表专用) function mkListLabel(parent, text, size, rgba, w, h) { var n = new cc.Node('Lbl'); n.parent = parent; n.setContentSize(Math.max(10, w), Math.max(10, h)); n.anchorX = 0; n.anchorY = 1; var l = n.addComponent(cc.Label); l.string = text || ''; l.fontSize = size; l.lineHeight = Math.round(size * 1.4); try { l.horizontalAlign = cc.Label.HorizontalAlign.LEFT; } catch (e1) {} try { l.verticalAlign = cc.Label.VerticalAlign.TOP; } catch (e2) {} try { l.overflow = cc.Label.Overflow.CLAMP; } catch (e3) {} try { n.color = new cc.Color(rgba[0], rgba[1], rgba[2], rgba[3]); } catch (e4) {} return l; } function mkButton(parent, text, x, y, w, h, fill, stroke, onTap, textColor) { var n = new cc.Node('Btn'); n.parent = parent; n.setContentSize(w, h); n.setPosition(x, y); var g = n.addComponent(cc.Graphics); g.fillColor = new cc.Color(fill[0], fill[1], fill[2], fill[3]); g.strokeColor = new cc.Color(stroke[0], stroke[1], stroke[2], stroke[3]); try { g.lineWidth = 2; } catch (e0) {} g.roundRect(-w / 2, -h / 2, w, h, 8); g.fill(); g.stroke(); mkLabel(n, text, 20, textColor || [0, 0, 0, 255], 0, 0, w - 8, 0, 'center'); n.on(ev('TOUCH_END', 'touch-end'), function (e) { try { if (e && e.stopPropagation) e.stopPropagation(); } catch (e1) {} try { onTap(); } catch (e2) {} }, n); return n; } // ==================== 纯 Cocos 状态弹窗 ==================== function showStatusToast(msg, isOk) { var scene = currentScene(); if (!cc.isValid(scene)) return; if (statusToast && cc.isValid(statusToast)) { try { statusToast.destroy(); } catch (e) {} statusToast = null; } var vs = getVS(); var W = Math.min(560, Math.floor(vs.width * 0.85)); var H = 130; statusToast = new cc.Node('StatusToast'); statusToast.parent = scene; statusToast.setContentSize(W, H); statusToast.setAnchorPoint(0.5, 0.5); statusToast.setPosition(vs.width / 2, vs.height / 2 - 120); statusToast.zIndex = 2147483600; var g = statusToast.addComponent(cc.Graphics); g.fillColor = new cc.Color(255, 255, 255, 255); g.strokeColor = new cc.Color(isOk ? 60 : 220, isOk ? 180 : 60, isOk ? 100 : 60, 255); g.lineWidth = 3; g.roundRect(-W / 2, -H / 2, W, H, 16); g.fill(); g.stroke(); mkLabel(statusToast, isOk ? '✅ 成功' : '⚠️ 提示', 22, [isOk ? 30 : 200, isOk ? 160 : 60, isOk ? 90 : 60, 255], 0, H / 2 - 34, W - 40, 0, 'center'); // 自动换行 var lines = []; var chunkSize = 24; for (var i = 0; i < msg.length; i += chunkSize) lines.push(msg.slice(i, i + chunkSize)); mkLabel(statusToast, lines.join('\n'), 16, [40, 40, 40, 255], 0, -10, W - 40, lines.length * 22, 'center'); statusToast.on(ev('TOUCH_END', 'touch-end'), function (e) { try { if (e && e.stopPropagation) e.stopPropagation(); } catch (er) {} if (statusToast && cc.isValid(statusToast)) { try { statusToast.destroy(); } catch (e) {} statusToast = null; } }, statusToast); setTimeout(function () { if (statusToast && cc.isValid(statusToast)) { try { statusToast.destroy(); } catch (e) {} statusToast = null; } }, 2500); } // ==================== 抓包核心 ==================== var _captureEnabled = true; var _logs = []; var MAX_LOG = 99999; var _protoHooked = false; function nowStr() { var d = new Date(); function p(n) { return n < 10 ? '0' + n : '' + n; } function p3(n) { return n < 10 ? '00' + n : (n < 100 ? '0' + n : '' + n); } return d.getFullYear() + '-' + p(d.getMonth() + 1) + '-' + p(d.getDate()) + ' ' + p(d.getHours()) + ':' + p(d.getMinutes()) + ':' + p(d.getSeconds()) + '.' + p3(d.getMilliseconds()); } function shortTime() { var d = new Date(); function p(n) { return n < 10 ? '0' + n : '' + n; } return p(d.getHours()) + ':' + p(d.getMinutes()) + ':' + p(d.getSeconds()); } function safeStr(v, max) { try { if (v === undefined || v === null) return ''; if (typeof v === 'string') return v.length > max ? v.slice(0, max) + '...' : v; var s = JSON.stringify(v); return s && s.length > max ? s.slice(0, max) + '...' : s; } catch (e) { return String(v); } } function normalizeMsg(msg) { if (msg === undefined || msg === null) return null; if (typeof msg === 'string') { try { return JSON.parse(msg); } catch (e) { return { _rawString: msg }; } } return msg; } function extractCmd(obj) { try { if (!obj) return 'unknown'; if (typeof obj === 'string') return 'raw:' + obj.slice(0, 40); if (typeof obj !== 'object') return String(obj).slice(0, 40); var names = ['cmd', 'command', '_cmd', 'name', 'action', 'op', 'type']; for (var i = 0; i < names.length; i++) { if (obj[names[i]] !== undefined && obj[names[i]] !== null && typeof obj[names[i]] !== 'object') { return String(obj[names[i]]); } } if (obj.body && typeof obj.body === 'object') { for (var j = 0; j < names.length; j++) { if (obj.body[names[j]] !== undefined && obj.body[names[j]] !== null) return String(obj.body[names[j]]); } } if (obj.params && typeof obj.params === 'object') { for (var k = 0; k < names.length; k++) { if (obj.params[names[k]] !== undefined && obj.params[names[k]] !== null) return String(obj.params[names[k]]); } } if (obj.data && typeof obj.data === 'object') { for (var m = 0; m < names.length; m++) { if (obj.data[names[m]] !== undefined) return String(obj.data[names[m]]); } } if (obj.payload && typeof obj.payload === 'object') { for (var n = 0; n < names.length; n++) { if (obj.payload[names[n]] !== undefined) return String(obj.payload[names[n]]); } } var keys = Object.keys(obj).slice(0, 6); return '[' + keys.join(',') + ']'; } catch (e) {} return 'unknown'; } function extractParams(obj) { try { if (!obj || typeof obj !== 'object') return ''; if (obj.params !== undefined) return obj.params; if (obj.data !== undefined) return obj.data; if (obj.body !== undefined) return obj.body; if (obj.payload !== undefined) return obj.payload; if (obj.extra !== undefined) return obj.extra; } catch (e) {} return ''; } function pushLog(entry) { if (!entry) return; _logs.push(entry); if (_logs.length > MAX_LOG) _logs.shift(); try { updatePanelStats(); } catch (e) {} } function recordMsg(msg) { if (!_captureEnabled) return; var obj = normalizeMsg(msg); pushLog({ time: nowStr(), shortTime: shortTime(), cmd: extractCmd(obj), params: safeStr(extractParams(obj), 800) }); } function hookProto() { try { if (_protoHooked) return; if (typeof WebSocket === 'undefined' || !WebSocket.prototype) return; var proto = WebSocket.prototype; var methods = ['send', 'sendAsync']; for (var i = 0; i < methods.length; i++) { (function (method) { if (typeof proto[method] === 'function' && !proto['__autoCap_' + method]) { var orig = proto[method]; proto['__autoCap_' + method] = true; proto[method] = function (msg) { try { recordMsg(msg); } catch (e) {} return orig.apply(this, arguments); }; } })(methods[i]); } _protoHooked = true; } catch (e) {} } function getConn() { var cands = [ window.ws, window.h5websocket && window.h5websocket.ws, window.h5websocket, window.gameWs, window.WebSocketClient, window._ws, window.gameSocket ]; for (var i = 0; i < cands.length; i++) { var c = cands[i]; if (c && (c.sendAsync || c.send)) return c; } return null; } function hookInstance(conn) { if (!conn) return; try { if (typeof conn.sendAsync === 'function' && !conn.__autoCapSA) { var origA = conn.sendAsync; conn.__autoCapSA = true; conn.sendAsync = function (msg) { try { recordMsg(msg); } catch (e) {} return origA.apply(this, arguments); }; } if (typeof conn.send === 'function' && !conn.__autoCapS) { var origS = conn.send; conn.__autoCapS = true; conn.send = function (msg) { try { recordMsg(msg); } catch (e) {} return origS.apply(this, arguments); }; } } catch (e) {} } function tickScan() { hookProto(); var conn = getConn(); if (conn) hookInstance(conn); setTimeout(tickScan, 1000); } // ==================== 内容生成 ==================== function buildFullText() { var lines = []; lines.push('==================== 自动抓包日志 ===================='); lines.push('导出时间: ' + nowStr()); lines.push('记录总数: ' + _logs.length); lines.push('======================================================'); lines.push(''); for (var i = 0; i < _logs.length; i++) { var e = _logs[i]; if (!e) continue; lines.push('#' + (i + 1) + ' [' + (e.time || '') + '] CMD: ' + (e.cmd || '')); if (e.params && e.params !== '{}' && e.params !== '""') { lines.push(' 请求: ' + e.params); } } lines.push(''); lines.push('==================== 结束 ===================='); return lines.join('\n'); } // ★ 单行显示: [时间] CMD 请求 function buildPreviewText() { if (!_logs.length) return '(暂无抓包记录,等待游戏发送请求...)'; var lines = []; var start = Math.max(0, _logs.length - 20); for (var i = start; i < _logs.length; i++) { var e = _logs[i]; if (!e) continue; var t = e.shortTime || (e.time || '').slice(11, 19); var line = '[' + t + '] ' + (e.cmd || 'unknown'); if (e.params && e.params !== '{}' && e.params !== '""') { var p = e.params; if (p.length > 60) p = p.slice(0, 60) + '..'; line += ' ' + p; } lines.push(line); } return lines.join('\n'); } // ==================== 复制 ==================== function copyText(text) { // ① Cocos 内置剪贴板(native 走 JSB 原生) try { if (typeof cc !== 'undefined' && cc.sys && cc.sys.clipboard) { if (typeof cc.sys.clipboard.writeText === 'function') { try { cc.sys.clipboard.writeText(text); return { ok: true, via: 'cc.clipboard' }; } catch (e) {} } if (typeof cc.sys.clipboard.setText === 'function') { try { cc.sys.clipboard.setText(text); return { ok: true, via: 'cc.clipboard.setText' }; } catch (e) {} } } } catch (e) {} // ② 原生桥接 try { var bridge = window.AndroidBridge || window.Android || window.jsBridge || window.nativeBridge; if (bridge) { var names = ['copyText', 'setClipboard', 'setClipboardText', 'copy', 'copyToClipboard']; for (var i = 0; i < names.length; i++) { if (typeof bridge[names[i]] === 'function') { try { bridge[names[i]](text); return { ok: true, via: 'bridge.' + names[i] }; } catch (e) {} } } } } catch (e) {} return { ok: false, via: 'none' }; } // ==================== 面板状态引用 ==================== var _panelRefs = { statusLabel: null, statsLabel: null, listLabel: null, statusTimer: null }; function updatePanelStats() { try { if (_panelRefs.statsLabel && cc.isValid(_panelRefs.statsLabel)) { _panelRefs.statsLabel.string = '已抓记录: ' + _logs.length + ' 条'; } if (_panelRefs.listLabel && cc.isValid(_panelRefs.listLabel)) { _panelRefs.listLabel.string = buildPreviewText(); } } catch (e) {} } function setStatus(txt, color) { try { if (!_panelRefs.statusLabel || !cc.isValid(_panelRefs.statusLabel)) return; _panelRefs.statusLabel.string = txt; var c = color || [60, 130, 220, 255]; _panelRefs.statusLabel.node.color = new cc.Color(c[0], c[1], c[2], 255); if (_panelRefs.statusTimer) clearTimeout(_panelRefs.statusTimer); _panelRefs.statusTimer = setTimeout(function () { if (_panelRefs.statusLabel && cc.isValid(_panelRefs.statusLabel)) { _panelRefs.statusLabel.string = '状态: ' + (_captureEnabled ? '抓包中' : '已暂停') + '(已抓 ' + _logs.length + ' 条)'; _panelRefs.statusLabel.node.color = new cc.Color(60, 130, 220, 255); } }, 3500); } catch (e) {} } // ==================== 完整内容面板(EditBox 可长按复制)==================== function showFullContentPanel() { var scene = currentScene(); if (!cc.isValid(scene)) return; if (fullPanel && cc.isValid(fullPanel)) { try { fullPanel.destroy(); } catch (e) {} fullPanel = null; } var vs = getVS(); var W = Math.min(660, Math.floor(vs.width * 0.95)); var H = Math.min(950, Math.floor(vs.height * 0.92)); fullPanel = new cc.Node('FullContentPanel'); fullPanel.parent = scene; fullPanel.setContentSize(W, H); fullPanel.setAnchorPoint(0.5, 0.5); fullPanel.setPosition(vs.width / 2, vs.height / 2); fullPanel.zIndex = 100002; fullPanel.on(ev('TOUCH_START', 'touch-start'), function (e) { try { e.stopPropagation(); } catch (er) {} }, fullPanel); fullPanel.on(ev('TOUCH_MOVE', 'touch-move'), function (e) { try { e.stopPropagation(); } catch (er) {} }, fullPanel); fullPanel.on(ev('TOUCH_END', 'touch-end'), function (e) { try { e.stopPropagation(); } catch (er) {} }, fullPanel); var bg = fullPanel.addComponent(cc.Graphics); bg.fillColor = new cc.Color(255, 255, 255, 255); bg.strokeColor = new cc.Color(60, 180, 100, 255); bg.lineWidth = 4; bg.roundRect(-W / 2 + 2, -H / 2 + 2, W - 4, H - 4, 20); bg.fill(); bg.stroke(); mkLabel(fullPanel, '📄 完整抓包内容', 24, [0, 0, 0, 255], 0, H / 2 - 34, W - 100, 0, 'center'); mkButton(fullPanel, '×', W / 2 - 32, H / 2 - 32, 44, 44, [220, 60, 60, 255], [255, 120, 120, 255], function () { if (fullPanel) fullPanel.active = false; }, [255, 255, 255, 255]); var fcStatus = mkLabel(fullPanel, '点下面按钮复制;或长按文本框选中后复制', 14, [100, 100, 100, 255], 0, H / 2 - 68, W - 40, 0, 'center'); var fcCount = mkLabel(fullPanel, '共 ' + _logs.length + ' 条记录', 16, [60, 130, 220, 255], 0, H / 2 - 96, W - 40, 0, 'center'); // 一行两个按钮 var btnY = H / 2 - 148; var halfW = (W - 80) / 2; mkButton(fullPanel, '📋 复制全部', -halfW / 2 - 8, btnY, halfW, 50, [60, 180, 100, 255], [255, 255, 255, 255], function () { if (_logs.length === 0) { fcStatus.string = '⚠️ 暂无抓包记录'; fcStatus.node.color = new cc.Color(220, 140, 60, 255); return; } var text = buildFullText(); var r = copyText(text); if (r.ok) { fcStatus.string = '✅ 已复制 ' + _logs.length + ' 条到剪贴板'; fcStatus.node.color = new cc.Color(30, 160, 90, 255); showStatusToast('已复制 ' + _logs.length + ' 条记录到剪贴板', true); } else { fcStatus.string = '❌ 自动复制失败,请长按文本框手动复制'; fcStatus.node.color = new cc.Color(220, 60, 60, 255); showStatusToast('自动复制失败,请长按文本框手动复制', false); } }, [255, 255, 255, 255]); mkButton(fullPanel, '🔄 刷新内容', halfW / 2 + 8, btnY, halfW, 50, [90, 150, 220, 255], [255, 255, 255, 255], function () { try { fullPanel.destroy(); } catch (e) {} fullPanel = null; showFullContentPanel(); showStatusToast('已刷新 ' + _logs.length + ' 条', true); }, [255, 255, 255, 255]); // 可编辑文本框:EditBox 长按可唤出系统"全选/复制" var cW = W - 44; var cH = H - 260; var boxNode = new cc.Node('ContentBox'); boxNode.parent = fullPanel; boxNode.setContentSize(cW, cH); boxNode.setPosition(0, -H / 2 + cH / 2 + 20); var cbg = boxNode.addComponent(cc.Graphics); cbg.fillColor = new cc.Color(248, 248, 252, 255); cbg.strokeColor = new cc.Color(200, 200, 200, 255); cbg.lineWidth = 1; cbg.roundRect(-cW / 2, -cH / 2, cW, cH, 8); cbg.fill(); cbg.stroke(); var text = _logs.length ? buildFullText() : '(暂无抓包记录,等待游戏发送请求...)'; var editBoxNode = new cc.Node('EditBox'); editBoxNode.parent = boxNode; editBoxNode.setContentSize(cW - 20, cH - 20); editBoxNode.setPosition(0, 0); editBoxNode.anchorX = 0.5; editBoxNode.anchorY = 0.5; var editBox = editBoxNode.addComponent(cc.EditBox); try { editBox.string = text; } catch (e) {} try { editBox.maxLength = 9999999; } catch (e) {} try { editBox.fontSize = 13; } catch (e) {} try { editBox.placeholder = '(无内容)'; } catch (e) {} try { editBox.placeholderFontSize = 13; } catch (e) {} try { editBox.fontColor = new cc.Color(0, 0, 0, 255); } catch (e) {} try { editBox.inputMode = cc.EditBox.InputMode.ANY; } catch (e) {} try { editBox.inputFlag = cc.EditBox.InputFlag.DEFAULT; } catch (e) {} try { editBox.returnType = cc.EditBox.KeyboardReturnType.DEFAULT; } catch (e) {} try { editBox.swallowTouches = false; } catch (e) {} // 补齐 textLabel / placeholderLabel try { if (!editBox.textLabel || !editBox.textLabel.node || !editBox.textLabel.node.isValid) { var tlNode = new cc.Node('TEXT_LABEL'); tlNode.parent = editBoxNode; tlNode.setContentSize(cW - 30, cH - 30); tlNode.setPosition(0, 0); tlNode.anchorX = 0.5; tlNode.anchorY = 0.5; var tl = tlNode.addComponent(cc.Label); tl.fontSize = 13; tl.lineHeight = 18; tl.horizontalAlign = cc.Label.HorizontalAlign.LEFT; tl.verticalAlign = cc.Label.VerticalAlign.TOP; try { tl.overflow = cc.Label.Overflow.CLAMP; } catch (e0) {} editBox.textLabel = tl; } else { try { editBox.textLabel.fontSize = 13; editBox.textLabel.lineHeight = 18; editBox.textLabel.horizontalAlign = cc.Label.HorizontalAlign.LEFT; editBox.textLabel.verticalAlign = cc.Label.VerticalAlign.TOP; editBox.textLabel.node.setContentSize(cW - 30, cH - 30); editBox.textLabel.node.setPosition(0, 0); } catch (e1) {} } if (!editBox.placeholderLabel || !editBox.placeholderLabel.node || !editBox.placeholderLabel.node.isValid) { var plNode = new cc.Node('PLACEHOLDER_LABEL'); plNode.parent = editBoxNode; plNode.setContentSize(cW - 30, cH - 30); plNode.setPosition(0, 0); plNode.anchorX = 0.5; plNode.anchorY = 0.5; var pl = plNode.addComponent(cc.Label); pl.fontSize = 13; pl.string = '(无内容)'; editBox.placeholderLabel = pl; } } catch (e) {} // 自动获取焦点(方便长按唤出系统菜单) try { setTimeout(function () { try { if (editBox && editBox.setFocus) editBox.setFocus(); } catch (e) {} }, 200); } catch (e) {} } // ==================== 主面板 ==================== function buildCapturePanel() { if (mainPanel && cc.isValid(mainPanel)) return mainPanel; var scene = currentScene(); if (!cc.isValid(scene)) return null; var vs = getVS(); var W = Math.min(640, Math.floor(vs.width * 0.92)); var H = Math.min(820, Math.floor(vs.height * 0.9)); mainPanel = new cc.Node('CapturePanel'); mainPanel.parent = scene; mainPanel.setContentSize(W, H); mainPanel.setAnchorPoint(0.5, 0.5); mainPanel.setPosition(vs.width / 2, vs.height / 2); mainPanel.active = false; mainPanel.zIndex = 100000; mainPanel.on(ev('TOUCH_START', 'touch-start'), function (e) { try { e.stopPropagation(); } catch (er) {} }, mainPanel); mainPanel.on(ev('TOUCH_MOVE', 'touch-move'), function (e) { try { e.stopPropagation(); } catch (er) {} }, mainPanel); mainPanel.on(ev('TOUCH_END', 'touch-end'), function (e) { try { e.stopPropagation(); } catch (er) {} }, mainPanel); var bg = mainPanel.addComponent(cc.Graphics); bg.fillColor = new cc.Color(255, 255, 255, 255); bg.strokeColor = new cc.Color(255, 105, 180, 255); bg.lineWidth = 4; bg.roundRect(-W / 2 + 2, -H / 2 + 2, W - 4, H - 4, 24); bg.fill(); bg.stroke(); mkLabel(mainPanel, '📡 自动抓包系统', 26, [0, 0, 0, 255], 0, H / 2 - 36, W - 100, 0, 'center'); mkButton(mainPanel, '×', W / 2 - 32, H / 2 - 30, 44, 44, [220, 60, 60, 255], [255, 120, 120, 255], function () { if (mainPanel) mainPanel.active = false; }, [255, 255, 255, 255]); _panelRefs.statusLabel = mkLabel(mainPanel, '状态: ' + (_captureEnabled ? '抓包中' : '已暂停') + '(已抓 ' + _logs.length + ' 条)', 17, [60, 130, 220, 255], 0, H / 2 - 80, W - 60, 0, 'center'); _panelRefs.statsLabel = mkLabel(mainPanel, '已抓记录: ' + _logs.length + ' 条', 16, [30, 160, 90, 255], 0, H / 2 - 108, W - 60, 0, 'center'); // 第一行:开/关 + 清空 var y1 = H / 2 - 156; var halfBtnW = (W - 80) / 2; mkButton(mainPanel, '开/关抓包', -halfBtnW / 2 - 8, y1, halfBtnW, 46, [90, 150, 220, 255], [255, 255, 255, 255], function () { _captureEnabled = !_captureEnabled; setStatus(_captureEnabled ? '✅ 抓包已开启' : '⏸ 抓包已暂停', _captureEnabled ? [30, 160, 90, 255] : [220, 140, 60, 255]); showStatusToast(_captureEnabled ? '抓包已开启' : '抓包已暂停', true); }, [255, 255, 255, 255]); mkButton(mainPanel, '清空记录', halfBtnW / 2 + 8, y1, halfBtnW, 46, [220, 130, 60, 255], [255, 255, 255, 255], function () { _logs = []; updatePanelStats(); setStatus('✅ 已清空记录', [30, 160, 90, 255]); showStatusToast('已清空所有记录', true); }, [255, 255, 255, 255]); // 第二行:复制全部 + 完整内容 var y2 = y1 - 56; mkButton(mainPanel, '📋 复制全部', -halfBtnW / 2 - 8, y2, halfBtnW, 52, [60, 180, 100, 255], [255, 255, 255, 255], function () { if (_logs.length === 0) { setStatus('⚠️ 暂无抓包记录', [220, 140, 60, 255]); showStatusToast('暂无抓包记录', false); return; } var text = buildFullText(); var r = copyText(text); if (r.ok) { setStatus('✅ 已复制 ' + _logs.length + ' 条(' + r.via + ')', [30, 160, 90, 255]); showStatusToast('已复制 ' + _logs.length + ' 条记录到剪贴板', true); } else { setStatus('❌ 自动复制失败,打开完整内容', [220, 60, 60, 255]); showStatusToast('自动复制失败,已打开完整内容', false); setTimeout(function () { showFullContentPanel(); }, 500); } }, [255, 255, 255, 255]); mkButton(mainPanel, '📄 完整内容', halfBtnW / 2 + 8, y2, halfBtnW, 52, [200, 120, 220, 255], [255, 255, 255, 255], function () { showFullContentPanel(); }, [255, 255, 255, 255]); // 预览区(★ 用 mkListLabel 修复多行显示) var listTop = H / 2 - 274; var listH = H - 330; // 剩余高度 mkLabel(mainPanel, '最近抓包记录:', 17, [0, 0, 0, 255], -W / 2 + 40, listTop, 400, 0, 'left'); var listBg = new cc.Node('ListBg'); listBg.parent = mainPanel; listBg.setContentSize(W - 60, listH); listBg.setPosition(0, listTop - 24 - listH / 2); var lbg = listBg.addComponent(cc.Graphics); lbg.fillColor = new cc.Color(248, 248, 252, 255); lbg.strokeColor = new cc.Color(200, 200, 200, 255); lbg.lineWidth = 1; lbg.roundRect(-(W - 60) / 2, -listH / 2, W - 60, listH, 10); lbg.fill(); lbg.stroke(); // ★ 关键修复:用 mkListLabel 指定高度,anchorY=1 顶部对齐 _panelRefs.listLabel = mkListLabel(listBg, buildPreviewText(), 14, [0, 0, 0, 255], W - 80, listH - 20); _panelRefs.listLabel.node.setPosition(-(W - 80) / 2 - 4, listH / 2 - 10); mkLabel(mainPanel, '「完整内容」可长按文本框手动复制', 13, [140, 140, 140, 255], 0, -H / 2 + 26, W - 40, 0, 'center'); return mainPanel; } // ==================== 悬浮球 ==================== function buildBall() { if (ball && cc.isValid(ball) && ball.parent) return ball; var scene = currentScene(); if (!cc.isValid(scene)) return null; var vs = getVS(); ball = new cc.Node('CaptureBall'); ball.parent = scene; ball.setContentSize(BALL_R * 2, BALL_R * 2); ball.setPosition(vs.width - BALL_R - 26, vs.height - BALL_R - 120); try { ball.zIndex = 2147483000; } catch (e0) {} var g = ball.addComponent(cc.Graphics); g.fillColor = new cc.Color(220, 60, 100, 255); g.circle(0, 0, BALL_R); g.fill(); g.strokeColor = new cc.Color(255, 255, 255, 255); try { g.lineWidth = 3; } catch (e1) {} g.circle(0, 0, BALL_R); g.stroke(); mkLabel(ball, '📡', 30, [255, 255, 255, 255], 0, 0, 60, 0, 'center'); var inGesture = false, startLoc = null, moved = false; ball.on(ev('TOUCH_START', 'touch-start'), function (e) { inGesture = true; moved = false; try { startLoc = e.getLocation(); } catch (e2) { startLoc = null; } }, ball); ball.on(ev('TOUCH_MOVE', 'touch-move'), function (e) { if (!inGesture) return; var loc = null; try { loc = e.getLocation(); } catch (e3) {} if (!loc) return; if (startLoc && (Math.abs(loc.x - startLoc.x) > 10 || Math.abs(loc.y - startLoc.y) > 10)) moved = true; if (!moved) return; var vs2 = getVS(); var nx = Math.max(BALL_R, Math.min(vs2.width - BALL_R, loc.x)); var ny = Math.max(BALL_R, Math.min(vs2.height - BALL_R, loc.y)); ball.setPosition(nx, ny); }, ball); ball.on(ev('TOUCH_END', 'touch-end'), function () { if (!inGesture) return; inGesture = false; if (!moved) { var p = buildCapturePanel(); if (p) { p.active = !p.active; if (p.active) updatePanelStats(); } } }, ball); ball.on(ev('TOUCH_CANCEL', 'touch-cancel'), function () { inGesture = false; }, ball); return ball; } // ==================== 启动弹窗 ==================== function showStartupToast() { var scene = currentScene(); if (!cc.isValid(scene)) return; if (startToast && cc.isValid(startToast)) { try { startToast.destroy(); } catch (e) {} } var vs = getVS(); var W = Math.min(500, Math.floor(vs.width * 0.85)); var H = 200; startToast = new cc.Node('CaptureStartToast'); startToast.parent = scene; startToast.setContentSize(W, H); startToast.setAnchorPoint(0.5, 0.5); startToast.setPosition(vs.width / 2, vs.height / 2); startToast.zIndex = 2147483600; var bg = startToast.addComponent(cc.Graphics); bg.fillColor = new cc.Color(255, 255, 255, 255); bg.strokeColor = new cc.Color(255, 105, 180, 255); bg.lineWidth = 4; bg.roundRect(-W / 2, -H / 2, W, H, 20); bg.fill(); bg.stroke(); mkLabel(startToast, '📡 正在开始抓包', 26, [0, 0, 0, 255], 0, H / 2 - 42, W - 80, 0, 'center'); mkLabel(startToast, '已自动连接协议,持续抓包中...', 17, [80, 80, 80, 255], 0, H / 2 - 80, W - 60, 0, 'center'); mkLabel(startToast, '点右上角 📡 悬浮球查看 / 复制', 15, [130, 130, 130, 255], 0, H / 2 - 110, W - 60, 0, 'center'); mkButton(startToast, '×', W / 2 - 28, H / 2 - 28, 44, 44, [220, 60, 60, 255], [255, 120, 120, 255], function () { if (startToast && cc.isValid(startToast)) { try { startToast.destroy(); } catch (e) {} startToast = null; } }, [255, 255, 255, 255]); startToast.on(ev('TOUCH_START', 'touch-start'), function (e) { try { e.stopPropagation(); } catch (er) {} }, startToast); startToast.on(ev('TOUCH_MOVE', 'touch-move'), function (e) { try { e.stopPropagation(); } catch (er) {} }, startToast); startToast.on(ev('TOUCH_END', 'touch-end'), function (e) { try { e.stopPropagation(); } catch (er) {} }, startToast); setTimeout(function () { if (startToast && cc.isValid(startToast)) { try { startToast.destroy(); } catch (e) {} startToast = null; } }, 3500); } // ==================== 保活与启动 ==================== function ensureAlive() { try { var scene = currentScene(); if (!scene) return; if (!ball || !cc.isValid(ball) || !ball.parent) buildBall(); if (mainPanel && cc.isValid(mainPanel) && !mainPanel.parent) mainPanel.parent = scene; if (fullPanel && cc.isValid(fullPanel) && !fullPanel.parent) fullPanel.parent = scene; } catch (e) {} } function boot() { if (typeof cc === 'undefined' || !cc) { setTimeout(boot, 300); return; } var scene = currentScene(); if (!scene) { setTimeout(boot, 300); return; } hookProto(); tickScan(); buildBall(); showStartupToast(); setInterval(ensureAlive, 2000); } window.__CAPTURE__ = { enable: function () { _captureEnabled = true; }, disable: function () { _captureEnabled = false; }, copyAll: function () { if (!_logs.length) { showStatusToast('暂无记录', false); return; } var r = copyText(buildFullText()); if (r.ok) showStatusToast('已复制 ' + _logs.length + ' 条', true); else { showStatusToast('复制失败,请打开完整内容', false); showFullContentPanel(); } }, openFull: function () { showFullContentPanel(); }, getText: function () { return buildFullText(); }, clear: function () { _logs = []; updatePanelStats(); }, getLogs: function () { return _logs.slice(); } }; boot(); try { console.log('[自动抓包系统 v6.0] 已注入'); } catch (e) {} })();