Skip to content

lief_modifier.py

ofrak.core.elf.lief_modifier

LiefAddSectionModifer (Modifier)

Adds new sections to ELF binaries using the LIEF (Library to Instrument Executable Formats) library, creating section headers and inserting section data at appropriate file offsets. The new sections can contain code, data, or other content and are automatically integrated into the ELF structure with proper size and offset adjustments. Use for injecting new data sections, adding custom metadata, creating space for code injection, or inserting resources. LIEF handles the complex details of ELF modification including offset recalculation and header updates.

modify(self, resource, config) async

Modify the given resource.

Users should not call this method directly; rather, they should run Resource.run.

Parameters:

Name Type Description Default
resource Resource required
config LiefAddSectionModifierConfig

Optional config for modification. If an implementation provides a default, this default will always be used when config would otherwise be None. Note that a copy of the default config will be passed, so the default config values cannot be modified persistently by a component run.

required
Source code in ofrak/core/elf/lief_modifier.py
async def modify(self, resource: Resource, config: LiefAddSectionModifierConfig):
    binary: Optional[lief.Binary] = lief.parse(await resource.get_data())
    if not binary or not isinstance(binary, lief.ELF.Binary):
        raise ValueError("Lief failed parsing binary.")
    section: lief.ELF.Section = lief.ELF.Section()
    section.name = config.name
    section.content = memoryview(bytearray(config.content))
    section.flags = config.flags
    binary.add(section)

    with tempfile.NamedTemporaryFile(delete_on_close=False) as temp_file:
        temp_file.close()
        binary.write(temp_file.name)
        with open(temp_file.name, "rb") as f_handle:
            new_data = f_handle.read()
    # replace all old content (old range) with new content from Lief
    resource.queue_patch(Range(0, await resource.get_data_length()), new_data)

LiefAddSectionModifierConfig (ComponentConfig) dataclass

LiefAddSectionModifierConfig(name: str, content: bytes, flags: int)

LiefAddSegmentConfig (ComponentConfig) dataclass

Config for the LiefAddSegmentModifier.

Attributes:

Name Type Description
virtual_address int

virtual address of the new segment

alignment int

alignment of the new segment

content List[int]

list of integers representing the raw bytes of the new segment

rwx_flags str

string representation of the new segment's R/W/X permissions

replace_note bool

replace the unused NOTE segment with the new segment, rather than adding a new segment. defaults to True, as adding a new segment may corrupt the ELF due to a LIEF bug.

physical_address Optional[int]

overwrite the default physical address (defaults to the virtual address)

LiefAddSegmentModifier (Modifier)

Adds new loadable program segments (PT_LOAD) to ELF binaries using LIEF, creating complete memory regions with configurable addresses, sizes, and permissions (RWX). The new segments are integrated into the program header table and can be used for code or data injection. Use when creating new memory regions for code injection, adding executable segments for custom functions, inserting data segments for configuration or resources, or expanding the binary's memory footprint. More comprehensive than just adding sections as it includes memory mapping information.

Not all ELF binaries may be compatible with this modifier. ELF binaries missing the NOTE segment will fail unless the component config specifies replace_note = False.

modify(self, resource, config) async

Modify the given resource.

Users should not call this method directly; rather, they should run Resource.run.

Parameters:

Name Type Description Default
resource Resource required
config LiefAddSegmentConfig

Optional config for modification. If an implementation provides a default, this default will always be used when config would otherwise be None. Note that a copy of the default config will be passed, so the default config values cannot be modified persistently by a component run.

required
Source code in ofrak/core/elf/lief_modifier.py
async def modify(self, resource: Resource, config: LiefAddSegmentConfig) -> None:
    binary: Optional[lief.Binary] = lief.parse(await resource.get_data())
    if not binary or not isinstance(binary, lief.ELF.Binary):
        raise ValueError("Lief failed parsing binary.")

    segment = lief.ELF.Segment()
    segment.type = lief.ELF.Segment.TYPE.LOAD
    segment.content = memoryview(bytearray(config.content))
    segment.alignment = config.alignment
    segment.virtual_address = config.virtual_address
    if config.physical_address is not None:
        segment.physical_address = config.physical_address
    if "r" in config.rwx_flags:
        segment.add(lief.ELF.Segment.FLAGS.R)
    if "w" in config.rwx_flags:
        segment.add(lief.ELF.Segment.FLAGS.W)
    if "x" in config.rwx_flags:
        segment.add(lief.ELF.Segment.FLAGS.X)

    if config.replace_note:
        # instead of adding a segment to the binary, replace a useless NOTE segment
        #   see https://github.com/lief-project/LIEF/issues/98
        #   and https://github.com/lief-project/LIEF/issues/143
        if not binary.has(lief.ELF.Segment.TYPE.NOTE):
            raise ValueError("Binary must have a NOTE section to add a new section")
        segment = binary.replace(segment, binary[lief.ELF.Segment.TYPE.NOTE])
        if config.physical_address is not None:
            segment.physical_address = config.physical_address
    else:
        _ = binary.add(segment)

    with tempfile.NamedTemporaryFile(delete_on_close=False) as temp_file:
        temp_file.close()
        binary.write(temp_file.name)
        with open(temp_file.name, "rb") as f_handle:
            new_data = f_handle.read()
    # replace all old content (old range) with new content from Lief
    resource.queue_patch(Range(0, await resource.get_data_length()), new_data)

LiefRemoveSectionModifier (Modifier)

Removes sections from ELF binaries using LIEF, deleting the section header and optionally removing the section data, then fixing up all dependent headers and offsets. Section removal can reduce binary size and complexity. Use for stripping debug sections (.debug_*), removing unused sections, cleaning up after analysis, deleting symbols (.symtab), removing comments, or preparing binaries for deployment. Must be careful not to remove sections required for execution.

modify(self, resource, config) async

Modify the given resource.

Users should not call this method directly; rather, they should run Resource.run.

Parameters:

Name Type Description Default
resource Resource required
config LiefRemoveSectionModifierConfig

Optional config for modification. If an implementation provides a default, this default will always be used when config would otherwise be None. Note that a copy of the default config will be passed, so the default config values cannot be modified persistently by a component run.

required
Source code in ofrak/core/elf/lief_modifier.py
async def modify(self, resource: Resource, config: LiefRemoveSectionModifierConfig):
    binary: Optional[lief.Binary] = lief.parse(await resource.get_data())
    if not binary or not isinstance(binary, lief.ELF.Binary):
        raise ValueError("Lief failed parsing binary.")
    section: lief.ELF.Section = binary.get_section(config.name)
    if section is None:
        raise AttributeError(f"No section with name {config.name}")
    binary.remove(section)

    with tempfile.NamedTemporaryFile(delete_on_close=False) as temp_file:
        temp_file.close()
        binary.write(temp_file.name)
        with open(temp_file.name, "rb") as f_handle:
            new_data = f_handle.read()
    # replace all old content (old range) with new content from Lief
    resource.queue_patch(Range(0, await resource.get_data_length()), new_data)

LiefRemoveSectionModifierConfig (ComponentConfig) dataclass

LiefRemoveSectionModifierConfig(name: str)