Last active
August 13, 2022 09:04
-
-
Save jn0/6bcbc3f00e39efe76e0a52de423f30e7 to your computer and use it in GitHub Desktop.
Two-pass multi-run-safe micro (85 LOC) patcher in python
This file contains 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 | |
''' | |
Two-pass re-run safe micro (85 LOC) patcher in python. | |
Sample patch config (yes, it's in YAML): | |
---->8---- | |
version: 0 | |
title: tribute to sublimetext_4126_crack_linux.py by dmknght @github | |
file: /tmp/sublime_text | |
# size: 8862888 # optional check for file size | |
patch: | |
- offset: 0x0002d78a | |
value: 34 31 32 36 # "4126" | |
- { offset: 0x00385797, value: 0f 84, replace: 0f 85 } | |
- { offset: 0x0038583d, value: 74 1f, replace: 75 1f } | |
# vim:set ft=yaml ai et ts=2 sts=2 sw=2:EOF # | |
---->8---- | |
''' | |
import sys, yaml, os | |
VERSION = 0 | |
def hexx(z): return ' '.join(f'{b:02x}' for b in z) | |
def binn(p): | |
data = ''.join(p.strip().split()).lower() | |
assert data and len(data) % 2 == 0, f'bad data length {len(data)}' | |
return bytes(int(data[i:i+2], 16) for i in range(0, len(data), 2)) | |
def read_at(f, pos, size): | |
assert f.seek(pos, os.SEEK_SET) == pos | |
return f.read(size) | |
def write_at(f, pos, data): | |
if data: | |
print(f"\t{pos:08x}", len(data), '/', hexx(data)) | |
assert f.seek(pos, os.SEEK_SET) == pos | |
f.write(data) | |
def verify(f, check, n): | |
offset, pattern, repl = check['offset'], binn(check['value']), check.get('replace') | |
if repl: | |
repl = binn(repl) | |
value = read_at(f, offset, len(repl)) | |
if value == repl: | |
print(f"{n:6d}: {offset:08x}", len(value), '=', hexx(value), '!=', hexx(pattern), 'patched') | |
return None | |
value = read_at(f, offset, len(pattern)) | |
assert pattern == value, f"at {offset:08x}: [{hexx(value)}] != [{hexx(pattern)}]" | |
print(f"{n:6d}: {offset:08x}", len(pattern), '=', hexx(pattern), 'ok,', 'patch' if repl else 'keep') | |
return repl | |
def check(cf): | |
print('check>', cf['file'], f'({len(cf["patch"])})') | |
with open(cf['file'], 'rb') as f: | |
size = cf.get('size') | |
assert not size or size == f.seek(0, os.SEEK_END), f'File size not matched {size}' | |
return [p for i, p in enumerate(cf['patch']) if verify(f, p, i + 1)] | |
def apply(cf, patches): | |
if patches: | |
print('patch>', cf['file'], f'({len(patches)} patch{"es" if len(patches) > 1 else ""} to apply)') | |
with open(cf['file'], 'rb+') as f: | |
for i, patch in enumerate(patches): | |
write_at(f, patch['offset'], verify(f, patch, i + 1)) | |
print('*', cf['file'], 'patched using', cf['__self']) | |
else: | |
print('*', cf['file'], 'has already been patched using', cf['__self'], 'or alike') | |
def patch(cf): | |
if cf.get('title'): print('#', cf.get('title')) | |
apply(cf, check(cf)) | |
try: | |
for arg in sys.argv[1:]: | |
print('@', arg) | |
with open(arg) as f: | |
cf = yaml.load(f, yaml.SafeLoader) | |
cf['__self'] = arg | |
assert cf.get('version') == VERSION, f'Patch version is not {VERSION!r} in {cf["__self"]!r}' | |
patch(cf) | |
except Exception as e: | |
print('!', e.__class__.__name__ + ':', e, file=sys.stderr) | |
sys.exit(1) | |
# vim:set ft=python ai et ts=4 sts=4 sw=4 cc=80:EOF # |
This file contains 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
version: 0 | |
title: tribute to sublimetext_4126_crack_linux.py by dmknght @github | |
file: /tmp/sublime_text | |
size: 8862888 # optional | |
patch: | |
- offset: 0x0002d78a | |
value: 34 31 32 36 # "4126", just to check, no "replace:" part | |
- { offset: 0x00385797, value: 0f 84, replace: 0f 85 } # one may have fun writing | |
- { offset: 0x0038583d, value: 74 1f, replace: 75 1f } # patches in "table form" | |
# vim:set ft=yaml ai et ts=2 sts=2 sw=2:EOF # |
This file contains 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
$ cp /snap/sublime-text/112/opt/sublime_text/sublime_text /tmp/ | |
$ ./patch.py patch.pth | |
@ patch.pth | |
# tribute to sublimetext_4126_crack_linux.py by dmknght @github | |
check> /tmp/sublime_text (3) | |
1: 0002d78a 4 = 34 31 32 36 ok, keep | |
2: 00385797 2 = 0f 84 ok, patch | |
3: 0038583d 2 = 74 1f ok, patch | |
patch> /tmp/sublime_text (2 patches to apply) | |
1: 00385797 2 = 0f 84 ok, patch | |
00385797 2 / 0f 85 | |
2: 0038583d 2 = 74 1f ok, patch | |
0038583d 2 / 75 1f | |
* /tmp/sublime_text patched using patch.pth | |
$ ./patch.py patch.pth | |
@ patch.pth | |
# tribute to sublimetext_4126_crack_linux.py by dmknght @github | |
check> /tmp/sublime_text (3) | |
1: 0002d78a 4 = 34 31 32 36 ok, keep | |
2: 00385797 2 = 0f 85 != 0f 84 patched | |
3: 0038583d 2 = 75 1f != 74 1f patched | |
* /tmp/sublime_text has already been patched using patch.pth or alike |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment