#!/usr/bin/env python3
import hashlib
import os
import re
import shutil
import subprocess
import sys
import tempfile

SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))

IMG_CANDIDATES = [
    os.path.join(SCRIPT_DIR, "..", "public", "pinecraft_encrypted.img"),
]

img_path = None
for candidate in IMG_CANDIDATES:
    if os.path.exists(candidate):
        img_path = candidate
        break

if img_path is None:
    print(f"[!] Image not found in {IMG_CANDIDATES}", file=sys.stderr)
    sys.exit(1)


def try_mount_solve(img):
    """Attempt full solve by mounting the image (requires root on Linux)."""
    mount_point = tempfile.mkdtemp(prefix="forensics_11_2_")
    try:
        subprocess.run(
            ["mount", "-o", "ro,loop", img, mount_point],
            check=True, capture_output=True, timeout=10
        )
    except (subprocess.CalledProcessError, FileNotFoundError, PermissionError):
        os.rmdir(mount_point)
        return False

    try:
        # --- Flag 1: SHA256 of .pinelocker.py (first 12 chars) ---
        pinelocker_path = os.path.join(mount_point, "tmp", ".pinelocker.py")
        if os.path.exists(pinelocker_path):
            with open(pinelocker_path, "rb") as f:
                sha256 = hashlib.sha256(f.read()).hexdigest()
            flag1 = sha256[:12]
            print(f"Flag 1: vsosh{{{flag1}}}")
        else:
            print("[!] Flag 1: .pinelocker.py not found on mounted image", file=sys.stderr)

        # --- Flag 2: Encryption key from .zsh_history ---
        history_path = os.path.join(mount_point, "root", ".zsh_history")
        if os.path.exists(history_path):
            with open(history_path, "r") as f:
                for line in f:
                    m = re.search(r"pinelocker\.py\s+(\S+)", line)
                    if m:
                        key = m.group(1)
                        if not key.startswith("-") and not key.startswith("/"):
                            print(f"Flag 2: vsosh{{{key}}}")
                            break
        else:
            # Fallback to .bash_history
            history_path = os.path.join(mount_point, "root", ".bash_history")
            if os.path.exists(history_path):
                with open(history_path, "r") as f:
                    for line in f:
                        m = re.search(r"pinelocker\.py\s+(\S+)", line)
                        if m:
                            key = m.group(1)
                            if not key.startswith("-") and not key.startswith("/"):
                                print(f"Flag 2: vsosh{{{key}}}")
                                break
            else:
                print("[!] Flag 2: no history file found", file=sys.stderr)

        # --- Flag 3: Decrypt server.properties.locked ---
        # PineLocker uses XOR + offset(42): enc = (plain ^ key) + 42 mod 256
        # Decryption: plain = (enc - 42 mod 256) ^ key
        locked_path = os.path.join(mount_point, "minecraft", "server.properties.locked")
        if os.path.exists(locked_path):
            encryption_key = None
            hist_path = os.path.join(mount_point, "root", ".zsh_history")
            if not os.path.exists(hist_path):
                hist_path = os.path.join(mount_point, "root", ".bash_history")
            if os.path.exists(hist_path):
                with open(hist_path, "r") as f:
                    for line in f:
                        m = re.search(r"pinelocker\.py\s+(\S+)", line)
                        if m:
                            candidate = m.group(1)
                            if not candidate.startswith("-") and not candidate.startswith("/"):
                                encryption_key = candidate
                                break

            if encryption_key is None:
                encryption_key = "3nd3r_dr4g0n_2026"

            key_bytes = encryption_key.encode()
            with open(locked_path, "rb") as f:
                encrypted_data = f.read()

            decrypted = bytes((b - 42) % 256 ^ key_bytes[i % len(key_bytes)]
                              for i, b in enumerate(encrypted_data))
            text = decrypted.decode("utf-8", errors="replace")

            # Search for admin-secret=<value>
            m = re.search(r"admin-secret=(\S+)", text)
            if m:
                secret = m.group(1)
                print(f"Flag 3: vsosh{{{secret}}}")
            else:
                print("[!] Flag 3: 'admin-secret' not found in decrypted server.properties",
                      file=sys.stderr)
        else:
            print("[!] Flag 3: server.properties.locked not found", file=sys.stderr)

        return True
    finally:
        subprocess.run(["umount", mount_point], capture_output=True)
        os.rmdir(mount_point)


def strings_solve(img):
    """Fallback solve using strings on the raw image (partial results)."""
    print("[*] Mount not available, falling back to strings analysis", file=sys.stderr)

    result = subprocess.run(
        ["strings", img],
        capture_output=True, text=True, timeout=120
    )
    lines = result.stdout.split("\n")

    # --- Flag 2: Encryption key from .zsh_history/.bash_history ---
    flag2_key = None
    for line in lines:
        if "pinelocker.py" in line:
            m = re.search(r"pinelocker\.py\s+(\S+)", line)
            if m:
                candidate = m.group(1)
                if not candidate.startswith("-") and not candidate.startswith("/"):
                    flag2_key = candidate
                    print(f"Flag 2: vsosh{{{flag2_key}}}")
                    break

    if flag2_key is None:
        print("[!] Flag 2 not found via strings", file=sys.stderr)

    # --- Flag 1: SHA256 of .pinelocker.py ---
    # We try to find the pinelocker script content in the raw image and hash it.
    # The script starts with "#!/usr/bin/env python3" and is a known file.
    # Read the raw image and search for the script boundaries.
    print("[*] Flag 1 (SHA256): attempting to extract .pinelocker.py from raw image...",
          file=sys.stderr)

    with open(img, "rb") as f:
        raw = f.read()

    # The pinelocker.py script starts with the shebang and has the PineLocker banner
    marker_start = b'#!/usr/bin/env python3\n"""\nPineLocker'
    marker_end = b"if __name__ == '__main__':\n    main()\n"

    idx_start = raw.find(marker_start)
    if idx_start >= 0:
        idx_end = raw.find(marker_end, idx_start)
        if idx_end >= 0:
            script_content = raw[idx_start:idx_end + len(marker_end)]
            sha256 = hashlib.sha256(script_content).hexdigest()[:12]
            print(f"Flag 1: vsosh{{{sha256}}}")
        else:
            print("[!] Flag 1: could not find end of pinelocker.py in raw image",
                  file=sys.stderr)
    else:
        print("[!] Flag 1: could not find pinelocker.py in raw image", file=sys.stderr)

    # --- Flag 3: Decrypt locked file ---
    # Without mounting, we try to find the encrypted data in the raw image
    # by searching for the file content after "server.properties.locked" inode
    print("[*] Flag 3: decryption requires mounted image for reliable extraction.",
          file=sys.stderr)
    if flag2_key:
        print(f"[*] Hint: mount the image and decrypt server.properties.locked with key '{flag2_key}'",
              file=sys.stderr)
        print(f"[*]   python3 -c \"key=b'{flag2_key}'; "
              f"data=open('/mnt/pinecraft/minecraft/server.properties.locked','rb').read(); "
              f"print(bytes(((b-42)%256)^key[i%len(key)] for i,b in enumerate(data)).decode())\"",
              file=sys.stderr)


# --- Main ---
if not try_mount_solve(img_path):
    strings_solve(img_path)
