Skip to content

modifier.py

ofrak.core.elf.modifier

ElfAddStringModifier (Modifier)

Adds one or more strings to the .strtab section in an ELF so that they can be used as the names for things like symbols. This modifier only inserts the strings and fixes up the succeeding section offsets; it does not modify any existing strings nor does it replace any existing strings.

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 ElfAddStringModifierConfig

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/modifier.py
async def modify(
    self,
    resource: Resource,
    config: ElfAddStringModifierConfig,
):
    elf = await resource.view_as(Elf)
    string_section = await elf.get_string_section()
    string_section_size = await string_section.resource.get_data_length()

    if type(config.strings) is str:
        strings: Union[Tuple[str, ...], str] = (config.strings,)
    else:
        strings = config.strings
    encoded_strings = b"\x00"
    for string in strings:
        encoded_strings += string.encode("ascii") + b"\x00"
    total_string_section_size_increase = len(encoded_strings) - 1
    # Overwrites the last null byte, but our patch starts with a null byte
    string_section.resource.queue_patch(
        Range.from_size(string_section_size - 1, 1), encoded_strings
    )
    string_section_header = await string_section.get_header()
    original_string_section_offset = string_section_header.sh_offset

    # Now shift all the sections after the string section
    sections = await elf.get_sections()
    for section in sections:
        section_header = await section.get_header()
        if section_header.sh_offset <= original_string_section_offset:
            continue
        if ElfSectionFlag.ALLOC in section_header.get_flags():
            raise NotImplementedError(
                "Expanding string section would shift offset of section "
                "which is loaded into memory! May be possible to "
                "handle, but this is not implemented."
            )
        await section_header.resource.run(
            ElfSectionHeaderModifier,
            ElfSectionHeaderModifierConfig(
                sh_offset=section_header.sh_offset + total_string_section_size_increase
            ),
        )

    await string_section_header.resource.run(
        ElfSectionHeaderModifier,
        ElfSectionHeaderModifierConfig(
            sh_size=string_section_size + total_string_section_size_increase
        ),
    )

    # Section table is probably at end of binary too
    elf_header = await elf.get_header()
    if elf_header.e_shoff > string_section_header.sh_offset:
        await elf_header.resource.run(
            ElfHeaderModifier,
            ElfHeaderModifierConfig(
                e_shoff=elf_header.e_shoff + total_string_section_size_increase
            ),
        )

ElfAddStringModifierConfig (ComponentConfig) dataclass

Configuration for adding strings to the ELF string table for use as symbol or section names.

Attributes:

Name Type Description
strings Union[Tuple[str, ...], str]

String or tuple of strings to add to .strtab section (will be null-terminated)

ElfDynamicEntryModifier (AbstractElfAttributeModifier, Modifier)

Modifies ELF dynamic section entries (Elf32_Dyn or Elf64_Dyn) by changing tags or values, affecting runtime dynamic linking behavior. Can modify library dependencies, search paths, symbol table locations, initialization functions, and many other dynamic linking parameters. Use for changing required libraries (DT_NEEDED), modifying library search paths (DT_RPATH/DT_RUNPATH), adjusting symbol table pointers, changing initialization/finalization functions, or configuring dynamic linking behavior. Critical for controlling how the runtime linker loads and resolves the binary.

modify(self, resource, config) async

Patches the Elf{32, 64}_Dyn struct

Source code in ofrak/core/elf/modifier.py
async def modify(
    self,
    resource: Resource,
    config: ElfDynamicEntryModifierConfig,
):
    """
    Patches the Elf{32, 64}_Dyn struct
    """
    original_attributes = await resource.analyze(AttributesType[ElfDynamicEntry])
    await self.serialize_and_patch(resource, original_attributes, config)

ElfDynamicEntryModifierConfig (ComponentConfig) dataclass

Attributes:

Name Type Description
d_tag Optional[int]

one of ElfDynamicTableTag

d_un Optional[int]

malleable word size value that changes meaning depending on d_tag

ElfHeaderModifier (Modifier, AbstractElfAttributeModifier)

