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: Union[int, NoneType] = None, e_machine: Union[int, NoneType] = None, e_version: Union[int, NoneType] = None, e_entry: Union[int, NoneType] = None, e_phoff: Union[int, NoneType] = None, e_shoff: Union[int, NoneType] = None, e_flags: Union[int, NoneType] = None, e_ehsize: Union[int, NoneType] = None, e_phentsize: Union[int, NoneType] = None, e_phnum: Union[int, NoneType] = None, e_shentsize: Union[int, NoneType] = None, e_shnum: Union[int, NoneType] = None, e_shstrndx: Union[int, NoneType] = 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: Union[int, NoneType] = None, p_offset: Union[int, NoneType] = None, p_vaddr: Union[int, NoneType] = None, p_paddr: Union[int, NoneType] = None, p_filesz: Union[int, NoneType] = None, p_memsz: Union[int, NoneType] = None, p_flags: Union[int, NoneType] = None, p_align: Union[int, NoneType] = 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: Union[int, NoneType] = None, sh_type: Union[int, NoneType] = None, sh_flags: Union[int, NoneType] = None, sh_addr: Union[int, NoneType] = None, sh_offset: Union[int, NoneType] = None, sh_size: Union[int, NoneType] = None, sh_link: Union[int, NoneType] = None, sh_info: Union[int, NoneType] = None, sh_addralign: Union[int, NoneType] = None, sh_entsize: Union[int, NoneType] = 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: Union[int, NoneType] = None, st_value: Union[int, NoneType] = None, st_size: Union[int, NoneType] = None, st_info: Union[int, NoneType] = None, st_other: Union[int, NoneType] = None, st_shndx: Union[int, NoneType] = 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 |