"""探测验证码 iframe 内 SDK 结构:script 源、addEventListener 采集点、轨迹缓冲。""" import asyncio, json, urllib.request, sys import websockets CDP_HTTP = "http://127.0.0.1:9222" def _json(path): try: with urllib.request.urlopen(f"{CDP_HTTP}{path}", timeout=5) as r: return json.load(r) except (OSError, ValueError): return None async def main(): ver = _json("/json/version") targets = _json("/json/list") or [] if not ver or "webSocketDebuggerUrl" not in ver: print("CDP 9222 不可用", file=sys.stderr); return cand = [t for t in targets if "rmc" in t.get("url","") or "captcha" in t.get("url","").lower()] if not cand: print("未找到 captcha iframe,targets:") for t in targets: print(" ", t["type"], t["url"][:80]) return tgt = cand[0] print("探测目标:", tgt["url"][:100]) async with websockets.connect(ver["webSocketDebuggerUrl"], max_size=64*1024*1024) as ws: mid = [0] async def cdp(method, params=None, sid=None): mid[0] += 1 m = {"id": mid[0], "method": method, "params": params or {}} if sid: m["sessionId"] = sid await ws.send(json.dumps(m)) while True: try: r = json.loads(await ws.recv()) except json.JSONDecodeError: continue if r.get("id") == mid[0]: if "error" in r: raise RuntimeError(f"{method}: {r['error']}") return r.get("result", {}) sid = (await cdp("Target.attachToTarget", {"targetId": tgt["id"], "flatten": True}))["sessionId"] await cdp("Runtime.enable", sid=sid) async def js(expr): r = await cdp("Runtime.evaluate", {"expression": expr, "returnByValue": True, "awaitPromise": True, "userGesture": True}, sid) return r.get("result", {}).get("value") # 1) 挂 listener 记录 hook(记录到 window 全局,跨 evaluate 可读) await js(""" (() => { if (window.__lh) return 'already'; const orig = EventTarget.prototype.addEventListener; window.__lh = []; EventTarget.prototype.addEventListener = function(t, f, o){ try { window.__lh.push([t, String(f && f.name || 'anon'), String(f).slice(0,120).replace(/\\n/g,' ')]); } catch(e){} return orig.call(this, t, f, o); }; return 'hook installed'; })() """) # 2) 已有 listener(getEventListeners 仅 DevTools 可用,改用枚举 on* + 重放脚本已知点) scripts = await js("[...document.scripts].map(s=>s.src||('inline:'+s.textContent.length)).join('\\n')") print("== scripts ==\\n" + str(scripts)) globals_ = await js(""" Object.getOwnPropertyNames(window).filter(k=>/captcha|verify|secsdk|byted|rms|rmc|track|slid|nocaptcha/i.test(k)).join('\\n') """) print("== 全局候选 ==\\n" + str(globals_)) await asyncio.sleep(15) log = await js("JSON.stringify(window.__lh||[])") try: rows = json.loads(log or "[]") except (TypeError, ValueError) as e: print(f"__lh 解析失败: {e}", file=sys.stderr) return print(f"== 15s 内 addEventListener 调用: {len(rows)} ==") for t, name, src in rows: print(f" [{t}] {name} :: {src[:110]}") asyncio.run(main())