summaryrefslogtreecommitdiff
path: root/tools/hooks.py
blob: 13c4e19a6fd9339d0fbb0b6a92701b20318b898e (plain)
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
import binascii
import struct

u32 = struct.Struct('>I')

def make_branch_insn(fromAddr, toAddr, branchType):
	branchTypes = ['b', 'bl', 'ba', 'bla']
	if branchType not in branchTypes:
		raise ValueError, 'invalid branch type: %s' % branchType
	
	extra = branchTypes.index(branchType)
	
	distance = toAddr - fromAddr
	if distance >= 0x2000000 or distance <= -0x2000000:
		raise ValueError, 'branching too far: %08x to %08x' % (fromAddr, toAddr)
	
	return (distance & 0x3FFFFFC) | 0x48000000 | extra



class HookContext(object):
	"""Object which can be used by each hook type to hold data."""
	
	def __init__(self):
		self.hooks = []



class Hook(object):
	"""Generic hook class"""
	
	has_context = False
	required_data = []
	
	def __init__(self, builder, module, data):
		"""Sets up a hook"""
		self.builder = builder
		self.module = module
		self.data = data
		
		if self.has_context:
			hookType = type(self)
			self.context = builder._hook_contexts[hookType]
			self.context.hooks.append(self)
		
		# validate the hook's data
		current_config_name = builder._config_short_name
		
		for field in self.required_data:
			field = field.replace('%CONFIG%', current_config_name)
			if field not in data:
				raise ValueError, 'hook %s : %s is missing the field %s' % (module.moduleName, data['name'], field)
	
	def create_patches(self):
		pass



class BasicPatchHook(Hook):
	"""Hook that simply patches data to an address"""
	
	required_data = ['addr_%CONFIG%', 'data']
	
	def __init__(self, builder, module, data):
		Hook.__init__(self, builder, module, data)
	
	def create_patches(self):
		addr = self.data['addr_%s' % self.builder._config_short_name]
		
		hex_data = self.data['data']
		
		whitespace = ' \n\r\t'
		for char in whitespace:
			hex_data = hex_data.replace(char, '')
		
		patch = binascii.unhexlify(hex_data)
		
		self.builder._add_patch(addr, patch)



class BranchInsnHook(Hook):
	"""Hook that replaces the instruction at a specific address with a branch"""
	
	required_data = ['branch_type', 'src_addr_%CONFIG%']
	
	def __init__(self, builder, module, data):
		Hook.__init__(self, builder, module, data)
	
	def create_patches(self):
		try:
			target_func = self.data['target_func']
		except KeyError:
			target_func = self.data['target_func_%s' % self.builder._config_short_name]
		
		if isinstance(target_func, str):
			target_func = self.builder._find_func_by_symbol(target_func)
		else:
			# assume it's an address
			pass
		
		
		src_addr = self.data['src_addr_%s' % self.builder._config_short_name]
		branch_insn = make_branch_insn(src_addr, target_func, self.data['branch_type'])
		
		self.builder._add_patch(src_addr, u32.pack(branch_insn))



class AddFunctionPointerHook(Hook):
	"""Hook that places a function pointer at an address"""
	
	required_data = ['src_addr_%CONFIG%']
	
	def __init__(self, builder, module, data):
		Hook.__init__(self, builder, module, data)
	
	def create_patches(self):
		try:
			target_func = self.data['target_func']
		except KeyError:
			target_func = self.data['target_func_%s' % self.builder._config_short_name]
		
		if isinstance(target_func, str):
			target_func = self.builder._find_func_by_symbol(target_func)
		else:
			# assume it's an address
			pass
		
		
		src_addr = self.data['src_addr_%s' % self.builder._config_short_name]
		
		self.builder._add_patch(src_addr, u32.pack(target_func))



class NopInsnHook(Hook):
	"""Hook that NOPs out the instruction(s) at an address"""
	
	required_data = ['area_%CONFIG%']
	
	def __init__(self, builder, module, data):
		Hook.__init__(self, builder, module, data)
	
	def create_patches(self):
		area = self.data['area_%s' % self.builder._config_short_name]
		
		if isinstance(area, list):
			addr, end = area
			count = (end + 4 - addr) / 4
			nop_patch = '\x60\x00\x00\x00' * count
		else:
			addr = area
			nop_patch = '\x60\x00\x00\x00'
		
		self.builder._add_patch(addr, nop_patch)



HookTypes = {
	'patch': BasicPatchHook,
	'branch_insn': BranchInsnHook,
	'add_func_pointer': AddFunctionPointerHook,
	'nop_insn': NopInsnHook,
}