Source code for helpers
"""Shared helpers for binary parsing and value formatting."""
from __future__ import annotations
import os
from enum import Enum
from typing import Any
import lief
[docs]
def hex_addr(value: int | None) -> str | None:
"""Format an integer as a hex address string, or return None."""
if value is None:
return None
return f"0x{value:x}"
[docs]
def safe_str(value: Any) -> str | None:
"""Convert bytes / str / other to a plain string, or None."""
if value is None:
return None
if isinstance(value, bytes):
return value.decode("utf-8", errors="replace")
return str(value)
[docs]
def safe_enum(value: Any) -> str | None:
"""Return the .name of an enum member, or str() for everything else."""
if value is None:
return None
if isinstance(value, Enum):
return value.name
return str(value)
[docs]
def parse_binary(file_path: str) -> lief.Binary:
"""Parse a binary file with LIEF, handling Mach-O fat binaries.
Returns the concrete (PE/ELF/MachO) Binary object.
Raises ``ValueError`` with a user-friendly message on failure.
"""
if not os.path.isfile(file_path):
raise ValueError(f"File not found: {file_path}")
# Try Mach-O first — lief.parse() on a fat binary returns None.
try:
fat = lief.MachO.parse(file_path)
if fat is not None:
binary = fat.at(0)
if binary is not None:
return binary
except Exception:
pass # Not a Mach-O — fall through to generic parse.
binary = lief.parse(file_path)
if binary is None:
raise ValueError(
f"LIEF could not parse the file (unsupported format?): {file_path}"
)
return binary
def _error(msg: str) -> dict:
"""Return a structured error dict instead of raising."""
return {"error": msg}