#!/usr/bin/env python3
"""Agent for detecting T1055 process injection via Sysmon event analysis."""

import argparse
import json
import subprocess
import sys
from datetime import datetime, timezone


INJECTION_ACCESS_MASKS = {
    "0x1F0FFF": "PROCESS_ALL_ACCESS",
    "0x001F0FFF": "PROCESS_ALL_ACCESS (full)",
    "0x0800": "PROCESS_SUSPEND_RESUME",
    "0x0020": "PROCESS_VM_WRITE",
    "0x0008": "PROCESS_VM_OPERATION",
    "0x0010": "PROCESS_VM_READ",
}

INJECTION_DLLS = [
    "ntdll.dll", "kernel32.dll", "kernelbase.dll",
]

SUSPICIOUS_API_CALLS = [
    "VirtualAllocEx", "WriteProcessMemory", "CreateRemoteThread",
    "NtMapViewOfSection", "QueueUserAPC", "SetWindowsHookEx",
    "RtlCreateUserThread", "NtQueueApcThread",
]


def query_sysmon_events(event_id, max_events=500):
    """Query Sysmon operational log for specific event ID."""
    if sys.platform != "win32":
        return []
    ps_cmd = (
        f"Get-WinEvent -FilterHashtable @{{LogName='Microsoft-Windows-Sysmon/Operational';"
        f"Id={event_id}}} -MaxEvents {max_events} "
        "| ForEach-Object { @{"
        "TimeCreated=$_.TimeCreated.ToString('o');"
        "Properties=@($_.Properties | ForEach-Object {$_.Value})"
        "}} | ConvertTo-Json -Depth 3"
    )
    try:
        result = subprocess.check_output(
            ["powershell", "-NoProfile", "-Command", ps_cmd],
            text=True, errors="replace", timeout=30
        )
        data = json.loads(result) if result.strip() else []
        return data if isinstance(data, list) else [data]
    except (subprocess.SubprocessError, json.JSONDecodeError):
        return []


def analyze_process_access():
    """Analyze Sysmon Event ID 10 for injection-related process access."""
    findings = []
    events = query_sysmon_events(10)
    for evt in events:
        props = evt.get("Properties", [])
        if len(props) < 11:
            continue
        source_img = str(props[4]) if len(props) > 4 else ""
        target_img = str(props[8]) if len(props) > 8 else ""
        access = str(props[10]) if len(props) > 10 else ""
        safe_sources = ["csrss.exe", "svchost.exe", "lsass.exe", "services.exe", "smss.exe"]
        if any(s in source_img.lower() for s in safe_sources):
            continue
        if access in INJECTION_ACCESS_MASKS:
            findings.append({
                "event": "ProcessAccess",
                "time": evt.get("TimeCreated", ""),
                "source": source_img,
                "target": target_img,
                "access_mask": access,
                "mask_meaning": INJECTION_ACCESS_MASKS.get(access, "Unknown"),
            })
    return findings


def analyze_create_remote_thread():
    """Analyze Sysmon Event ID 8 for CreateRemoteThread injection."""
    findings = []
    events = query_sysmon_events(8)
    for evt in events:
        props = evt.get("Properties", [])
        if len(props) < 8:
            continue
        source_img = str(props[4]) if len(props) > 4 else ""
        target_img = str(props[7]) if len(props) > 7 else ""
        findings.append({
            "event": "CreateRemoteThread",
            "time": evt.get("TimeCreated", ""),
            "source": source_img,
            "target": target_img,
            "severity": "HIGH",
        })
    return findings


def analyze_image_loads():
    """Analyze Sysmon Event ID 7 for suspicious DLL loads in unusual processes."""
    findings = []
    events = query_sysmon_events(7, max_events=200)
    for evt in events:
        props = evt.get("Properties", [])
        if len(props) < 6:
            continue
        image = str(props[3]) if len(props) > 3 else ""
        loaded = str(props[5]) if len(props) > 5 else ""
        if loaded.lower().endswith("amsi.dll") and "powershell" not in image.lower():
            findings.append({
                "event": "SuspiciousImageLoad",
                "time": evt.get("TimeCreated", ""),
                "process": image,
                "loaded_image": loaded,
                "note": "AMSI DLL loaded by non-PowerShell process",
            })
    return findings


def main():
    parser = argparse.ArgumentParser(
        description="Detect T1055 process injection via Sysmon telemetry"
    )
    parser.add_argument("--output", "-o", help="Output JSON report")
    parser.add_argument("--max-events", type=int, default=500)
    parser.add_argument("--verbose", "-v", action="store_true")
    args = parser.parse_args()

    print("[*] T1055 Process Injection Detection Agent (Sysmon)")
    report = {"timestamp": datetime.now(timezone.utc).isoformat(), "findings": {}}

    access = analyze_process_access()
    report["findings"]["process_access"] = access
    print(f"[*] Suspicious process access: {len(access)}")

    threads = analyze_create_remote_thread()
    report["findings"]["remote_threads"] = threads
    print(f"[*] Remote thread creation: {len(threads)}")

    loads = analyze_image_loads()
    report["findings"]["suspicious_loads"] = loads
    print(f"[*] Suspicious image loads: {len(loads)}")

    total = len(access) + len(threads) + len(loads)
    report["risk_level"] = "CRITICAL" if total >= 5 else "HIGH" if total >= 2 else "MEDIUM" if total > 0 else "LOW"

    if args.output:
        with open(args.output, "w") as f:
            json.dump(report, f, indent=2)
        print(f"[*] Report saved to {args.output}")
    else:
        print(json.dumps(report, indent=2))


if __name__ == "__main__":
    main()
