#!/usr/bin/env python3
"""Agent for Linux memory forensics using LiME acquisition and Volatility 3."""

import json
import subprocess
import argparse
from datetime import datetime
from pathlib import Path


def acquire_memory_lime(output_path, lime_format="lime"):
    """Acquire memory using LiME kernel module."""
    kernel_version = subprocess.run(
        ["uname", "-r"], capture_output=True, text=True, timeout=120
    ).stdout.strip()
    lime_module = f"lime-{kernel_version}.ko"
    if not Path(lime_module).exists():
        lime_module = "lime.ko"
    cmd = ["insmod", lime_module, f"path={output_path}", f"format={lime_format}"]
    result = subprocess.run(cmd, capture_output=True, text=True, timeout=120)
    return {
        "status": "success" if result.returncode == 0 else "failed",
        "output_path": output_path,
        "format": lime_format,
        "kernel": kernel_version,
        "stderr": result.stderr,
    }


def run_vol3_plugin(image_path, plugin_name, extra_args=None):
    """Run a Volatility 3 plugin and capture output."""
    cmd = ["vol3", "-f", image_path, plugin_name]
    if extra_args:
        cmd.extend(extra_args)
    try:
        result = subprocess.run(
            cmd, capture_output=True, text=True, timeout=300,
        )
        lines = result.stdout.strip().splitlines()
        return {"plugin": plugin_name, "output": lines, "error": result.stderr.strip()}
    except subprocess.TimeoutExpired:
        return {"plugin": plugin_name, "output": [], "error": "Timeout"}


def parse_pslist_output(lines):
    """Parse Volatility linux.pslist output into structured data."""
    processes = []
    for line in lines:
        parts = line.split()
        if len(parts) >= 4 and parts[0].isdigit():
            processes.append({
                "pid": int(parts[0]),
                "ppid": int(parts[1]) if parts[1].isdigit() else 0,
                "name": parts[-1],
            })
    return processes


def list_processes(image_path):
    """List all processes from memory image."""
    result = run_vol3_plugin(image_path, "linux.pslist")
    return parse_pslist_output(result.get("output", []))


def extract_bash_history(image_path):
    """Extract bash command history from memory."""
    result = run_vol3_plugin(image_path, "linux.bash")
    commands = []
    for line in result.get("output", []):
        parts = line.split(None, 3)
        if len(parts) >= 4 and parts[0].isdigit():
            commands.append({
                "pid": int(parts[0]),
                "name": parts[1],
                "timestamp": parts[2] if len(parts) > 2 else "",
                "command": parts[3] if len(parts) > 3 else "",
            })
    return commands


def list_network_connections(image_path):
    """List network connections from memory."""
    result = run_vol3_plugin(image_path, "linux.sockstat")
    connections = []
    for line in result.get("output", []):
        if "TCP" in line or "UDP" in line:
            connections.append(line.strip())
    return connections


def list_kernel_modules(image_path):
    """List loaded kernel modules to detect rootkits."""
    result = run_vol3_plugin(image_path, "linux.lsmod")
    modules = []
    for line in result.get("output", []):
        parts = line.split()
        if parts and not parts[0].startswith("Offset"):
            modules.append({"name": parts[-1] if parts else line.strip()})
    return modules


def detect_hidden_processes(image_path):
    """Compare pslist vs psscan to find hidden processes."""
    pslist = run_vol3_plugin(image_path, "linux.pslist")
    psscan = run_vol3_plugin(image_path, "linux.psscan")
    pslist_pids = set()
    for line in pslist.get("output", []):
        parts = line.split()
        if parts and parts[0].isdigit():
            pslist_pids.add(int(parts[0]))
    hidden = []
    for line in psscan.get("output", []):
        parts = line.split()
        if parts and parts[0].isdigit():
            pid = int(parts[0])
            if pid not in pslist_pids and pid > 0:
                hidden.append({"pid": pid, "line": line.strip()})
    return hidden


def detect_suspicious_commands(bash_history):
    """Flag suspicious commands in bash history."""
    suspicious_patterns = [
        "curl.*|.*sh", "wget.*&&.*chmod", "base64.*-d",
        "nc.*-e", "python.*-c.*import.*socket",
        "nohup", "rm.*-rf.*/var/log", "history.*-c",
        "iptables.*-F", "chmod.*777", "chattr.*-i",
    ]
    import re
    findings = []
    for entry in bash_history:
        cmd = entry.get("command", "")
        for pattern in suspicious_patterns:
            if re.search(pattern, cmd, re.IGNORECASE):
                findings.append({
                    "pid": entry["pid"],
                    "command": cmd,
                    "pattern": pattern,
                    "severity": "HIGH",
                })
                break
    return findings


def check_malfind(image_path):
    """Run malfind to detect injected code."""
    result = run_vol3_plugin(image_path, "linux.malfind")
    return result.get("output", [])


def main():
    parser = argparse.ArgumentParser(description="LiME + Volatility 3 Forensics Agent")
    parser.add_argument("--image", help="Path to memory image")
    parser.add_argument("--acquire", help="Output path for LiME acquisition")
    parser.add_argument("--output", default="memory_forensics_report.json")
    parser.add_argument("--action", choices=[
        "acquire", "pslist", "bash", "network", "modules",
        "hidden", "malfind", "full_analysis"
    ], default="full_analysis")
    args = parser.parse_args()

    report = {"generated_at": datetime.utcnow().isoformat(), "findings": {}}

    if args.action == "acquire" and args.acquire:
        result = acquire_memory_lime(args.acquire)
        report["findings"]["acquisition"] = result
        print(f"[+] Memory acquisition: {result['status']}")
        return

    if not args.image:
        print("[-] --image required for analysis actions")
        return

    if args.action in ("pslist", "full_analysis"):
        procs = list_processes(args.image)
        report["findings"]["processes"] = procs
        print(f"[+] Processes: {len(procs)}")

    if args.action in ("bash", "full_analysis"):
        history = extract_bash_history(args.image)
        report["findings"]["bash_history"] = history
        suspicious = detect_suspicious_commands(history)
        report["findings"]["suspicious_commands"] = suspicious
        print(f"[+] Bash commands: {len(history)}, Suspicious: {len(suspicious)}")

    if args.action in ("network", "full_analysis"):
        conns = list_network_connections(args.image)
        report["findings"]["connections"] = conns
        print(f"[+] Network connections: {len(conns)}")

    if args.action in ("modules", "full_analysis"):
        modules = list_kernel_modules(args.image)
        report["findings"]["kernel_modules"] = modules
        print(f"[+] Kernel modules: {len(modules)}")

    if args.action in ("hidden", "full_analysis"):
        hidden = detect_hidden_processes(args.image)
        report["findings"]["hidden_processes"] = hidden
        print(f"[+] Hidden processes: {len(hidden)}")

    with open(args.output, "w") as f:
        json.dump(report, f, indent=2, default=str)
    print(f"[+] Report saved to {args.output}")


if __name__ == "__main__":
    main()
