hook_jsvmp_interpreter
Probe JSVMP interpreters by wrapping Reflect and installing proxies on browser globals to intercept timing APIs and function calls for dynamic analysis.
Instructions
Install a JSVMP runtime probe.
Multi-path instrumentation for JSVMP interpreters. Wraps Reflect.get/apply, installs Proxies on globals (navigator, screen, etc.), intercepts timing APIs.
LIMITATIONS: "proxy" mode is DETECTABLE by RS/AK-style signature-based anti-bot. For those, use instrumentation(action='install') (source-level rewrite) or mode='transparent' instead.
IMPORTANT — timing for sync-loaded SDKs (e.g. webmssdk): JSVMP interpreters capture native references at startup via closures. If you install hooks AFTER the SDK has loaded, the SDK's closures already hold the original (un-hooked) references — your hooks will never fire. You MUST install hooks BEFORE navigate(): 1. launch_browser() 2. hook_jsvmp_interpreter(mode='transparent', persistent=True) 3. navigate("https://www.douyin.com/...") If already navigated, call instrumentation(action='reload') after installing hooks to force a page reload with hooks active.
Args: script_url: Target script URL substring for stack filtering. persistent: Survive navigation (default True). mode: "proxy" (full coverage, detectable) or "transparent" (safe, lower coverage). track_calls, track_props, track_reflect: Only for mode="proxy". proxy_objects: Objects to proxy (default: navigator, screen, etc.). max_entries: Log buffer cap (default 10000).
Returns: dict with status, mode, coverage summary.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| script_url | No | ||
| persistent | No | ||
| mode | No | proxy | |
| track_calls | No | ||
| track_props | No | ||
| track_reflect | No | ||
| proxy_objects | No | ||
| max_entries | No |
Implementation Reference
- Main handler for the hook_jsvmp_interpreter MCP tool. Decorated with @mcp.tool(). Accepts parameters (script_url, persistent, mode, track_calls, track_props, track_reflect, proxy_objects, max_entries) and installs JSVMP runtime probes in two modes: 'proxy' (full coverage using Proxy objects on globals) or 'transparent' (signature-safe getter replacement on prototypes). Reads template hook JS files from the hooks/ directory, performs string substitution, and injects them into the page via page.evaluate(), with optional persistent script registration.
@mcp.tool() async def hook_jsvmp_interpreter( script_url: str = "", persistent: bool = True, mode: str = "proxy", track_calls: bool = True, track_props: bool = True, track_reflect: bool = True, proxy_objects: list[str] | None = None, max_entries: int = 10000, ) -> dict: """Install a JSVMP runtime probe. Multi-path instrumentation for JSVMP interpreters. Wraps Reflect.get/apply, installs Proxies on globals (navigator, screen, etc.), intercepts timing APIs. LIMITATIONS: "proxy" mode is DETECTABLE by RS/AK-style signature-based anti-bot. For those, use instrumentation(action='install') (source-level rewrite) or mode='transparent' instead. IMPORTANT — timing for sync-loaded SDKs (e.g. webmssdk): JSVMP interpreters capture native references at startup via closures. If you install hooks AFTER the SDK has loaded, the SDK's closures already hold the original (un-hooked) references — your hooks will never fire. You MUST install hooks BEFORE navigate(): 1. launch_browser() 2. hook_jsvmp_interpreter(mode='transparent', persistent=True) 3. navigate("https://www.douyin.com/...") If already navigated, call instrumentation(action='reload') after installing hooks to force a page reload with hooks active. Args: script_url: Target script URL substring for stack filtering. persistent: Survive navigation (default True). mode: "proxy" (full coverage, detectable) or "transparent" (safe, lower coverage). track_calls, track_props, track_reflect: Only for mode="proxy". proxy_objects: Objects to proxy (default: navigator, screen, etc.). max_entries: Log buffer cap (default 10000). Returns: dict with status, mode, coverage summary. """ try: hooks_dir = os.path.join(os.path.dirname(os.path.dirname(__file__)), "hooks") page = await browser_manager.get_active_page() if mode == "transparent": hook_path = os.path.join(hooks_dir, "jsvmp_transparent_hook.js") if not os.path.exists(hook_path): return {"error": "jsvmp_transparent_hook.js not found"} with open(hook_path, "r", encoding="utf-8") as f: template = f.read() hook_js = (template .replace("{{SCRIPT_URL}}", script_url.replace('"', '\\"').replace("'", "\\'")) .replace("{{MAX_ENTRIES}}", str(max_entries))) if persistent: await browser_manager.add_persistent_script( f"jsvmp_transparent:{script_url or 'all'}", hook_js) # v1.0.1: detect if page already loaded (timing issue) page_already_loaded = page.url and page.url != "about:blank" try: await page.evaluate(hook_js) except Exception as e: return {"status": "partial", "mode": "transparent", "warning": f"Evaluate failed: {e}", "persistent": persistent} result = { "status": "instrumented", "mode": "transparent", "script_url": script_url or "(all)", "persistent": persistent, "data_location": "window.__mcp_jsvmp_log", } if page_already_loaded: result["warnings"] = [ "Hooks installed on already-loaded page. If the target " "SDK (e.g. webmssdk) was loaded BEFORE this call, it " "likely captured native references at startup (closure " "capture) and won't trigger your hooks. Call " "instrumentation(action='reload') or re-navigate to " "force SDK re-init with hooks in place." ] return result elif mode == "proxy": if proxy_objects is None: proxy_objects = ["navigator", "screen", "history", "localStorage", "sessionStorage", "performance"] with open(os.path.join(hooks_dir, "jsvmp_hook.js"), "r", encoding="utf-8") as f: template = f.read() hook_js = (template .replace("{{SCRIPT_URL}}", script_url.replace('"', '\\"').replace("'", "\\'")) .replace("{{MAX_ENTRIES}}", str(max_entries)) .replace("{{TRACK_CALLS}}", "true" if track_calls else "false") .replace("{{TRACK_PROPS}}", "true" if track_props else "false") .replace("{{TRACK_REFLECT}}", "true" if track_reflect else "false") .replace("'{{PROXY_OBJECTS}}'", json.dumps(json.dumps(proxy_objects)))) if persistent: await browser_manager.add_persistent_script( f"jsvmp_probe:{script_url or 'all'}", hook_js) # v1.0.1: detect if page already loaded (timing issue) page_already_loaded = page.url and page.url != "about:blank" try: await page.evaluate(hook_js) except Exception as e: return {"status": "partial", "mode": "proxy", "warning": f"Evaluate failed: {e}", "persistent": persistent} result = { "status": "instrumented", "mode": "proxy", "script_url": script_url or "(all)", "persistent": persistent, "data_location": "window.__mcp_jsvmp_log", "warning": "proxy mode is detectable by RS/AK-style anti-bot.", } if page_already_loaded: result.setdefault("warnings", []) if isinstance(result.get("warning"), str): result["warnings"].append(result.pop("warning")) result["warnings"].append( "Hooks installed on already-loaded page. If the target " "SDK was loaded BEFORE this call, it likely captured " "native references at startup (closure capture) and " "won't trigger your hooks. Call " "instrumentation(action='reload') to re-trigger." ) return result else: return {"error": f"unknown mode '{mode}', use 'proxy' or 'transparent'"} except Exception as e: return {"error": str(e)} - Function signature and docstring for hook_jsvmp_interpreter defining all parameters and their types: script_url (str), persistent (bool), mode (str: 'proxy' or 'transparent'), track_calls/track_props/track_reflect (bool), proxy_objects (list[str]|None), max_entries (int). The return type is dict.
@mcp.tool() async def hook_jsvmp_interpreter( script_url: str = "", persistent: bool = True, mode: str = "proxy", track_calls: bool = True, track_props: bool = True, track_reflect: bool = True, proxy_objects: list[str] | None = None, max_entries: int = 10000, ) -> dict: - src/camoufox_reverse_mcp/server.py:20-22 (registration)Registration of the jsvmp tools module. Line 22 imports from .tools import jsvmp which triggers the @mcp.tool() decorator on hook_jsvmp_interpreter and compare_env, registering them with the FastMCP server.
from .tools import storage # noqa: E402, F401 — cookies() + get_storage + export/import state from .tools import jsvmp # noqa: E402, F401 — hook_jsvmp_interpreter + compare_env from .tools import instrumentation # noqa: E402, F401 — instrumentation(action=...) - The 'proxy' mode hook JavaScript file. A self-executing function that wraps Function.prototype.apply/call/bind, Reflect.* methods, and installs Proxy objects on specified globals (navigator, screen, etc.) to intercept all property access, function calls, and Reflect operations. Logs to window.__mcp_jsvmp_log. Includes an uninstall function (window.__mcp_jsvmp_uninstall) to restore originals.
/** * jsvmp_hook.js - 通用 JSVMP 运行时探针 * * 设计目标: * 不依赖 VMP 具体实现,覆盖 JS 访问宿主环境的所有入口通道, * 让 RS、AK、TK JSVMP、常见混淆工具等 * VMP 执行时的每一次环境读取/API 调用都被记录。 * * 模板变量: * {{SCRIPT_URL}} - 目标脚本 URL 子串,用于栈过滤。空串 = 不过滤 * {{MAX_ENTRIES}} - 日志条数上限 * {{TRACK_CALLS}} - 是否追踪函数调用 (true/false) * {{TRACK_PROPS}} - 是否追踪属性读取 (true/false) * {{TRACK_REFLECT}} - 是否追踪 Reflect.* (true/false) * {{PROXY_OBJECTS}} - 要装 Proxy 的全局对象名列表 JSON 字符串 * * 输出: * window.__mcp_jsvmp_log - 结构化日志数组 */ (function() { if (window.__mcp_jsvmp_installed) { console.log('[JSVMP] Already installed, skipping'); return; } window.__mcp_jsvmp_installed = true; window.__mcp_jsvmp_log = window.__mcp_jsvmp_log || []; var CFG = { scriptUrl: '{{SCRIPT_URL}}', maxEntries: {{MAX_ENTRIES}}, trackCalls: {{TRACK_CALLS}}, trackProps: {{TRACK_PROPS}}, trackReflect: {{TRACK_REFLECT}}, proxyObjects: JSON.parse('{{PROXY_OBJECTS}}') }; // 保存一组"原始引用" - 所有 hook 内部使用这组,避免被页面覆盖后互相污染 var _Error = Error; var _Array = Array; var _JSON_stringify = JSON.stringify; var _Object_defineProperty = Object.defineProperty; var _Object_getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor; var _Object_getPrototypeOf = Object.getPrototypeOf; var _FP_apply = Function.prototype.apply; var _FP_call = Function.prototype.call; var _FP_bind = Function.prototype.bind; var _FP_toString = Function.prototype.toString; var _Reflect_apply = Reflect.apply; var _Reflect_get = Reflect.get; var _Reflect_set = Reflect.set; var _Reflect_construct = Reflect.construct; function preview(v, maxLen) { maxLen = maxLen || 200; try { if (v === null) return 'null'; if (v === undefined) return 'undefined'; var t = typeof v; if (t === 'function') { var src = ''; try { src = _FP_call.call(_FP_toString, v); } catch (e) {} return '[Function ' + (v.name || 'anonymous') + (src.length < 80 ? ': ' + src : '') + ']'; } if (t === 'object') { var s = _JSON_stringify(v); return s && s.length > maxLen ? s.substring(0, maxLen) + '...' : s; } var s2 = String(v); return s2.length > maxLen ? s2.substring(0, maxLen) + '...' : s2; } catch (e) { try { return String(v).substring(0, maxLen); } catch (e2) { return '[unprintable]'; } } } function shortStack() { try { var s = new _Error().stack || ''; // 只保留前 6 层 return s.split('\n').slice(2, 8).join('\n'); } catch (e) { return ''; } } function inTargetScript(stack) { if (!CFG.scriptUrl) return true; return stack && stack.indexOf(CFG.scriptUrl) !== -1; } function log(entry) { if (window.__mcp_jsvmp_log.length >= CFG.maxEntries) return; entry.ts = Date.now(); window.__mcp_jsvmp_log.push(entry); } // ========== 1. Function.prototype.apply / call / bind ========== if (CFG.trackCalls) { Function.prototype.apply = function (thisArg, argsArray) { try { var stack = shortStack(); if (inTargetScript(stack)) { log({ type: 'fn_apply', name: this.name || 'anonymous', args: argsArray ? preview(_Array.from(argsArray), 300) : '[]', thisType: thisArg ? (thisArg.constructor && thisArg.constructor.name) || typeof thisArg : 'null', stack: stack }); } } catch (e) {} return _FP_apply.call(this, thisArg, argsArray); }; Function.prototype.apply.toString = function () { return 'function apply() { [native code] }'; }; Function.prototype.call = function (thisArg) { try { var stack = shortStack(); if (inTargetScript(stack)) { var argsArr = _Array.prototype.slice.call(arguments, 1); log({ type: 'fn_call', name: this.name || 'anonymous', args: preview(argsArr, 300), thisType: thisArg ? (thisArg.constructor && thisArg.constructor.name) || typeof thisArg : 'null', stack: stack }); } } catch (e) {} return _FP_apply.call(this, thisArg, _Array.prototype.slice.call(arguments, 1)); }; Function.prototype.call.toString = function () { return 'function call() { [native code] }'; }; Function.prototype.bind = function (thisArg) { try { var stack = shortStack(); if (inTargetScript(stack)) { log({ type: 'fn_bind', name: this.name || 'anonymous', boundThisType: thisArg ? (thisArg.constructor && thisArg.constructor.name) || typeof thisArg : 'null', stack: stack }); } } catch (e) {} return _FP_apply.call(_FP_bind, this, arguments); }; } // ========== 2. Reflect.apply / get / set / construct ========== if (CFG.trackReflect) { Reflect.apply = function (target, thisArg, args) { try { var stack = shortStack(); if (inTargetScript(stack)) { log({ type: 'reflect_apply', name: (target && target.name) || 'anonymous', args: preview(args, 300), stack: stack }); } } catch (e) {} return _Reflect_apply(target, thisArg, args); }; Reflect.get = function (target, key, receiver) { var val = arguments.length >= 3 ? _Reflect_get(target, key, receiver) : _Reflect_get(target, key); try { var stack = shortStack(); if (inTargetScript(stack)) { log({ type: 'reflect_get', targetType: (target && target.constructor && target.constructor.name) || typeof target, key: String(key), value: preview(val, 150), stack: stack }); } } catch (e) {} return val; }; Reflect.set = function (target, key, value, receiver) { try { var stack = shortStack(); if (inTargetScript(stack)) { log({ type: 'reflect_set', targetType: (target && target.constructor && target.constructor.name) || typeof target, key: String(key), value: preview(value, 150), stack: stack }); } } catch (e) {} return arguments.length >= 4 ? _Reflect_set(target, key, value, receiver) : _Reflect_set(target, key, value); }; Reflect.construct = function (target, args, newTarget) { try { var stack = shortStack(); if (inTargetScript(stack)) { log({ type: 'reflect_construct', name: (target && target.name) || 'anonymous', args: preview(args, 300), stack: stack }); } } catch (e) {} return arguments.length >= 3 ? _Reflect_construct(target, args, newTarget) : _Reflect_construct(target, args); }; } // ========== 3. Proxy 式属性读取追踪 ========== if (CFG.trackProps) { var wrapObjectWithProxy = function (parent, propName) { var orig; try { orig = parent[propName]; } catch (e) { return false; } if (!orig || (typeof orig !== 'object' && typeof orig !== 'function')) return false; // v0.6.0: backup original object for uninstall window.__mcp_proxy_originals = window.__mcp_proxy_originals || {}; if (!(propName in window.__mcp_proxy_originals)) { window.__mcp_proxy_originals[propName] = orig; } // v0.6.0: per-proxy reentrance guard var _inGetTrap = false; var _inSetTrap = false; var proxy = new Proxy(orig, { get: function (target, key, receiver) { if (_inGetTrap) { try { return target[key]; } catch (e) { return undefined; } } _inGetTrap = true; var val; try { try { val = _Reflect_get(target, key, receiver); } catch (e) { try { val = target[key]; } catch (e2) { val = undefined; } } try { if (typeof key === 'string' && key.indexOf('__mcp_') !== 0) { var stack = shortStack(); if (inTargetScript(stack)) { log({ type: 'proxy_get', obj: propName, key: key, value: preview(val, 150), stack: stack }); } } } catch (e) {} } finally { _inGetTrap = false; } if (typeof val === 'function') { try { return val.bind(target); } catch (e) { return val; } } return val; }, set: function (target, key, value, receiver) { if (_inSetTrap) { try { return Reflect.set(target, key, value, receiver); } catch (e) { try { target[key] = value; } catch (e2) {} return true; } } _inSetTrap = true; try { try { if (typeof key === 'string' && key.indexOf('__mcp_') !== 0) { var stack = shortStack(); if (inTargetScript(stack)) { log({ type: 'proxy_set', obj: propName, key: key, value: preview(value, 150), stack: stack }); } } } catch (e) {} return _Reflect_set(target, key, value, receiver); } finally { _inSetTrap = false; } }, has: function (target, key) { try { var stack = shortStack(); if (inTargetScript(stack)) { log({ type: 'proxy_has', obj: propName, key: String(key), stack: stack }); } } catch (e) {} return key in target; } }); try { _Object_defineProperty(parent, propName, { value: proxy, writable: true, configurable: true, enumerable: true }); return true; } catch (e) { try { parent[propName] = proxy; return true; } catch (e2) { return false; } } }; for (var i = 0; i < CFG.proxyObjects.length; i++) { try { wrapObjectWithProxy(window, CFG.proxyObjects[i]); } catch (e) { console.warn('[JSVMP] Failed to proxy', CFG.proxyObjects[i], e.message); } } } // ========== 4. 常见环境探测 API ========== if (CFG.trackCalls) { var sensitiveApis = [ { obj: Date, name: 'now', kind: 'static' }, { obj: performance, name: 'now', kind: 'instance' }, { obj: Math, name: 'random', kind: 'static' } ]; for (var j = 0; j < sensitiveApis.length; j++) { try { var api = sensitiveApis[j]; var orig = api.obj[api.name]; if (typeof orig !== 'function') continue; (function(apiRef, origFn) { apiRef.obj[apiRef.name] = function () { var r = _FP_apply.call(origFn, this, arguments); try { var stack = shortStack(); if (inTargetScript(stack)) { log({ type: 'api_call', name: (apiRef.obj.constructor ? apiRef.obj.constructor.name : 'Object') + '.' + apiRef.name, args: preview(_Array.from(arguments), 100), returnValue: preview(r, 100), stack: stack }); } } catch (e) {} return r; }; try { apiRef.obj[apiRef.name].toString = function () { return 'function ' + apiRef.name + '() { [native code] }'; }; } catch (e) {} })(api, orig); } catch (e) {} } } // v0.6.0: uninstall function for remove_hooks to restore original objects window.__mcp_jsvmp_uninstall = function() { var restored = []; var originals = window.__mcp_proxy_originals || {}; for (var name in originals) { try { _Object_defineProperty(window, name, { value: originals[name], writable: true, configurable: true, enumerable: true }); restored.push(name); } catch (e) { try { window[name] = originals[name]; restored.push(name + '(fallback)'); } catch (e2) {} } } window.__mcp_jsvmp_installed = false; window.__mcp_proxy_originals = {}; return { restored: restored }; }; console.log('[JSVMP] Probe installed. scriptUrl=' + (CFG.scriptUrl || '(all)') + ' calls=' + CFG.trackCalls + ' props=' + CFG.trackProps + ' reflect=' + CFG.trackReflect + ' proxyObjects=' + CFG.proxyObjects.length); })(); - The 'transparent' mode hook JavaScript file. Avoids Proxy and Function.prototype manipulation entirely. Instead, replaces getter functions on prototype objects (Navigator.prototype, Screen.prototype, etc.) with wrappers that preserve the original toString() output. This makes it undetectable by signature-based anti-bot checks. Logs to window.__mcp_jsvmp_log. Includes an uninstall function (window.__mcp_transparent_uninstall).
/** * jsvmp_transparent_hook.js - Signature-safe runtime observation. * * Only replaces getter functions on prototype objects (Navigator.prototype, * Screen.prototype, Document.prototype, etc.). Proxy is never used. * Function.prototype is never touched. * * Detection resistance: * - typeof navigator, navigator.constructor, Navigator itself, and the * prototype chain are all identical to pristine. * - Function.prototype.toString.call(the-new-getter) returns the SAME * string the original getter's toString() would return. * - No Proxy objects exist, so proxy-detection heuristics all fail. * - Only residual artifact: the getter function object identity differs. * * Template variables: * {{SCRIPT_URL}} - target script URL substring for stack filtering * {{MAX_ENTRIES}} - log buffer cap * * Output: * window.__mcp_jsvmp_log - structured log array (shared with proxy hook) */ (function () { if (window.__mcp_jsvmp_transparent_installed) { try { console.log('[JSVMP-T] Already installed, skipping'); } catch (e) {} return; } window.__mcp_jsvmp_transparent_installed = true; window.__mcp_jsvmp_log = window.__mcp_jsvmp_log || []; var CFG = { scriptUrl: '{{SCRIPT_URL}}', maxEntries: {{MAX_ENTRIES}} }; var _Error = Error; var _JSON_stringify = JSON.stringify; var _FP_toString = Function.prototype.toString; var _Object_defineProperty = Object.defineProperty; var _Object_getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor; var _Object_getOwnPropertyNames = Object.getOwnPropertyNames; var _Object_getPrototypeOf = Object.getPrototypeOf; function preview(v, maxLen) { maxLen = maxLen || 150; try { if (v === null) return 'null'; if (v === undefined) return 'undefined'; var t = typeof v; if (t === 'function') return '[Function ' + (v.name || '') + ']'; if (t === 'object') { var s = _JSON_stringify(v); return s && s.length > maxLen ? s.substring(0, maxLen) + '...' : s; } var ss = String(v); return ss.length > maxLen ? ss.substring(0, maxLen) + '...' : ss; } catch (e) { try { return String(v).substring(0, maxLen); } catch (e2) { return '[unprintable]'; } } } function shortStack() { try { var s = new _Error().stack || ''; return s.split('\n').slice(2, 8).join('\n'); } catch (e) { return ''; } } function inTargetScript(stack) { if (!CFG.scriptUrl) return true; return stack && stack.indexOf(CFG.scriptUrl) !== -1; } function log(entry) { if (window.__mcp_jsvmp_log.length >= CFG.maxEntries) return; entry.ts = Date.now(); window.__mcp_jsvmp_log.push(entry); } function tapGetter(ownerName, owner, prop) { try { var desc = _Object_getOwnPropertyDescriptor(owner, prop); if (!desc) return false; if (desc.configurable === false) return false; if (typeof desc.get !== 'function') return false; var origGet = desc.get; var origToStringSrc; try { origToStringSrc = _FP_toString.call(origGet); } catch (e) { origToStringSrc = 'function ' + prop + '() { [native code] }'; } var fakeGet = function () { var v = origGet.call(this); try { var stack = shortStack(); if (inTargetScript(stack)) { log({ type: 'transparent_get', owner: ownerName, key: prop, value: preview(v), stack: stack }); } } catch (e) {} return v; }; try { _Object_defineProperty(fakeGet, 'toString', { value: function () { return origToStringSrc; }, writable: true, configurable: true, enumerable: false }); } catch (e) {} try { _Object_defineProperty(fakeGet, 'name', { value: origGet.name || prop, writable: false, configurable: true }); } catch (e) {} // v0.6.0: save original descriptor for uninstall window.__mcp_transparent_originals = window.__mcp_transparent_originals || []; window.__mcp_transparent_originals.push({ owner: owner, prop: prop, desc: desc }); _Object_defineProperty(owner, prop, { get: fakeGet, set: desc.set, configurable: true, enumerable: desc.enumerable }); return true; } catch (e) { return false; } } var targets = []; function addTarget(name, obj) { if (obj && typeof obj === 'object') targets.push([name, obj]); } try { addTarget('Navigator', _Object_getPrototypeOf(navigator)); } catch (e) {} try { addTarget('Screen', _Object_getPrototypeOf(screen)); } catch (e) {} try { addTarget('History', _Object_getPrototypeOf(history)); } catch (e) {} try { addTarget('Performance', _Object_getPrototypeOf(performance)); } catch (e) {} try { addTarget('Location', _Object_getPrototypeOf(location)); } catch (e) {} try { var docProto = _Object_getPrototypeOf(document); addTarget('HTMLDocument', docProto); var docProto2 = _Object_getPrototypeOf(docProto); if (docProto2) addTarget('Document', docProto2); } catch (e) {} var tapStats = { total: 0, tapped: 0 }; for (var i = 0; i < targets.length; i++) { var ownerName = targets[i][0]; var owner = targets[i][1]; var props; try { props = _Object_getOwnPropertyNames(owner); } catch (e) { continue; } for (var j = 0; j < props.length; j++) { var p = props[j]; if (p === 'constructor' || p === '__proto__' || p === 'toString' || p === 'valueOf') continue; tapStats.total++; if (tapGetter(ownerName, owner, p)) tapStats.tapped++; } } // v0.6.0: uninstall function window.__mcp_transparent_uninstall = function() { var restored = []; var origs = window.__mcp_transparent_originals || []; for (var i = 0; i < origs.length; i++) { try { _Object_defineProperty(origs[i].owner, origs[i].prop, origs[i].desc); restored.push(origs[i].prop); } catch (e) {} } window.__mcp_jsvmp_transparent_installed = false; window.__mcp_transparent_originals = []; return { restored: restored }; }; try { console.log('[JSVMP-T] Transparent probe installed. Tapped ' + tapStats.tapped + '/' + tapStats.total + ' getters. ' + 'scriptUrl=' + (CFG.scriptUrl || '(all)')); } catch (e) {} })();