#!/usr/bin/env python3
"""Windows Amcache.hve forensic analysis agent.

Parses Amcache.hve registry hive to extract program execution history,
file metadata, and device information using the regipy library.
"""

import argparse
import json
import sys
import datetime

try:
    from regipy.registry import RegistryHive
    HAS_REGIPY = True
except ImportError:
    HAS_REGIPY = False


AMCACHE_FILE_KEY = "Root\InventoryApplicationFile"
AMCACHE_APP_KEY = "Root\InventoryApplication"
AMCACHE_DEVICE_KEY = "Root\InventoryDevicePnp"
AMCACHE_DRIVER_KEY = "Root\InventoryDriverBinary"

SUSPICIOUS_PATHS = [
    "\\temp\\", "\\tmp\\", "\\appdata\\local\\temp",
    "\\downloads\\", "\\public\\", "\\programdata\\",
    "\\recycle", "\\users\\public",
]

SUSPICIOUS_NAMES = [
    "mimikatz", "psexec", "lazagne", "procdump", "rubeus",
    "sharphound", "bloodhound", "cobalt", "beacon",
    "powershell_ise", "certutil", "mshta",
]


def parse_amcache_files(hive_path):
    """Parse InventoryApplicationFile entries from Amcache.hve."""
    if not HAS_REGIPY:
        return {"error": "regipy not installed. pip install regipy"}
    try:
        reg = RegistryHive(hive_path)
        entries = []
        for subkey in reg.get_key(AMCACHE_FILE_KEY).iter_subkeys():
            values = {v.name: v.value for v in subkey.iter_values()}
            entries.append({
                "name": values.get("Name", ""),
                "lower_case_path": values.get("LowerCaseLongPath", ""),
                "publisher": values.get("Publisher", ""),
                "version": values.get("Version", ""),
                "sha1": values.get("FileId", "").lstrip("0000").lower() if values.get("FileId") else "",
                "size": values.get("Size", 0),
                "link_date": values.get("LinkDate", ""),
                "program_id": values.get("ProgramId", ""),
                "last_modified": subkey.header.last_modified.isoformat() if subkey.header.last_modified else "",
            })
        return entries
    except Exception as e:
        return {"error": str(e)}


def parse_amcache_apps(hive_path):
    """Parse InventoryApplication entries."""
    if not HAS_REGIPY:
        return {"error": "regipy not installed"}
    try:
        reg = RegistryHive(hive_path)
        apps = []
        for subkey in reg.get_key(AMCACHE_APP_KEY).iter_subkeys():
            values = {v.name: v.value for v in subkey.iter_values()}
            apps.append({
                "name": values.get("Name", ""),
                "version": values.get("Version", ""),
                "publisher": values.get("Publisher", ""),
                "install_date": values.get("InstallDate", ""),
                "source": values.get("Source", ""),
                "uninstall_string": values.get("UninstallString", ""),
                "registry_key_path": values.get("RegistryKeyPath", ""),
            })
        return apps
    except Exception as e:
        return {"error": str(e)}


def detect_suspicious(entries):
    """Flag suspicious entries based on path and name patterns."""
    findings = []
    for entry in entries:
        if isinstance(entry, dict) and "error" not in entry:
            path = entry.get("lower_case_path", "").lower()
            name = entry.get("name", "").lower()
            reasons = []
            for sp in SUSPICIOUS_PATHS:
                if sp in path:
                    reasons.append(f"Suspicious path: {sp}")
            for sn in SUSPICIOUS_NAMES:
                if sn in name:
                    reasons.append(f"Suspicious name: {sn}")
            if not entry.get("publisher"):
                reasons.append("Missing publisher metadata")
            if reasons:
                findings.append({
                    "name": entry.get("name", ""),
                    "path": entry.get("lower_case_path", ""),
                    "sha1": entry.get("sha1", ""),
                    "reasons": reasons,
                })
    return findings


def main():
    parser = argparse.ArgumentParser(description="Amcache.hve forensic analysis agent")
    parser.add_argument("hive", nargs="?", help="Path to Amcache.hve file")
    parser.add_argument("--apps", action="store_true", help="Parse InventoryApplication entries")
    parser.add_argument("--suspicious-only", action="store_true", help="Show only suspicious entries")
    parser.add_argument("--output", "-o", help="Output JSON report path")
    args = parser.parse_args()

    print("[*] Amcache.hve Forensic Analysis Agent")
    print(f"    regipy available: {HAS_REGIPY}")

    if not args.hive:
        print("\n[DEMO] Amcache.hve location: C:\\Windows\\AppCompat\\Programs\\Amcache.hve")
        print("  Usage: python agent.py Amcache.hve [--apps] [--suspicious-only]")
        print("  Extracts: file paths, SHA-1 hashes, publisher, timestamps, install info")
        print(json.dumps({"demo": True, "regipy_available": HAS_REGIPY}, indent=2))
        sys.exit(0)

    report = {"timestamp": datetime.datetime.utcnow().isoformat() + "Z", "hive": args.hive}

    files = parse_amcache_files(args.hive)
    if isinstance(files, list):
        report["file_entries"] = len(files)
        suspicious = detect_suspicious(files)
        report["suspicious_count"] = len(suspicious)
        if args.suspicious_only:
            report["findings"] = suspicious
        else:
            report["entries"] = files[:100]
            report["suspicious"] = suspicious
        print(f"[*] File entries: {len(files)}")
        print(f"[*] Suspicious: {len(suspicious)}")
        for s in suspicious[:10]:
            print(f"    [!] {s['name']}: {', '.join(s['reasons'])}")
    else:
        report["error"] = files

    if args.apps:
        apps = parse_amcache_apps(args.hive)
        if isinstance(apps, list):
            report["app_entries"] = len(apps)
            print(f"[*] Application entries: {len(apps)}")

    if args.output:
        with open(args.output, "w") as f:
            json.dump(report, f, indent=2, default=str)

    print(json.dumps({"file_entries": report.get("file_entries", 0),
                       "suspicious": report.get("suspicious_count", 0)}, indent=2))


if __name__ == "__main__":
    main()
