#!/usr/bin/env python3 """Analyze extracted Excel 4.0 (XLM) macro formulas for malicious behavior. Reads a text dump of macro-sheet formulas and flags auto-run entry points, execution/download primitives (EXEC/CALL/REGISTER), and obfuscation (CHAR concatenation, FORMULA.FILL self-writing). Parses text only; executes nothing and never opens the workbook. Defangs recovered URLs. """ from __future__ import annotations import argparse import json import re import sys from pathlib import Path AUTO_NAMES = ["auto_open", "auto_close", "auto_activate", "auto_deactivate"] EXEC_PRIMS = ["exec(", "call(", "register(", "fopen(", "fwrite("] DOWNLOAD_HINTS = ["urlmon", "urldownloadtofile", "http", "msxml2", "winhttp", "shellexecute"] EXECUTORS = ["regsvr32", "rundll32", "mshta", "powershell", "cmd.exe", "wscript", "cscript"] OBFUSCATION = ["char(", "formula.fill", "formula(", "mid(", "&", "concatenate("] RE_URL = re.compile(r"https?://[^\s\"'<>)]+", re.I) RE_CHAR = re.compile(r"CHAR\((\d{1,3})\)", re.I) def _defang(s: str) -> str: return s.replace("http", "hxxp").replace(".", "[.]") def _reconstruct_char(line: str) -> str: chars = RE_CHAR.findall(line) if len(chars) < 3: return "" try: return "".join(chr(int(c)) for c in chars if 0 <= int(c) < 256) except ValueError: return "" def analyze(path: str) -> dict: text = Path(path).read_text(encoding="utf-8-sig", errors="replace") low = text.lower() auto = sorted({n for n in AUTO_NAMES if n in low}) exec_prims = sorted({p.rstrip("(") for p in EXEC_PRIMS if p in low}) downloads = sorted({d for d in DOWNLOAD_HINTS if d in low}) executors = sorted({e for e in EXECUTORS if e in low}) obf = sorted({o.rstrip("(") for o in OBFUSCATION if o in low}) urls = sorted({_defang(u) for u in RE_URL.findall(text)}) reconstructed = sorted({r for line in text.splitlines() if (r := _reconstruct_char(line))})[:25] return { "auto_run_triggers": auto, "execution_primitives": exec_prims, "download_indicators": downloads, "executors": executors, "obfuscation_techniques": obf, "urls": urls[:50], "reconstructed_char_strings": reconstructed, "is_likely_malicious_xlm": bool(auto and (exec_prims or downloads)), } def main(argv: list[str] | None = None) -> int: parser = argparse.ArgumentParser(description="Analyze Excel 4.0 (XLM) macros") sub = parser.add_subparsers(dest="cmd", required=True) p = sub.add_parser("analyze") p.add_argument("file") args = parser.parse_args(argv) print(json.dumps(analyze(args.file), indent=2)) return 0 if __name__ == "__main__": sys.exit(main())