init
This commit is contained in:
@@ -0,0 +1,130 @@
|
||||
"""Shared paths and seed helpers for coruna-lab tooling."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
LAB_ROOT = Path(__file__).resolve().parents[1]
|
||||
SOURCE_ROOT = LAB_ROOT / "source"
|
||||
ONLINE_ROOT = LAB_ROOT.parent / "coruna-online"
|
||||
MODULE_HUNT = ONLINE_ROOT / "module_hunt"
|
||||
if str(MODULE_HUNT) not in sys.path:
|
||||
sys.path.insert(0, str(MODULE_HUNT))
|
||||
|
||||
CAMPAIGN_HASH = "34f5121f572d6742703eb84ec2f866a6"
|
||||
|
||||
# Campaign originals (current seeds embedded in type-0x01 + core)
|
||||
ORIGINAL_DEPLOYMENT_SEED = "09d0b8d58a71653cd1c89c64c866f2e6"
|
||||
ORIGINAL_REPORTING_SEED = "2d2aebba0bf3d7d694194a7ab93b0a96"
|
||||
OLD_DEP = ORIGINAL_DEPLOYMENT_SEED.encode("ascii")
|
||||
OLD_REP = ORIGINAL_REPORTING_SEED.encode("ascii")
|
||||
|
||||
# Active tree root for --apply targets (project dir with web/ + sync/).
|
||||
# Defaults to SOURCE_ROOT so accidental --apply without --root does not invent paths;
|
||||
# new_project / patch_all --root override this before applying.
|
||||
_TREE_ROOT = SOURCE_ROOT
|
||||
CAMPAIGN_DIR = _TREE_ROOT / "web" / CAMPAIGN_HASH
|
||||
SYNC_DIR = _TREE_ROOT / "sync"
|
||||
|
||||
C2_FETCH = ONLINE_ROOT / "c2_fetch"
|
||||
CORE_DYLIB = (
|
||||
ONLINE_ROOT
|
||||
/ "evidence/cases/2026-08-02-coruna-all-26/downloads/extracted"
|
||||
/ "80fa600e2486e588bb7991766c6b6bada5a5de0a3351a83b46b6920ee4b55ac1"
|
||||
/ "tmp.dylib"
|
||||
)
|
||||
DAILY_BODY = (
|
||||
ONLINE_ROOT
|
||||
/ "evidence/cases/2026-08-02-coruna-har/responses"
|
||||
/ "har-36_6b5f8ad2b8e41bf097b4811c9fb082523a32f72dd83eed49ed09c5a7f9b04027.body"
|
||||
)
|
||||
SECONDARY_KEYS = Path(__file__).resolve().parent / "secondary_keys.json"
|
||||
|
||||
# Representative dylib per group (same bytes as the other stems in that group)
|
||||
GROUP_DYLIBS = {
|
||||
"A": C2_FETCH / "65704c0722165a7bdedad3f3f61258b2f95470f6_type0x01.dylib",
|
||||
"B": C2_FETCH / "7f208248c748f97956fe4a7cf246c91235852e67_type0x01.dylib",
|
||||
}
|
||||
|
||||
|
||||
def tree_root() -> Path:
|
||||
return _TREE_ROOT
|
||||
|
||||
|
||||
def set_tree_root(root: Path) -> Path:
|
||||
"""Point CAMPAIGN_DIR / SYNC_DIR at root/web/... and root/sync."""
|
||||
global _TREE_ROOT, CAMPAIGN_DIR, SYNC_DIR
|
||||
root = root.resolve()
|
||||
_TREE_ROOT = root
|
||||
CAMPAIGN_DIR = root / "web" / CAMPAIGN_HASH
|
||||
SYNC_DIR = root / "sync"
|
||||
return root
|
||||
|
||||
|
||||
def ensure_tree_layout(root: Path) -> None:
|
||||
camp = root / "web" / CAMPAIGN_HASH
|
||||
sync = root / "sync"
|
||||
if not camp.is_dir():
|
||||
raise SystemExit(f"missing campaign dir: {camp}")
|
||||
if not sync.is_dir():
|
||||
raise SystemExit(f"missing sync dir: {sync}")
|
||||
|
||||
|
||||
def pack_seed(value: str) -> bytes:
|
||||
data = value.encode("ascii")
|
||||
if len(data) > 32:
|
||||
raise SystemExit(
|
||||
f"seed longer than 32 bytes ({len(data)}): {value!r}\n"
|
||||
"Provide at most 32 ASCII characters (recommend exactly 32 hex chars)."
|
||||
)
|
||||
try:
|
||||
data.decode("ascii")
|
||||
except UnicodeDecodeError as exc:
|
||||
raise SystemExit("seed must be ASCII") from exc
|
||||
return data + b"\x00" * (32 - len(data))
|
||||
|
||||
|
||||
def validate_seed_arg(name: str, value: str) -> str:
|
||||
if not value:
|
||||
raise SystemExit(f"{name} must be non-empty")
|
||||
pack_seed(value) # length check
|
||||
return value
|
||||
|
||||
|
||||
def replace_seed(blob: bytearray, old: bytes, new32: bytes) -> int:
|
||||
count = 0
|
||||
start = 0
|
||||
while True:
|
||||
index = blob.find(old, start)
|
||||
if index < 0:
|
||||
break
|
||||
blob[index : index + 32] = new32
|
||||
count += 1
|
||||
start = index + 32
|
||||
return count
|
||||
|
||||
|
||||
def patch_seeds_in_dylib(
|
||||
data: bytes,
|
||||
deployment_seed: str,
|
||||
reporting_seed: str,
|
||||
*,
|
||||
expect_dep: int,
|
||||
expect_rep: int,
|
||||
label: str,
|
||||
) -> bytes:
|
||||
buf = bytearray(data)
|
||||
nd = replace_seed(buf, OLD_DEP, pack_seed(deployment_seed))
|
||||
nr = replace_seed(buf, OLD_REP, pack_seed(reporting_seed))
|
||||
if nd != expect_dep or nr != expect_rep:
|
||||
raise SystemExit(
|
||||
f"{label}: unexpected seed hits dep={nd} rep={nr} "
|
||||
f"(want {expect_dep}/{expect_rep}). Already patched?"
|
||||
)
|
||||
return bytes(buf)
|
||||
|
||||
|
||||
def sha256_hex(data: bytes) -> str:
|
||||
return hashlib.sha256(data).hexdigest()
|
||||
Reference in New Issue
Block a user