Module refinery.lib.lief

A wrapper around the LIEF library.

Expand source code Browse git
"""
A wrapper around the LIEF library.
"""
from __future__ import annotations

import io

from typing import TYPE_CHECKING

if TYPE_CHECKING:
    import lief.COFF as COFF
    import lief.ELF as ELF
    import lief.MachO as MachO
    import lief.PE as PE

    from lief import Binary as AbstractBinary
    from lief import Header, Relocation, Section, Symbol

    from refinery.lib.types import buf

__all__ = [
    'ELF',
    'MachO',
    'PE',
    'COFF',
    'AbstractBinary',
    'Relocation',
    'Header',
    'Symbol',
    'Section',
    'load_pe',
    'load_pe_fast',
    'load_macho',
    'load',
    'string',
]

__pdoc__ = {_forward: False for _forward in __all__[:9]}

_lib = None


def _init():
    global _lib
    if _lib is not None:
        return _lib
    import lief as lib
    lib.disable_leak_warning()
    lib.logging.disable()
    _lib = lib
    return lib


def __getattr__(name):
    lib = _init()
    if name == 'lib':
        return lib
    if name == 'ELF':
        import lief.ELF as ELF
        return ELF
    if name == 'MachO':
        import lief.MachO as MachO
        return MachO
    if name == 'PE':
        import lief.PE as PE
        return PE
    if name == 'COFF':
        import lief.COFF as COFF
        return COFF
    if name == 'AbstractBinary':
        return lib.Binary
    if name == 'Relocation':
        return lib.Relocation
    if name == 'Header':
        return lib.Header
    if name == 'Symbol':
        return lib.Symbol
    if name == 'Section':
        return lib.Section
    raise AttributeError(F'module {__name__!r} has no attribute {name!r}')


def load_pe(
    data: buf,
    parse_exports: bool = True,
    parse_imports: bool = True,
    parse_reloc: bool = True,
    parse_rsrc: bool = True,
    parse_signature: bool = True,
):
    """
    Load a PE file using LIEF. This is an ease-of-use function which forwards the keyworda rguments
    to a config object and then invokes the LIEF parser. Everything is parsed by default. For speed
    over completeness, see `refinery.lib.lief.load_pe_fast`.
    """
    import lief.PE as PE
    with io.BytesIO(data) as stream:
        cfg = PE.ParserConfig()
        cfg.parse_exports = bool(parse_exports)
        cfg.parse_imports = bool(parse_imports)
        cfg.parse_reloc = bool(parse_reloc)
        cfg.parse_rsrc = bool(parse_rsrc)
        cfg.parse_signature = bool(parse_signature)
        if parsed := PE.parse(stream, cfg):
            return parsed
        raise ValueError


def load_pe_fast(
    data: buf,
    parse_exports: bool = False,
    parse_imports: bool = False,
    parse_reloc: bool = False,
    parse_rsrc: bool = False,
    parse_signature: bool = False,
):
    """
    This is equivalent to `refinery.lib.lief.load_pe` with the sole exception that the parser
    settings are optimized for speed rather than for parsing as many components as possible.
    """
    return load_pe(
        data,
        parse_exports=parse_exports,
        parse_imports=parse_imports,
        parse_reloc=parse_reloc,
        parse_rsrc=parse_rsrc,
        parse_signature=parse_signature,
    )


def load_macho(data: buf):
    """
    Load a MachO file using LIEF.
    """
    import lief.MachO as MachO
    with io.BytesIO(data) as stream:
        config = MachO.ParserConfig()
        config.parse_dyld_bindings = True
        config.parse_dyld_rebases = True
        config.parse_dyld_exports = True
        if parsed := MachO.parse(stream, config):
            return parsed
        raise ValueError


def load_elf(data: buf):
    """
    Load an ELF file using LIEF.
    """
    import lief.ELF as ELF
    with io.BytesIO(data) as stream:
        config = ELF.ParserConfig()
        config.parse_dyn_symbols = True
        config.parse_relocations = True
        config.parse_symtab_symbols = True
        if parsed := ELF.parse(stream):
            return parsed
        raise ValueError


def load(data: buf):
    """
    Load a PE, ELF, or MachO executable using LIEF. The function first attempts to parse the file
    based on its first 4 bytes using a specific LIEF parser and reverts to LIEF's general purpose
    loader if these fail.
    """
    if data[:2] == B'MZ':
        return load_pe(data)
    if data[:4] == B'\x7FELF':
        return load_elf(data)
    if set(data[:4]) <= {0xFE, 0xED, 0xFA, 0xCE, 0xCF}:
        return load_macho(data)
    raise ValueError


