# -*- coding: utf-8 -*- """验证:纯 HTTP 静态扫描登录页 chunk,提取模块236(n.d(t,"b")双公钥模块)的登录公钥, 并与 runtime hook 导出的真钥比对。""" import re import requests import urllib3 urllib3.disable_warnings() TRUE_KEY = ("MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDAjCQvqNPnOZm6GVDqeOb5S2pJtbN3FMZ8" "vddW3B2f3jB0mf7vd1B+yjjfex2A5XVxr4YwAY/w4d1/m3v3Bn/qgAnLMKnpjeEwxQU3cU81eXAam" "4fMB+nlmC291quIuC0qFFL8YgT2bDjARsMRYbmKd6LTc58sKYeJD2MvkkOdKwIDAQAB") s = requests.Session() s.verify = False s.headers["User-Agent"] = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/124.0.0.0 Safari/537.36" # 1. 登录页 HTML → chunk 列表 html = s.get("https://newpc.996db.com/login/account", timeout=20).text names = set(re.findall(r"/static/js/([\w.]+\.chunk\.js)", html)) print("html chunks:", sorted(names)) # 2. main chunk 的 runtime chunk-id→hash 映射,扩充候选 main_name = next((n for n in names if n.startswith("main")), None) if main_name: main_js = s.get(f"https://newpc.996db.com/static/js/{main_name}", timeout=30).text # runtime: {0:"hash",1:"hash"...} 或 {id:"hash"} 映射 + 命名模板 tpl = re.search(r'"static/js/"+[esr.]+\+"([^"]*)"', main_js) print("name template:", tpl.group(1) if tpl else None) for m in re.finditer(r'\{((?:\d+:"[a-f0-9]{8}",?)+)\}', main_js): ids = re.findall(r'(\d+):"([a-f0-9]{8})"', m.group(1)) for cid, h in ids: names.add(f"{cid}v2.6.01789526607.chunk.js") # 版本号从已知文件名推断 break print("candidates:", len(names)) # 3. 逐 chunk 扫描模块236签名: n.d(t,"b" ... setPublicKey("...") PAT = re.compile(r'n\.d\(t,"b"[\s\S]{0,800}?setPublicKey\("((?:[^"\\]|\\.)*)"') found = {} for name in sorted(names): try: js = s.get(f"https://newpc.996db.com/static/js/{name}", timeout=30).text except Exception as e: print(name, "ERR", str(e)[:60]); continue for m in PAT.finditer(js): raw = m.group(1) key = raw.replace("\\n", "").replace(" ", "").strip() found.setdefault(key, []).append(name) for key, chunks in found.items(): tag = " <<< TRUE KEY" if key == TRUE_KEY else "" print(f"key ...{key[95:125]} len={len(key)} chunks={chunks}{tag}") print("\nMATCH:", TRUE_KEY in found)