Open whyitfor opened 8 months ago
The following code snippet adapting example_1 can be used to recreate this issue:
"""
This example showcases the simplicity of performing binary code modifications with OFRAK.
The input program is a compiled binary ELF file which prints "Hello, World!" to the console.
The example performs code modification in the input binary (without extension). It leverages
Ghidra to analyze the executable binary, and Keystone to rewrite an instruction so that the
binary loops back to its beginning instead of returning and exiting at the end of the main function.
Someone is chasing its tail and never catching it 😹
"""
import argparse
import dataclasses
import os
import ofrak_angr
from ofrak import OFRAK, OFRAKContext, ResourceFilter, ResourceAttributeValueFilter
from ofrak.core import (
ProgramAttributes,
BinaryPatchConfig,
BinaryPatchModifier,
ComplexBlock,
Instruction,
)
from ofrak.service.assembler.assembler_service_keystone import KeystoneAssemblerService
ASSETS_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), "assets"))
BINARY_FILE = os.path.join(ASSETS_DIR, "example_program")
async def main(ofrak_context: OFRAKContext, file_path: str, output_file_name: str):
# Create resource
binary_resource = await ofrak_context.create_root_resource_from_file(file_path)
# Unpack resource
await binary_resource.unpack_recursively()
# Get the "main" function complex block
main_cb = await binary_resource.get_only_descendant_as_view(
v_type=ComplexBlock,
r_filter=ResourceFilter(
attribute_filters=(ResourceAttributeValueFilter(ComplexBlock.Symbol, "main"),)
),
)
print(f"main: {main_cb}")
# What we want
main_cb.name = "wow"
await main_cb.resource.save()
# What actually works
# main_cb.resource.add_view(dataclasses.replace(main_cb, name="wow"))
# await main_cb.resource.save()
wow = await binary_resource.get_only_descendant_as_view(
v_type=ComplexBlock,
r_filter=ResourceFilter(
attribute_filters=(ResourceAttributeValueFilter(ComplexBlock.Symbol, "wow"),)
),
)
print(f"wow: {wow}")
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--hello-world-file", default=BINARY_FILE)
parser.add_argument("--output-file-name", default="./example_1_meow")
args = parser.parse_args()
ofrak = OFRAK()
ofrak.injector.discover(ofrak_angr)
ofrak.run(main, args.hello_world_file, args.output_file_name)
What is the use case for the feature? It would be nice to be able to updated a
ResourceView
field in OFRAK with code similar to the following:Unfortunately, this fails, since the filter for a
ComplexBlock
withSymbol
returns no results. Interestingly enough, filtering for theSymbol
"main"
works, but this actually returns aComplexBlock
whereComplexBlock.name == "wow"
!OFRAK currently works around this issue with the following unidiomatic, unintuitive code: https://github.com/redballoonsecurity/ofrak/blob/master/ofrak_core/ofrak/core/patch_maker/linkable_binary.py#L304.
Does the feature contain any proprietary information about another company's intellectual property? No.
How would you implement this feature? I would like to see the following code update indexable attributes:
This probably involves updating indexable attributes whenever the underlying attribute is updated. We should never have inconsistency between the indexable attributes and the actual attributes, and ideally users do not need to know much about this distinction.
Are there any (reasonable) alternative approaches? Perhaps -- open to them!
Are you interested in implementing it yourself? Sure!