Modifies ELF header fields such as entry point address (where execution starts), program header table offset and count, section header table offset and count, processor flags, or header size. These fields control how the ELF file is interpreted and executed. Use for adjusting execution entry point, fixing header tables after modifications, changing architecture flags, updating counts after adding/removing headers, or repairing corrupted ELF files. Must be very careful as incorrect values can make the ELF unloadable.

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 ElfHeaderModifierConfig

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/modifier.py
async def modify(self, resource: Resource, config: ElfHeaderModifierConfig):
    original_attributes = await resource.analyze(AttributesType[ElfHeader])
    await self.serialize_and_patch(resource, original_attributes, config)

ElfHeaderModifierConfig (ComponentConfig) dataclass

Configuration for modifying ELF header fields that control binary interpretation and execution.

Attributes:

Name Type Description
e_type Optional[int]

ELF file type (executable, shared object, relocatable, core dump)

e_machine Optional[int]

Target architecture/machine type (x86, ARM, MIPS, etc.)

e_version Optional[int]

ELF format version number

e_entry Optional[int]

Virtual address where execution begins

e_phoff Optional[int]

File offset to program header table

e_shoff Optional[int]

File offset to section header table

e_flags Optional[int]

Architecture-specific processor flags

e_ehsize Optional[int]

Size of the ELF header in bytes

e_phentsize Optional[int]

Size of one program header table entry

e_phnum Optional[int]

Number of program header entries

e_shentsize Optional[int]

Size of one section header table entry

e_shnum Optional[int]

Number of section header entries

e_shstrndx Optional[int]

Section header table index of section name string table

ElfPointerArraySectionAddModifier (Modifier)

Adds a constant offset value to all pointer entries in ELF pointer array sections like .init_array, .fini_array, .ctors, and .dtors. This batch operation updates every pointer in the section by the same amount. Use when relocating code or data that is referenced by constructor/destructor arrays, adjusting for base address changes, or fixing up pointers after memory layout modifications. Essential when code injection or relocation changes the addresses of initialization/cleanup functions.

modify(self, resource, config) async

Patches the virtual addresses, doesn't change the ElfPointerArraySection attributes

Source code in ofrak/core/elf/modifier.py
async def modify(
    self,
    resource: Resource,
    config: ElfPointerArraySectionAddModifierConfig,
):
    """
    Patches the virtual addresses, doesn't change the ElfPointerArraySection attributes
    """

    elf_resource = await resource.get_only_ancestor_as_view(Elf, ResourceFilter.with_tags(Elf))
    e_basic_header_r = await elf_resource.get_basic_header()
    values = list()
    deserializer = BinaryDeserializer(
        io.BytesIO(await resource.get_data()),
        endianness=e_basic_header_r.get_endianness(),
        word_size=e_basic_header_r.get_bitwidth().get_word_size(),
    )
    num_values = (
        await resource.get_data_length() // e_basic_header_r.get_bitwidth().get_word_size()
    )
    for i in range(num_values):
        values.append(deserializer.unpack_ulong())

    buf = io.BytesIO()
    serializer = BinarySerializer(
        buf,
        endianness=e_basic_header_r.get_endianness(),
        word_size=e_basic_header_r.get_bitwidth().get_word_size(),
    )
    for value in values:
        if value not in config.skip_list:
            serializer.pack_ulong(value + config.add_value)
        else:
            serializer.pack_ulong(value)

    patch_length = await resource.get_data_length()
    resource.queue_patch(Range.from_size(0, patch_length), buf.getvalue())

ElfPointerArraySectionAddModifierConfig (ComponentConfig) dataclass

Attributes:

Name Type Description
skip_list Iterable[int]

values that should not be modified

add_value int

value to add to all pointers

ElfProgramHeaderModifier (AbstractElfAttributeModifier, Modifier)

