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
|
from arch.arch import Arch
from interpreter import SymbolResolver, SymbolResolveError
class MemoryAccessError(Exception):
def __init__(self, msg: str):
super().__init__(msg)
class SparseMemory:
"""Sparse memory.
Note that out-of-bound reads are possible when performed on unwritten
sections of existing pages and that there is no safeguard check for them.
"""
def __init__(self, page_size=1024):
self.page_size = page_size
self._pages: dict[int, bytes] = {}
def _to_page_addr_and_offset(self, addr: int) -> tuple[int, int]:
off = addr % self.page_size
return addr - off, off
def read(self, addr: int, size: int) -> bytes:
"""Read a number of bytes from memory.
:param addr: The offset from where to read.
:param size: The number of bytes to read, starting at at `addr`.
:return: `size` bytes of data.
:raise MemoryAccessError: If `[addr, addr + size)` is not entirely
contained in the set of stored bytes.
:raise ValueError: If `size < 0`.
"""
if size < 0:
raise ValueError(f'A negative size is not allowed!')
res = bytes()
while size > 0:
page_addr, off = self._to_page_addr_and_offset(addr)
if page_addr not in self._pages:
raise MemoryAccessError(f'Address {addr} is not contained in'
f' the sparse memory.')
data = self._pages[page_addr]
assert(len(data) == self.page_size)
read_size = min(size, self.page_size - off)
res += data[off:off+read_size]
size -= read_size
addr += read_size
return res
def write(self, addr: int, data: bytes):
"""Store bytes in the memory.
:param addr: The address at which to store the data.
:param data: The data to store at `addr`.
"""
while len(data) > 0:
page_addr, off = self._to_page_addr_and_offset(addr)
if page_addr not in self._pages:
self._pages[page_addr] = bytes(self.page_size)
page = self._pages[page_addr]
assert(len(page) == self.page_size)
write_size = min(len(data), self.page_size - off)
new_page = page[:off] + data[:write_size] + page[off+write_size:]
assert(len(new_page) == self.page_size)
self._pages[page_addr] = new_page
data = data[write_size:]
addr += write_size
class ProgramState:
"""A snapshot of the program's state."""
def __init__(self, arch: Arch):
self.arch = arch
dict_t = dict[str, int | None]
self.regs: dict_t = { reg: None for reg in arch.regnames }
self.mem = SparseMemory()
def read(self, reg: str) -> int:
"""Read a register's value.
:raise KeyError: If `reg` is not a register name.
:raise ValueError: If the register has no value.
"""
regname = self.arch.to_regname(reg)
if regname is None:
raise KeyError(f'Not a register name: {reg}')
assert(regname in self.regs)
regval = self.regs[regname]
if regval is None:
raise ValueError(f'Unable to read value of register {reg} (aka.'
f' {regname}): The register contains no value.')
return regval
def set(self, reg: str, value: int):
"""Assign a value to a register.
:raise KeyError: If `reg` is not a register name.
"""
regname = self.arch.to_regname(reg)
if regname is None:
raise KeyError(f'Not a register name: {reg}')
self.regs[regname] = value
def read_memory(self, addr: int, size: int) -> bytes:
return self.mem.read(addr, size)
def write_memory(self, addr: int, data: bytes):
self.mem.write(addr, data)
def __repr__(self):
return repr(self.regs)
class SnapshotSymbolResolver(SymbolResolver):
def __init__(self, snapshot: ProgramState):
self._state = snapshot
def resolve(self, symbol: str):
if symbol not in self._state.arch.regnames:
raise SymbolResolveError(symbol, 'Symbol is not a register name.')
return self._state.read(symbol)
|