Created
February 17, 2025 13:13
-
-
Save veygax/5b3136a2d4316f050c08b8a707993c82 to your computer and use it in GitHub Desktop.
Python scripts I made for generating and applying custom hex patches.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
import sys | |
import json | |
import os | |
from pathlib import Path | |
import argparse | |
def apply_hex_patches(patches_file, target_file, output_file=None): | |
with open(patches_file, 'r') as f: | |
patch_data = json.load(f) | |
with open(target_file, 'rb') as f: | |
file_data = bytearray(f.read()) | |
if output_file is None: | |
target_path = Path(target_file) | |
output_file = f"{target_path.parent / target_path.stem}_patched{target_path.suffix}" | |
for patch in patch_data['patches']: | |
# Convert hex offset to integer | |
offset = int(patch['offset'], 16) | |
modified_bytes = bytes.fromhex(patch['modified_bytes'].replace(' ', '')) | |
# Handle file extension cases | |
if not patch['original_bytes'] and modified_bytes: | |
file_data.extend(modified_bytes) | |
elif not patch['modified_bytes']: | |
file_data = file_data[:offset] | |
else: | |
original_bytes = bytes.fromhex(patch['original_bytes'].replace(' ', '')) | |
if file_data[offset:offset + len(original_bytes)] != original_bytes: | |
raise ValueError(f"Original bytes at offset {patch['offset']} do not match the expected values") | |
file_data[offset:offset + len(modified_bytes)] = modified_bytes | |
with open(output_file, 'wb') as f: | |
f.write(file_data) | |
return output_file | |
def main(): | |
parser = argparse.ArgumentParser(description='Apply hex patches to a file') | |
parser.add_argument('patches_file', help='JSON file containing the patches') | |
parser.add_argument('target_file', help='File to apply patches to') | |
parser.add_argument('-o', '--output', help='Output file path (optional)') | |
args = parser.parse_args() | |
if not args.patches_file.lower().endswith('.json'): | |
print("Error: Patches file must be a JSON file") | |
sys.exit(1) | |
try: | |
output_path = apply_hex_patches(args.patches_file, args.target_file, args.output) | |
print(f"Patches applied successfully. Output file: {output_path}") | |
except Exception as e: | |
print(f"Error: {str(e)}") | |
sys.exit(1) | |
if __name__ == "__main__": | |
main() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
import sys | |
import json | |
import os | |
from pathlib import Path | |
import argparse | |
def generate_hex_patches(original_file, modified_file, output_file=None): | |
# Read both files in binary mode | |
with open(original_file, 'rb') as f1, open(modified_file, 'rb') as f2: | |
original_data = f1.read() | |
modified_data = f2.read() | |
patches = [] | |
offset = 0 | |
current_patch = None | |
# Compare byte by byte | |
for orig_byte, mod_byte in zip(original_data, modified_data): | |
if orig_byte != mod_byte: | |
if current_patch is None: | |
current_patch = { | |
'offset': f'0x{offset:08x}', # Convert to hex string with 8 digits | |
'original_bytes': [orig_byte], | |
'modified_bytes': [mod_byte] | |
} | |
else: | |
# If this difference is consecutive, append to current patch | |
if offset == int(current_patch['offset'], 16) + len(current_patch['original_bytes']): | |
current_patch['original_bytes'].append(orig_byte) | |
current_patch['modified_bytes'].append(mod_byte) | |
else: | |
# Convert bytes to hex strings and finalize current patch | |
current_patch['original_bytes'] = ' '.join([f'{b:02x}' for b in current_patch['original_bytes']]) | |
current_patch['modified_bytes'] = ' '.join([f'{b:02x}' for b in current_patch['modified_bytes']]) | |
patches.append(current_patch) | |
current_patch = { | |
'offset': f'0x{offset:08x}', # Convert to hex string with 8 digits | |
'original_bytes': [orig_byte], | |
'modified_bytes': [mod_byte] | |
} | |
else: | |
if current_patch is not None: | |
# Convert bytes to hex strings and finalize current patch | |
current_patch['original_bytes'] = ' '.join([f'{b:02x}' for b in current_patch['original_bytes']]) | |
current_patch['modified_bytes'] = ' '.join([f'{b:02x}' for b in current_patch['modified_bytes']]) | |
patches.append(current_patch) | |
current_patch = None | |
offset += 1 | |
if current_patch is not None: | |
current_patch['original_bytes'] = ' '.join([f'{b:02x}' for b in current_patch['original_bytes']]) | |
current_patch['modified_bytes'] = ' '.join([f'{b:02x}' for b in current_patch['modified_bytes']]) | |
patches.append(current_patch) | |
if len(original_data) != len(modified_data): | |
if len(original_data) < len(modified_data): | |
extra_bytes = modified_data[len(original_data):] | |
patches.append({ | |
'offset': f'0x{len(original_data):08x}', # Convert to hex string with 8 digits | |
'original_bytes': '', | |
'modified_bytes': ' '.join([f'{b:02x}' for b in extra_bytes]) | |
}) | |
else: | |
missing_bytes = original_data[len(modified_data):] | |
patches.append({ | |
'offset': f'0x{len(modified_data):08x}', # Convert to hex string with 8 digits | |
'original_bytes': ' '.join([f'{b:02x}' for b in missing_bytes]), | |
'modified_bytes': '' | |
}) | |
if output_file is None: | |
original_path = Path(original_file) | |
output_file = f"{original_path.stem}_{original_path.suffix[1:]}_patches.json" | |
else: | |
if not output_file.lower().endswith('.json'): | |
output_file += '.json' | |
with open(output_file, 'w') as f: | |
json.dump({ | |
'original_file': original_file, | |
'modified_file': modified_file, | |
'patches': patches | |
}, f, indent=2) | |
return output_file | |
def main(): | |
parser = argparse.ArgumentParser(description='Generate hex patches between two files') | |
parser.add_argument('original_file', help='Path to the original file') | |
parser.add_argument('modified_file', help='Path to the modified file') | |
parser.add_argument('-o', '--output', help='Output JSON file path (optional)') | |
args = parser.parse_args() | |
try: | |
output_path = generate_hex_patches(args.original_file, args.modified_file, args.output) | |
print(f"Patch file generated successfully: {output_path}") | |
except Exception as e: | |
print(f"Error: {str(e)}") | |
sys.exit(1) | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment