# This submodule stores and manages FLIRT signatures
from typing import Set, List, Dict, Optional
import os
import json
from collections import defaultdict
import logging
import nampa
_l = logging.getLogger(__name__)
[docs]class FlirtSignature:
"""
This class describes a FLIRT signature.
"""
def __init__(
self,
arch: str,
platform: str,
sig_name: str,
sig_path: str,
unique_strings: Optional[Set[str]] = None,
compiler: Optional[str] = None,
compiler_version: Optional[str] = None,
os_name: Optional[str] = None,
os_version: Optional[str] = None,
):
self.arch = arch
self.platform = platform
self.sig_name = sig_name
self.sig_path = sig_path
self.unique_strings = unique_strings
self.compiler = compiler
self.compiler_version = compiler_version
self.os_name = os_name
self.os_version = os_version
def __repr__(self):
if self.os_name:
if self.os_version:
return f"<{self.sig_name}@{self.arch}-{self.os_name}-{self.os_version}>"
return f"<{self.sig_name}@{self.arch}-{self.os_name}>"
return f"<{self.sig_name}@{self.arch}-{self.platform}>"
FS = FlirtSignature
# A dict from architecture names to FLIRT signatures under that architecture. Arch names are always in lower case.
FLIRT_SIGNATURES_BY_ARCH: Dict[str, List[FlirtSignature]] = defaultdict(list)
LIBRARY_TO_SIGNATURES: Dict[str, List[FlirtSignature]] = defaultdict(list)
STRING_TO_LIBRARIES: Dict[str, Set[str]] = defaultdict(set)
[docs]def load_signatures(path: str) -> None:
"""
Recursively load all FLIRT signatures under a specific path.
:param path: Location of FLIRT signatures.
"""
FLIRT_SIGNATURES_BY_ARCH.clear()
LIBRARY_TO_SIGNATURES.clear()
STRING_TO_LIBRARIES.clear()
for root, _, filenames in os.walk(path):
for filename in filenames:
if filename.endswith(".sig"):
# parse it
sig_path = os.path.join(root, filename)
try:
with open(sig_path, "rb") as f:
flirt_header = nampa.flirt.parse_header(f)
except nampa.flirt.FlirtException:
_l.warning("Failed to load FLIRT signature file %s.", sig_path)
continue
# is there a meta data file?
meta_path = os.path.join(root, filename[:-4] + ".meta")
if os.path.isfile(meta_path):
# yes!
with open(meta_path) as f:
meta = json.load(f)
arch = meta.get("arch", None)
platform = meta.get("platform", None)
os_name = meta.get("os", None)
os_version = meta.get("os_version", None)
compiler = meta.get("compiler", None)
compiler_version = meta.get("compiler_version", None)
unique_strings = meta.get("unique_strings", None)
else:
# nope... we need to extract information from the signature file
# TODO: Convert them to angr-specific strings
arch = flirt_header.arch
platform = flirt_header.os_types
os_name = None
os_version = None
unique_strings = None
compiler = None
compiler_version = None
signature = FlirtSignature(
arch,
platform,
flirt_header.library_name.decode("utf-8"),
sig_path,
unique_strings=unique_strings,
compiler=compiler,
compiler_version=compiler_version,
os_name=os_name,
os_version=os_version,
)
FLIRT_SIGNATURES_BY_ARCH[arch].append(signature)
# fill in LIBRARY_TO_SIGNATURES and STRING_TO_LIBRARIES
for sigs in FLIRT_SIGNATURES_BY_ARCH.values():
for sig in sigs:
LIBRARY_TO_SIGNATURES[sig.sig_name].append(sig)
if sig.unique_strings:
for us in sig.unique_strings:
STRING_TO_LIBRARIES[us].add(sig.sig_name)