Modifies ELF program header (Phdr) fields including segment type, file and memory addresses, sizes, protection flags (read/write/execute), and alignment requirements. Program headers define how segments are loaded into memory and their permissions. Use when adjusting ELF loading behavior, changing memory protection (making segments executable or writable), resizing segments, relocating segments in memory, or fixing up program headers after other modifications. Critical for controlling how the binary is loaded and mapped by the operating system.

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 ElfProgramHeaderModifierConfig

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/modifier.py
async def modify(self, resource: Resource, config: ElfProgramHeaderModifierConfig):
    original_attributes = await resource.analyze(AttributesType[ElfProgramHeader])
    await self.serialize_and_patch(resource, original_attributes, config)

ElfProgramHeaderModifierConfig (ComponentConfig) dataclass

Configuration for modifying ELF program header (Phdr) fields that control segment loading and memory mapping.

Attributes:

Name Type Description
p_type Optional[int]

Segment type (PT_LOAD, PT_DYNAMIC, PT_INTERP, etc.)

p_offset Optional[int]

File offset where segment data begins

p_vaddr Optional[int]

Virtual address where segment is loaded in memory

p_paddr Optional[int]

Physical address (for systems where it matters)

p_filesz Optional[int]

Size of segment in the file (bytes)

p_memsz Optional[int]

Size of segment in memory (can be larger than filesz for BSS)

p_flags Optional[int]

Segment permissions (PF_R=read, PF_W=write, PF_X=execute)

p_align Optional[int]

Segment alignment in memory and file

ElfRelaModifier (AbstractElfAttributeModifier, Modifier)

Modifies individual fields in ELF relocation entries with addends (Elf32_Rela or Elf64_Rela), including the offset where relocation applies, symbol index, relocation type, and addend constant. Relocations control how addresses are adjusted during linking and loading. Use when adjusting relocations during binary patching, fixing up relocations after code injection, changing symbol references, modifying relocation types, or debugging position-independent code issues. Must maintain consistency between relocations and actual code/data.

modify(self, resource, config) async

Patches the Elf{32, 64}_Rela struct

Source code in ofrak/core/elf/modifier.py
async def modify(
    self,
    resource: Resource,
    config: ElfRelaModifierConfig,
):
    """
    Patches the Elf{32, 64}_Rela struct
    """
    original_attributes = await resource.analyze(AttributesType[ElfRelaEntry])
    await self.serialize_and_patch(resource, original_attributes, config)

ElfRelaModifierConfig (ComponentConfig) dataclass

Attributes:

Name Type Description
r_offset Optional[int]

vm offset information for each relocation entry

r_info Optional[int]

Describes the type of relocation and sometimes the symbol related to the relocation

r_addend Optional[int]

Describes the VM offset for each relocation itself

ElfRelocateSymbolsModifier (Modifier)

Changes the virtual address value of symbols in an ELF file. If that ELF is an object file and is subsequently linked into an executable, any instructions referencing that symbol will now refer to the new address. This works even on implicitly or automatically generated symbols, like the absolute branches between basic blocks within a function. A linker script cannot change the targets of these branches individually, but this modifier can.

The config includes a dictionary, which should map from the original address of a symbol to the new address that symbol should be defined as. For example, if a branch jumps to 0x1000 and the goal is to change that branch to instead jump to 0x1800, the config dictionary should include the pair {0x1000: 0x1800}.

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 ElfRelocateSymbolsModifierConfig

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/modifier.py
async def modify(self, resource: Resource, config: ElfRelocateSymbolsModifierConfig) -> None:
    elf = await resource.view_as(Elf)
    symbol_section = await elf.get_symbol_section()
    for symbol in await symbol_section.get_symbols():
        if symbol.st_value in config.new_symbol_vaddrs:
            await symbol.resource.run(
                ElfSymbolModifier,
                ElfSymbolModifierConfig(
                    st_value=config.new_symbol_vaddrs[symbol.st_value],
                ),
            )

ElfRelocateSymbolsModifierConfig (ComponentConfig) dataclass

Configuration for changing symbol addresses in an ELF file, updating where symbols point in memory.

Attributes:

Name Type Description
new_symbol_vaddrs Dict[int, int]

Dictionary mapping original symbol virtual addresses to new virtual addresses

ElfSectionHeaderModifier (AbstractElfAttributeModifier, Modifier)

