-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathpatcher.py
105 lines (71 loc) · 3.1 KB
/
patcher.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
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
#!/usr/bin/env python3
import struct
import sys
import logging
logging.basicConfig()
LOGGER = logging.getLogger('patcher')
def patch(originaldata, patchset):
"""
Apply KXAM patches to a target
Patch format
4 byte offset
4 byte count
4 byte * count patch payload
:param originaldata:
:param patchset:
:return:
"""
# Get the patch offset
# Get the patch size
patched_data = originaldata
currentoffset = 0
patchoffsetbytes = bytes(patchset[currentoffset:currentoffset + 4])
while (patchoffsetbytes != b'\xFF\xFF\xFF\xFF'):
patchoffsetbytes = bytes(patchset[currentoffset:currentoffset + 4])
patchoffset = struct.unpack('>I', patchoffsetbytes)[0]
LOGGER.debug('patch offset: ' + str(hex(patchoffset)))
currentoffset += 4
patchcountbytes = bytes(patchset[currentoffset:currentoffset + 4])
patchcount = struct.unpack('>I', patchcountbytes)[0]
LOGGER.debug('patch count : ' + str(hex(patchcount)))
LOGGER.debug('payload size: ' + str(hex(patchcount * 4)))
currentoffset += 4
patchpayloadbytes = bytes(patchset[currentoffset:currentoffset + 4 * patchcount])
LOGGER.debug('payload : ' + str(patchpayloadbytes))
patched_data[patchoffset:patchoffset + patchcount] = [patchpayloadbytes]
currentoffset += 4 * patchcount
return bytes(patched_data)
"""
There are two implementations of the actual patching algorithm here because writing directly to files is much lighter on MemoryStream
There is no reason to completely load the files into RAM before modifying when they can be modified InitializeComponent
"""
def main(argv):
target = argv[1] if len(argv) > 0 else None
patch = argv[2] if len(argv) > 1 else None
if not (target and patch):
print('Usage: applypatch.py target.bin patches.kxam')
# Patch format
# 4 byte offet
# 4 byte count
# 4 byte * count patch payload
with open(patch, 'rb') as patchfile:
with open(target, 'r+b') as targetfile:
while (patchfile.readable()):
patchoffsetbytes = patchfile.read(4)
if patchoffsetbytes == b'\xFF\xFF\xFF\xFF':
break
patchoffset = struct.unpack('>I', patchoffsetbytes)[0]
print('patchoffset: ' + str(hex(patchoffset)))
patchcountbytes = patchfile.read(4)
patchcount = struct.unpack('>I', patchcountbytes)[0]
print('patchcount: ' + str(hex(patchcount)))
print('expected payload size: ' + str(hex(patchcount * 4)))
patchpayloadbytes = patchfile.read(4 * patchcount)
print('payload length: ' + str(hex(len(patchpayloadbytes))))
print('payload: ' + str(patchpayloadbytes))
print('Writing patch of length ' + str(hex(patchcount)) + ' to offset ' + str(hex(patchoffset)))
targetfile.seek(patchoffset, 0)
targetfile.write(patchpayloadbytes)
print('Successfully wrote patches')
if __name__ == '__main__':
main(sys.argv)