"""Semantic model for libksse command 1114139 / EngineProxy.sted. This is not a full native allocator port. It captures the recovered behavior that matters for the EGID/cache_m path: * the decoded string table used by FUN_00142bf4; * sentinel file names written by FUN_00140ea8 and read by FUN_00142bf4; * suffix recovery polarity: existing path selects the candidate character; * product-keyed JSON returned by FUN_00143d98. """ from __future__ import annotations import json import hashlib import time from dataclasses import dataclass from typing import Callable, Iterable from .ksse_crc import ksse_crc32 KSSE_DFP_TABLE_RAW = ( "0123456789ABCDEF@/.Android_@KUAISHOU@a21c40ada1eb475b@" "DFP@/sdcard/Android@NEBULA" ) KSSE_DOCUMENTS_DIR = "/sdcard/Documents" STED_CACHE_FILE_TOKEN = "LnNrdmVj" STED_CACHE_FILE_NAME = ".skvec" STED_SHARED_PREF_KEY = "kwtk_n" DFP_SUFFIX_LEN = 0x3D class KsseSentinelMissing(ValueError): """Raised when the sentinel set cannot recover a full DFP suffix.""" @dataclass(frozen=True) class KsseDfpStringTable: alphabet: str android_hidden_dir_suffix: str default_product: str static_salt: str dfp_prefix: str sdcard_android_dir: str active_product: str @classmethod def parse(cls, raw: str = KSSE_DFP_TABLE_RAW) -> "KsseDfpStringTable": parts = raw.split("@") if len(parts) != 7: raise ValueError(f"unexpected libksse DFP table entry count: {len(parts)}") return cls( alphabet=parts[0], android_hidden_dir_suffix=parts[1], default_product=parts[2], static_salt=parts[3], dfp_prefix=parts[4], sdcard_android_dir=parts[5], active_product=parts[6], ) @dataclass(frozen=True) class StedJsonInsertionPlan: """One product-keyed JSON insertion attempt in ``FUN_00143d98``.""" output_key: str stage: str candidate_paths: list[str] guard: str @dataclass(frozen=True) class StedPersistenceArtifacts: """Java/native EGID persistence artifacts after ``rq0.d.e(cache_e, cache_m)``.""" cache_json: str in_memory_cache: dict[str, str] shared_preferences: dict[str, str] app_private_files: dict[str, str] product_marker: str native_base_path: str native_sentinel_paths: list[str] native_readback_json: str def normalize_product_marker(marker: str | None, fallback: str = "NEBULA") -> str: """Convert native marker like ``0NEBULA`` / ``1KWE_N`` to product key.""" if not marker: return fallback marker = str(marker) if marker[:1] in {"0", "1"}: marker = marker[1:] return marker or fallback def ksse_md5_hex16(data: str | bytes) -> str: """Return native ``FUN_001575d0`` + ``FUN_00151204(..., 8)`` output. ``FUN_00151204`` uses the lowercase nibble table at ``DAT_0015cd1f`` (`0123456789abcdef...`), and top-level callers pass only the first 8 digest bytes, so the Python equivalent is the first 16 lowercase hexadecimal characters of MD5. """ if isinstance(data, str): data = data.encode("utf-8") return hashlib.md5(data).hexdigest()[:16] def engine_sted_product_marker(product: str = "NEBULA", writable_external_storage: bool = False) -> str: """Return EngineProxy.sted's native marker argument. Java builds this as ``("1" if z else "0") + productName`` before calling ``Watermelon.jniCommand(1114139, "", str, marker)``. On targetSdk >= 30 the rq0.d.j() caller passes ``z=false``; legacy external-storage paths can pass ``z=true`` when READ/WRITE external storage permission is available. """ product = normalize_product_marker(product) return ("1" if writable_external_storage else "0") + product def build_sted_cache_json(cache_e: str, cache_m: str, c_time_ms: int) -> str: """Build the compact JSONObject written by ``rq0.d.e``. Native Java insertion order is ``c_time`` -> ``cache_e`` -> ``cache_m``. The same text is written to SharedPreferences ``kwtk_n`` and app-private files ``.skvec``. """ return json.dumps( { "c_time": int(c_time_ms), "cache_e": str(cache_e), "cache_m": str(cache_m), }, ensure_ascii=False, separators=(",", ":"), ) def hidden_cache_path(base_path: str, suffix: str, table: KsseDfpStringTable | None = None) -> str: """Return ``base + '/.Android_' + suffix`` as constructed by native code.""" table = table or KsseDfpStringTable.parse() return f"{str(base_path).rstrip('/')}{table.android_hidden_dir_suffix}{suffix}" def sted_external_candidate_paths( product_marker: str | None, *, table: KsseDfpStringTable | None = None, documents_dir: str = KSSE_DOCUMENTS_DIR, ) -> list[str]: """First ``FUN_00143d98`` candidate pair: Documents then Android. Native initializes the primary base from the decoded ``/sdcard/Documents`` constant and the fallback from table slot ``0x78`` (``/sdcard/Android``). For ``KUAISHOU`` it appends the static salt; every other product key uses ``md5(product_key)[:16]``. """ table = table or KsseDfpStringTable.parse() product = normalize_product_marker(product_marker, table.active_product) suffix = table.static_salt if product == table.default_product else ksse_md5_hex16(product) return [ hidden_cache_path(documents_dir, suffix, table), hidden_cache_path(table.sdcard_android_dir, suffix, table), ] def sted_product_salt_candidate_paths( *, table: KsseDfpStringTable | None = None, documents_dir: str = KSSE_DOCUMENTS_DIR, ) -> list[str]: """Candidate pair using the static table salt ``a21c40ada1eb475b``.""" table = table or KsseDfpStringTable.parse() return [ hidden_cache_path(documents_dir, table.static_salt, table), hidden_cache_path(table.sdcard_android_dir, table.static_salt, table), ] def sted_hidden_md5_candidate_paths( *, table: KsseDfpStringTable | None = None, documents_dir: str = KSSE_DOCUMENTS_DIR, active_product: str | None = None, ) -> list[str]: """Candidate pair using ``md5(table[0x90])[:16]``. Static flow hashes the active table product (currently ``NEBULA``), not the dynamic marker passed in ``param_5``. """ table = table or KsseDfpStringTable.parse() product = active_product or table.active_product suffix = ksse_md5_hex16(product) return [ hidden_cache_path(documents_dir, suffix, table), hidden_cache_path(table.sdcard_android_dir, suffix, table), ] def sted_candidate_path_families( product_marker: str | None, *, table: KsseDfpStringTable | None = None, documents_dir: str = KSSE_DOCUMENTS_DIR, ) -> dict[str, list[str]]: """Return the ordered path families currently recovered from ``sted``.""" table = table or KsseDfpStringTable.parse() return { "external": sted_external_candidate_paths( product_marker, table=table, documents_dir=documents_dir, ), "product_salt": sted_product_salt_candidate_paths( table=table, documents_dir=documents_dir, ), "hidden_md5": sted_hidden_md5_candidate_paths( table=table, documents_dir=documents_dir, ), } def sted_json_insertion_plan( product_marker: str | None, *, table: KsseDfpStringTable | None = None, documents_dir: str = KSSE_DOCUMENTS_DIR, ) -> list[StedJsonInsertionPlan]: """Return native JSON insertion attempts for ``FUN_00143d98``. Each attempt still depends on successful suffix recovery. The native code checks the output string length before prepending ``DFP`` and inserting the member into the JSON object. """ table = table or KsseDfpStringTable.parse() product = normalize_product_marker(product_marker, table.active_product) plans = [ StedJsonInsertionPlan( output_key=product, stage="external_current", candidate_paths=sted_external_candidate_paths( product, table=table, documents_dir=documents_dir, ), guard="always attempted first; insert only if recovered suffix is non-empty", ) ] if product == table.active_product: plans.append( StedJsonInsertionPlan( output_key=table.default_product, stage="product_salt_default", candidate_paths=sted_product_salt_candidate_paths( table=table, documents_dir=documents_dir, ), guard="bVar6 == true; product matches table[0x90] active product", ) ) elif product == table.default_product: plans.append( StedJsonInsertionPlan( output_key=table.active_product, stage="hidden_md5_active", candidate_paths=sted_hidden_md5_candidate_paths( table=table, documents_dir=documents_dir, ), guard="bVar1 == true; product matches table[0x30] default product", ) ) else: plans.extend( [ StedJsonInsertionPlan( output_key=table.default_product, stage="product_salt_default", candidate_paths=sted_product_salt_candidate_paths( table=table, documents_dir=documents_dir, ), guard="!bVar1 && !bVar6; product is neither default nor active", ), StedJsonInsertionPlan( output_key=table.active_product, stage="hidden_md5_active", candidate_paths=sted_hidden_md5_candidate_paths( table=table, documents_dir=documents_dir, ), guard="!bVar1 && !bVar6; product is neither default nor active", ), ] ) return plans def join_sentinel_base(base_path: str) -> str: """Return the native sentinel prefix equivalent to ``base + '/.'``.""" base = str(base_path).rstrip("/") return f"{base}/." def sentinel_half(index: int, char: str) -> str: """Return the half marker value used by FUN_00140ea8. Native compares the byte with ASCII ``'8'``: ``0`` covers 0..7 and ``1`` covers 8..F for the uppercase hex alphabet. """ if len(char) != 1: raise ValueError("char must be one byte/character") return "0" if ord(char) < ord("8") else "1" def sentinel_half_path(base_path: str, index: int, half: str | int) -> str: """Path for ``.@``.""" if index < 1: raise ValueError("native sentinel indices are one-based") half_text = str(half) if half_text not in {"0", "1"}: raise ValueError("half must be 0 or 1") return f"{join_sentinel_base(base_path)}{index}@{half_text}" def sentinel_char_path(base_path: str, index: int, char: str) -> str: """Path for ``._``.""" if index < 1: raise ValueError("native sentinel indices are one-based") if len(char) != 1: raise ValueError("char must be one byte/character") return f"{join_sentinel_base(base_path)}{index}_{char}" def sentinel_paths_for_suffix(base_path: str, suffix: str) -> list[str]: """Return all sentinel paths FUN_00140ea8 would materialize.""" paths: list[str] = [] for index, char in enumerate(suffix, start=1): paths.append(sentinel_half_path(base_path, index, sentinel_half(index, char))) paths.append(sentinel_char_path(base_path, index, char)) return paths def recover_suffix_from_sentinels( base_path: str, exists: Callable[[str], bool], *, alphabet: str | None = None, length: int = DFP_SUFFIX_LEN, ) -> str: """Recover the 61-byte DFP suffix using FUN_00142bf4's probe order.""" table = KsseDfpStringTable.parse() alphabet = alphabet or table.alphabet if len(alphabet) < 16: raise ValueError("alphabet must contain at least 16 characters") recovered: list[str] = [] for index in range(1, length + 1): if exists(sentinel_half_path(base_path, index, "0")): candidates = alphabet[:8] elif exists(sentinel_half_path(base_path, index, "1")): candidates = alphabet[8:16] else: candidates = alphabet[:16] for char in candidates: if exists(sentinel_char_path(base_path, index, char)): recovered.append(char) break else: raise KsseSentinelMissing( f"missing sentinel for suffix index {index} under {base_path!r}" ) return "".join(recovered) def ksse_suffix_crc_material( suffix: str, *, model: str = "", table_raw: str = KSSE_DFP_TABLE_RAW, ) -> bytes: """Material that FUN_00142bf4 feeds to FUN_001567b8 / CRC32. Static evidence shows the native code appends the raw table, then the recovered suffix after in-place reversal, then ``ro.product.model``. """ return (table_raw + suffix[::-1] + model).encode("utf-8") def ksse_suffix_crc32( suffix: str, *, model: str = "", table_raw: str = KSSE_DFP_TABLE_RAW, ) -> int: return ksse_crc32(ksse_suffix_crc_material(suffix, model=model, table_raw=table_raw)) def dfp_suffix_from_value( value: str, *, table: KsseDfpStringTable | None = None, strict: bool = True, ) -> str: """Return the 61-byte suffix from ``DFP...`` or a raw suffix string.""" table = table or KsseDfpStringTable.parse() text = str(value) if text.startswith(table.dfp_prefix): text = text[len(table.dfp_prefix) :] if strict and len(text) != DFP_SUFFIX_LEN: raise ValueError(f"DFP suffix must be {DFP_SUFFIX_LEN} characters, got {len(text)}") return text def ksse_dfp_suffix_crc_material( suffix: str, *, model: str = "", table_raw: str = KSSE_DFP_TABLE_RAW, ) -> bytes: """CRC material after native reverses recovered marker-order suffix back.""" if len(suffix) != DFP_SUFFIX_LEN: raise ValueError(f"DFP suffix must be {DFP_SUFFIX_LEN} characters, got {len(suffix)}") return (table_raw + suffix + model).encode("utf-8") def ksse_dfp_suffix_crc32( suffix: str, *, model: str = "", table_raw: str = KSSE_DFP_TABLE_RAW, ) -> int: return ksse_crc32(ksse_dfp_suffix_crc_material(suffix, model=model, table_raw=table_raw)) def sted_writer_marker_suffix(value: str, *, table: KsseDfpStringTable | None = None) -> str: """Suffix order written by ``FUN_00140ea8`` sentinel files. The writer receives a Java-visible ``DFP`` value, strips the ``DFP`` prefix, reverses the 61-byte suffix, then materializes sentinels for that reversed byte order. """ return dfp_suffix_from_value(value, table=table)[::-1] def sted_crc_marker_path( base_path: str, value: str, *, model: str = "", table: KsseDfpStringTable | None = None, table_raw: str = KSSE_DFP_TABLE_RAW, ) -> str: """Path for the CRC guard marker written after all byte sentinels.""" suffix = dfp_suffix_from_value(value, table=table) crc = ksse_dfp_suffix_crc32(suffix, model=model, table_raw=table_raw) return f"{join_sentinel_base(base_path)}{crc}" def sted_writer_sentinel_paths(base_path: str, value: str) -> list[str]: """Per-byte sentinel files created by ``FUN_00140ea8`` for a DFP value.""" return sentinel_paths_for_suffix(base_path, sted_writer_marker_suffix(value)) def sted_writer_paths( base_path: str, value: str, *, model: str = "", include_crc: bool = True, ) -> list[str]: """All deterministic marker paths created by ``FUN_00140ea8``. File contents are just a small truth marker; path names carry the data. """ paths = sted_writer_sentinel_paths(base_path, value) if include_crc: paths.append(sted_crc_marker_path(base_path, value, model=model)) return paths def build_sted_persistence_artifacts( cache_e: str, cache_m: str, *, c_time_ms: int | None = None, product: str = "NEBULA", writable_external_storage: bool = False, model: str = "", include_crc: bool = True, table: KsseDfpStringTable | None = None, documents_dir: str = KSSE_DOCUMENTS_DIR, ) -> StedPersistenceArtifacts: """Build deterministic Java/native persistence artifacts for an EGID. This models the confirmed ``rq0.d.e(cache_e, cache_m)`` chain: 1. put ``c_time/cache_e/cache_m`` into the in-memory map; 2. write the same JSON to SharedPreferences key ``kwtk_n``; 3. write the same JSON to app-private file ``.skvec``; 4. call ``EngineProxy.sted(cache_e, z)`` to materialize native sentinel files for command ``1114139``. It deliberately does not try to reproduce the optional external Java-serialized ``LinkedHashMap`` file from ``rq0.d.j`` because that path is encrypted through ``uq0.o`` and is not needed for the native STED readback. """ table = table or KsseDfpStringTable.parse() dfp_suffix_from_value(cache_e, table=table) if c_time_ms is None: c_time_ms = int(time.time() * 1000) cache_json = build_sted_cache_json(cache_e, cache_m, c_time_ms) product_marker = engine_sted_product_marker( product, writable_external_storage=writable_external_storage, ) native_base_path = sted_external_candidate_paths( product_marker, table=table, documents_dir=documents_dir, )[0] native_sentinel_paths = sted_writer_paths( native_base_path, cache_e, model=model, include_crc=include_crc, ) existing = set(native_sentinel_paths) native_readback_json = recover_sted_result_json_from_sentinels( product_marker, existing.__contains__, model=model, require_crc=include_crc, table=table, documents_dir=documents_dir, ) return StedPersistenceArtifacts( cache_json=cache_json, in_memory_cache={ "c_time": str(int(c_time_ms)), "cache_e": str(cache_e), "cache_m": str(cache_m), }, shared_preferences={STED_SHARED_PREF_KEY: cache_json}, app_private_files={STED_CACHE_FILE_NAME: cache_json}, product_marker=product_marker, native_base_path=native_base_path, native_sentinel_paths=native_sentinel_paths, native_readback_json=native_readback_json, ) def recover_dfp_suffix_from_sted_sentinels( base_path: str, exists: Callable[[str], bool], *, model: str = "", require_crc: bool = False, ) -> str: """Recover the Java-visible DFP suffix from writer-created sentinels.""" marker_order = recover_suffix_from_sentinels(base_path, exists) suffix = marker_order[::-1] if require_crc and not exists(sted_crc_marker_path(base_path, suffix, model=model)): raise KsseSentinelMissing(f"missing CRC marker for recovered suffix under {base_path!r}") return suffix def recover_dfp_value_from_sted_sentinels( base_path: str, exists: Callable[[str], bool], *, model: str = "", require_crc: bool = False, table: KsseDfpStringTable | None = None, ) -> str: """Recover ``DFP`` + suffix from writer-created sentinels.""" table = table or KsseDfpStringTable.parse() suffix = recover_dfp_suffix_from_sted_sentinels( base_path, exists, model=model, require_crc=require_crc, ) return table.dfp_prefix + suffix def recover_first_dfp_value_from_candidate_paths( candidate_paths: Iterable[str], exists: Callable[[str], bool], *, model: str = "", require_crc: bool = False, table: KsseDfpStringTable | None = None, ) -> str | None: """Try native primary/fallback path order and return the first DFP value.""" table = table or KsseDfpStringTable.parse() for base_path in candidate_paths: try: return recover_dfp_value_from_sted_sentinels( base_path, exists, model=model, require_crc=require_crc, table=table, ) except KsseSentinelMissing: continue return None def recover_sted_values_from_sentinels( product_marker: str | None, exists: Callable[[str], bool], *, model: str = "", require_crc: bool = False, table: KsseDfpStringTable | None = None, documents_dir: str = KSSE_DOCUMENTS_DIR, ) -> dict[str, str]: """Recover ``FUN_00143d98`` product-keyed values from sentinel files. This models the Java-visible ``EngineProxy.sted(null,z)`` read path: every planned JSON member is attempted in native insertion order, each member uses its own primary/fallback path pair, and missing sentinel sets simply skip that JSON member. """ table = table or KsseDfpStringTable.parse() values: dict[str, str] = {} for plan in sted_json_insertion_plan( product_marker, table=table, documents_dir=documents_dir, ): value = recover_first_dfp_value_from_candidate_paths( plan.candidate_paths, exists, model=model, require_crc=require_crc, table=table, ) if value: values[plan.output_key] = value return values def recover_sted_result_json_from_sentinels( product_marker: str | None, exists: Callable[[str], bool], *, model: str = "", require_crc: bool = False, table: KsseDfpStringTable | None = None, documents_dir: str = KSSE_DOCUMENTS_DIR, ) -> str: """Return compact product-keyed JSON recovered from native sentinels.""" return build_sted_result_json( recover_sted_values_from_sentinels( product_marker, exists, model=model, require_crc=require_crc, table=table, documents_dir=documents_dir, ) ) def build_sted_result_json(values: dict[str, str]) -> str: """Serialize product-keyed DFP values like FUN_00154a0c compact mode.""" return json.dumps(values, ensure_ascii=False, separators=(",", ":")) def parse_sted_result_json(data: str) -> dict[str, str]: parsed = json.loads(data) if not isinstance(parsed, dict): raise ValueError("sted result must be a JSON object") return {str(key): str(value) for key, value in parsed.items()} def select_sted_product_value(data: str | dict[str, str], marker: str | None) -> str: """Select current product's DFP value from FUN_00143d98-style JSON.""" values = parse_sted_result_json(data) if isinstance(data, str) else data product = normalize_product_marker(marker) if product in values: return values[product] table = KsseDfpStringTable.parse() if table.default_product in values: return values[table.default_product] if values: return next(iter(values.values())) raise KeyError("empty sted result") __all__ = [ "DFP_SUFFIX_LEN", "KSSE_DOCUMENTS_DIR", "KSSE_DFP_TABLE_RAW", "STED_CACHE_FILE_NAME", "STED_CACHE_FILE_TOKEN", "STED_SHARED_PREF_KEY", "KsseDfpStringTable", "KsseSentinelMissing", "StedJsonInsertionPlan", "StedPersistenceArtifacts", "build_sted_cache_json", "build_sted_persistence_artifacts", "build_sted_result_json", "dfp_suffix_from_value", "engine_sted_product_marker", "hidden_cache_path", "join_sentinel_base", "ksse_dfp_suffix_crc32", "ksse_dfp_suffix_crc_material", "ksse_md5_hex16", "ksse_suffix_crc32", "ksse_suffix_crc_material", "normalize_product_marker", "parse_sted_result_json", "recover_first_dfp_value_from_candidate_paths", "recover_dfp_value_from_sted_sentinels", "recover_dfp_suffix_from_sted_sentinels", "recover_suffix_from_sentinels", "recover_sted_result_json_from_sentinels", "recover_sted_values_from_sentinels", "select_sted_product_value", "sentinel_char_path", "sentinel_half", "sentinel_half_path", "sentinel_paths_for_suffix", "sted_candidate_path_families", "sted_external_candidate_paths", "sted_hidden_md5_candidate_paths", "sted_json_insertion_plan", "sted_product_salt_candidate_paths", "sted_crc_marker_path", "sted_writer_marker_suffix", "sted_writer_paths", "sted_writer_sentinel_paths", ]