forked from RubyFeinstein/generic_patcher
-
Notifications
You must be signed in to change notification settings - Fork 0
/
generic_patcher.py
166 lines (139 loc) · 5.69 KB
/
generic_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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
#!python
from subprocess import check_call, STDOUT
from os import unlink
import os
from os.path import exists
import struct
class BasePatch(object):
def __init__(self, pos, data):
self.pos = pos
self.data = data
def apply(self, firmware, **kargs):
firmware += max(0, self.pos - len(firmware)) * "\x00"
return firmware[:self.pos] + self.data + firmware[self.pos+len(self.data):]
class ArmPatch(BasePatch):
def __init__(self, pos, lines, extra_data=""):
self.pos = pos
self.lines = lines
self.extra_data = extra_data
def _delete_temp(self,path):
if exists(path):
unlink(path)
def _generate_bytes(self):
try:
temp_file = file('temp.asm','w')
for i in self.lines:
temp_file.write(i + "\n")
temp_file.close()
check_call("arm-elf-as -mthumb -o temp.o temp.asm", shell=True)
check_call("arm-elf-objcopy -O binary temp.o temp.bin", shell=True)
data = file('temp.bin','rb').read() + self.extra_data
print "data_len: %d" % len(data)
print "data: %s" % data.encode("hex")
return data
finally:
self._delete_temp("temp.bin")
self._delete_temp("temp.o")
self._delete_temp("temp.asm")
def get_bytes(self):
return self._generate_bytes()
def apply(self, firmware, **kargs):
self.data = self._generate_bytes()
return super(ArmPatch, self).apply(firmware)
class CPatch(BasePatch):
def __init__(self, pos, filename, extra_data="", max_size=0xffffffff):
self.pos = pos
self.filename = filename
self.extra_data = extra_data
self.max_size = max_size
def _delete_temp(self,path):
if exists(path):
unlink(path)
def _generate_bytes(self, define):
try:
defines = " ".join(["-D%s=%s" % (i, define[i]) for i in define])
check_call("arm-elf-gcc %s -Wno-multichar -nostdlib -mcpu=arm7tdmi -mthumb -fPIC -O1 -c -o temp.o %s" % (defines, self.filename), shell=True)
check_call("arm-elf-ld -EL -emy_start temp.o -o temp2.o", shell=True)
check_call("arm-elf-objcopy -O binary temp2.o temp.bin", shell=True)
data = file('temp.bin','rb').read() + self.extra_data
print "generating: %s" % self.filename
print "data_len: %d" % len(data)
print "data: %s" % data.encode("hex")
if(len(data) > self.max_size):
raise Exception("detour on address 0x%08x size limit reached (%d > %d)" % (self.pos, len(data), self.max_size))
return data
finally:
self._delete_temp("temp.bin")
self._delete_temp("temp.o")
self._delete_temp("temp2.o")
def get_bytes(self):
return self._generate_bytes()
def apply(self, firmware, **kargs):
define = kargs.get("define", {})
self.data = self._generate_bytes(define)
return super(CPatch, self).apply(firmware)
class TrapPatch(BasePatch):
def __init__(self, pos):
self.pos = pos
def apply(self, firmware, **kargs):
self.data = "00e8".decode("hex") # illegal opcode
return super(TrapPatch, self).apply(firmware)
class PutPatch(BasePatch):
def __init__(self, pos, data):
self.pos = pos
self.data = data
class ReplacePatch(BasePatch):
def __init__(self, search, replace):
if len(search) != len(replace):
raise Exception("len(search) != len(replace)")
self.search = search
self.replace = replace
def apply(self, firmware, **kargs):
self.data = "00e8".decode("hex") # illegal opcode
return firmware.replace(self.search, self.replace)
class BLPatch(BasePatch):
def __init__(self, pos, to):
self.pos = pos
self.data = self.patch_bl(pos, to)
def patch_bl(self, src, dst):
#patched_file = list(file(src_file, "rb").read())
diff = dst - src - 4
#print hex(diff)
diff_upper = (diff >> 12) & 0x7ff
diff_lower = (diff & 0xfff) >> 1
a = (0xF000 | diff_upper)# << 16
b = (0xF800 | diff_lower)
c = (a << 16) | b
print "patched: %X to call %X BL opcode: %X" % (src, dst, c)
return struct.pack("<HH", a, b)
class BPatch(BasePatch):
''' Generates a B.W opcode patch'''
def __init__(self, pos, to, real_from=None):
'''Creates a B.W patch at pos which will jump to the 'to' destination.
The read_from paramter needs to be used when the opcodes are copied
from the pos address to another, and it should contain the address
which the patch will run from.'''
if real_from == None:
real_from = pos
self.pos = pos
self.data = self.patch_b(real_from, to)
def patch_b(self, src, dst):
diff = dst - src - 4
diff_upper = (diff >> 12) & 0x3ff
diff_lower = ((diff & 0xfff) >> 1) | (((diff >>22)&1) << 11) | (((diff >> 23) & 1) <<13)
a = (0xF000 | diff_upper)# << 16
b = (0x9000 | diff_lower)
if diff < 0:
a |= 0x0400
else:
b = b^ 0x2800
c = (a << 16) | b
print "patched: %X to call %X B.W opcode: %s" % (src, dst,
struct.pack("<HH", a, b).encode('hex'))
return struct.pack("<HH", a, b)
def patch_firmware(src,dst, patchs, extra = "", **kargs):
firmware = file(src,'rb').read()
for p in patchs:
firmware = p.apply(firmware, **kargs)
firmware += extra
firmware = file(dst,'wb').write(firmware)