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

ElfAddStringModifierConfig(strings: Union[Tuple[str, ...], str])

ElfDynamicEntryModifier (AbstractElfAttributeModifier, Modifier)

The ElfRelaModifier updates values in an Elf{32, 64}_Dyn struct

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)

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

ElfHeaderModifierConfig(e_type: Optional[int] = None, e_machine: Optional[int] = None, e_version: Optional[int] = None, e_entry: Optional[int] = None, e_phoff: Optional[int] = None, e_shoff: Optional[int] = None, e_flags: Optional[int] = None, e_ehsize: Optional[int] = None, e_phentsize: Optional[int] = None, e_phnum: Optional[int] = None, e_shentsize: Optional[int] = None, e_shnum: Optional[int] = None, e_shstrndx: Optional[int] = None)

ElfPointerArraySectionAddModifier (Modifier)

The ElfPointerArrayAddModifier updates batches of pointer values

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)

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

ElfProgramHeaderModifierConfig(p_type: Optional[int] = None, p_offset: Optional[int] = None, p_vaddr: Optional[int] = None, p_paddr: Optional[int] = None, p_filesz: Optional[int] = None, p_memsz: Optional[int] = None, p_flags: Optional[int] = None, p_align: Optional[int] = None)

ElfRelaModifier (AbstractElfAttributeModifier, Modifier)

The ElfRelaModifier updates values in an Elf{32, 64}_Rela struct

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

ElfRelocateSymbolsModifierConfig(new_symbol_vaddrs: Dict[int, int])

ElfSectionHeaderModifier (AbstractElfAttributeModifier, Modifier)

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

ElfSectionHeaderModifierConfig(sh_name: Optional[int] = None, sh_type: Optional[int] = None, sh_flags: Optional[int] = None, sh_addr: Optional[int] = None, sh_offset: Optional[int] = None, sh_size: Optional[int] = None, sh_link: Optional[int] = None, sh_info: Optional[int] = None, sh_addralign: Optional[int] = None, sh_entsize: Optional[int] = None)

ElfSymbolModifier (AbstractElfAttributeModifier, Modifier)

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

ElfSymbolModifierConfig(st_name: Optional[int] = None, st_value: Optional[int] = None, st_size: Optional[int] = None, st_info: Optional[int] = None, st_other: Optional[int] = None, st_shndx: Optional[int] = None)

ElfVirtualAddressModifier (AbstractElfAttributeModifier, Modifier)

The ElfVirtualAddressModifier updates a pointer value

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