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 |