Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updating ResourceView attributes is unintuitive. #411

Open
whyitfor opened this issue Feb 6, 2024 · 1 comment
Open

Updating ResourceView attributes is unintuitive. #411

whyitfor opened this issue Feb 6, 2024 · 1 comment
Labels
enhancement New feature or request

Comments

@whyitfor
Copy link
Contributor

whyitfor commented Feb 6, 2024

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:

>>> main_cb
ComplexBlock(virtual_address=4195898, size=38, name='main')
>>> 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"),)
        ),
    )
>>> wow
ComplexBlock(virtual_address=4195898, size=38, name='wow')
>>> main_cb
ComplexBlock(virtual_address=4195898, size=38, name='wow')

Unfortunately, this fails, since the filter for a ComplexBlock with Symbol returns no results. Interestingly enough, filtering for the Symbol "main" works, but this actually returns a ComplexBlock where ComplexBlock.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:

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"),)
    ),
)

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!

@whyitfor
Copy link
Contributor Author

whyitfor commented Feb 6, 2024

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)

@whyitfor whyitfor added the enhancement New feature or request label Apr 19, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

1 participant