-
Notifications
You must be signed in to change notification settings - Fork 412
/
memory.py
226 lines (187 loc) · 9.54 KB
/
memory.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
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
import logging
import six
import pygments
import pygments.formatters
from pygments.token import *
from voltron.view import TerminalView, VoltronView
from voltron.plugin import ViewPlugin, api_request
log = logging.getLogger("view")
class MemoryView(TerminalView):
printable_filter = ''.join([(len(repr(chr(x))) == 3) and chr(x) or '.' for x in range(256)])
asynchronous = True
last_memory = None
last_address = 0
@classmethod
def configure_subparser(cls, subparsers):
sp = subparsers.add_parser('memory', help='display a chunk of memory', aliases=('m', 'mem'))
VoltronView.add_generic_arguments(sp)
group = sp.add_mutually_exclusive_group(required=False)
group.add_argument('--deref', '-d', action='store_true',
help='display the data in a column one CPU word wide and dereference any valid pointers',
default=False)
group.add_argument('--bytes', '-b', action='store', type=int, help='bytes per line (default 16)', default=16)
group.add_argument('--words', '-w', action='store', type=int, help='machine words per line', default=0)
sp.add_argument('--reverse', '-v', action='store_true', help='reverse the output', default=False)
sp.add_argument('--track', '-t', action='store_true', help='track and highlight changes', default=True)
sp.add_argument('--no-track', '-T', action='store_false', help='don\'t track and highlight changes')
group = sp.add_mutually_exclusive_group(required=False)
group.add_argument('--address', '-a', action='store',
help='address (in hex or decimal) from which to start reading memory')
group.add_argument('--command', '-c', action='store',
help='command to execute resulting in the address from which to start reading memory. '
'voltron will do his almighty best to find an address. e.g. "print \$rip + 0x1234"',
default=None)
group.add_argument('--register', '-r', action='store',
help='register containing the address from which to start reading memory', default=None)
sp.set_defaults(func=MemoryView)
def build_requests(self):
height, width = self.window_size()
# check args
if self.args.register:
args = {'register': self.args.register}
elif self.args.command:
args = {'command': self.args.command}
elif self.args.address:
if self.args.address.startswith('0x'):
addr = int(self.args.address, 16)
else:
try:
addr = int(self.args.address, 10)
except:
addr = int(self.args.address, 16)
args = {'address': addr}
else:
args = {'register': 'sp'}
if self.args.deref:
args['words'] = height
args['offset'] = self.scroll_offset if self.args.reverse else -self.scroll_offset
else:
args['length'] = height * self.args.bytes * 2
args['offset'] = self.scroll_offset * self.args.bytes * (1 if self.args.reverse else -1)
# get memory and target info
return [
api_request('targets'),
api_request('memory', deref=self.args.deref is True, **args)
]
def generate_tokens(self, results):
t_res, m_res = results
if t_res and t_res.is_success and len(t_res.targets) > 0:
target = t_res.targets[0]
if m_res and m_res.is_success:
bytes_per_chunk = self.args.words*target['addr_size'] if self.args.words else self.args.bytes
for c in range(0, m_res.bytes, bytes_per_chunk):
chunk = m_res.memory[c:c + bytes_per_chunk]
yield (Name.Label, self.format_address(m_res.address + c, size=target['addr_size'], pad=False))
yield (Name.Label, ': ')
# Hex bytes
byte_array = []
for i, x in enumerate(six.iterbytes(chunk)):
n = "%02X" % x
token = Text if x else Comment
if self.args.track and self.last_memory and self.last_address == m_res.address:
if x != six.indexbytes(self.last_memory, c + i):
token = Error
byte_array.append((token, n))
if self.args.words:
if target['byte_order'] =='little':
byte_array_words = [byte_array[i:i+ target['addr_size']] for i in range(0, bytes_per_chunk, target['addr_size'])]
for word in byte_array_words:
word.reverse()
for x in word:
yield x
yield (Text, ' ')
else:
for x in byte_array:
yield x
yield (Text, ' ')
# ASCII representation
yield (Punctuation, '| ')
for i, x in enumerate(six.iterbytes(chunk)):
token = String.Char if x else Comment
if self.args.track and self.last_memory and self.last_address == m_res.address:
if x != six.indexbytes(self.last_memory, c + i):
token = Error
yield (token, ((x <= 127 and self.printable_filter[x]) or '.'))
yield (Punctuation, ' | ')
# Deref chain
if self.args.deref:
chain = m_res.deref.pop(0)
for i, (t, item) in enumerate(chain):
if t == "pointer":
yield (Number.Hex, self.format_address(item, size=target['addr_size'], pad=False))
elif t == "string":
for r in ['\n', '\r', '\v']:
item = item.replace(r, '\\{:x}'.format(ord(r)))
yield (String.Double, '"' + item + '"')
elif t == "unicode":
for r in ['\n', '\r', '\v']:
item = item.replace(r, '\\{:x}'.format(ord(r)))
yield (String.Double, 'u"' + item + '"')
elif t == "symbol":
yield (Name.Function, '`' + item + '`')
elif t == "circular":
yield (Text, '(circular)')
if i < len(chain) - 1:
yield (Punctuation, ' => ')
yield (Text, '\n')
def render(self, results):
target = None
self.trunc_top = self.args.reverse
t_res, m_res = results
if t_res and t_res.is_success and len(t_res.targets) > 0:
target = t_res.targets[0]
if self.args.deref or self.args.words:
self.args.bytes = target['addr_size']
f = pygments.formatters.get_formatter_by_name(self.config.format.pygments_formatter,
style=self.config.format.pygments_style)
if m_res and m_res.is_success:
lines = pygments.format(self.generate_tokens(results), f).split('\n')
self.body = '\n'.join(reversed(lines)).strip() if self.args.reverse else '\n'.join(lines)
self.info = '[0x{0:0=4x}:'.format(len(m_res.memory)) + self.config.format.addr_format.format(m_res.address) + ']'
else:
log.error("Error reading memory: {}".format(m_res.message))
self.body = pygments.format([(Error, m_res.message)], f)
self.info = ''
# Store the memory
if self.args.track:
self.last_address = m_res.address
self.last_memory = m_res.memory
else:
self.body = self.colour("Failed to get targets", 'red')
if not self.title:
self.title = "[memory]"
super(MemoryView, self).render(results)
def format_address(self, address, size=8, pad=True, prefix='0x'):
fmt = '{:' + ('0=' + str(size * 2) if pad else '') + 'X}'
addr_str = fmt.format(address)
if prefix:
addr_str = prefix + addr_str
return addr_str
class MemoryViewPlugin(ViewPlugin):
plugin_type = 'view'
name = 'memory'
view_class = MemoryView
class StackView(MemoryView):
@classmethod
def configure_subparser(cls, subparsers):
sp = subparsers.add_parser('stack', help='display a chunk of stack memory', aliases=('s', 'st'))
VoltronView.add_generic_arguments(sp)
sp.set_defaults(func=StackView)
sp.add_argument('--reverse', '-v', action='store_false', help='(un)reverse the output', default=True)
sp.add_argument('--track', '-t', action='store_true', help='track and highlight changes', default=True)
sp.add_argument('--no-track', '-T', action='store_false', help='don\'t track and highlight changes')
def build_requests(self):
self.args.deref = True
self.args.words = False
self.args.register = 'sp'
self.args.command = None
self.args.address = None
self.args.bytes = None
return super(StackView, self).build_requests()
def render(self, results):
self.title = '[stack]'
super(StackView, self).render(results)
class StackViewPlugin(ViewPlugin):
plugin_type = 'view'
name = 'stack'
view_class = StackView