初步增加, 扫码登录成功
This commit is contained in:
@@ -0,0 +1,147 @@
|
||||
"""mall 侧(PlaceOrder,pagedoo CHAOS VM 108 opcode)高层 API。
|
||||
|
||||
与 goods 侧(pyvm/goods.py)同属 YYB 加密体系:
|
||||
- goods: web_save / web_new_encrypt(midas.gtimg.cn/goodsBiz,116 opcode)
|
||||
- mall: PlaceOrder(pagedoo.pay.qq.com,108 opcode,详情页 e377650 变换核心)
|
||||
|
||||
mall 加密链路(E3 已验证,U-2022 闭环):
|
||||
会话态(xMidasOps 59620 + key16/d/Te + 624B 中间态)
|
||||
→ e377650 变换核心(正确回调链 e344354=[624B中间态, d, e336201, key16])
|
||||
→ 624B 密文 = encrypt_msg(1248 hex)
|
||||
|
||||
注意:xMidasOps 是 mall 详情页页面级数据表(59620 长度,服务端生成),
|
||||
必须从浏览器捕获(与 goods 的 59640 不同)。
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import copy
|
||||
import json
|
||||
import re
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from .algorithm import UNDEF, JSObject, Window
|
||||
from .pagedoo_vm import PagedooVM
|
||||
|
||||
REPLAY = Path(__file__).resolve().parent.parent / "replay" / "mall"
|
||||
GLOBALS = [UNDEF, None, True, False, 4294967295, 3995986053, 2103143698, 1622111212,
|
||||
4263108271, 3162892160, 1960464030, 2867129963, 3224029870, 3514649446,
|
||||
1382846327, 1898428403, 1268470028, 1457769175, 1595352606, 1100935262]
|
||||
|
||||
|
||||
class MallSession:
|
||||
"""mall 会话态:e377650 变换核心的完整输入。
|
||||
|
||||
transform_input: e377650 创建参数(18 槽)——从浏览器 deepCap/J 转储提取
|
||||
[0]=key16(16B) [1..5]=Te/S-box [6]=d(16B) [7]=[211] [8]=S-box2
|
||||
[9]=outbuf [10]=624B中间态(a:8输出) [11..16]=1024/256表 [17]=回调
|
||||
xmidas_ops: mall 详情页 xMidasOps(59620,服务端生成,页面级)
|
||||
"""
|
||||
|
||||
def __init__(self, transform_input: list, xmidas_ops: list):
|
||||
self.transform_input = transform_input
|
||||
self.xmidas_ops = xmidas_ops
|
||||
self.validate()
|
||||
|
||||
def validate(self) -> None:
|
||||
if len(self.transform_input) != 18:
|
||||
raise ValueError(f"transform_input 应为 18 槽,实际 {len(self.transform_input)}")
|
||||
if len(self.xmidas_ops) != 59620:
|
||||
raise ValueError(f"mall xMidasOps 应为 59620(非 goods 59640),实际 {len(self.xmidas_ops)}")
|
||||
mid = self.transform_input[10]
|
||||
if isinstance(mid, list) and mid and isinstance(mid[0], list):
|
||||
if len(mid[0]) != 624:
|
||||
raise ValueError(f"624B 中间态长度 != 624: {len(mid[0])}")
|
||||
|
||||
@classmethod
|
||||
def from_capture_file(cls, frames_jsonl: str | Path) -> "MallSession":
|
||||
"""从捕获的 frames.jsonl 提取同会话 transform_input + xMidasOps。
|
||||
|
||||
frames.jsonl 由 scripts/capture-mall-session.mjs 实时落盘:
|
||||
- J|...|e377650|{JSON} e377650 创建参数(transform_input)
|
||||
- XMIDAS_OPS|url|59620数组 mall 详情页 xMidasOps
|
||||
"""
|
||||
lines = Path(frames_jsonl).read_text(encoding="utf-8", errors="replace").splitlines()
|
||||
transform_input = None
|
||||
xmidas = None
|
||||
# xMidasOps:取 mall 详情页(z.iwan / pagedoo)那条
|
||||
for l in lines:
|
||||
if l.startswith("XMIDAS_OPS"):
|
||||
parts = l.split("|")
|
||||
if len(parts) >= 3 and ("z.iwan" in parts[1] or "pagedoo" in parts[1]):
|
||||
xmidas = [int(x) for x in parts[2].split(",")]
|
||||
break
|
||||
if xmidas is None:
|
||||
# 回退:任意 59620 长度
|
||||
for l in lines:
|
||||
if l.startswith("XMIDAS_OPS"):
|
||||
parts = l.split("|")
|
||||
vals = [int(x) for x in parts[2].split(",")]
|
||||
if len(vals) == 59620:
|
||||
xmidas = vals
|
||||
break
|
||||
for l in lines:
|
||||
if l.startswith("J|") and re.search(r"\|e377650\|", l):
|
||||
transform_input = json.loads(l.split("|e377650|", 1)[1])
|
||||
break
|
||||
if transform_input is None:
|
||||
raise ValueError("frames.jsonl 中未找到 e377650 J 转储(需 mall 详情页购买触发加密)")
|
||||
if xmidas is None:
|
||||
raise ValueError("frames.jsonl 中未找到 59620 长度 xMidasOps")
|
||||
return cls(transform_input, xmidas)
|
||||
|
||||
def to_json(self) -> dict:
|
||||
return {
|
||||
"transform_input": self.transform_input,
|
||||
"xmidas_ops": self.xmidas_ops,
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, d: dict) -> "MallSession":
|
||||
return cls(d["transform_input"], d["xmidas_ops"])
|
||||
|
||||
|
||||
def _mk_window(xmidas: list) -> Window:
|
||||
w = Window()
|
||||
for k in ("window", "self", "globalThis", "top", "parent", "frames"):
|
||||
w.set(k, w)
|
||||
w.set("document", JSObject())
|
||||
w.set("navigator", JSObject())
|
||||
w.set("location", JSObject())
|
||||
w.set("localStorage", JSObject())
|
||||
w.set("sessionStorage", JSObject())
|
||||
w.set("screen", JSObject())
|
||||
w.set("history", JSObject())
|
||||
w.set("XMLHttpRequest", type("XHR", (), {
|
||||
"open": lambda *a: None, "send": lambda *a: None, "setRequestHeader": lambda *a: None}))
|
||||
w.set("fetch", lambda *a: None)
|
||||
w.set("xMidasOps", xmidas)
|
||||
return w
|
||||
|
||||
|
||||
def generate_encrypt_msg(session: MallSession, random_seed: int = 1) -> str:
|
||||
"""用会话态重放 e377650 变换核心,产出 encrypt_msg(1248 hex)。
|
||||
|
||||
E3 验证:同会话 transform_input + xMidasOps(59620) → 1248 hex 完全一致。
|
||||
"""
|
||||
bytecode = json.loads((REPLAY / "vm" / "bytecode-478657.json").read_text())
|
||||
w = _mk_window(session.xmidas_ops)
|
||||
vm = PagedooVM(bytecode, GLOBALS, w, random_seed=random_seed)
|
||||
vm._root_this = w
|
||||
for k, v in vm._hosts.items():
|
||||
if k != "Array.prototype" and w.get(k) is UNDEF:
|
||||
w.set(k, v)
|
||||
|
||||
h = copy.deepcopy(session.transform_input)
|
||||
frame_rand = vm.make(336201, [], w, GLOBALS, None)
|
||||
# 正确回调链:e344354 = [624B中间态, d, e336201随机帧, key16]
|
||||
h344 = [h[10], h[6], [frame_rand], h[0]]
|
||||
frame344 = vm.make(344354, h344, w, GLOBALS, None)
|
||||
h[17] = [frame344]
|
||||
frame = vm.make(377650, h, w, GLOBALS, None)
|
||||
vm.run(frame, [])
|
||||
|
||||
h9 = h[9][0] if isinstance(h[9], list) and h[9] else h[9]
|
||||
if not isinstance(h9, list) or len(h9) != 624:
|
||||
raise RuntimeError(f"e377650 输出异常: {type(h9).__name__} len={len(h9) if isinstance(h9, list) else '?'}")
|
||||
return "".join(f"{x & 255:02x}" for x in h9)
|
||||
Reference in New Issue
Block a user