初步增加, 扫码登录成功
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
"""YYB 加密引擎(goods + mall 统一框架,纯 Python)。
|
||||
|
||||
- algorithm.py: goods 侧 CHAOS VM(116 opcode)——web_save / web_new_encrypt
|
||||
- pagedoo_vm.py: mall 侧 pagedoo VM(108 opcode)——PlaceOrder
|
||||
- goods.py: goods 侧高层 API(GoodsSession + generate_encrypt_msg)
|
||||
- mall.py: mall 侧高层 API(MallSession + generate_encrypt_msg)
|
||||
- session.py: 会话态模型
|
||||
"""
|
||||
from .algorithm import (
|
||||
build_plaintext,
|
||||
decode_d,
|
||||
generate_encrypt_msg,
|
||||
generate_encrypt_msg_offline,
|
||||
)
|
||||
from .goods import GoodsSession, generate_encrypt_msg as goods_generate
|
||||
from .mall import MallSession, generate_encrypt_msg as mall_generate
|
||||
from .pagedoo_vm import PagedooVM, run_frame
|
||||
from .session import SessionState, load_session
|
||||
|
||||
__all__ = [
|
||||
"build_plaintext",
|
||||
"decode_d",
|
||||
"generate_encrypt_msg",
|
||||
"generate_encrypt_msg_offline",
|
||||
"GoodsSession",
|
||||
"goods_generate",
|
||||
"MallSession",
|
||||
"mall_generate",
|
||||
"PagedooVM",
|
||||
"run_frame",
|
||||
"SessionState",
|
||||
"load_session",
|
||||
]
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,74 @@
|
||||
"""goods 侧(web_save / web_new_encrypt,goodsBiz CHAOS VM 116 opcode)高层 API。
|
||||
|
||||
与 mall 侧(pyvm/mall.py)同属 YYB 加密体系。goods 侧 E3 已验证:
|
||||
会话态(xMidasOps 59640 + key16/key1) + 订单参数
|
||||
→ build_plaintext 21 字段明文(528 字符)
|
||||
→ a:8 变换(Te0-3 + key16 按块轮转)
|
||||
→ webSave(33 块变换)
|
||||
→ encrypt_msg(1056 hex)
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from .algorithm import generate_encrypt_msg_offline
|
||||
|
||||
REPLAY = Path(__file__).resolve().parent.parent / "replay"
|
||||
ORDER_FIELDS = ["token_id", "openid", "openkey", "session_id", "session_type", "zoneid",
|
||||
"pay_method", "buy_quantity", "mb_pwd", "pay_id", "auth_key",
|
||||
"card_value", "accounttype", "provide_uin", "extend", "ts",
|
||||
"from_h5", "webversion"]
|
||||
|
||||
|
||||
class GoodsSession:
|
||||
"""goods 会话态:xMidasOps(59640,服务端生成)+ key16/key1 + args_template。"""
|
||||
|
||||
def __init__(self, xmidas_ops: list, key16: list, key1: list,
|
||||
args_template_d: str = "", xmidas_token: str = ""):
|
||||
self.xmidas_ops = xmidas_ops
|
||||
self.key16 = key16
|
||||
self.key1 = key1
|
||||
self.args_template_d = args_template_d
|
||||
self.xmidas_token = xmidas_token
|
||||
self.validate()
|
||||
|
||||
def validate(self) -> None:
|
||||
if len(self.xmidas_ops) != 59640:
|
||||
raise ValueError(f"goods xMidasOps 应为 59640,实际 {len(self.xmidas_ops)}")
|
||||
if len(self.key16) != 16 or len(self.key1) != 16:
|
||||
raise ValueError("key16/key1 应为 16 字节")
|
||||
|
||||
@classmethod
|
||||
def from_session_state(cls, path: str | Path) -> "GoodsSession":
|
||||
"""从 scripts/capture-session.mjs 生成的 session-state.json 加载。"""
|
||||
d = json.loads(Path(path).read_text(encoding="utf-8"))
|
||||
return cls(d["xmidas_ops"], d["key16"], d["key1"],
|
||||
d.get("args_template_d", ""), d.get("xmidas_token", ""))
|
||||
|
||||
def to_json(self) -> dict:
|
||||
return {
|
||||
"xmidas_ops": self.xmidas_ops,
|
||||
"key16": self.key16,
|
||||
"key1": self.key1,
|
||||
"args_template_d": self.args_template_d,
|
||||
"xmidas_token": self.xmidas_token,
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, d: dict) -> "GoodsSession":
|
||||
return cls(d["xmidas_ops"], d["key16"], d["key1"],
|
||||
d.get("args_template_d", ""), d.get("xmidas_token", ""))
|
||||
|
||||
|
||||
def generate_encrypt_msg(session: GoodsSession, order: dict) -> str:
|
||||
"""用会话态 + 订单参数生成 goods encrypt_msg(1056 hex)。"""
|
||||
from .algorithm import decode_d
|
||||
params = {k: order.get(k, "") for k in ORDER_FIELDS}
|
||||
args_tpl = decode_d(session.args_template_d) if session.args_template_d else None
|
||||
return generate_encrypt_msg_offline(
|
||||
params, order.get("fk_extend", ""), order.get("ts", ""), order.get("_rand", ""),
|
||||
xmidas=session.xmidas_ops, xmidas_token=session.xmidas_token,
|
||||
args_template=args_tpl, key16=session.key16, key1=session.key1,
|
||||
)
|
||||
@@ -0,0 +1,27 @@
|
||||
"""YYB mall APIs require different session fields for WeChat and QQ OAuth."""
|
||||
from __future__ import annotations
|
||||
|
||||
WECHAT_APPID = "wxd44977328b36e647"
|
||||
QQ_APPID = "102033112"
|
||||
|
||||
|
||||
def midas_login_params(cookies: dict[str, str]) -> dict[str, str]:
|
||||
"""Build the provider-specific Midas session fields from OAuth cookies."""
|
||||
login_type = str(cookies.get("logintype", cookies.get("login_type", "WX"))).upper()
|
||||
if login_type == "QC":
|
||||
return {
|
||||
"openid": cookies.get("openid", ""),
|
||||
"openkey": cookies.get("accesstoken", ""),
|
||||
"session_id": "openid",
|
||||
"session_type": "kp_accesstoken",
|
||||
"wx_appid": "",
|
||||
"qq_appid": cookies.get("appid", QQ_APPID),
|
||||
}
|
||||
return {
|
||||
"openid": cookies.get("openid", ""),
|
||||
"openkey": cookies.get("accesstoken", ""),
|
||||
"session_id": "hy_gameid",
|
||||
"session_type": "wc_actoken",
|
||||
"wx_appid": cookies.get("appid", WECHAT_APPID),
|
||||
"qq_appid": "",
|
||||
}
|
||||
@@ -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)
|
||||
@@ -0,0 +1,90 @@
|
||||
"""Read YYB's official order list after a payment is completed."""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from curl_cffi import requests
|
||||
|
||||
ORDER_LIST_URL = (
|
||||
"https://ydd.yyb.qq.com/trpc.wesee_live.private_domain_creator_shop_svr."
|
||||
"private_domain_creator_shop_svr/GetPrivateDomainOrderList"
|
||||
)
|
||||
USER_AGENT = (
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) "
|
||||
"AppleWebKit/537.36 (KHTML, like Gecko) Chrome/151.0.0.0 Safari/537.36"
|
||||
)
|
||||
|
||||
|
||||
def get_official_orders(cookies: dict[str, str], count: int = 20) -> dict[str, Any]:
|
||||
"""Fetch the same completed-order list used by YYB's success page."""
|
||||
response = requests.post(
|
||||
ORDER_LIST_URL,
|
||||
json={"count": count, "breakpoint": 0, "type": 1},
|
||||
headers={
|
||||
"Accept": "application/json, text/plain, */*",
|
||||
"Origin": "https://m.yyb.qq.com",
|
||||
"Referer": "https://m.yyb.qq.com/boc-mall/goods-mall/product-order-success",
|
||||
"User-Agent": USER_AGENT,
|
||||
},
|
||||
cookies=cookies,
|
||||
impersonate="chrome",
|
||||
timeout=30,
|
||||
)
|
||||
if response.status_code != 200:
|
||||
raise RuntimeError(f"订单状态查询失败: HTTP {response.status_code}")
|
||||
try:
|
||||
document = response.json()
|
||||
except ValueError as exc:
|
||||
raise RuntimeError("订单状态查询返回非 JSON") from exc
|
||||
if not isinstance(document, dict):
|
||||
raise RuntimeError("订单状态查询响应格式异常")
|
||||
if document.get("ret_code") not in (None, 0, "0"):
|
||||
raise RuntimeError(
|
||||
f"订单状态查询失败: {document.get('ret_code')} {document.get('ret_msg', '')}"
|
||||
)
|
||||
if not isinstance(document.get("list", []), list):
|
||||
raise RuntimeError("订单状态查询响应缺少 list")
|
||||
return document
|
||||
|
||||
|
||||
def order_ids(document: dict[str, Any]) -> set[str]:
|
||||
"""Return the stable order IDs visible in an order-list response."""
|
||||
return {
|
||||
str(item["order_id"])
|
||||
for item in document.get("list", [])
|
||||
if isinstance(item, dict) and item.get("order_id") not in (None, "")
|
||||
}
|
||||
|
||||
|
||||
def is_finished(item: dict[str, Any]) -> bool:
|
||||
"""YYB's completed-order representation observed on product-order-success."""
|
||||
return item.get("is_finished") is True and item.get("status") in (1, "1")
|
||||
|
||||
|
||||
def order_completion_states(document: dict[str, Any]) -> dict[str, bool]:
|
||||
"""Snapshot whether each currently visible order is completed."""
|
||||
return {
|
||||
str(item["order_id"]): is_finished(item)
|
||||
for item in document.get("list", [])
|
||||
if isinstance(item, dict) and item.get("order_id") not in (None, "")
|
||||
}
|
||||
|
||||
|
||||
def find_completed_order(document: dict[str, Any], previous_states: dict[str, bool]) -> dict[str, Any] | None:
|
||||
"""Find an order that appeared or transitioned to completed after the QR display."""
|
||||
for item in document.get("list", []):
|
||||
if not isinstance(item, dict):
|
||||
continue
|
||||
order_id = str(item.get("order_id", ""))
|
||||
if order_id and is_finished(item) and not previous_states.get(order_id, False):
|
||||
return item
|
||||
return None
|
||||
|
||||
|
||||
def completion_summary(item: dict[str, Any]) -> dict[str, Any]:
|
||||
"""Persist only the state needed to identify a confirmed completion."""
|
||||
return {
|
||||
"order_id": str(item.get("order_id", "")),
|
||||
"is_finished": item.get("is_finished"),
|
||||
"status": item.get("status"),
|
||||
}
|
||||
@@ -0,0 +1,987 @@
|
||||
"""pagedoo shop CHAOS VM(108 opcode)纯 Python 移植 — mall PlaceOrder 加密地基。
|
||||
|
||||
对应浏览器 `p_5c660516.*` chunk 内的 `__TENCENT_CHAOS_VM`(pagedoo 变体,108 opcode 0..107)。
|
||||
与 goods 侧(pyvm/algorithm.py,116 opcode)同属 CHAOS VM 家族:语义相同,opcode 编号不同(F-2055)。
|
||||
JS 语义辅助复用 algorithm.py(_ic/i32/js_add/js_index/JSObject/JSFunction 等)。
|
||||
|
||||
当前验证锚点:
|
||||
- 帧 e215058(encodeURI + %XX 解码 → 字节数组):Node 重放返回输入 JSON 串的 UTF-8 字节(F-2059)
|
||||
- 帧 e377650 变换核心:Node 指令级复现(F-2060,254937/254938 一致)
|
||||
输出编排帧链(e454218/e423160 getter 链)仍依赖 VM C 栈续延语义,见 cases/yyb-chaos-vm-xmidas-webnewencrypt.md 未决项。
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import math
|
||||
import urllib.parse
|
||||
from pathlib import Path
|
||||
|
||||
from .algorithm import (
|
||||
JSDate,
|
||||
UNDEF,
|
||||
JSFunction,
|
||||
JSObject,
|
||||
HostFunction,
|
||||
_ic,
|
||||
i32,
|
||||
u32,
|
||||
ushr,
|
||||
shl,
|
||||
shr,
|
||||
js_typeof,
|
||||
js_truthy,
|
||||
js_str,
|
||||
js_add,
|
||||
js_num,
|
||||
js_eq,
|
||||
js_streq,
|
||||
js_index,
|
||||
js_set,
|
||||
js_del,
|
||||
js_keys,
|
||||
js_call,
|
||||
js_apply,
|
||||
js_new,
|
||||
h_math_floor,
|
||||
h_math_round,
|
||||
h_math_ceil,
|
||||
h_math_min,
|
||||
h_math_max,
|
||||
h_math_abs,
|
||||
h_math_pow,
|
||||
h_math_sqrt,
|
||||
h_parseint,
|
||||
h_parsefloat,
|
||||
h_isnan,
|
||||
h_encodeuri,
|
||||
h_encodeuricomponent,
|
||||
h_decodeuri,
|
||||
h_decodeuricomponent,
|
||||
h_string_fromcharcode,
|
||||
h_new_date,
|
||||
)
|
||||
|
||||
__all__ = ["PagedooVM", "run_frame", "REPLAY"]
|
||||
|
||||
|
||||
# ---------------------------------------------------------------- 辅助
|
||||
|
||||
def _cmp_lt(a, b):
|
||||
try:
|
||||
return a < b
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
|
||||
def _cmp_le(a, b):
|
||||
try:
|
||||
return a <= b
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
|
||||
def _cmp_gt(a, b):
|
||||
try:
|
||||
return a > b
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
|
||||
def _cmp_ge(a, b):
|
||||
try:
|
||||
return a >= b
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
|
||||
# ---------------------------------------------------------------- 宿主函数
|
||||
|
||||
def h_arr_push(this, *args):
|
||||
if this is UNDEF or this is None:
|
||||
this = []
|
||||
if len(args) == 1:
|
||||
this.append(args[0])
|
||||
else:
|
||||
this.extend(args)
|
||||
return len(this)
|
||||
|
||||
|
||||
def h_arr_shift(this):
|
||||
if not this:
|
||||
return UNDEF
|
||||
return this.pop(0)
|
||||
|
||||
|
||||
def h_arr_join(this, sep=None):
|
||||
if sep is None:
|
||||
sep = ","
|
||||
return sep.join("" if x is UNDEF or x is None else js_str(x) for x in this)
|
||||
|
||||
|
||||
def h_arr_slice(this, a=None, b=None):
|
||||
n = len(this)
|
||||
if a is None or a is UNDEF:
|
||||
a = 0
|
||||
if b is None or b is UNDEF:
|
||||
b = n
|
||||
a = int(a) if a == a else 0
|
||||
b = int(b) if b == b else n
|
||||
if a < 0:
|
||||
a = max(0, n + a)
|
||||
if b < 0:
|
||||
b = max(0, n + b)
|
||||
return list(this[a:b])
|
||||
|
||||
|
||||
def h_arr_indexof(this, x, frm=0):
|
||||
try:
|
||||
return this.index(x, frm)
|
||||
except ValueError:
|
||||
return -1
|
||||
|
||||
|
||||
def h_arr_concat(this, *others):
|
||||
out = list(this)
|
||||
for o in others:
|
||||
if isinstance(o, list):
|
||||
out.extend(o)
|
||||
else:
|
||||
out.append(o)
|
||||
return out
|
||||
|
||||
|
||||
def h_arr_pop(this):
|
||||
if not this:
|
||||
return UNDEF
|
||||
return this.pop()
|
||||
|
||||
|
||||
def h_arr_unshift(this, *vals):
|
||||
for v in reversed(vals):
|
||||
this.insert(0, v)
|
||||
return len(this)
|
||||
|
||||
|
||||
def h_arr_reverse(this):
|
||||
this.reverse()
|
||||
return this
|
||||
|
||||
|
||||
def h_arr_splice(this, start, delete_count=0, *items):
|
||||
n = len(this)
|
||||
if start < 0:
|
||||
start = max(0, n + start)
|
||||
if delete_count is UNDEF or delete_count is None:
|
||||
delete_count = n - start
|
||||
removed = this[start:start + delete_count]
|
||||
del this[start:start + delete_count]
|
||||
for i, it in enumerate(items):
|
||||
this.insert(start + i, it)
|
||||
return removed
|
||||
|
||||
|
||||
def h_arr_map(this, fn):
|
||||
return [js_call(fn, UNDEF, [x]) for x in this]
|
||||
|
||||
|
||||
def h_arr_foreach(this, fn):
|
||||
for x in this:
|
||||
js_call(fn, UNDEF, [x])
|
||||
return UNDEF
|
||||
|
||||
|
||||
def h_arr_filter(this, fn):
|
||||
return [x for x in this if js_truthy(js_call(fn, UNDEF, [x]))]
|
||||
|
||||
|
||||
def h_char_code_at(this, s, i=0):
|
||||
if isinstance(this, str) and isinstance(s, int) and 0 <= s < len(this):
|
||||
return ord(this[s])
|
||||
return float("nan")
|
||||
|
||||
|
||||
def h_char_at(this, i=0):
|
||||
if isinstance(this, str) and isinstance(i, int) and 0 <= i < len(this):
|
||||
return this[i]
|
||||
return ""
|
||||
|
||||
|
||||
def h_str_indexof(this, search, frm=0):
|
||||
if not isinstance(this, str):
|
||||
return -1
|
||||
try:
|
||||
return this.index(search, frm)
|
||||
except ValueError:
|
||||
return -1
|
||||
|
||||
|
||||
def h_str_slice(this, a=None, b=None):
|
||||
return this[a:b] if isinstance(this, str) else ""
|
||||
|
||||
|
||||
def h_str_split(this, sep=None):
|
||||
return this.split(sep) if isinstance(this, str) else [this]
|
||||
|
||||
|
||||
def h_str_tolower(this):
|
||||
return this.lower() if isinstance(this, str) else this
|
||||
|
||||
|
||||
def h_str_toupper(this):
|
||||
return this.upper() if isinstance(this, str) else this
|
||||
|
||||
|
||||
def h_str_substr(this, start=0, length=None):
|
||||
if not isinstance(this, str):
|
||||
return ""
|
||||
n = len(this)
|
||||
if start < 0:
|
||||
start = max(0, n + start)
|
||||
if length is None or length is UNDEF:
|
||||
return this[start:]
|
||||
return this[start:start + int(length)]
|
||||
|
||||
|
||||
def h_str_substring(this, start=0, end=None):
|
||||
if not isinstance(this, str):
|
||||
return ""
|
||||
if end is None or end is UNDEF:
|
||||
end = len(this)
|
||||
start = max(0, min(int(start), len(this)))
|
||||
end = max(0, min(int(end), len(this)))
|
||||
if start > end:
|
||||
start, end = end, start
|
||||
return this[start:end]
|
||||
|
||||
|
||||
def h_str_tostring(this):
|
||||
return js_str(this)
|
||||
|
||||
|
||||
def h_object_ctor(this, *args):
|
||||
if len(args) == 1 and args[0] is not UNDEF and args[0] is not None:
|
||||
return args[0]
|
||||
return JSObject()
|
||||
|
||||
|
||||
def _js_to_py(v):
|
||||
"""JSON.stringify 辅助:把 JS 值转 JSON 可序列化。"""
|
||||
if v is UNDEF:
|
||||
return None
|
||||
if isinstance(v, list):
|
||||
return [_js_to_py(x) for x in v]
|
||||
if isinstance(v, JSObject):
|
||||
keys = v._d if hasattr(v, "_d") else {}
|
||||
return {k: _js_to_py(val) for k, val in (keys.items() if isinstance(keys, dict) else [])}
|
||||
if isinstance(v, Window):
|
||||
return {}
|
||||
return v
|
||||
|
||||
|
||||
def _py_to_js(v):
|
||||
if isinstance(v, dict):
|
||||
o = JSObject()
|
||||
for k, val in v.items():
|
||||
o.set(k, _py_to_js(val))
|
||||
return o
|
||||
if isinstance(v, list):
|
||||
return [_py_to_js(x) for x in v]
|
||||
return v
|
||||
|
||||
|
||||
def _pg_index(obj, key):
|
||||
"""pagedoo 专用 js_index:补充字符串原型方法(goods 侧不需要)。"""
|
||||
if isinstance(obj, JSDate):
|
||||
if key == "getTime":
|
||||
return HostFunction(lambda this: this.t if isinstance(this, JSDate) else obj.t, "getTime")
|
||||
if key == "toString":
|
||||
return HostFunction(lambda this: str(this), "toString")
|
||||
if key == "valueOf":
|
||||
return HostFunction(lambda this: this.t if isinstance(this, JSDate) else obj.t, "valueOf")
|
||||
if isinstance(obj, str):
|
||||
if isinstance(key, str):
|
||||
if key == "length":
|
||||
return len(obj)
|
||||
if key == "charCodeAt":
|
||||
return HostFunction(h_char_code_at, "charCodeAt")
|
||||
if key == "charAt":
|
||||
return HostFunction(h_char_at, "charAt")
|
||||
if key == "indexOf":
|
||||
return HostFunction(h_str_indexof, "indexOf")
|
||||
if key == "slice":
|
||||
return HostFunction(h_str_slice, "slice")
|
||||
if key == "split":
|
||||
return HostFunction(h_str_split, "split")
|
||||
if key == "toLowerCase":
|
||||
return HostFunction(h_str_tolower, "toLowerCase")
|
||||
if key == "toUpperCase":
|
||||
return HostFunction(h_str_toupper, "toUpperCase")
|
||||
if key == "toString":
|
||||
return HostFunction(h_str_tostring, "toString")
|
||||
if key == "substr":
|
||||
return HostFunction(h_str_substr, "substr")
|
||||
if key == "substring":
|
||||
return HostFunction(h_str_substring, "substring")
|
||||
return js_index(obj, key)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------- VM
|
||||
|
||||
class PagedooVM:
|
||||
"""pagedoo CHAOS VM 解释器(108 opcode,0..107)。
|
||||
|
||||
bytecode: 字节码数组(如 vm/bytecode-478657.json)
|
||||
constants: 常量数组(浏览器调用第 4 参,即 globals)
|
||||
window: 宿主环境对象(JSObject/Window)
|
||||
"""
|
||||
|
||||
def __init__(self, bytecode, constants, window, random_seed=None):
|
||||
self.o = bytecode
|
||||
self.constants = constants
|
||||
self.window = window
|
||||
self.inst_id = 0
|
||||
self.use_init_c = False
|
||||
self.init_c = None
|
||||
self._root_this = UNDEF
|
||||
self._host_log = []
|
||||
import random as _r
|
||||
self._random = _r
|
||||
if random_seed is not None:
|
||||
self._random.seed(random_seed)
|
||||
self._hosts = self._build_hosts()
|
||||
|
||||
def _build_hosts(self):
|
||||
m = {}
|
||||
m["Math"] = JSObject()
|
||||
m["Math"].set("random", HostFunction(lambda this: self._random.random(), "random"))
|
||||
m["Math"].set("floor", HostFunction(h_math_floor, "floor"))
|
||||
m["Math"].set("round", HostFunction(h_math_round, "round"))
|
||||
m["Math"].set("ceil", HostFunction(h_math_ceil, "ceil"))
|
||||
m["Math"].set("min", HostFunction(h_math_min, "min"))
|
||||
m["Math"].set("max", HostFunction(h_math_max, "max"))
|
||||
m["Math"].set("abs", HostFunction(h_math_abs, "abs"))
|
||||
m["Math"].set("pow", HostFunction(h_math_pow, "pow"))
|
||||
m["Math"].set("sqrt", HostFunction(h_math_sqrt, "sqrt"))
|
||||
m["parseInt"] = HostFunction(h_parseint, "parseInt")
|
||||
m["parseFloat"] = HostFunction(h_parsefloat, "parseFloat")
|
||||
m["isNaN"] = HostFunction(h_isnan, "isNaN")
|
||||
m["encodeURI"] = HostFunction(h_encodeuri, "encodeURI")
|
||||
m["encodeURIComponent"] = HostFunction(h_encodeuricomponent, "encodeURIComponent")
|
||||
m["decodeURI"] = HostFunction(h_decodeuri, "decodeURI")
|
||||
m["decodeURIComponent"] = HostFunction(h_decodeuricomponent, "decodeURIComponent")
|
||||
m["String"] = JSObject()
|
||||
m["String"].set("fromCharCode", HostFunction(h_string_fromcharcode, "fromCharCode"))
|
||||
m["Date"] = HostFunction(h_new_date, "Date")
|
||||
m["Array"] = JSObject()
|
||||
m["Array"].set("isArray", HostFunction(lambda this, x: isinstance(x, list), "isArray"))
|
||||
m["Object"] = HostFunction(h_object_ctor, "Object")
|
||||
m["Number"] = HostFunction(lambda this, x=None: js_num(x) if x is not None and x is not UNDEF else 0.0, "Number")
|
||||
m["Boolean"] = HostFunction(lambda this, x=None: bool(js_truthy(x)) if x is not None and x is not UNDEF else False, "Boolean")
|
||||
jso = JSObject()
|
||||
jso.set("stringify", HostFunction(lambda this, x, *a: json.dumps(_js_to_py(x), ensure_ascii=False), "stringify"))
|
||||
jso.set("parse", HostFunction(lambda this, s: _py_to_js(json.loads(s)), "parse"))
|
||||
m["JSON"] = jso
|
||||
m["RegExp"] = HostFunction(lambda this, *a: JSObject(), "RegExp")
|
||||
# Array.prototype 方法挂到 Array 对象上(VM 通过 i[A].push 等调用)
|
||||
proto = JSObject()
|
||||
for name, fn in [
|
||||
("push", h_arr_push), ("shift", h_arr_shift), ("join", h_arr_join),
|
||||
("slice", h_arr_slice), ("indexOf", h_arr_indexof), ("concat", h_arr_concat),
|
||||
("pop", h_arr_pop), ("unshift", h_arr_unshift), ("reverse", h_arr_reverse),
|
||||
("splice", h_arr_splice), ("map", h_arr_map), ("forEach", h_arr_foreach),
|
||||
("filter", h_arr_filter),
|
||||
]:
|
||||
proto.set(name, HostFunction(fn, name))
|
||||
m["Array.prototype"] = proto
|
||||
return m
|
||||
|
||||
def host(self, name):
|
||||
h = self.window.get(name)
|
||||
if h is not UNDEF:
|
||||
return h
|
||||
return self._hosts.get(name, UNDEF)
|
||||
|
||||
def make(self, entry, args, s, n, t):
|
||||
return JSFunction(self, entry, args, s, n, t)
|
||||
|
||||
# -- 原型方法查找:i[A].push(...) 形式,i[A] 是数组 --
|
||||
def _arr_method(self, name):
|
||||
p = self._hosts.get("Array.prototype")
|
||||
if p is not UNDEF and isinstance(p, JSObject):
|
||||
v = p.get(name)
|
||||
if v is not UNDEF:
|
||||
return v
|
||||
return UNDEF
|
||||
|
||||
def run(self, fn: JSFunction, call_args, trace=None, csnap=None):
|
||||
self.inst_id += 1
|
||||
if trace is not None:
|
||||
self._trace = trace
|
||||
self._csnap = csnap
|
||||
if self.use_init_c and self.init_c is not None:
|
||||
C = self.init_c
|
||||
self.use_init_c = False
|
||||
else:
|
||||
root_this = getattr(self, "_root_this", UNDEF)
|
||||
C = [fn.s, fn.n, fn.args, root_this, call_args, fn, self.o, 0]
|
||||
C = list(C)
|
||||
p = UNDEF
|
||||
u = fn.entry
|
||||
d = [] # 异常续延栈(JS C)
|
||||
t = UNDEF # 最近异常值(op3/op52)
|
||||
o = self.o
|
||||
l = fn.t # 异常处理器(JS l)
|
||||
_get = _pg_index
|
||||
_set = js_set
|
||||
_arr = self._arr_method
|
||||
|
||||
while True:
|
||||
try:
|
||||
while True:
|
||||
u += 1
|
||||
op = o[u]
|
||||
if op in (0, 4, 11, 18, 23, 26, 43, 44, 48, 50, 84, 107) and len(self._host_log) < 2000:
|
||||
# 记录调用目标(简化)
|
||||
_tgt = o[u + 2] if op in (4, 11, 44, 50) else (o[u + 2] if op in (0, 18, 23, 26, 43, 48, 84, 107) else o[u + 2])
|
||||
try:
|
||||
_tv = C[_tgt] if 0 <= _tgt < len(C) else UNDEF
|
||||
_tr = type(_tv).__name__ if not isinstance(_tv, (int, float, str, bool, type(None))) else repr(_tv)[:30]
|
||||
self._host_log.append((op, u, _tr))
|
||||
except Exception:
|
||||
self._host_log.append((op, u, "?"))
|
||||
if getattr(self, "_trace", None) is not None and len(self._trace) < 500000:
|
||||
self._trace.append((op, u))
|
||||
if self._csnap is not None and len(self._csnap) < 500:
|
||||
snap = []
|
||||
for _si in range(min(42, len(C))):
|
||||
_v = C[_si]
|
||||
if isinstance(_v, str):
|
||||
snap.append("s:" + _v[:30])
|
||||
elif isinstance(_v, list):
|
||||
snap.append(f"a[{len(_v)}]")
|
||||
elif _v is UNDEF:
|
||||
snap.append("u")
|
||||
elif _v is None:
|
||||
snap.append("n")
|
||||
elif isinstance(_v, (int, float, bool)):
|
||||
snap.append(_v)
|
||||
else:
|
||||
snap.append("o")
|
||||
self._csnap.append((op, u, snap))
|
||||
# ---------------- opcode dispatch(108,逐条对照 interpreter-clean.js)----------------
|
||||
# 约定:S() 读下一个槽索引操作数并取 C[slot];imm 直接读。
|
||||
if op == 0:
|
||||
# for(h=[],f=c[++u];f>0;f--)h.push(i[c[++u]]);i[A]=i[B].apply(i[C],h)
|
||||
f = o[u + 1]; u += 1
|
||||
h = []
|
||||
for _ in range(f):
|
||||
h.append(_get(C, o[u + 1])); u += 1
|
||||
a = o[u + 1]; b = o[u + 2]; c = o[u + 3]; u += 3
|
||||
_set(C, a, js_apply(_get(C, b), _get(C, c), h))
|
||||
elif op == 1:
|
||||
a = o[u + 1]; b = o[u + 2]; c = o[u + 3]; u += 3
|
||||
_set(C, a, js_num(_get(C, b)) - js_num(_get(C, c)))
|
||||
elif op == 2:
|
||||
a = o[u + 1]; u += 1
|
||||
_set(C, a, False)
|
||||
elif op == 3:
|
||||
a = o[u + 1]; u += 1
|
||||
_set(C, a, t)
|
||||
elif op == 4:
|
||||
a = o[u + 1]; b = o[u + 2]; c = o[u + 3]; dd = o[u + 4]; u += 4
|
||||
_set(C, a, js_call(_get(C, b), p, [_get(C, c), _get(C, dd)]))
|
||||
elif op == 5:
|
||||
a = o[u + 1]; b = o[u + 2]; u += 2
|
||||
_set(C, a, _get(C, b))
|
||||
c = o[u + 1]; dd = o[u + 2]; imm = o[u + 3]; u += 3
|
||||
_set(C, c, _get(_get(C, dd), imm))
|
||||
e = o[u + 1]; u += 1
|
||||
_set(C, e, "")
|
||||
elif op == 6:
|
||||
a = o[u + 1]; b = o[u + 2]; c = o[u + 3]; u += 3
|
||||
_set(C, a, _cmp_lt(js_num(_get(C, b)), js_num(_get(C, c))))
|
||||
elif op == 7:
|
||||
a = o[u + 1]; b = o[u + 2]; c = o[u + 3]; u += 3
|
||||
_set(C, a, js_del(_get(C, b), _get(C, c)))
|
||||
elif op == 8:
|
||||
a = o[u + 1]; b = o[u + 2]; imm1 = o[u + 3]; u += 3
|
||||
_set(C, a, _get(_get(C, b), imm1))
|
||||
c = o[u + 1]; dd = o[u + 2]; imm2 = o[u + 3]; u += 3
|
||||
_set(C, c, _get(_get(C, dd), imm2))
|
||||
elif op == 9:
|
||||
if d:
|
||||
d.pop()
|
||||
elif op == 10:
|
||||
f = o[u + 1]; u += 1
|
||||
h = []
|
||||
for _ in range(f):
|
||||
h.append(_get(C, o[u + 1])); u += 1
|
||||
dest = o[u + 1]; off = o[u + 2]; u += 2
|
||||
_set(C, dest, self.make(u + off, h, C[0], C[1], l))
|
||||
elif op == 11:
|
||||
a = o[u + 1]; b = o[u + 2]; c = o[u + 3]; u += 3
|
||||
_set(C, a, js_call(_get(C, b), p, [_get(C, c)]))
|
||||
elif op == 12:
|
||||
a = o[u + 1]; b = o[u + 2]; imm = o[u + 3]; u += 3
|
||||
_set(C, a, shl(js_num(_get(C, b)), imm))
|
||||
elif op == 13:
|
||||
a = o[u + 1]; b = o[u + 2]; c = o[u + 3]; dd = o[u + 4]; e = o[u + 5]; u += 5
|
||||
_set(C, a, _get(C, b))
|
||||
v = _cmp_lt(js_num(_get(C, dd)), js_num(_get(C, e)))
|
||||
_set(C, c, v)
|
||||
u0 = u
|
||||
if js_truthy(v):
|
||||
u = u0 + o[u0 + 1]
|
||||
else:
|
||||
u = u0 + o[u0 + 2]
|
||||
elif op == 14:
|
||||
a = o[u + 1]; b = o[u + 2]; c = o[u + 3]; u += 3
|
||||
_set(C, a, i32(_ic(_get(C, b))) | i32(_ic(_get(C, c))))
|
||||
elif op == 15:
|
||||
a = o[u + 1]; b = o[u + 2]; imm = o[u + 3]; u += 3
|
||||
_set(C, a, js_num(_get(C, b)) - imm)
|
||||
elif op == 16:
|
||||
a = o[u + 1]; b = o[u + 2]; u += 2
|
||||
_set(C, a, js_new(_get(C, b), []))
|
||||
elif op == 17:
|
||||
a = o[u + 1]; b = o[u + 2]; imm = o[u + 3]; u += 3
|
||||
_set(C, a, "")
|
||||
_set(C, b, js_str(_get(C, b)) + chr(imm & 0xFFFF))
|
||||
elif op == 18:
|
||||
a = o[u + 1]; b = o[u + 2]; c = o[u + 3]; dd = o[u + 4]; e = o[u + 5]; f = o[u + 6]; u += 6
|
||||
_set(C, a, js_call(_get(C, b), _get(C, c), [_get(C, dd), _get(C, e), _get(C, f)]))
|
||||
elif op == 19:
|
||||
a = o[u + 1]; b = o[u + 2]; u += 2
|
||||
_set(C, a, _get(C, b))
|
||||
c = o[u + 1]; imm = o[u + 2]; u += 2
|
||||
_set(C, c, imm)
|
||||
dd = o[u + 1]; e = o[u + 2]; u += 2
|
||||
_set(C, dd, _get(C, e))
|
||||
elif op == 20:
|
||||
a = o[u + 1]; u += 1
|
||||
_set(C, a, "")
|
||||
elif op == 21:
|
||||
a = o[u + 1]; b = o[u + 2]; c = o[u + 3]; u += 3
|
||||
_set(C, a, _get(_get(C, b), _get(C, c)))
|
||||
dd = o[u + 1]; e = o[u + 2]; imm = o[u + 3]; u += 3
|
||||
v = _cmp_gt(js_num(_get(C, e)), imm)
|
||||
_set(C, dd, v)
|
||||
u0 = u
|
||||
if js_truthy(v):
|
||||
u = u0 + o[u0 + 1]
|
||||
else:
|
||||
u = u0 + o[u0 + 2]
|
||||
elif op == 22:
|
||||
a = o[u + 1]; b = o[u + 2]; c = o[u + 3]; u += 3
|
||||
_set(C, a, js_num(_get(C, b)) * js_num(_get(C, c)))
|
||||
elif op == 23:
|
||||
a = o[u + 1]; b = o[u + 2]; c = o[u + 3]; dd = o[u + 4]; u += 4
|
||||
_set(C, a, js_call(_get(C, b), _get(C, c), [_get(C, dd)]))
|
||||
elif op == 24:
|
||||
a = o[u + 1]; b = o[u + 2]; u += 2
|
||||
_set(C, a, js_num(_get(C, b)))
|
||||
c = o[u + 1]; dd = o[u + 2]; u += 2
|
||||
_set(C, c, js_num(_get(C, dd)) + 1)
|
||||
e = o[u + 1]; f = o[u + 2]; u += 2
|
||||
_set(C, e, _get(C, f))
|
||||
elif op == 25:
|
||||
a = o[u + 1]; b = o[u + 2]; c = o[u + 3]; dd = o[u + 4]; e = o[u + 5]; u += 5
|
||||
_set(C, a, js_new(_get(C, b), [_get(C, c), _get(C, dd), _get(C, e)]))
|
||||
elif op == 26:
|
||||
a = o[u + 1]; b = o[u + 2]; c = o[u + 3]; u += 3
|
||||
_set(C, a, _get(_get(C, b), _get(C, c)))
|
||||
dd = o[u + 1]; e = o[u + 2]; f = o[u + 3]; g = o[u + 4]; u += 4
|
||||
_set(C, dd, js_call(_get(C, e), _get(C, f), [_get(C, g)]))
|
||||
hh = o[u + 1]; ii = o[u + 2]; j = o[u + 3]; kk = o[u + 4]; ll = o[u + 5]; u += 5
|
||||
_set(C, hh, js_call(_get(C, ii), _get(C, j), [_get(C, kk), _get(C, ll)]))
|
||||
elif op == 27:
|
||||
a = o[u + 1]; n = o[u + 2]; u += 2
|
||||
_set(C, a, [UNDEF] * int(n))
|
||||
elif op == 28:
|
||||
a = o[u + 1]; b = o[u + 2]; c = o[u + 3]; u += 3
|
||||
_set(C, a, i32(_ic(_get(C, b))) ^ i32(_ic(_get(C, c))))
|
||||
elif op == 29:
|
||||
a = o[u + 1]; b = o[u + 2]; imm = o[u + 3]; u += 3
|
||||
_set(C, a, _cmp_gt(js_num(_get(C, b)), imm))
|
||||
elif op == 30:
|
||||
a = o[u + 1]; b = o[u + 2]; c = o[u + 3]; u += 3
|
||||
_set(C, a, i32(_ic(_get(C, b))) & i32(_ic(_get(C, c))))
|
||||
elif op == 31:
|
||||
a = o[u + 1]; b = o[u + 2]; u += 2
|
||||
_set(C, a, _get(C, b))
|
||||
c = o[u + 1]; dd = o[u + 2]; u += 2
|
||||
_set(C, c, js_num(_get(C, dd)))
|
||||
e = o[u + 1]; f = o[u + 2]; u += 2
|
||||
_set(C, e, js_num(_get(C, f)) + 1)
|
||||
elif op == 32:
|
||||
# u += i[A] ? c[++u] : c[(++u,++u)] —— LHS u 在 body 开始读取
|
||||
u0 = u
|
||||
a = o[u0 + 1]
|
||||
if js_truthy(_get(C, a)):
|
||||
u = u0 + o[u0 + 2]
|
||||
else:
|
||||
u = u0 + o[u0 + 3]
|
||||
elif op == 33:
|
||||
a = o[u + 1]; imm = o[u + 2]; u += 2
|
||||
_set(C, a, js_str(_get(C, a)) + chr(imm & 0xFFFF))
|
||||
b = o[u + 1]; c = o[u + 2]; dd = o[u + 3]; u += 3
|
||||
_set(C, b, _get(_get(C, c), _get(C, dd)))
|
||||
elif op == 34:
|
||||
a = o[u + 1]; b = o[u + 2]; u += 2
|
||||
_set(C, a, js_typeof(_get(C, b)))
|
||||
elif op == 35:
|
||||
a = o[u + 1]; b = o[u + 2]; c = o[u + 3]; u += 3
|
||||
_set(C, a, _cmp_le(js_num(_get(C, b)), js_num(_get(C, c))))
|
||||
elif op == 36:
|
||||
a = o[u + 1]; b = o[u + 2]; u += 2
|
||||
_set(C, a, -js_num(_get(C, b)))
|
||||
elif op == 37:
|
||||
a = o[u + 1]; u += 1
|
||||
_set(C, a, True)
|
||||
elif op == 38:
|
||||
a = o[u + 1]; b = o[u + 2]; c = o[u + 3]; u += 3
|
||||
_set(C, a, isinstance(_get(C, b), type(_get(C, c))) if isinstance(_get(C, c), type) else False)
|
||||
elif op == 39:
|
||||
a = o[u + 1]; b = o[u + 2]; imm = o[u + 3]; u += 3
|
||||
_set(C, a, js_add(_get(C, b), imm))
|
||||
elif op == 40:
|
||||
a = o[u + 1]; b = o[u + 2]; c = o[u + 3]; u += 3
|
||||
js_set(_get(C, a), _get(C, b), _get(C, c))
|
||||
elif op == 41:
|
||||
a = o[u + 1]; imm = o[u + 2]; u += 2
|
||||
_set(C, a, imm)
|
||||
b = o[u + 1]; u += 1
|
||||
_set(C, b, _get(C, b))
|
||||
c = o[u + 1]; dd = o[u + 2]; e = o[u + 3]; u += 3
|
||||
_set(C, c, _cmp_lt(js_num(_get(C, dd)), js_num(_get(C, e))))
|
||||
elif op == 42:
|
||||
a = o[u + 1]; b = o[u + 2]; c = o[u + 3]; u += 3
|
||||
_set(C, a, js_num(_get(C, b)) / js_num(_get(C, c)))
|
||||
elif op == 43:
|
||||
a = o[u + 1]; b = o[u + 2]; c = o[u + 3]; dd = o[u + 4]; e = o[u + 5]; u += 5
|
||||
_set(C, a, js_call(_get(C, b), _get(C, c), [_get(C, dd), _get(C, e)]))
|
||||
elif op == 44:
|
||||
a = o[u + 1]; b = o[u + 2]; u += 2
|
||||
_bv = _get(C, b)
|
||||
if getattr(self, "_dbg_u", None) == u:
|
||||
print(f" [dbg] op44@{u}: A={a} B={b} i[B]={type(_bv).__name__}: {str(_bv)[:80]}")
|
||||
_set(C, a, js_call(_bv, p, []))
|
||||
elif op == 45:
|
||||
a = o[u + 1]; u += 1
|
||||
_set(C, a, p)
|
||||
b = o[u + 1]; n = o[u + 2]; u += 2
|
||||
_set(C, b, [UNDEF] * int(n))
|
||||
c = o[u + 1]; u += 1
|
||||
_set(C, c, "")
|
||||
elif op == 46:
|
||||
a = o[u + 1]; u += 1
|
||||
obj = JSObject()
|
||||
_set(C, a, obj)
|
||||
b = o[u + 1]; imm = o[u + 2]; c = o[u + 3]; u += 3
|
||||
js_set(_get(C, b), imm, _get(C, c))
|
||||
dd = o[u + 1]; imm2 = o[u + 2]; e = o[u + 3]; u += 3
|
||||
js_set(_get(C, dd), imm2, _get(C, e))
|
||||
elif op == 47:
|
||||
a = o[u + 1]; imm = o[u + 2]; u += 2
|
||||
_set(C, a, js_str(_get(C, a)) + chr(imm & 0xFFFF))
|
||||
elif op == 48:
|
||||
a = o[u + 1]; b = o[u + 2]; c = o[u + 3]; dd = o[u + 4]; u += 4
|
||||
_set(C, a, js_call(_get(C, b), _get(C, c), [_get(C, dd)]))
|
||||
e = o[u + 1]; f = o[u + 2]; g = o[u + 3]; hh = o[u + 4]; ii = o[u + 5]; j = o[u + 6]; u += 6
|
||||
_set(C, e, js_call(_get(C, f), _get(C, g), [_get(C, hh), _get(C, ii)]))
|
||||
return _get(C, j)
|
||||
elif op == 49:
|
||||
a = o[u + 1]; u += 1
|
||||
_set(C, a, JSObject())
|
||||
elif op == 50:
|
||||
a = o[u + 1]; b = o[u + 2]; c = o[u + 3]; dd = o[u + 4]; u += 4
|
||||
_set(C, a, js_call(_get(C, b), p, [_get(C, c), _get(C, dd)]))
|
||||
elif op == 51:
|
||||
a = o[u + 1]; imm = o[u + 2]; u += 2
|
||||
_set(C, a, js_str(_get(C, a)) + chr(imm & 0xFFFF))
|
||||
f = o[u + 1]; u += 1
|
||||
h = []
|
||||
for _ in range(f):
|
||||
h.append(_get(C, o[u + 1])); u += 1
|
||||
dest = o[u + 1]; off = o[u + 2]; u += 2
|
||||
frame = self.make(u + off, h, C[0], C[1], l)
|
||||
_set(C, dest, frame)
|
||||
b = o[u + 1]; c = o[u + 2]; e = o[u + 3]; u += 3
|
||||
js_set(_get(C, b), _get(C, c), _get(C, e))
|
||||
elif op == 52:
|
||||
t = _get(C, o[u + 1]); u += 1
|
||||
raise _VMThrow(t)
|
||||
elif op == 53:
|
||||
a = o[u + 1]; b = o[u + 2]; u += 2
|
||||
_set(C, a, js_num(_get(C, b)))
|
||||
elif op == 54:
|
||||
a = o[u + 1]; b = o[u + 2]; imm = o[u + 3]; u += 3
|
||||
_set(C, a, _cmp_lt(js_num(_get(C, b)), imm))
|
||||
elif op == 55:
|
||||
a = o[u + 1]; b = o[u + 2]; c = o[u + 3]; u += 3
|
||||
_set(C, a, _get(_get(C, b), _get(C, c)))
|
||||
elif op == 56:
|
||||
# C.push(u+c[++u]) —— LHS u 在 c[++u] 前读取,且 u 推进 1
|
||||
u0 = u
|
||||
imm = o[u0 + 1]
|
||||
d.append(u0 + imm)
|
||||
u = u0 + 1
|
||||
elif op == 57:
|
||||
a = o[u + 1]; b = o[u + 2]; c = o[u + 3]; u += 3
|
||||
_set(C, a, _cmp_ge(js_num(_get(C, b)), js_num(_get(C, c))))
|
||||
elif op == 58:
|
||||
a = o[u + 1]; b = o[u + 2]; c = o[u + 3]; u += 3
|
||||
_set(C, a, js_eq(_get(C, b), _get(C, c)))
|
||||
elif op == 59:
|
||||
a = o[u + 1]; b = o[u + 2]; u += 2
|
||||
_set(C, a, js_keys(_get(C, b)))
|
||||
elif op == 60:
|
||||
u += o[u + 1]
|
||||
elif op == 61:
|
||||
a = o[u + 1]; imm = o[u + 2]; b = o[u + 3]; u += 3
|
||||
js_set(_get(C, a), imm, _get(C, b))
|
||||
elif op == 62:
|
||||
a = o[u + 1]; b = o[u + 2]; u += 2
|
||||
_set(C, a, _get(C, b))
|
||||
c = o[u + 1]; dd = o[u + 2]; e = o[u + 3]; u += 3
|
||||
js_set(_get(C, c), _get(C, dd), _get(C, e))
|
||||
elif op == 63:
|
||||
return _get(C, o[u + 1])
|
||||
elif op == 64:
|
||||
a = o[u + 1]; b = o[u + 2]; imm = o[u + 3]; u += 3
|
||||
_set(C, a, _get(_get(C, b), imm))
|
||||
c = o[u + 1]; u += 1
|
||||
_set(C, c, "")
|
||||
dd = o[u + 1]; imm2 = o[u + 2]; u += 2
|
||||
_set(C, dd, js_str(_get(C, dd)) + chr(imm2 & 0xFFFF))
|
||||
elif op == 65:
|
||||
a = o[u + 1]; b = o[u + 2]; c = o[u + 3]; u += 3
|
||||
_set(C, a, shr(js_num(_get(C, b)), js_num(_get(C, c))))
|
||||
elif op == 66:
|
||||
a = o[u + 1]; imm1 = o[u + 2]; u += 2
|
||||
_set(C, a, js_str(_get(C, a)) + chr(imm1 & 0xFFFF))
|
||||
b = o[u + 1]; imm2 = o[u + 2]; u += 2
|
||||
_set(C, b, js_str(_get(C, b)) + chr(imm2 & 0xFFFF))
|
||||
elif op == 67:
|
||||
a = o[u + 1]; u += 1
|
||||
_set(C, a, p)
|
||||
elif op == 68:
|
||||
a = o[u + 1]; b = o[u + 2]; c = o[u + 3]; u += 3
|
||||
_set(C, a, _get(_get(C, b), _get(C, c)))
|
||||
dd = o[u + 1]; e = o[u + 2]; u += 2
|
||||
_set(C, dd, _get(C, e))
|
||||
f = o[u + 1]; u += 1
|
||||
_set(C, f, "")
|
||||
elif op == 69:
|
||||
a = o[u + 1]; imm = o[u + 2]; u += 2
|
||||
_set(C, a, js_num(_get(C, a)) - imm)
|
||||
b = o[u + 1]; c = o[u + 2]; dd = o[u + 3]; e = o[u + 4]; u += 4
|
||||
_set(C, b, js_new(_get(C, c), [_get(C, dd), _get(C, e)]))
|
||||
f = o[u + 1]; g = o[u + 2]; u += 2
|
||||
_set(C, f, _get(C, g))
|
||||
elif op == 70:
|
||||
a = o[u + 1]; b = o[u + 2]; imm = o[u + 3]; u += 3
|
||||
_set(C, a, i32(_ic(_get(C, b))) | imm)
|
||||
elif op == 71:
|
||||
f = o[u + 1]; u += 1
|
||||
h = []
|
||||
for _ in range(f):
|
||||
h.append(_get(C, o[u + 1])); u += 1
|
||||
dest = o[u + 1]; off = o[u + 2]; u += 2
|
||||
_set(C, dest, self.make(u + off, h, C[0], C[1], l))
|
||||
elif op == 72:
|
||||
a = o[u + 1]; u += 1
|
||||
_set(C, a, None)
|
||||
elif op == 73:
|
||||
a = o[u + 1]; b = o[u + 2]; imm = o[u + 3]; u += 3
|
||||
_set(C, a, i32(_ic(_get(C, b))) & imm)
|
||||
elif op == 74:
|
||||
a = o[u + 1]; imm1 = o[u + 2]; b = o[u + 3]; u += 3
|
||||
js_set(_get(C, a), imm1, _get(C, b))
|
||||
c = o[u + 1]; u += 1
|
||||
obj = JSObject()
|
||||
_set(C, c, obj)
|
||||
dd = o[u + 1]; imm2 = o[u + 2]; e = o[u + 3]; u += 3
|
||||
js_set(_get(C, dd), imm2, _get(C, e))
|
||||
elif op == 75:
|
||||
a = o[u + 1]; b = o[u + 2]; imm = o[u + 3]; u += 3
|
||||
_set(C, a, shr(js_num(_get(C, b)), imm))
|
||||
elif op == 76:
|
||||
a = o[u + 1]; imm = o[u + 2]; b = o[u + 3]; u += 3
|
||||
_set(C, a, imm + js_num(_get(C, b)))
|
||||
elif op == 77:
|
||||
a = o[u + 1]; b = o[u + 2]; c = o[u + 3]; dd = o[u + 4]; u += 4
|
||||
_set(C, a, js_new(_get(C, b), [_get(C, c), _get(C, dd)]))
|
||||
elif op == 78:
|
||||
a = o[u + 1]; b = o[u + 2]; c = o[u + 3]; u += 3
|
||||
_set(C, a, ushr(js_num(_get(C, b)), js_num(_get(C, c))))
|
||||
elif op == 79:
|
||||
a = o[u + 1]; u += 1
|
||||
_set(C, a, js_num(_get(C, a)) + 1)
|
||||
elif op == 80:
|
||||
a = o[u + 1]; imm = o[u + 2]; u += 2
|
||||
_set(C, a, imm)
|
||||
elif op == 81:
|
||||
a = o[u + 1]; imm = o[u + 2]; b = o[u + 3]; u += 3
|
||||
_set(C, a, imm - js_num(_get(C, b)))
|
||||
elif op == 82:
|
||||
a = o[u + 1]; b = o[u + 2]; c = o[u + 3]; u += 3
|
||||
_set(C, a, _cmp_gt(js_num(_get(C, b)), js_num(_get(C, c))))
|
||||
elif op == 83:
|
||||
a = o[u + 1]; b = o[u + 2]; imm = o[u + 3]; u += 3
|
||||
_set(C, a, ushr(js_num(_get(C, b)), imm))
|
||||
elif op == 84:
|
||||
a = o[u + 1]; b = o[u + 2]; c = o[u + 3]; u += 3
|
||||
_set(C, a, js_call(_get(C, b), _get(C, c), []))
|
||||
elif op == 85:
|
||||
a = o[u + 1]; imm1 = o[u + 2]; b = o[u + 3]; u += 3
|
||||
js_set(_get(C, a), imm1, _get(C, b))
|
||||
c = o[u + 1]; imm2 = o[u + 2]; dd = o[u + 3]; u += 3
|
||||
js_set(_get(C, c), imm2, _get(C, dd))
|
||||
e = o[u + 1]; u += 1
|
||||
_set(C, e, "")
|
||||
elif op == 86:
|
||||
a = o[u + 1]; b = o[u + 2]; u += 2
|
||||
h = _get(C, a)
|
||||
if h is not UNDEF and h is not None and len(h):
|
||||
_set(C, b, True)
|
||||
c = o[u + 1]; u += 1
|
||||
_set(C, c, h.pop(0))
|
||||
else:
|
||||
_set(C, b, False)
|
||||
u += 1
|
||||
elif op == 87:
|
||||
a = o[u + 1]; b = o[u + 2]; imm = o[u + 3]; u += 3
|
||||
_set(C, a, _get(_get(C, b), imm))
|
||||
elif op == 88:
|
||||
a = o[u + 1]; u += 1
|
||||
_set(C, a, js_num(_get(C, a)) - 1)
|
||||
elif op == 89:
|
||||
a = o[u + 1]; b = o[u + 2]; c = o[u + 3]; u += 3
|
||||
_set(C, a, shl(js_num(_get(C, b)), js_num(_get(C, c))))
|
||||
elif op == 90:
|
||||
a = o[u + 1]; b = o[u + 2]; c = o[u + 3]; u += 3
|
||||
_set(C, a, js_eq(_get(C, b), _get(C, c)))
|
||||
elif op == 91:
|
||||
a = o[u + 1]; b = o[u + 2]; c = o[u + 3]; u += 3
|
||||
_set(C, a, js_new(_get(C, b), [_get(C, c)]))
|
||||
elif op == 92:
|
||||
a = o[u + 1]; b = o[u + 2]; imm = o[u + 3]; u += 3
|
||||
_set(C, a, _cmp_le(js_num(_get(C, b)), imm))
|
||||
elif op == 93:
|
||||
a = o[u + 1]; b = o[u + 2]; c = o[u + 3]; u += 3
|
||||
_set(C, a, js_num(_get(C, b)) % js_num(_get(C, c)))
|
||||
elif op == 94:
|
||||
a = o[u + 1]; b = o[u + 2]; imm = o[u + 3]; u += 3
|
||||
_set(C, a, i32(_ic(_get(C, b))) ^ imm)
|
||||
elif op == 95:
|
||||
a = o[u + 1]; b = o[u + 2]; c = o[u + 3]; u += 3
|
||||
_set(C, a, js_new(_get(C, b), [_get(C, c)]))
|
||||
dd = o[u + 1]; e = o[u + 2]; u += 2
|
||||
_set(C, dd, _get(C, e))
|
||||
f = o[u + 1]; imm = o[u + 2]; u += 2
|
||||
_set(C, f, imm)
|
||||
elif op == 96:
|
||||
a = o[u + 1]; b = o[u + 2]; c = o[u + 3]; u += 3
|
||||
obj2 = _get(C, c)
|
||||
_set(C, a, _get(C, b) in obj2 if isinstance(obj2, (list, dict, JSObject)) else False)
|
||||
elif op == 97:
|
||||
a = o[u + 1]; b = o[u + 2]; u += 2
|
||||
_set(C, a, not js_truthy(_get(C, b)))
|
||||
elif op == 98:
|
||||
a = o[u + 1]; b = o[u + 2]; imm = o[u + 3]; u += 3
|
||||
_set(C, a, _cmp_ge(js_num(_get(C, b)), imm))
|
||||
elif op == 99:
|
||||
a = o[u + 1]; b = o[u + 2]; c = o[u + 3]; u += 3
|
||||
_set(C, a, js_add(_get(C, b), _get(C, c)))
|
||||
elif op == 100:
|
||||
a = o[u + 1]; b = o[u + 2]; imm = o[u + 3]; u += 3
|
||||
_set(C, a, _get(C, b) == imm)
|
||||
elif op == 101:
|
||||
a = o[u + 1]; b = o[u + 2]; c = o[u + 3]; u += 3
|
||||
_set(C, a, js_eq(_get(C, b), _get(C, c)))
|
||||
elif op == 102:
|
||||
a = o[u + 1]; b = o[u + 2]; u += 2
|
||||
_set(C, a, _get(C, b))
|
||||
elif op == 103:
|
||||
a = o[u + 1]; b = o[u + 2]; imm1 = o[u + 3]; u += 3
|
||||
_set(C, a, _get(_get(C, b), imm1))
|
||||
c = o[u + 1]; dd = o[u + 2]; u += 2
|
||||
_set(C, c, _get(C, dd))
|
||||
e = o[u + 1]; f = o[u + 2]; imm2 = o[u + 3]; u += 3
|
||||
_set(C, e, _get(_get(C, f), imm2))
|
||||
elif op == 104:
|
||||
a = o[u + 1]; b = o[u + 2]; u += 2
|
||||
_set(C, a, js_num(_get(C, b)))
|
||||
elif op == 105:
|
||||
a = o[u + 1]; b = o[u + 2]; c = o[u + 3]; u += 3
|
||||
v = _get(_get(C, b), _get(C, c))
|
||||
_set(C, a, v)
|
||||
dd = o[u + 1]; e = o[u + 2]; f = o[u + 3]; u += 3
|
||||
js_set(_get(C, dd), _get(C, e), _get(C, f))
|
||||
u0 = u
|
||||
u = u0 + o[u0 + 1]
|
||||
elif op == 106:
|
||||
a = o[u + 1]; b = o[u + 2]; u += 2
|
||||
_set(C, a, ~i32(_ic(_get(C, b))))
|
||||
elif op == 107:
|
||||
f = o[u + 1]; u += 1
|
||||
h = []
|
||||
for _ in range(f):
|
||||
h.append(_get(C, o[u + 1])); u += 1
|
||||
a = o[u + 1]; b = o[u + 2]; u += 2
|
||||
_set(C, a, js_apply(_get(C, b), p, h))
|
||||
else:
|
||||
raise RuntimeError(f"未知 opcode {op} @u={u}")
|
||||
|
||||
except _VMThrow as ex:
|
||||
t = ex.value
|
||||
if d:
|
||||
u = d.pop()
|
||||
continue
|
||||
if l is not UNDEF and l is not None:
|
||||
return js_call(l, UNDEF, [t, C, []])
|
||||
raise
|
||||
except Exception:
|
||||
if d:
|
||||
u = d.pop()
|
||||
continue
|
||||
raise
|
||||
|
||||
|
||||
class _VMThrow(Exception):
|
||||
def __init__(self, value):
|
||||
super().__init__("vm throw")
|
||||
self.value = value
|
||||
|
||||
|
||||
# ---------------------------------------------------------------- 便捷入口
|
||||
|
||||
def _mk_window(vmlog=None):
|
||||
from .algorithm import 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", HostFunction(lambda this, *a: None, "XHR"))
|
||||
w.set("fetch", HostFunction(lambda this, *a: None, "fetch"))
|
||||
return w
|
||||
|
||||
|
||||
def run_frame(entry, h, bytecode, constants, window=None, call_args=(), random_seed=None):
|
||||
"""便捷入口:用 pagedoo VM 执行指定 entry 的帧(h 为创建参数数组)。"""
|
||||
if window is None:
|
||||
window = _mk_window()
|
||||
vm = PagedooVM(bytecode, constants, window, random_seed=random_seed)
|
||||
# 挂宿主
|
||||
for k, v in vm._hosts.items():
|
||||
if k != "Array.prototype" and window.get(k) is UNDEF:
|
||||
window.set(k, v)
|
||||
frame = vm.make(entry, h, window, constants, None)
|
||||
return vm.run(frame, list(call_args)), vm
|
||||
@@ -0,0 +1,91 @@
|
||||
"""会话态模型:web_new_encrypt 离线生成所需的会话级输入。
|
||||
|
||||
F-2049/F-2051(E3):encrypt_msg 与当前会话绑定——
|
||||
- xMidasOps: goods.shtml 页面内嵌伪随机表(59640 值,服务端每次生成,页面级)
|
||||
- key16: goodsBiz VM 加载期 Math.random 前 16 次调用(页面级恒定)
|
||||
- key1: 诱饵密钥(点击级,捕获即可)
|
||||
服务端能验证 key 派生状态(随机 key 变体 ret:1099),因此新订单必须先捕获会话态。
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from dataclasses import dataclass, field
|
||||
from pathlib import Path
|
||||
|
||||
DEFAULT_XMIDAS_TOKEN = "DE46DBA4754D42A6B66ADD4319FF80C144D9ED22ED73384C271793D9A9AA2FFBD6C104BE8D4A7F4ED2A8688FCB6F7540"
|
||||
|
||||
|
||||
@dataclass
|
||||
class SessionState:
|
||||
"""一次 goods 页加载捕获的会话态。
|
||||
|
||||
xmidas_ops: list[int] 59640 项(页面级)
|
||||
key16: list[int] 16 字节(VM 加载期 Math.random 前 16 次)
|
||||
key1: list[int] 16 字节(诱饵)
|
||||
xmidas_token: str
|
||||
cookies: dict[str, str](提交 web_save 用,可选)
|
||||
openid/openkey: str(提交 web_save 用,可选)
|
||||
"""
|
||||
|
||||
xmidas_ops: list[int] = field(default_factory=list)
|
||||
key16: list[int] = field(default_factory=list)
|
||||
key1: list[int] = field(default_factory=list)
|
||||
xmidas_token: str = DEFAULT_XMIDAS_TOKEN
|
||||
args_template_d: str = "" # webSave 18 参深拷贝(deepcap PC 85091 的 C[2] 原始 D 编码,会话绑定)
|
||||
cookies: dict[str, str] = field(default_factory=dict)
|
||||
openid: str = ""
|
||||
openkey: str = ""
|
||||
source: str = "" # 捕获来源说明
|
||||
|
||||
def validate(self) -> list[str]:
|
||||
errs: list[str] = []
|
||||
if len(self.xmidas_ops) != 59640:
|
||||
errs.append(f"xmidas_ops 长度 {len(self.xmidas_ops)} != 59640")
|
||||
if len(self.key16) != 16:
|
||||
errs.append(f"key16 长度 {len(self.key16)} != 16")
|
||||
if len(self.key1) != 16:
|
||||
errs.append(f"key1 长度 {len(self.key1)} != 16")
|
||||
if not self.xmidas_token:
|
||||
errs.append("xmidas_token 为空")
|
||||
if not self.args_template_d:
|
||||
errs.append("args_template_d 为空(需 deepcap PC 85091 的 C[2] 原始 D 编码)")
|
||||
return errs
|
||||
|
||||
def to_json(self) -> dict:
|
||||
return {
|
||||
"xmidas_ops": self.xmidas_ops,
|
||||
"key16": self.key16,
|
||||
"key1": self.key1,
|
||||
"xmidas_token": self.xmidas_token,
|
||||
"args_template_d": self.args_template_d,
|
||||
"cookies": self.cookies,
|
||||
"openid": self.openid,
|
||||
"openkey": self.openkey,
|
||||
"source": self.source,
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, d: dict) -> "SessionState":
|
||||
return cls(
|
||||
xmidas_ops=list(d.get("xmidas_ops", [])),
|
||||
key16=list(d.get("key16", [])),
|
||||
key1=list(d.get("key1", [])),
|
||||
xmidas_token=d.get("xmidas_token", DEFAULT_XMIDAS_TOKEN),
|
||||
args_template_d=d.get("args_template_d", ""),
|
||||
cookies=dict(d.get("cookies", {})),
|
||||
openid=d.get("openid", ""),
|
||||
openkey=d.get("openkey", ""),
|
||||
source=d.get("source", ""),
|
||||
)
|
||||
|
||||
|
||||
def load_session(path: str | Path) -> SessionState:
|
||||
"""从 JSON 加载会话态并校验。"""
|
||||
p = Path(path)
|
||||
if not p.exists():
|
||||
raise FileNotFoundError(f"会话态文件不存在: {p}")
|
||||
st = SessionState.from_json(json.loads(p.read_text(encoding="utf-8")))
|
||||
errs = st.validate()
|
||||
if errs:
|
||||
raise ValueError("会话态校验失败:\n " + "\n ".join(errs))
|
||||
return st
|
||||
Reference in New Issue
Block a user