2025-10-19 21:24:14 +00:00
|
|
|
from copapy._binwrite import data_reader, Command, ByteOrder
|
2025-10-08 20:59:51 +00:00
|
|
|
import argparse
|
2025-10-30 09:55:23 +00:00
|
|
|
from typing import Literal
|
|
|
|
|
|
|
|
|
|
def patch(data: bytearray, offset: int, patch_mask: int, value: int, byteorder: Literal['little', 'big']) -> None:
|
|
|
|
|
# Read 4 bytes at the offset as a little-endian uint32
|
|
|
|
|
original = int.from_bytes(data[offset:offset+4], byteorder)
|
|
|
|
|
|
|
|
|
|
# Apply the patch
|
|
|
|
|
new_value = (original & ~patch_mask) | (value & patch_mask)
|
|
|
|
|
|
|
|
|
|
# Write the new value back to the bytearray
|
|
|
|
|
data[offset:offset+4] = new_value.to_bytes(4, byteorder)
|
2025-10-07 20:56:04 +00:00
|
|
|
|
2025-10-08 20:59:51 +00:00
|
|
|
if __name__ == "__main__":
|
|
|
|
|
parser = argparse.ArgumentParser()
|
|
|
|
|
parser.add_argument("input_file", type=str, help="Input file path with copapy commands")
|
|
|
|
|
parser.add_argument("output_file", type=str, help="Output file with patched code")
|
2025-10-14 20:59:51 +00:00
|
|
|
parser.add_argument("--data_section_offset", type=int, default=0x3000, help="Offset for data relative to code section")
|
2025-10-08 20:59:51 +00:00
|
|
|
parser.add_argument("--byteorder", type=str, choices=['little', 'big'], default='little', help="Select byteorder")
|
|
|
|
|
args = parser.parse_args()
|
2025-10-07 20:56:04 +00:00
|
|
|
|
2025-10-08 20:59:51 +00:00
|
|
|
input_file: str = args.input_file
|
|
|
|
|
output_file: str = args.output_file
|
|
|
|
|
data_section_offset: int = args.data_section_offset
|
|
|
|
|
byteorder: ByteOrder = args.byteorder
|
2025-10-07 20:56:04 +00:00
|
|
|
|
2025-10-30 09:55:23 +00:00
|
|
|
with open(input_file, mode='rb') as f_in:
|
|
|
|
|
dr = data_reader(f_in.read(), byteorder)
|
2025-10-07 20:56:04 +00:00
|
|
|
|
2025-10-08 20:59:51 +00:00
|
|
|
buffer_index: int = 0
|
|
|
|
|
end_flag: int = 0
|
|
|
|
|
program_data: bytearray = bytearray([])
|
2025-10-07 20:56:04 +00:00
|
|
|
|
2025-10-08 20:59:51 +00:00
|
|
|
while (end_flag == 0):
|
|
|
|
|
com = dr.read_com()
|
2025-10-07 20:56:04 +00:00
|
|
|
|
2025-10-08 20:59:51 +00:00
|
|
|
if com == Command.ALLOCATE_DATA:
|
|
|
|
|
size = dr.read_int()
|
|
|
|
|
print(f"ALLOCATE_DATA size={size}")
|
|
|
|
|
elif com == Command.ALLOCATE_CODE:
|
|
|
|
|
size = dr.read_int()
|
|
|
|
|
program_data = bytearray(size)
|
|
|
|
|
print(f"ALLOCATE_CODE size={size}")
|
|
|
|
|
elif com == Command.COPY_DATA:
|
|
|
|
|
offs = dr.read_int()
|
|
|
|
|
size = dr.read_int()
|
|
|
|
|
datab = dr.read_bytes(size)
|
|
|
|
|
print(f"COPY_DATA offs={offs} size={size} data={' '.join(hex(d) for d in datab)}")
|
|
|
|
|
elif com == Command.COPY_CODE:
|
|
|
|
|
offs = dr.read_int()
|
|
|
|
|
size = dr.read_int()
|
|
|
|
|
datab = dr.read_bytes(size)
|
|
|
|
|
program_data[offs:offs + size] = datab
|
|
|
|
|
print(f"COPY_CODE offs={offs} size={size} data={' '.join(hex(d) for d in datab[:5])}...")
|
|
|
|
|
elif com == Command.PATCH_FUNC:
|
|
|
|
|
offs = dr.read_int()
|
2025-10-29 21:29:15 +00:00
|
|
|
mask = dr.read_int()
|
2025-10-08 20:59:51 +00:00
|
|
|
value = dr.read_int(signed=True)
|
2025-10-30 09:55:23 +00:00
|
|
|
patch(program_data, offs, mask, value, byteorder)
|
2025-10-29 21:29:15 +00:00
|
|
|
print(f"PATCH_FUNC patch_offs={offs} mask=0x{mask:x} value={value}")
|
2025-10-08 20:59:51 +00:00
|
|
|
elif com == Command.PATCH_OBJECT:
|
|
|
|
|
offs = dr.read_int()
|
2025-10-29 21:29:15 +00:00
|
|
|
mask = dr.read_int()
|
2025-10-08 20:59:51 +00:00
|
|
|
value = dr.read_int(signed=True)
|
2025-10-30 09:55:23 +00:00
|
|
|
patch(program_data, offs, mask, value + data_section_offset, byteorder)
|
2025-10-29 21:29:15 +00:00
|
|
|
print(f"PATCH_OBJECT patch_offs={offs} mask=ox{mask:x} value={value}")
|
2025-10-12 21:21:34 +00:00
|
|
|
elif com == Command.ENTRY_POINT:
|
|
|
|
|
rel_entr_point = dr.read_int()
|
|
|
|
|
print(f"ENTRY_POINT rel_entr_point={rel_entr_point}")
|
2025-10-08 20:59:51 +00:00
|
|
|
elif com == Command.RUN_PROG:
|
2025-10-18 21:20:27 +00:00
|
|
|
print("RUN_PROG")
|
2025-10-08 20:59:51 +00:00
|
|
|
elif com == Command.READ_DATA:
|
|
|
|
|
offs = dr.read_int()
|
|
|
|
|
size = dr.read_int()
|
|
|
|
|
print(f"READ_DATA offs={offs} size={size}")
|
|
|
|
|
elif com == Command.FREE_MEMORY:
|
|
|
|
|
print("READ_DATA")
|
2025-10-12 21:21:34 +00:00
|
|
|
elif com == Command.END_COM:
|
|
|
|
|
print("END_COM")
|
2025-10-08 20:59:51 +00:00
|
|
|
end_flag = 1
|
|
|
|
|
else:
|
|
|
|
|
assert False, f"Unknown command: {com}"
|
2025-10-07 20:56:04 +00:00
|
|
|
|
2025-10-30 09:55:23 +00:00
|
|
|
with open(output_file, mode='wb') as f_out:
|
|
|
|
|
f_out.write(program_data)
|
2025-10-07 20:56:04 +00:00
|
|
|
|
2025-10-08 20:59:51 +00:00
|
|
|
print(f"Code written to {output_file}.")
|