38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129  | import argparse
import os
import ofrak_ghidra
from ofrak import OFRAK, OFRAKContext, ResourceFilter, ResourceAttributeValueFilter
from ofrak.core import (
    BinaryPatchModifier,
    BinaryPatchConfig,
    CodeRegion,
    ComplexBlock,
    Instruction,
    LiefAddSegmentConfig,
    LiefAddSegmentModifier,
    ElfProgramHeader,
)
ASSETS_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), "assets"))
BINARY_FILE = os.path.join(ASSETS_DIR, "example_program")
KITTEH = rb"""
       | | | |  ___  | || |  ___   | | / /(_)  _     _
       | |_| | / _ \ | || | / _ \  | |/ /  _ _| |_ _| |_  _  _
       |  _  |/ /_\ \| || |/ / \ \ |   /  | |_   _|_   _|| |/ /
       | | | |\ ,___/| || |\ \_/ / | |\ \ | | | |_  | |_ | / /
       |_| |_| \___/ |_||_| \___/  |_| \_\|_| \___| \___||  /
                              _           _              / /
                             / \_______ /|_\             \/
                            /          /_/ \__
                           /             \_/ /
                         _|_              |/|_
                         _|_  O    _    O  _|_
                         _|_      (_)      _|_
                          \                 /
                           _\_____________/_
                          /  \/  (___)  \/  \
                          \__(  o     o  )__/       kitteh! demands obedience..."""
SEVEN_KITTEH = 7 * KITTEH + b"\x00"
PAGE_ALIGN = 0x1000
async def main(ofrak_context: OFRAKContext, file_path: str, output_file_name: str):
    root_resource = await ofrak_context.create_root_resource_from_file(file_path)
    # Add a segment
    empty_vaddr = 0x108000
    config = LiefAddSegmentConfig(empty_vaddr, PAGE_ALIGN, [0 for _ in range(0x2000)], "rw")
    await root_resource.run(LiefAddSegmentModifier, config)
    await root_resource.unpack_recursively(do_not_unpack=(CodeRegion,))
    file_segments = await root_resource.get_descendants_as_view(
        ElfProgramHeader, r_filter=ResourceFilter(tags=(ElfProgramHeader,))
    )
    new_segment = [seg for seg in file_segments if seg.p_vaddr == empty_vaddr].pop()
    kitty_bytes_offset = new_segment.p_offset
    # Add KITTEH to the new segment
    patch_config = BinaryPatchConfig(kitty_bytes_offset, SEVEN_KITTEH)
    await root_resource.run(BinaryPatchModifier, patch_config)
    # Hello world is being loaded by a lea instruction.
    # Let's point it to load from the new entry point instead!
    await root_resource.unpack_recursively()
    main_cb = await root_resource.get_only_descendant_as_view(
        v_type=ComplexBlock,
        r_filter=ResourceFilter(
            attribute_filters=(ResourceAttributeValueFilter(ComplexBlock.Symbol, "main"),)
        ),
    )
    main_cb_assembly = await main_cb.get_assembly()
    lea_instruction = await main_cb.resource.get_only_descendant_as_view(
        v_type=Instruction,
        r_filter=ResourceFilter(
            attribute_filters=(ResourceAttributeValueFilter(Instruction.Mnemonic, "lea"),)
        ),
    )
    ghidra_empty_vaddr = empty_vaddr + 0x100000  # Ghidra bases PIE executables at 0x100000
    kitty_offset = ghidra_empty_vaddr - lea_instruction.virtual_address - 7
    await lea_instruction.modify_assembly("lea", f"rdi, [rip + {kitty_offset}]")
    await root_resource.pack()
    await root_resource.flush_data_to_disk(output_file_name)
    print(f"Done! Output file written to {output_file_name}")
if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("--hello-world-file", default=BINARY_FILE)
    parser.add_argument("--output-file-name", default="./example_5_kitteh")
    args = parser.parse_args()
    ofrak = OFRAK()
    ofrak.discover(ofrak_ghidra)
    ofrak.run(main, args.hello_world_file, args.output_file_name)
  |