# SPDX-License-Identifier: GPL-3.0-or-later """memory_obfuscation — memory-map indicators of hiding or encrypted payloads. Encrypted/packed implants still need executable memory after decrypting code. This detector does not read process memory; it only inspects ``/proc//maps`` for high-signal shapes: executable anonymous mappings, executable memfd/deleted mappings, RWX pages, and mapped libraries associated with process hiding. """ from __future__ import annotations from collections.abc import Iterator from ..alert import Alert, Severity from ..config import Config from ..system import MemoryMap, SystemState SID_MEMORY_OBFUSCATION = 100039 SID_PROCESS_HIDING_LIBRARY = 100048 _HIDE_LIBRARY_HINTS = frozenset({ "libhide", "libprocesshide", "process_hide", "proc_hide", "rootkit_hide", }) def _maps(proc) -> list[MemoryMap]: maps = getattr(proc, "memory_maps", []) return maps() if callable(maps) else maps def _is_anon_exec_path(path: str) -> bool: if not path: return True if path in {"[heap]", "[stack]"}: return True return path.startswith("[anon") or path.startswith("[stack:") def _hide_library(path: str) -> bool: name = path.rsplit("/", 1)[-1].lower() return any(hint in name for hint in _HIDE_LIBRARY_HINTS) def _allowed_path(path: str, cfg: Config) -> bool: for prefix in cfg.memory_obfuscation_allow_paths: if prefix and path.startswith(prefix): return True return False def _indicator(mm: MemoryMap, cfg: Config) -> tuple[str, Severity, str]: path = mm.path if path and _allowed_path(path, cfg): return "", Severity.MEDIUM, "" if path and _hide_library(path): return "process_hiding_library", Severity.CRITICAL, ( f"mapped library name suggests process hiding: {path}" ) executable = "x" in mm.perms writable = "w" in mm.perms if executable and ("memfd:" in path or "(deleted)" in path): return "memory_obfuscation", Severity.CRITICAL, ( f"executable transient mapping perms={mm.perms} path={path or ''}" ) if executable and writable: return "memory_obfuscation", Severity.HIGH, ( f"writable+executable memory mapping perms={mm.perms} path={path or ''}" ) if executable and _is_anon_exec_path(path): return "memory_obfuscation", Severity.HIGH, ( f"executable anonymous memory mapping perms={mm.perms} path={path or ''}" ) return "", Severity.MEDIUM, "" def detect(state: SystemState, cfg: Config) -> Iterator[Alert]: for proc in state.processes: comm = proc.comm or "?" seen: set[str] = set() for mm in _maps(proc): signature, severity, detail = _indicator(mm, cfg) if not signature or signature in seen: continue if signature == "memory_obfuscation" and comm in cfg.memory_obfuscation_allow_comms: continue seen.add(signature) sid = (SID_PROCESS_HIDING_LIBRARY if signature == "process_hiding_library" else SID_MEMORY_OBFUSCATION) yield Alert( severity=severity, signature=signature, key=f"mem:{signature}:{proc.pid}:{mm.start:x}-{mm.end:x}", detail=( f"pid={proc.pid} comm={comm} {detail} " f"range={mm.start:x}-{mm.end:x}" ), pids=(proc.pid,), sid=sid, classtype=("process-hiding" if signature == "process_hiding_library" else "memory-obfuscation"), )