306 lines
9.1 KiB
Python
306 lines
9.1 KiB
Python
from __future__ import annotations
|
||
|
||
import json
|
||
import zlib
|
||
|
||
from .device_profile import DeviceProfile
|
||
|
||
|
||
KWE_N = "KWE_N"
|
||
KWE_NPN = "KWE_NPN"
|
||
LITE_DFP_VERSION = "9.5.4lite.79.137a838b"
|
||
|
||
# 运营商 -> MCC-MNC(从 profile.isp 派生,不再硬编码)
|
||
_ISP_MCC_MNC = {"CUCC": "46001", "CTCC": "46011", "CMCC": "46000"}
|
||
|
||
LITE_KEYS = [
|
||
"k5",
|
||
"k14",
|
||
"k22",
|
||
"k23",
|
||
"k27",
|
||
"k29",
|
||
"k31",
|
||
"k34",
|
||
"k35",
|
||
"k36",
|
||
"k39",
|
||
"k40",
|
||
"k46",
|
||
"k57",
|
||
"k61",
|
||
"k64",
|
||
"k66",
|
||
"k68",
|
||
"k83",
|
||
"k86",
|
||
"k93",
|
||
"k97",
|
||
"k101",
|
||
"k102",
|
||
"k105",
|
||
"k106",
|
||
"k107",
|
||
"k108",
|
||
"k109",
|
||
"k110",
|
||
"k111",
|
||
"k112",
|
||
"k113",
|
||
]
|
||
|
||
FULL_KEYS = [f"k{index}" for index in range(1, 120)]
|
||
|
||
FULL_DEFAULTS = {
|
||
**{key: KWE_NPN for key in ("k2", "k9", "k12", "k13", "k18", "k21", "k33", "k41", "k43")},
|
||
**{key: KWE_NPN for key in ("k55", "k57", "k62", "k65", "k76", "k79", "k80", "k81")},
|
||
**{key: KWE_NPN for key in ("k54", "k68", "k71", "k73", "k74", "k75", "k77", "k82", "k88")},
|
||
**{key: KWE_NPN for key in ("k106", "k114", "k115", "k116", "k117", "k118")},
|
||
**{key: KWE_N for key in ("k24", "k31", "k53", "k70", "k85", "k86", "k87")},
|
||
**{key: KWE_N for key in ("k90", "k91", "k94", "k96", "k101", "k103", "k104")},
|
||
"k63": "root",
|
||
"k95": "KWE_NS",
|
||
}
|
||
|
||
|
||
def _android_suffix(value: str) -> str:
|
||
return value.removeprefix("ANDROID_")
|
||
|
||
|
||
def _text(value: object, default: str = KWE_N) -> str:
|
||
if value is None:
|
||
return default
|
||
text = str(value)
|
||
return text if text != "" else default
|
||
|
||
|
||
def _mcc_mnc(profile: DeviceProfile) -> str:
|
||
return {
|
||
"CUCC": "46011",
|
||
"CTCC": "46003",
|
||
"CMCC": "46000",
|
||
}.get(profile.isp.upper(), KWE_NPN)
|
||
|
||
|
||
def _k93_json(profile: DeviceProfile) -> str:
|
||
return json.dumps(
|
||
{
|
||
"0": 1,
|
||
"1": "7",
|
||
"2": "true",
|
||
"4": KWE_N,
|
||
"8": "zz abcdabcdabcdabcdabcd",
|
||
"11": profile.model,
|
||
"12": KWE_N,
|
||
"19": "100",
|
||
"20": profile.install_time_ms,
|
||
"23": KWE_N,
|
||
"24": KWE_N,
|
||
"25": f"{profile.runtime_hints.wifi_mac or '02:00:00:00:00:00'}@{profile.install_time_ms}@{profile.model}@CTExcel@{_ISP_MCC_MNC.get(profile.isp.upper(), '46001')}",
|
||
"30": {
|
||
"op_old": profile.runtime_hints.oaid or KWE_N,
|
||
"cost_old": 5,
|
||
"cost_new": 22,
|
||
},
|
||
"27": True,
|
||
"28": profile.g_rdi2,
|
||
},
|
||
ensure_ascii=False,
|
||
separators=(",", ":"),
|
||
)
|
||
|
||
|
||
def _k93_lite_json(profile: DeviceProfile) -> str:
|
||
return json.dumps(
|
||
{
|
||
"19": "100",
|
||
"20": profile.install_time_ms,
|
||
"23": KWE_N,
|
||
"24": KWE_N,
|
||
"25": f"{profile.runtime_hints.wifi_mac or '02:00:00:00:00:00'}@{profile.install_time_ms}@{profile.model}@CTExcel@{_ISP_MCC_MNC.get(profile.isp.upper(), '46001')}",
|
||
"30": {
|
||
"op_old": profile.runtime_hints.oaid or KWE_N,
|
||
"cost_old": 6,
|
||
"cost_new": 23,
|
||
},
|
||
"27": True,
|
||
"28": profile.g_rdi2,
|
||
},
|
||
ensure_ascii=False,
|
||
separators=(",", ":"),
|
||
)
|
||
|
||
|
||
def _dalvik_vm(profile: DeviceProfile) -> str:
|
||
return (
|
||
f"Dalvik/2.1.0 (Linux; U; Android {profile.android_release}; "
|
||
f"{profile.model} Build/{profile.build_id})"
|
||
)
|
||
|
||
|
||
def _build_fingerprint(profile: DeviceProfile) -> str:
|
||
if profile.build_fingerprint:
|
||
return profile.build_fingerprint
|
||
product = profile.build_product or profile.model
|
||
return (
|
||
f"{profile.manufacturer}/{profile.model}/{product}:"
|
||
f"{profile.android_release}/{profile.build_id}/{profile.build_display}:"
|
||
f"{profile.build_type}/{profile.build_tags}"
|
||
)
|
||
|
||
|
||
def recompute_k14_crc(values: dict[str, str], ordered_keys: list[str]) -> str:
|
||
crc = 0
|
||
for key in ordered_keys:
|
||
raw = b"AND" if key == "k14" else str(values.get(key, "")).encode("utf-8")
|
||
crc = zlib.crc32(raw, crc)
|
||
return f"AND:{crc & 0xFFFFFFFF}"
|
||
|
||
|
||
def build_lite_knn(
|
||
profile: DeviceProfile,
|
||
overrides: dict[str, str] | None = None,
|
||
) -> dict[str, str]:
|
||
hints = profile.runtime_hints
|
||
values = {key: KWE_N for key in LITE_KEYS}
|
||
values.update(
|
||
{
|
||
"k5": str(profile.install_time_ms),
|
||
"k14": "AND",
|
||
"k22": profile.app_version,
|
||
"k23": profile.manufacturer,
|
||
"k27": profile.model,
|
||
"k29": _dalvik_vm(profile),
|
||
"k31": KWE_N,
|
||
"k34": profile.screen_metrics,
|
||
"k35": profile.android_release,
|
||
"k36": LITE_DFP_VERSION,
|
||
"k39": hints.did_gt or str(profile.install_time_ms),
|
||
"k40": _build_fingerprint(profile),
|
||
"k46": hints.total_memory_bytes or str(profile.total_memory_mb * 1024 * 1024),
|
||
"k57": KWE_NPN,
|
||
"k61": profile.brand,
|
||
"k66": _android_suffix(profile.o_did),
|
||
"k68": KWE_NPN,
|
||
"k83": profile.egid or "KWE_N",
|
||
"k93": _k93_lite_json(profile),
|
||
"k97": hints.oaid,
|
||
"k101": hints.res_soc,
|
||
"k102": hints.boot_id,
|
||
"k105": hints.grdi,
|
||
"k106": KWE_NPN,
|
||
"k107": str(profile.cdid_tag),
|
||
"k108": hints.ipv6_map,
|
||
"k109": hints.lpss,
|
||
"k110": hints.keeper_seed,
|
||
"k111": hints.du,
|
||
"k112": hints.sted_cache_json or "KWE_N",
|
||
"k113": hints.manus,
|
||
}
|
||
)
|
||
if overrides:
|
||
for key, value in overrides.items():
|
||
if key not in values:
|
||
raise KeyError(key)
|
||
values[key] = "" if value is None else str(value)
|
||
values["k14"] = recompute_k14_crc(values, LITE_KEYS)
|
||
return {key: _text(values.get(key), default="") for key in LITE_KEYS}
|
||
|
||
|
||
def build_full_knn(
|
||
profile: DeviceProfile,
|
||
overrides: dict[str, str] | None = None,
|
||
) -> dict[str, str]:
|
||
hints = profile.runtime_hints
|
||
values = {key: FULL_DEFAULTS.get(key, KWE_N) for key in FULL_KEYS}
|
||
values.update(
|
||
{
|
||
"k1": "isContent",
|
||
"k3": profile.package_name,
|
||
"k4": hints.k4_native,
|
||
"k5": str(profile.install_time_ms),
|
||
"k6": "0",
|
||
"k7": profile.did,
|
||
"k8": profile.build_type,
|
||
"k10": "0",
|
||
"k11": "0",
|
||
"k14": "AND",
|
||
"k15": "0",
|
||
"k16": "kvm-slave-build-s-system-11410184",
|
||
"k17": "192.168.8.34",
|
||
"k19": "sun",
|
||
"k20": str(hints.storage_available_bytes),
|
||
"k22": profile.app_version,
|
||
"k23": profile.manufacturer,
|
||
"k25": "1",
|
||
"k26": "arm64-v8a,",
|
||
"k27": profile.model,
|
||
"k28": "qcom",
|
||
"k29": _dalvik_vm(profile),
|
||
"k30": profile.build_display,
|
||
"k32": "[GMT+08:00,Asia/Shanghai]",
|
||
"k34": profile.screen_metrics,
|
||
"k35": profile.android_release,
|
||
"k36": LITE_DFP_VERSION,
|
||
"k37": profile.build_id,
|
||
"k38": "91",
|
||
"k39": hints.did_gt or str(profile.install_time_ms),
|
||
"k40": _build_fingerprint(profile),
|
||
"k42": "[5,100]",
|
||
"k44": profile.build_tags,
|
||
"k45": "isContent",
|
||
"k46": hints.total_memory_bytes or str(profile.total_memory_mb * 1024 * 1024),
|
||
"k47": "unknown",
|
||
"k48": "isContent",
|
||
"k49": "0",
|
||
"k50": "notExist",
|
||
"k51": hints.k51_native,
|
||
"k52": profile.model,
|
||
"k56": "notExist",
|
||
"k58": profile.build_product or hints.build_product or profile.model,
|
||
"k59": "1",
|
||
"k60": "unknown",
|
||
"k61": profile.brand,
|
||
"k66": _android_suffix(profile.o_did),
|
||
"k67": "gb",
|
||
"k69": "1",
|
||
"k72": profile.country_code,
|
||
"k78": _mcc_mnc(profile),
|
||
"k83": profile.egid or "KWE_FIRST",
|
||
"k84": hints.k84_native,
|
||
"k89": "160816899",
|
||
"k92": str(profile.cold_launch_time_ms),
|
||
"k93": _k93_json(profile),
|
||
"k97": hints.oaid,
|
||
"k101": hints.res_soc,
|
||
"k102": hints.boot_id,
|
||
"k105": hints.grdi,
|
||
"k107": str(profile.cdid_tag),
|
||
"k108": hints.ipv6_map,
|
||
"k109": hints.lpss,
|
||
"k110": hints.keeper_seed,
|
||
"k111": hints.du,
|
||
"k112": hints.sted_cache_json,
|
||
"k113": hints.manus,
|
||
"k119": hints.gaid,
|
||
}
|
||
)
|
||
if overrides:
|
||
for key, value in overrides.items():
|
||
if key not in values:
|
||
raise KeyError(key)
|
||
values[key] = "" if value is None else str(value)
|
||
values["k14"] = recompute_k14_crc(values, FULL_KEYS)
|
||
return {key: _text(values.get(key), default="") for key in FULL_KEYS}
|
||
|
||
|
||
__all__ = [
|
||
"FULL_KEYS",
|
||
"KWE_N",
|
||
"LITE_DFP_VERSION",
|
||
"LITE_KEYS",
|
||
"build_full_knn",
|
||
"build_lite_knn",
|
||
"recompute_k14_crc",
|
||
]
|