183 lines
5.5 KiB
Python
183 lines
5.5 KiB
Python
"""纯 Python 复现 ``new mf(context, event).a()`` 的设备状态 JSON。"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import hashlib
|
||
import json
|
||
import time
|
||
from dataclasses import dataclass
|
||
from typing import Any, Mapping
|
||
|
||
from .device_profile import DeviceProfile
|
||
from .weapon_vimg import generate_passport_account_image
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class WeaponRuntimeSnapshot:
|
||
"""一次 WeaponHI 上报使用的易变 Android 运行时状态。"""
|
||
|
||
now_ms: int
|
||
elapsed_realtime_ms: int
|
||
uptime_ms: int
|
||
boot_count: int
|
||
report_counter: int = 1
|
||
|
||
def __post_init__(self) -> None:
|
||
if self.now_ms < 0:
|
||
raise ValueError("now_ms must not be negative")
|
||
if self.elapsed_realtime_ms < 0:
|
||
raise ValueError("elapsed_realtime_ms must not be negative")
|
||
if not 0 <= self.uptime_ms <= self.elapsed_realtime_ms:
|
||
raise ValueError("uptime_ms must be between 0 and elapsed_realtime_ms")
|
||
if self.boot_count < 0 or self.report_counter < 1:
|
||
raise ValueError("boot_count/report_counter out of range")
|
||
|
||
@classmethod
|
||
def fresh(
|
||
cls,
|
||
profile: DeviceProfile,
|
||
*,
|
||
now_ms: int | None = None,
|
||
report_counter: int = 1,
|
||
) -> "WeaponRuntimeSnapshot":
|
||
current_ms = int(time.time() * 1000) if now_ms is None else int(now_ms)
|
||
seed = _profile_seed(profile)
|
||
boot_age_ms = 6 * 60 * 60 * 1000 + seed % (5 * 24 * 60 * 60 * 1000)
|
||
sleep_ms = (seed >> 17) % max(1, boot_age_ms // 3)
|
||
return cls(
|
||
now_ms=current_ms,
|
||
elapsed_realtime_ms=boot_age_ms,
|
||
uptime_ms=boot_age_ms - sleep_ms,
|
||
boot_count=1 + ((seed >> 29) % 180),
|
||
report_counter=report_counter,
|
||
)
|
||
|
||
|
||
def _profile_seed(profile: DeviceProfile) -> int:
|
||
material = "|".join(
|
||
(
|
||
profile.android_id,
|
||
profile.local_did,
|
||
profile.sid,
|
||
str(profile.install_time_ms),
|
||
)
|
||
)
|
||
return int.from_bytes(hashlib.sha256(material.encode("utf-8")).digest()[:8], "big")
|
||
|
||
|
||
def _installation_id(profile: DeviceProfile) -> str:
|
||
source = profile.sid.replace("-", "")
|
||
if len(source) < 16:
|
||
source = hashlib.sha256(profile.android_id.encode("ascii")).hexdigest()
|
||
random_half = "".join(source[index * 2] for index in range(8))
|
||
created_ms = profile.install_time_ms or profile.cold_launch_time_ms
|
||
time_half = f"{created_ms:x}"[-8:].zfill(8)
|
||
return f"a_{random_half}{time_half}"
|
||
|
||
|
||
def build_mf_payload(
|
||
profile: DeviceProfile,
|
||
*,
|
||
event: int = 1,
|
||
snapshot: WeaponRuntimeSnapshot | None = None,
|
||
field_overrides: Mapping[str, Any] | None = None,
|
||
) -> dict[str, Any]:
|
||
"""按 ``mf.a()`` 的插入顺序构建设备状态对象。"""
|
||
|
||
runtime = snapshot or WeaponRuntimeSnapshot.fresh(profile)
|
||
seed = _profile_seed(profile)
|
||
battery_level = 40 + seed % 61
|
||
storage_bytes = profile.runtime_hints.storage_available_bytes or 491_765_592_064
|
||
boot_epoch_seconds = (runtime.now_ms - runtime.elapsed_realtime_ms) // 1000
|
||
|
||
payload: dict[str, Any] = {
|
||
"0": 1,
|
||
"3": 1,
|
||
"8": 0,
|
||
"24": 0,
|
||
"35": 0,
|
||
"30": 0,
|
||
"31": 0,
|
||
"65": 0,
|
||
"66": 0,
|
||
"68": 0,
|
||
"101": 0,
|
||
"102": 0,
|
||
"1021": _installation_id(profile),
|
||
"6": "0",
|
||
"47": "0",
|
||
"48": "0",
|
||
"37": "0",
|
||
"38": "0",
|
||
"45": "0",
|
||
"91": "0",
|
||
"54": "0",
|
||
"55": "0",
|
||
"79": "0",
|
||
"80": "1",
|
||
"83": '["{\\"1\\":1}","{\\"2\\":1}"]',
|
||
"87": "0",
|
||
"89": "0",
|
||
"75": "0",
|
||
"88": "0",
|
||
"92": "0",
|
||
"98": "0",
|
||
"100": "0",
|
||
"02001": profile.manufacturer,
|
||
"02002": profile.manufacturer,
|
||
"02003": profile.model,
|
||
"02008": profile.build_display,
|
||
"02016": profile.android_release,
|
||
"03014": True,
|
||
"03113": "1970-01-01",
|
||
"07025": "0",
|
||
"03020": "USB charger",
|
||
"03033": False,
|
||
"03043": runtime.elapsed_realtime_ms,
|
||
"03044": runtime.uptime_ms,
|
||
"03045": boot_epoch_seconds,
|
||
"03085": str(runtime.boot_count),
|
||
"03086": str(runtime.boot_count),
|
||
"02029": f"{280 + seed % 40}.{(seed >> 8) % 100:02d}",
|
||
"03128": str(storage_bytes),
|
||
"03030": 20 + ((seed >> 16) % 100),
|
||
"03006": (seed >> 24) % 8,
|
||
"03007": f"{battery_level}%",
|
||
"03015": "0",
|
||
"03115": (seed >> 7) % 1_000_000_000,
|
||
# 主 APP 在云 DID 更新后调用 WeaponHI.setG,mf 的 ne.k() 读取当前 DID。
|
||
"03000": profile.did,
|
||
"20000": int(event),
|
||
"11113": 0,
|
||
"11111": runtime.now_ms // 1000,
|
||
"11112": runtime.report_counter,
|
||
"07069": 0,
|
||
"07070": 0,
|
||
}
|
||
if field_overrides:
|
||
for key, value in field_overrides.items():
|
||
payload[str(key)] = value
|
||
return payload
|
||
|
||
|
||
def serialize_mf_payload(payload: Mapping[str, Any]) -> str:
|
||
"""复现 Android ``JSONObject.toString()`` 的紧凑 JSON。"""
|
||
|
||
return json.dumps(payload, ensure_ascii=False, separators=(",", ":"))
|
||
|
||
|
||
def generate_profile_passport_account_image(
|
||
profile: DeviceProfile,
|
||
*,
|
||
event: int = 1,
|
||
snapshot: WeaponRuntimeSnapshot | None = None,
|
||
field_overrides: Mapping[str, Any] | None = None,
|
||
) -> str:
|
||
payload = build_mf_payload(
|
||
profile,
|
||
event=event,
|
||
snapshot=snapshot,
|
||
field_overrides=field_overrides,
|
||
)
|
||
return generate_passport_account_image(serialize_mf_payload(payload))
|