-
Notifications
You must be signed in to change notification settings - Fork 12
/
mem.py
209 lines (174 loc) · 7 KB
/
mem.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
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
from collections import defaultdict
import logging
from ghidra.program.model.mem import MemoryAccessException
from .instr import instruction_finder
from .arch import LE, BE
from .mem_markers import UniqueMarker
logger = logging.getLogger(__name__)
class InvalidAddrException(Exception):
pass
class NotCodeException(Exception):
pass
class InvalidRegException(Exception):
pass
class CodeWord(list):
"""
This class represents a memory location containing code. It will
store a set of PCODE instruction classes.
Note that this implementation leads to a serious deficiency - code and
data are not interchangable here! For instance - if code is
self-modifying then the modified result will not be interpreted as code
by the emulator, and the emulator will die when it tries to execute
that (see Ram's get_code for that...). Of course - in order to interpret
self-modifying code, we also need a way to turn that code into PCODE,
which is beyond the scope of this software, therefore, this serious
deficiency is reasonable.
"""
pass
class MemChunk(defaultdict):
"""
This is intended to be an implementation of both reg and ram
"""
def __init__(self, api_base, arch):
super(MemChunk, self).__init__(int)
self.arch = arch
self.api_base = api_base
def _validate_code(self, addr):
addr = hex(addr)
if addr.endswith("L"):
addr = addr[:-1]
return self.api_base.getInstructionAt(
self.api_base.toAddr(addr)) is not None
def _validate_writeable(self, addr):
# TODO: check for actual writeableness, maybe
return not self._validate_code(addr)
def store(self, address, length, value):
"""Store a value at the given address, of "length" bytes, with
endianness matching the architecture
:param address: The index at which to store value
:type addres: int
:param length: The number of bytes over which to store value
:type length: int
:param value: The value to store
:type value: int
:raises InvalidAddrException: When the address was not writeable
"""
if not self._validate_writeable(address):
raise InvalidAddrException("Tried write at non-writeable spot")
address = long(address)
value = long(value)
# Note that python handles bitwise operation on negative numbers as
# 2s complement and like there are an infinite number of 1's in
# front of the most significant bit.
# This means that the below operations are already sign extended,
# and this is what we'd expect a processor to do.
# Thus - negative numbers just work.
cur_val = value
for ind in range(length):
if self.arch.endian is LE:
st_loc = address + ind
else:
st_loc = address + ((length - 1) - ind)
self[st_loc] = cur_val & (2**self.arch.bits_per_byte - 1)
cur_val >>= self.arch.bits_per_byte
def load_byte(self, address):
"""Load just one byte from address
"""
return self[address]
def load(self, address, length):
"""Load a value from the given address, of "length" bytes, with
endianness matching the architecture.
:param address: The index from which to load
:type addres: int
:param length: The number of bytes to load
:type length: int
:return: The value loaded
:rtype: int
"""
address = long(address)
cur_val = 0
for ind in range(length):
if self.arch.endian is LE:
st_loc = address + ind
else:
st_loc = address + ((length - 1) - ind)
one_byte = self.load_byte(st_loc) % 256
cur_val += one_byte << (ind * self.arch.bits_per_byte)
return long(cur_val)
def __str__(self):
sorted_keys = sorted(self.keys())
return ", ".join("0x{:x}: {}".format(key, hex(self[key])) for key in sorted_keys)
class Registers(MemChunk):
def _validate_writeable(self, addr):
# This assumes that Ghidra will only try to write to writeable
# registers.
return True
def __str__(self):
def fmt_key(key):
reg = None
try:
# Get the register object if possible
reg = self.arch.lookup_reg_by_offset(key)
except IndexError as e:
logging.debug("Register not found {} {}".format(key, e))
reg_size = 1
reg_name = key
if reg is not None:
reg_size = reg.getMinimumByteSize()
reg_name = "{}({:x})".format(reg.name, key)
out_txt = "{}: 0x{:x}".format(reg_name, self.load(key, reg_size))
used_keys = set(range(key, key + reg_size))
return out_txt, used_keys
sorted_keys = sorted(self.keys())
vals = list()
all_used_keys = set()
for key in sorted_keys:
if key in all_used_keys:
continue
out_txt, used_keys = fmt_key(key)
all_used_keys = all_used_keys.union(used_keys)
vals.append(out_txt)
return ", ".join(vals)
class Uniques(MemChunk):
def _validate_writeable(self, addr):
# This assumes that Ghidra will only try to write to writeable
# uniques.
return True
def store(self, address, length, value):
"""Store a value just like for the parent class, however, if
the value is a UniqueMarker instance, store it as a special case
at only the address.
"""
if isinstance(value, UniqueMarker):
self[address] = value
else:
super(Uniques, self).store(address, length, value)
def load(self, address, length):
"""Load a value just like for the parent class, however, if
the value is a UniqueMarker instance return only it.
"""
if isinstance(self[address], UniqueMarker):
return self[address]
else:
return super(Uniques, self).load(address, length)
class Ram(MemChunk):
def load_byte(self, address):
if address in self:
return self[address]
else:
try:
# It handles 64 bit values better when they're hex strings
# without an L at the end
addr = self.api_base.toAddr(hex(long(address))[:-1])
return self.api_base.getByte(addr)
except MemoryAccessException as e:
logger.debug("mem access except")
return 0
def get_code(self, address):
if not self._validate_code(address):
raise InvalidAddrException("No code at address")
inst = self.api_base.getInstructionAt(self.api_base.toAddr(address))
inst_size = inst.length
pcodes = inst.getPcode()
instrs = [instruction_finder(pcode, self.arch) for pcode in pcodes]
return instrs, inst_size