def string(value: str | buf) -> str:
    """
    A function to convert LIEF values to a string, regardless of whether it is exposed as bytes
    or string by the foreign interface.
    """
    if not isinstance(value, str):
        if isinstance(value, memoryview):
            value = bytes(value)
        value, _, _ = value.partition(B'\0')
        value = value.decode('utf8')
    return value

Functions

def load_pe(data, parse_exports=True, parse_imports=True, parse_reloc=True, parse_rsrc=True, parse_signature=True)

Load a PE file using LIEF. This is an ease-of-use function which forwards the keyworda rguments to a config object and then invokes the LIEF parser. Everything is parsed by default. For speed over completeness, see load_pe_fast().

Expand source code Browse git
def load_pe(
    data: buf,
    parse_exports: bool = True,
    parse_imports: bool = True,
    parse_reloc: bool = True,
    parse_rsrc: bool = True,
    parse_signature: bool = True,
):
    """
    Load a PE file using LIEF. This is an ease-of-use function which forwards the keyworda rguments
    to a config object and then invokes the LIEF parser. Everything is parsed by default. For speed
    over completeness, see `refinery.lib.lief.load_pe_fast`.
    """
    import lief.PE as PE
    with io.BytesIO(data) as stream:
        cfg = PE.ParserConfig()
        cfg.parse_exports = bool(parse_exports)
        cfg.parse_imports = bool(parse_imports)
        cfg.parse_reloc = bool(parse_reloc)
        cfg.parse_rsrc = bool(parse_rsrc)
        cfg.parse_signature = bool(parse_signature)
        if parsed := PE.parse(stream, cfg):
            return parsed
        raise ValueError
def load_pe_fast(data, parse_exports=False, parse_imports=False, parse_reloc=False, parse_rsrc=False, parse_signature=False)

This is equivalent to load_pe() with the sole exception that the parser settings are optimized for speed rather than for parsing as many components as possible.

Expand source code Browse git
def load_pe_fast(
    data: buf,
    parse_exports: bool = False,
    parse_imports: bool = False,
    parse_reloc: bool = False,
    parse_rsrc: bool = False,
    parse_signature: bool = False,
):
    """
    This is equivalent to `refinery.lib.lief.load_pe` with the sole exception that the parser
    settings are optimized for speed rather than for parsing as many components as possible.
    """
    return load_pe(
        data,
        parse_exports=parse_exports,
        parse_imports=parse_imports,
        parse_reloc=parse_reloc,
        parse_rsrc=parse_rsrc,
        parse_signature=parse_signature,
    )
def load_macho(data)

Load a MachO file using LIEF.

Expand source code Browse git
def load_macho(data: buf):
    """
    Load a MachO file using LIEF.
    """
    import lief.MachO as MachO
    with io.BytesIO(data) as stream:
        config = MachO.ParserConfig()
        config.parse_dyld_bindings = True
        config.parse_dyld_rebases = True
        config.parse_dyld_exports = True
        if parsed := MachO.parse(stream, config):
            return parsed
        raise ValueError
def load(data)

Load a PE, ELF, or MachO executable using LIEF. The function first attempts to parse the file based on its first 4 bytes using a specific LIEF parser and reverts to LIEF's general purpose loader if these fail.

Expand source code Browse git
def load(data: buf):
    """
    Load a PE, ELF, or MachO executable using LIEF. The function first attempts to parse the file
    based on its first 4 bytes using a specific LIEF parser and reverts to LIEF's general purpose
    loader if these fail.
    """
    if data[:2] == B'MZ':
        return load_pe(data)
    if data[:4] == B'\x7FELF':
        return load_elf(data)
    if set(data[:4]) <= {0xFE, 0xED, 0xFA, 0xCE, 0xCF}:
        return load_macho(data)
    raise ValueError
def string(value)

A function to convert LIEF values to a string, regardless of whether it is exposed as bytes or string by the foreign interface.

Expand source code Browse git
def string(value: str | buf) -> str:
    """
    A function to convert LIEF values to a string, regardless of whether it is exposed as bytes
    or string by the foreign interface.
    """
    if not isinstance(value, str):
        if isinstance(value, memoryview):
            value = bytes(value)
        value, _, _ = value.partition(B'\0')
        value = value.decode('utf8')
    return value