reverse-skills-max: initial public release (restored, redacted, hardened)
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Summarize a Procmon-style CSV operation log into a behavioral profile.
|
||||
|
||||
Reads a CSV export (Process Monitor "Save as CSV") with at least Process Name, Operation, Path,
|
||||
and Detail columns. Categorizes operations, reconstructs the process tree from Process Create
|
||||
events, and highlights persistence-relevant writes. Parses logs only; executes nothing.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import csv
|
||||
import json
|
||||
import re
|
||||
import sys
|
||||
from collections import Counter
|
||||
|
||||
PERSIST_HINTS = ["\\run", "\\runonce", "currentversion\\run", "\\services\\", "startup"]
|
||||
LOLBINS = ["powershell", "rundll32", "mshta", "regsvr32", "wscript", "cscript", "cmd.exe",
|
||||
"certutil", "bitsadmin"]
|
||||
RE_URL = re.compile(r"https?://[^\s\"'<>]+", re.I)
|
||||
|
||||
|
||||
def _category(op: str) -> str:
|
||||
op = op.lower()
|
||||
if "process" in op or "thread" in op:
|
||||
return "process_thread"
|
||||
if "reg" in op:
|
||||
return "registry"
|
||||
if "tcp" in op or "udp" in op or "connect" in op:
|
||||
return "network"
|
||||
if "file" in op or "create" in op or "write" in op or "read" in op:
|
||||
return "file"
|
||||
return "other"
|
||||
|
||||
|
||||
def summarize(path: str) -> dict:
|
||||
cats: Counter = Counter()
|
||||
children = []
|
||||
persistence = []
|
||||
urls = set()
|
||||
with open(path, newline="", encoding="utf-8-sig", errors="replace") as fh:
|
||||
reader = csv.DictReader(fh)
|
||||
for row in reader:
|
||||
op = row.get("Operation", "")
|
||||
target = row.get("Path", "")
|
||||
detail = row.get("Detail", "")
|
||||
cats[_category(op)] += 1
|
||||
if op.lower().startswith("process create"):
|
||||
children.append({"parent": row.get("Process Name", ""), "detail": detail})
|
||||
low = target.lower()
|
||||
if any(h in low for h in PERSIST_HINTS) and "write" in op.lower():
|
||||
persistence.append({"op": op, "path": target})
|
||||
for u in RE_URL.findall(detail) + RE_URL.findall(target):
|
||||
urls.add(u)
|
||||
return {
|
||||
"operation_categories": dict(cats),
|
||||
"process_creates": children[:50],
|
||||
"lolbin_children": sorted({b for c in children for b in LOLBINS
|
||||
if b in c["detail"].lower()}),
|
||||
"persistence_writes": persistence[:50],
|
||||
"urls": sorted(u.replace("http", "hxxp").replace(".", "[.]") for u in urls),
|
||||
}
|
||||
|
||||
|
||||
def main(argv: list[str] | None = None) -> int:
|
||||
parser = argparse.ArgumentParser(description="Summarize Procmon CSV behavior")
|
||||
sub = parser.add_subparsers(dest="cmd", required=True)
|
||||
p = sub.add_parser("summarize")
|
||||
p.add_argument("csv")
|
||||
args = parser.parse_args(argv)
|
||||
print(json.dumps(summarize(args.csv), indent=2))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user