Modifies ELF section header (Shdr) fields including section name, type, flags (writable, allocatable, executable), virtual address, file offset, size, link fields, info field, alignment, and entry size. Section headers organize the file for linking and debugging. Use for adjusting section properties, changing section addresses or sizes, modifying section flags (making sections writable or executable), fixing section headers after modifications, or reconfiguring section relationships. Essential for maintaining ELF structure integrity after changes.

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 ElfSectionHeaderModifierConfig

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/modifier.py
async def modify(
    self,
    resource: Resource,
    config: ElfSectionHeaderModifierConfig,
):
    original_attributes = await resource.analyze(AttributesType[ElfSectionHeader])
    await self.serialize_and_patch(resource, original_attributes, config)

ElfSectionHeaderModifierConfig (ComponentConfig) dataclass

Configuration for modifying ELF section header (Shdr) fields that organize file structure for linking and debugging.

Attributes:

Name Type Description
sh_name Optional[int]

Index into section name string table

sh_type Optional[int]

Section type (SHT_PROGBITS, SHT_SYMTAB, SHT_STRTAB, etc.)

sh_flags Optional[int]

Section attributes (SHF_WRITE, SHF_ALLOC, SHF_EXECINSTR, etc.)

sh_addr Optional[int]

Virtual address of section in memory (if loaded)

sh_offset Optional[int]

File offset where section data begins

sh_size Optional[int]

Size of section in bytes

sh_link Optional[int]

Section index of associated section (meaning depends on type)

sh_info Optional[int]

Extra section information (meaning depends on type)

sh_addralign Optional[int]

Section alignment requirement (power of 2)

sh_entsize Optional[int]

Size of each entry if section holds table of fixed-size entries

ElfSymbolModifier (AbstractElfAttributeModifier, Modifier)

Modifies ELF symbol entry fields including name (string table index), value/address, size, binding (local/global/weak), type (function/object/section), visibility, and section index. These modifications change what the ELF header claims, not what's actually in the binary. Use when you need to update the symbol table that the OS loader will read (e.g., after manually modifying code locations), but note that modifying code doesn't automatically update these symbols - you must manually sync them.

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 ElfSymbolModifierConfig

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/modifier.py
async def modify(
    self,
    resource: Resource,
    config: ElfSymbolModifierConfig,
):
    original_attributes = await resource.analyze(AttributesType[ElfSymbol])
    await self.serialize_and_patch(resource, original_attributes, config)

ElfSymbolModifierConfig (ComponentConfig) dataclass

Configuration for modifying ELF symbol table entries that define functions, variables, and other symbols.

Attributes:

Name Type Description
st_name Optional[int]

Index into symbol name string table

st_value Optional[int]

Symbol value/address (typically virtual address for functions/variables)

st_size Optional[int]

Size of symbol in bytes (size of function or data object)

st_info Optional[int]

Symbol binding (local/global/weak) and type (function/object/section) packed into one byte

st_other Optional[int]

Symbol visibility (default/internal/hidden/protected)

st_shndx Optional[int]

Section index where symbol is defined (or special values like SHN_UNDEF)

ElfVirtualAddressModifier (AbstractElfAttributeModifier, Modifier)

Modifies individual pointer values within ELF pointer array sections, updating specific function pointer entries to reference new addresses. Each pointer can be independently modified. Use for redirecting specific constructor/destructor functions, changing function pointer table entries, updating callback addresses, modifying initialization function targets, or implementing function hooking via pointer tables. More surgical than ElfPointerArraySectionAddModifier which modifies all pointers uniformly.

modify(self, resource, config) async

Patches the virtual address

Source code in ofrak/core/elf/modifier.py
async def modify(
    self,
    resource: Resource,
    config: ElfVirtualAddressModifierConfig,
):
    """
    Patches the virtual address
    """
    original_attributes = await resource.analyze(AttributesType[ElfVirtualAddress])
    await self.serialize_and_patch(resource, original_attributes, config)

ElfVirtualAddressModifierConfig (ComponentConfig) dataclass

Attributes:

Name Type Description
value Optional[int]

an address