smallworld-re 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- smallworld/__init__.py +35 -0
- smallworld/analyses/__init__.py +14 -0
- smallworld/analyses/analysis.py +88 -0
- smallworld/analyses/code_coverage.py +31 -0
- smallworld/analyses/colorizer.py +682 -0
- smallworld/analyses/colorizer_summary.py +100 -0
- smallworld/analyses/field_detection/__init__.py +14 -0
- smallworld/analyses/field_detection/field_analysis.py +536 -0
- smallworld/analyses/field_detection/guards.py +26 -0
- smallworld/analyses/field_detection/hints.py +133 -0
- smallworld/analyses/field_detection/malloc.py +211 -0
- smallworld/analyses/forced_exec/__init__.py +3 -0
- smallworld/analyses/forced_exec/forced_exec.py +87 -0
- smallworld/analyses/underlays/__init__.py +4 -0
- smallworld/analyses/underlays/basic.py +13 -0
- smallworld/analyses/underlays/underlay.py +31 -0
- smallworld/analyses/unstable/__init__.py +4 -0
- smallworld/analyses/unstable/angr/__init__.py +0 -0
- smallworld/analyses/unstable/angr/base.py +12 -0
- smallworld/analyses/unstable/angr/divergence.py +274 -0
- smallworld/analyses/unstable/angr/model.py +383 -0
- smallworld/analyses/unstable/angr/nwbt.py +63 -0
- smallworld/analyses/unstable/angr/typedefs.py +170 -0
- smallworld/analyses/unstable/angr/utils.py +25 -0
- smallworld/analyses/unstable/angr/visitor.py +315 -0
- smallworld/analyses/unstable/angr_nwbt.py +106 -0
- smallworld/analyses/unstable/code_coverage.py +54 -0
- smallworld/analyses/unstable/code_reachable.py +44 -0
- smallworld/analyses/unstable/control_flow_tracer.py +71 -0
- smallworld/analyses/unstable/pointer_finder.py +90 -0
- smallworld/arch/__init__.py +0 -0
- smallworld/arch/aarch64_arch.py +286 -0
- smallworld/arch/amd64_arch.py +86 -0
- smallworld/arch/i386_arch.py +44 -0
- smallworld/emulators/__init__.py +14 -0
- smallworld/emulators/angr/__init__.py +7 -0
- smallworld/emulators/angr/angr.py +1652 -0
- smallworld/emulators/angr/default.py +15 -0
- smallworld/emulators/angr/exceptions.py +7 -0
- smallworld/emulators/angr/exploration/__init__.py +9 -0
- smallworld/emulators/angr/exploration/bounds.py +27 -0
- smallworld/emulators/angr/exploration/default.py +17 -0
- smallworld/emulators/angr/exploration/terminate.py +22 -0
- smallworld/emulators/angr/factory.py +55 -0
- smallworld/emulators/angr/machdefs/__init__.py +35 -0
- smallworld/emulators/angr/machdefs/aarch64.py +292 -0
- smallworld/emulators/angr/machdefs/amd64.py +192 -0
- smallworld/emulators/angr/machdefs/arm.py +387 -0
- smallworld/emulators/angr/machdefs/i386.py +221 -0
- smallworld/emulators/angr/machdefs/machdef.py +138 -0
- smallworld/emulators/angr/machdefs/mips.py +184 -0
- smallworld/emulators/angr/machdefs/mips64.py +189 -0
- smallworld/emulators/angr/machdefs/ppc.py +101 -0
- smallworld/emulators/angr/machdefs/riscv.py +261 -0
- smallworld/emulators/angr/machdefs/xtensa.py +255 -0
- smallworld/emulators/angr/memory/__init__.py +7 -0
- smallworld/emulators/angr/memory/default.py +10 -0
- smallworld/emulators/angr/memory/fixups.py +43 -0
- smallworld/emulators/angr/memory/memtrack.py +105 -0
- smallworld/emulators/angr/scratch.py +43 -0
- smallworld/emulators/angr/simos.py +53 -0
- smallworld/emulators/angr/utils.py +70 -0
- smallworld/emulators/emulator.py +1013 -0
- smallworld/emulators/hookable.py +252 -0
- smallworld/emulators/panda/__init__.py +5 -0
- smallworld/emulators/panda/machdefs/__init__.py +28 -0
- smallworld/emulators/panda/machdefs/aarch64.py +93 -0
- smallworld/emulators/panda/machdefs/amd64.py +71 -0
- smallworld/emulators/panda/machdefs/arm.py +89 -0
- smallworld/emulators/panda/machdefs/i386.py +36 -0
- smallworld/emulators/panda/machdefs/machdef.py +86 -0
- smallworld/emulators/panda/machdefs/mips.py +94 -0
- smallworld/emulators/panda/machdefs/mips64.py +91 -0
- smallworld/emulators/panda/machdefs/ppc.py +79 -0
- smallworld/emulators/panda/panda.py +575 -0
- smallworld/emulators/unicorn/__init__.py +13 -0
- smallworld/emulators/unicorn/machdefs/__init__.py +28 -0
- smallworld/emulators/unicorn/machdefs/aarch64.py +310 -0
- smallworld/emulators/unicorn/machdefs/amd64.py +326 -0
- smallworld/emulators/unicorn/machdefs/arm.py +321 -0
- smallworld/emulators/unicorn/machdefs/i386.py +137 -0
- smallworld/emulators/unicorn/machdefs/machdef.py +117 -0
- smallworld/emulators/unicorn/machdefs/mips.py +202 -0
- smallworld/emulators/unicorn/unicorn.py +684 -0
- smallworld/exceptions/__init__.py +5 -0
- smallworld/exceptions/exceptions.py +85 -0
- smallworld/exceptions/unstable/__init__.py +1 -0
- smallworld/exceptions/unstable/exceptions.py +25 -0
- smallworld/extern/__init__.py +4 -0
- smallworld/extern/ctypes.py +94 -0
- smallworld/extern/unstable/__init__.py +1 -0
- smallworld/extern/unstable/ghidra.py +129 -0
- smallworld/helpers.py +107 -0
- smallworld/hinting/__init__.py +8 -0
- smallworld/hinting/hinting.py +214 -0
- smallworld/hinting/hints.py +427 -0
- smallworld/hinting/unstable/__init__.py +2 -0
- smallworld/hinting/utils.py +19 -0
- smallworld/instructions/__init__.py +18 -0
- smallworld/instructions/aarch64.py +20 -0
- smallworld/instructions/arm.py +18 -0
- smallworld/instructions/bsid.py +67 -0
- smallworld/instructions/instructions.py +258 -0
- smallworld/instructions/mips.py +21 -0
- smallworld/instructions/x86.py +100 -0
- smallworld/logging.py +90 -0
- smallworld/platforms.py +95 -0
- smallworld/py.typed +0 -0
- smallworld/state/__init__.py +6 -0
- smallworld/state/cpus/__init__.py +32 -0
- smallworld/state/cpus/aarch64.py +563 -0
- smallworld/state/cpus/amd64.py +676 -0
- smallworld/state/cpus/arm.py +630 -0
- smallworld/state/cpus/cpu.py +71 -0
- smallworld/state/cpus/i386.py +239 -0
- smallworld/state/cpus/mips.py +374 -0
- smallworld/state/cpus/mips64.py +372 -0
- smallworld/state/cpus/powerpc.py +229 -0
- smallworld/state/cpus/riscv.py +357 -0
- smallworld/state/cpus/xtensa.py +80 -0
- smallworld/state/memory/__init__.py +7 -0
- smallworld/state/memory/code.py +70 -0
- smallworld/state/memory/elf/__init__.py +3 -0
- smallworld/state/memory/elf/elf.py +564 -0
- smallworld/state/memory/elf/rela/__init__.py +32 -0
- smallworld/state/memory/elf/rela/aarch64.py +27 -0
- smallworld/state/memory/elf/rela/amd64.py +32 -0
- smallworld/state/memory/elf/rela/arm.py +51 -0
- smallworld/state/memory/elf/rela/i386.py +32 -0
- smallworld/state/memory/elf/rela/mips.py +45 -0
- smallworld/state/memory/elf/rela/ppc.py +45 -0
- smallworld/state/memory/elf/rela/rela.py +63 -0
- smallworld/state/memory/elf/rela/riscv64.py +27 -0
- smallworld/state/memory/elf/rela/xtensa.py +15 -0
- smallworld/state/memory/elf/structs.py +55 -0
- smallworld/state/memory/heap.py +85 -0
- smallworld/state/memory/memory.py +181 -0
- smallworld/state/memory/stack/__init__.py +31 -0
- smallworld/state/memory/stack/aarch64.py +22 -0
- smallworld/state/memory/stack/amd64.py +42 -0
- smallworld/state/memory/stack/arm.py +66 -0
- smallworld/state/memory/stack/i386.py +22 -0
- smallworld/state/memory/stack/mips.py +34 -0
- smallworld/state/memory/stack/mips64.py +34 -0
- smallworld/state/memory/stack/ppc.py +34 -0
- smallworld/state/memory/stack/riscv.py +22 -0
- smallworld/state/memory/stack/stack.py +127 -0
- smallworld/state/memory/stack/xtensa.py +34 -0
- smallworld/state/models/__init__.py +6 -0
- smallworld/state/models/mmio.py +186 -0
- smallworld/state/models/model.py +163 -0
- smallworld/state/models/posix.py +455 -0
- smallworld/state/models/x86/__init__.py +2 -0
- smallworld/state/models/x86/microsoftcdecl.py +35 -0
- smallworld/state/models/x86/systemv.py +240 -0
- smallworld/state/state.py +962 -0
- smallworld/state/unstable/__init__.py +0 -0
- smallworld/state/unstable/elf.py +393 -0
- smallworld/state/x86_registers.py +30 -0
- smallworld/utils.py +935 -0
- smallworld_re-1.0.0.dist-info/LICENSE.txt +21 -0
- smallworld_re-1.0.0.dist-info/METADATA +189 -0
- smallworld_re-1.0.0.dist-info/RECORD +166 -0
- smallworld_re-1.0.0.dist-info/WHEEL +5 -0
- smallworld_re-1.0.0.dist-info/entry_points.txt +2 -0
- smallworld_re-1.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,962 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import abc
|
4
|
+
import collections
|
5
|
+
import copy
|
6
|
+
import ctypes
|
7
|
+
import logging as lg
|
8
|
+
import typing
|
9
|
+
|
10
|
+
import claripy
|
11
|
+
|
12
|
+
from .. import analyses, emulators, exceptions, logging, platforms, state, utils
|
13
|
+
|
14
|
+
logger = lg.getLogger(__name__)
|
15
|
+
|
16
|
+
|
17
|
+
class Stateful(metaclass=abc.ABCMeta):
|
18
|
+
"""An abstract class whose subclasses represent system state that can be applied to/loaded from an emulator."""
|
19
|
+
|
20
|
+
@abc.abstractmethod
|
21
|
+
def extract(self, emulator: emulators.Emulator) -> None:
|
22
|
+
"""Load state from an emulator.
|
23
|
+
|
24
|
+
Arguments:
|
25
|
+
emulator: The emulator from which to load
|
26
|
+
"""
|
27
|
+
|
28
|
+
pass
|
29
|
+
|
30
|
+
@abc.abstractmethod
|
31
|
+
def apply(self, emulator: emulators.Emulator) -> None:
|
32
|
+
"""Apply state to an emulator.
|
33
|
+
|
34
|
+
Arguments:
|
35
|
+
emulator: The emulator to which state should applied.
|
36
|
+
"""
|
37
|
+
|
38
|
+
pass
|
39
|
+
|
40
|
+
def __hash__(self) -> int:
|
41
|
+
return id(self)
|
42
|
+
|
43
|
+
|
44
|
+
class Value(metaclass=abc.ABCMeta):
|
45
|
+
"""An abstract class whose subclasses all have a tuple of content, type, and label. Content is the value which must be convertable into bytes. The type is a ctype reprensenting the type of content. Label is a string that is a human label for the object. Any or all are optional."""
|
46
|
+
|
47
|
+
def __init__(self: typing.Any) -> None:
|
48
|
+
self._content: typing.Union[None, int, bytes, claripy.ast.bv.BV] = None
|
49
|
+
self._type: typing.Optional[typing.Any] = None
|
50
|
+
self._label: typing.Optional[str] = None
|
51
|
+
|
52
|
+
@abc.abstractmethod
|
53
|
+
def get_size(self) -> int:
|
54
|
+
"""Get the size of this value.
|
55
|
+
|
56
|
+
Returns:
|
57
|
+
The number of bytes this value should occupy in memory.
|
58
|
+
"""
|
59
|
+
|
60
|
+
return 0
|
61
|
+
|
62
|
+
def get_content(self) -> typing.Union[None, int, bytes, claripy.ast.bv.BV]:
|
63
|
+
"""Get the content of this value.
|
64
|
+
|
65
|
+
Returns:
|
66
|
+
The content of this value.
|
67
|
+
"""
|
68
|
+
|
69
|
+
return self._content
|
70
|
+
|
71
|
+
def set_content(
|
72
|
+
self, content: typing.Union[None, int, bytes, claripy.ast.bv.BV]
|
73
|
+
) -> None:
|
74
|
+
"""Set the content of this value.
|
75
|
+
|
76
|
+
Arguments:
|
77
|
+
content: The content to which the value will be set.
|
78
|
+
"""
|
79
|
+
|
80
|
+
self._content = content
|
81
|
+
|
82
|
+
def get_type(self) -> typing.Optional[typing.Any]:
|
83
|
+
"""Get the type of this value.
|
84
|
+
|
85
|
+
Returns:
|
86
|
+
The type of this value.
|
87
|
+
"""
|
88
|
+
|
89
|
+
return self._type
|
90
|
+
|
91
|
+
def set_type(self, type: typing.Optional[typing.Any]) -> None:
|
92
|
+
"""Set the type of this value.
|
93
|
+
|
94
|
+
Arguments:
|
95
|
+
type: The type value to set.
|
96
|
+
"""
|
97
|
+
|
98
|
+
self._type = type
|
99
|
+
|
100
|
+
def get_label(self) -> typing.Optional[str]:
|
101
|
+
"""Get the label of this value.
|
102
|
+
|
103
|
+
Returns:
|
104
|
+
The label of this value.
|
105
|
+
"""
|
106
|
+
|
107
|
+
return self._label
|
108
|
+
|
109
|
+
def set_label(self, label: typing.Optional[str]) -> None:
|
110
|
+
"""Set the label of this value.
|
111
|
+
|
112
|
+
Arguments:
|
113
|
+
type: The label value to set.
|
114
|
+
"""
|
115
|
+
|
116
|
+
self._label = label
|
117
|
+
|
118
|
+
def get(self) -> typing.Union[None, int, bytes, claripy.ast.bv.BV]:
|
119
|
+
"""A helper to get the content of this value.
|
120
|
+
|
121
|
+
Returns:
|
122
|
+
The content of this value.
|
123
|
+
"""
|
124
|
+
|
125
|
+
return self.get_content()
|
126
|
+
|
127
|
+
def set(self, content: typing.Union[None, int, bytes, claripy.ast.bv.BV]) -> None:
|
128
|
+
"""A helper to set the content of this value.
|
129
|
+
|
130
|
+
Arguments:
|
131
|
+
content: The content value to set.
|
132
|
+
"""
|
133
|
+
|
134
|
+
self.set_content(content)
|
135
|
+
|
136
|
+
def to_symbolic(
|
137
|
+
self, byteorder: platforms.Byteorder
|
138
|
+
) -> typing.Optional[claripy.ast.bv.BV]:
|
139
|
+
"""Convert this value into a symbolic expression
|
140
|
+
|
141
|
+
For an unlabeled value, this will be a bit vector symbol containing the label.
|
142
|
+
Otherwise, it will be a bit vector value containing the contents.
|
143
|
+
|
144
|
+
Arguments:
|
145
|
+
byteorder: The byte order to use in the conversion.
|
146
|
+
|
147
|
+
Returns:
|
148
|
+
Symbolic expression object, or None if both content and label are None
|
149
|
+
"""
|
150
|
+
|
151
|
+
size = self.get_size()
|
152
|
+
|
153
|
+
if self.get_label() is not None:
|
154
|
+
# This has a label; assume we use it.
|
155
|
+
label = self.get_label()
|
156
|
+
if not isinstance(label, str):
|
157
|
+
raise exceptions.ConfigurationError(
|
158
|
+
f"Cannot create a symbol from label of type {type(self._label)}; must be str"
|
159
|
+
)
|
160
|
+
|
161
|
+
# Bit vectors are bit vectors; multiply size in bytes by 8
|
162
|
+
return claripy.BVS(label, size * 8, explicit_name=True)
|
163
|
+
|
164
|
+
elif self._content is not None:
|
165
|
+
content = self._content
|
166
|
+
|
167
|
+
if isinstance(content, claripy.ast.bv.BV):
|
168
|
+
# The content is already a symbolic expression
|
169
|
+
return content
|
170
|
+
|
171
|
+
if isinstance(content, int):
|
172
|
+
# The content is an int; convert to bytes for universal handling
|
173
|
+
if byteorder == platforms.Byteorder.BIG:
|
174
|
+
content = content.to_bytes(size, "big")
|
175
|
+
else:
|
176
|
+
content = content.to_bytes(size, "little")
|
177
|
+
|
178
|
+
if not isinstance(content, bytes) and not isinstance(content, bytearray):
|
179
|
+
# The content is not something I know how to handle.
|
180
|
+
raise exceptions.ConfigurationError(
|
181
|
+
f"Cannot create a symbol from content of type {type(self._content)}; "
|
182
|
+
"must be a claripy expression, int, bytes, or bytearray"
|
183
|
+
)
|
184
|
+
|
185
|
+
if size == 0:
|
186
|
+
raise exceptions.ConfigurationError(
|
187
|
+
"Cannot create a bitvector of size zero"
|
188
|
+
)
|
189
|
+
|
190
|
+
if len(content) != size:
|
191
|
+
raise exceptions.ConfigurationError(
|
192
|
+
f"Expected size {size}, but content has size {len(content)}"
|
193
|
+
)
|
194
|
+
|
195
|
+
return claripy.BVV(content)
|
196
|
+
|
197
|
+
else:
|
198
|
+
return None
|
199
|
+
|
200
|
+
@abc.abstractmethod
|
201
|
+
def to_bytes(self, byteorder: platforms.Byteorder) -> bytes:
|
202
|
+
"""Convert this value into a byte string.
|
203
|
+
|
204
|
+
Arguments:
|
205
|
+
byteorder: Byteorder for conversion to raw bytes.
|
206
|
+
|
207
|
+
Returns:
|
208
|
+
Bytes for this value with the given byteorder.
|
209
|
+
"""
|
210
|
+
|
211
|
+
return b""
|
212
|
+
|
213
|
+
@classmethod
|
214
|
+
def from_ctypes(cls, ctype: typing.Any, label: str):
|
215
|
+
"""Load from an existing ctypes value.
|
216
|
+
|
217
|
+
Arguements:
|
218
|
+
ctype: The data in ctype form.
|
219
|
+
|
220
|
+
Reutrns:
|
221
|
+
The value constructed from ctypes data.
|
222
|
+
"""
|
223
|
+
|
224
|
+
class CTypeValue(Value):
|
225
|
+
_type = ctype.__class__
|
226
|
+
_label = label
|
227
|
+
_content = ctype
|
228
|
+
|
229
|
+
def get_size(self) -> int:
|
230
|
+
return ctypes.sizeof(self._content)
|
231
|
+
|
232
|
+
def to_bytes(self, byteorder: platforms.Byteorder) -> bytes:
|
233
|
+
return bytes(self._content)
|
234
|
+
|
235
|
+
return CTypeValue()
|
236
|
+
|
237
|
+
|
238
|
+
class SymbolicValue(Value):
|
239
|
+
"""A symbolic value
|
240
|
+
|
241
|
+
This value is expressed as a bitvector expression,
|
242
|
+
compatible with angr's internal state representation.
|
243
|
+
If used with a concrete emulator, it defaults to zeroes.
|
244
|
+
|
245
|
+
Arguments:
|
246
|
+
size: The size of the region
|
247
|
+
bv: Optional bitvector value
|
248
|
+
type: Optional typedef information
|
249
|
+
label: An optional metadata label
|
250
|
+
"""
|
251
|
+
|
252
|
+
def __init__(
|
253
|
+
self,
|
254
|
+
size: int,
|
255
|
+
bv: typing.Optional[claripy.ast.bv.BV],
|
256
|
+
type: typing.Optional[typing.Any],
|
257
|
+
label: typing.Optional[str],
|
258
|
+
):
|
259
|
+
super().__init__()
|
260
|
+
self._content = bv
|
261
|
+
self._size = size
|
262
|
+
self._type = type
|
263
|
+
self._label = label
|
264
|
+
|
265
|
+
def get_size(self) -> int:
|
266
|
+
return self._size
|
267
|
+
|
268
|
+
def to_bytes(self):
|
269
|
+
return b"\0" * self._size
|
270
|
+
|
271
|
+
|
272
|
+
class IntegerValue(Value):
|
273
|
+
"""An integer value.
|
274
|
+
|
275
|
+
This is useful for using python integers, but passing them to emulators that care about things like width. The type is derived based on the size.
|
276
|
+
|
277
|
+
Arguments:
|
278
|
+
size: The size of the integer. Must be 1, 2, 4, 8
|
279
|
+
label: An optional metadata label
|
280
|
+
"""
|
281
|
+
|
282
|
+
def __init__(
|
283
|
+
self, integer: int, size: int, label: typing.Optional[str], signed: bool = True
|
284
|
+
) -> None:
|
285
|
+
super().__init__()
|
286
|
+
if size == 8:
|
287
|
+
if signed:
|
288
|
+
self._type = ctypes.c_int64
|
289
|
+
else:
|
290
|
+
self._type = ctypes.c_uint64
|
291
|
+
elif size == 4:
|
292
|
+
if signed:
|
293
|
+
self._type = ctypes.c_int32
|
294
|
+
else:
|
295
|
+
self._type = ctypes.c_uint32
|
296
|
+
elif size == 2:
|
297
|
+
if signed:
|
298
|
+
self._type = ctypes.c_int16
|
299
|
+
else:
|
300
|
+
self._type = ctypes.c_uint16
|
301
|
+
elif size == 1:
|
302
|
+
if signed:
|
303
|
+
self._type = ctypes.c_int8
|
304
|
+
else:
|
305
|
+
self._type = ctypes.c_uint8
|
306
|
+
else:
|
307
|
+
raise NotImplementedError(f"{size}-bit integers are not yet implemented")
|
308
|
+
self._content = integer
|
309
|
+
self._label = label
|
310
|
+
self._size = size
|
311
|
+
|
312
|
+
def get_size(self) -> int:
|
313
|
+
return self._size
|
314
|
+
|
315
|
+
def to_bytes(self, byteorder: platforms.Byteorder) -> bytes:
|
316
|
+
if self._content is None:
|
317
|
+
raise ValueError("IntegerValue must have an integer value")
|
318
|
+
if not isinstance(self._content, int):
|
319
|
+
raise TypeError("IntegerValue is not an integer")
|
320
|
+
value = self._content
|
321
|
+
if value < 0:
|
322
|
+
# Convert signed python into unsigned int containing 2s-compliment value.
|
323
|
+
# Python's to_bytes() doesn't do this on its own.
|
324
|
+
value = 2 ** (self._size * 8) + value
|
325
|
+
if byteorder == platforms.Byteorder.LITTLE:
|
326
|
+
return value.to_bytes(self._size, byteorder="little")
|
327
|
+
elif byteorder == platforms.Byteorder.BIG:
|
328
|
+
return value.to_bytes(self._size, byteorder="big")
|
329
|
+
else:
|
330
|
+
raise NotImplementedError("middle endian integers are not yet implemented")
|
331
|
+
|
332
|
+
|
333
|
+
class BytesValue(Value):
|
334
|
+
"""A bytes value.
|
335
|
+
|
336
|
+
This is for representing a python bytes or bytearray as a value.
|
337
|
+
|
338
|
+
Arguments:
|
339
|
+
label: An optional metadata label
|
340
|
+
"""
|
341
|
+
|
342
|
+
def __init__(
|
343
|
+
self, content: typing.Union[bytes, bytearray], label: typing.Optional[str]
|
344
|
+
) -> None:
|
345
|
+
super().__init__()
|
346
|
+
self._content = bytes(content)
|
347
|
+
self._label = label
|
348
|
+
self._size = len(self._content)
|
349
|
+
self._type = ctypes.c_ubyte * self._size
|
350
|
+
|
351
|
+
def get_size(self) -> int:
|
352
|
+
return self._size
|
353
|
+
|
354
|
+
def to_bytes(self, byteorder: platforms.Byteorder) -> bytes:
|
355
|
+
if self._content is None or not isinstance(self._content, bytes):
|
356
|
+
raise ValueError("BytesValue must have a bytes value")
|
357
|
+
return self._content
|
358
|
+
|
359
|
+
|
360
|
+
class Register(Value, Stateful):
|
361
|
+
"""An individual register.
|
362
|
+
|
363
|
+
Arguments:
|
364
|
+
name: The canonical name of the register.
|
365
|
+
size: The size (in bytes) of the register.
|
366
|
+
"""
|
367
|
+
|
368
|
+
def __init__(self, name: str, size: int = 4):
|
369
|
+
super().__init__()
|
370
|
+
|
371
|
+
self.name: str = name
|
372
|
+
"""Canonical name."""
|
373
|
+
|
374
|
+
self.size = size
|
375
|
+
"""Register size in bytes."""
|
376
|
+
|
377
|
+
def __str__(self):
|
378
|
+
s = f"Reg({self.name},{self.size})="
|
379
|
+
x = self.get_content()
|
380
|
+
if x is None:
|
381
|
+
s = s + "=None"
|
382
|
+
elif isinstance(x, int):
|
383
|
+
s = s + f"0x{x:x}"
|
384
|
+
else:
|
385
|
+
s = s + str(type(x))
|
386
|
+
return s
|
387
|
+
|
388
|
+
def set_content(self, content: typing.Union[None, int, bytes, claripy.ast.bv.BV]):
|
389
|
+
if content is not None:
|
390
|
+
if not isinstance(content, int) and not isinstance(
|
391
|
+
content, claripy.ast.bv.BV
|
392
|
+
):
|
393
|
+
raise TypeError(
|
394
|
+
f"Expected None, int, or claripy expression as content for Register {self.name}, got {type(content)}"
|
395
|
+
)
|
396
|
+
if isinstance(content, int) and content < 0:
|
397
|
+
logger.warn(
|
398
|
+
"Converting content {hex(content)} of {self.name} to unsigned."
|
399
|
+
)
|
400
|
+
content = content + (2 ** (self.size * 8))
|
401
|
+
if isinstance(content, claripy.ast.bv.BV):
|
402
|
+
if content.size() != self.size * 8:
|
403
|
+
raise ValueError(
|
404
|
+
f"Claripy content had size {content.size()} bits, but expected {self.size * 8} bits"
|
405
|
+
)
|
406
|
+
super().set_content(content)
|
407
|
+
|
408
|
+
def get_size(self) -> int:
|
409
|
+
return self.size
|
410
|
+
|
411
|
+
def extract(self, emulator: emulators.Emulator) -> None:
|
412
|
+
content: typing.Union[int, claripy.ast.bv.BV] = 0
|
413
|
+
try:
|
414
|
+
content = emulator.read_register_content(self.name)
|
415
|
+
if content is not None:
|
416
|
+
self.set_content(content)
|
417
|
+
except exceptions.SymbolicValueError:
|
418
|
+
content = emulator.read_register_symbolic(self.name)
|
419
|
+
if content is not None:
|
420
|
+
self.set_content(content)
|
421
|
+
except exceptions.UnsupportedRegisterError:
|
422
|
+
return
|
423
|
+
|
424
|
+
type = emulator.read_register_type(self.name)
|
425
|
+
if type is not None:
|
426
|
+
self.set_type(type)
|
427
|
+
|
428
|
+
try:
|
429
|
+
label = emulator.read_register_label(self.name)
|
430
|
+
if label is not None:
|
431
|
+
self.set_label(label)
|
432
|
+
except exceptions.SymbolicValueError:
|
433
|
+
pass
|
434
|
+
|
435
|
+
def apply(self, emulator: emulators.Emulator) -> None:
|
436
|
+
content = self.get_content()
|
437
|
+
if isinstance(content, bytes):
|
438
|
+
raise TypeError("Register content cannot be bytes")
|
439
|
+
if content is not None:
|
440
|
+
emulator.write_register_content(self.name, content)
|
441
|
+
if self.get_type() is not None:
|
442
|
+
emulator.write_register_type(self.name, self.get_type())
|
443
|
+
if self.get_label() is not None:
|
444
|
+
emulator.write_register_label(self.name, self.get_label())
|
445
|
+
|
446
|
+
def to_bytes(self, byteorder: platforms.Byteorder) -> bytes:
|
447
|
+
value = self.get_content()
|
448
|
+
|
449
|
+
if value is None:
|
450
|
+
# Default to zeros if no value present.
|
451
|
+
return b"\0" * self.size
|
452
|
+
elif isinstance(value, claripy.ast.bv.BV):
|
453
|
+
raise exceptions.SymbolicValueError(
|
454
|
+
"Cannot convert a symbolic expression to bytes"
|
455
|
+
)
|
456
|
+
elif isinstance(value, bytes):
|
457
|
+
# This never happens, but let's keep mypy happy
|
458
|
+
return value
|
459
|
+
elif byteorder == platforms.Byteorder.LITTLE:
|
460
|
+
return value.to_bytes(self.size, byteorder="little")
|
461
|
+
elif byteorder == platforms.Byteorder.BIG:
|
462
|
+
return value.to_bytes(self.size, byteorder="big")
|
463
|
+
else:
|
464
|
+
raise ValueError(f"unsupported byteorder {byteorder}")
|
465
|
+
|
466
|
+
|
467
|
+
class RegisterAlias(Register):
|
468
|
+
"""An alias to a partial register.
|
469
|
+
|
470
|
+
Arguments:
|
471
|
+
name: The cannonical name of the register.
|
472
|
+
reference: A register which this alias references.
|
473
|
+
size: The size (in bytes) of the register.
|
474
|
+
offset: The offset from the start of the register that this alias
|
475
|
+
references.
|
476
|
+
|
477
|
+
"""
|
478
|
+
|
479
|
+
def __init__(self, name: str, reference: Register, size: int = 4, offset: int = 0):
|
480
|
+
super().__init__(name, size)
|
481
|
+
|
482
|
+
self.reference: Register = reference
|
483
|
+
"""The register referenced by this alias."""
|
484
|
+
|
485
|
+
self.offset: int = offset
|
486
|
+
"""'The offset into the referenced register."""
|
487
|
+
|
488
|
+
@property
|
489
|
+
def mask(self) -> int:
|
490
|
+
mask = (1 << self.size * 8) - 1
|
491
|
+
mask <<= self.offset * 8
|
492
|
+
|
493
|
+
return mask
|
494
|
+
|
495
|
+
def get_content(self) -> typing.Union[None, int, bytes, claripy.ast.bv.BV]:
|
496
|
+
r = self.reference.get_content()
|
497
|
+
if r is None:
|
498
|
+
return r
|
499
|
+
value = self.reference.get_content()
|
500
|
+
if value is not None:
|
501
|
+
if isinstance(value, int):
|
502
|
+
value = value & self.mask
|
503
|
+
value >>= self.offset * 8
|
504
|
+
elif isinstance(value, claripy.ast.bv.BV):
|
505
|
+
lo = self.offset * 8
|
506
|
+
hi = lo + self.size * 8 - 1
|
507
|
+
value = claripy.simplify(value[hi:lo])
|
508
|
+
else:
|
509
|
+
raise TypeError(f"Unexpected register content {type(value)}")
|
510
|
+
return value
|
511
|
+
|
512
|
+
def set_content(
|
513
|
+
self, content: typing.Union[None, int, bytes, claripy.ast.bv.BV]
|
514
|
+
) -> None:
|
515
|
+
if content is not None:
|
516
|
+
if isinstance(content, int):
|
517
|
+
if content < 0:
|
518
|
+
logger.warn(
|
519
|
+
f"Converting content {hex(content)} of {self.name} to unsigned."
|
520
|
+
)
|
521
|
+
content = content + (2 ** (self.size * 8))
|
522
|
+
|
523
|
+
elif isinstance(content, claripy.ast.bv.BV):
|
524
|
+
# Bitvectors can only interoperate with bitvectors of the same size.
|
525
|
+
content = claripy.ZeroExt(
|
526
|
+
self.reference.size * 8 - self.size * 8, content
|
527
|
+
)
|
528
|
+
|
529
|
+
else:
|
530
|
+
raise TypeError(
|
531
|
+
f"Can only accept None, int, or BV, not {type(content)}"
|
532
|
+
)
|
533
|
+
|
534
|
+
value = self.reference.get_content()
|
535
|
+
if value is None:
|
536
|
+
value = 0
|
537
|
+
if isinstance(value, bytes):
|
538
|
+
raise TypeError("Value should not be bytes")
|
539
|
+
|
540
|
+
# mypy completely loses the plot trying to determine type for content
|
541
|
+
content = content << (self.offset * 8) # type: ignore[operator]
|
542
|
+
value = (value & ~self.mask) | content
|
543
|
+
if isinstance(value, claripy.ast.bv.BV):
|
544
|
+
value = claripy.simplify(value)
|
545
|
+
|
546
|
+
self.reference.set_content(value)
|
547
|
+
|
548
|
+
def get_type(self) -> typing.Optional[typing.Any]:
|
549
|
+
return self.reference.get_type()
|
550
|
+
|
551
|
+
def set_type(self, type: typing.Optional[typing.Any]) -> None:
|
552
|
+
self.reference.set_type(type)
|
553
|
+
|
554
|
+
def get_label(self) -> typing.Optional[str]:
|
555
|
+
return self.reference.get_label()
|
556
|
+
|
557
|
+
def set_label(self, label: typing.Optional[str]) -> None:
|
558
|
+
self.reference.set_label(label)
|
559
|
+
|
560
|
+
def extract(self, emulator: emulators.Emulator) -> None:
|
561
|
+
pass
|
562
|
+
|
563
|
+
def apply(self, emulator: emulators.Emulator) -> None:
|
564
|
+
pass
|
565
|
+
|
566
|
+
|
567
|
+
class FixedRegister(Register):
|
568
|
+
"""A Register that holds a fixed value, and should not be set.
|
569
|
+
|
570
|
+
A number of ISAs have registers hard-wired to zero.
|
571
|
+
It would be nice for the harness author to not have
|
572
|
+
to remember to set it each time.
|
573
|
+
|
574
|
+
Arguments:
|
575
|
+
name: Name of the register
|
576
|
+
size: Size of the register in bytes
|
577
|
+
value: Fixed value of the register
|
578
|
+
"""
|
579
|
+
|
580
|
+
def __init__(self, name, size=4, value=0):
|
581
|
+
super().__init__(name, size=size)
|
582
|
+
super().set_content(value)
|
583
|
+
|
584
|
+
def set_content(self, value: typing.Optional[typing.Any]):
|
585
|
+
raise exceptions.ConfigurationError(
|
586
|
+
"Register {self.name} is fixed; it cannot be set"
|
587
|
+
)
|
588
|
+
|
589
|
+
def get_label(self) -> typing.Optional[str]:
|
590
|
+
# Fixed registers cannot have labels
|
591
|
+
return None
|
592
|
+
|
593
|
+
def set_label(self, label: typing.Optional[str]) -> None:
|
594
|
+
# Fixed registers cannot have labels
|
595
|
+
pass
|
596
|
+
|
597
|
+
def get_type(self) -> typing.Optional[typing.Any]:
|
598
|
+
# Fixed registers do not have types
|
599
|
+
return None
|
600
|
+
|
601
|
+
def set_type(self, type: typing.Optional[typing.Any]) -> None:
|
602
|
+
# Fixed registers do not have types
|
603
|
+
pass
|
604
|
+
|
605
|
+
def extract(self, emulator: emulators.Emulator) -> None:
|
606
|
+
# Don't bother extracting content
|
607
|
+
pass
|
608
|
+
|
609
|
+
def apply(self, emulator: emulators.Emulator) -> None:
|
610
|
+
try:
|
611
|
+
super().apply(emulator)
|
612
|
+
except exceptions.UnsupportedRegisterError:
|
613
|
+
# If the register isn't supported, that's okay
|
614
|
+
pass
|
615
|
+
|
616
|
+
|
617
|
+
class StatefulSet(Stateful, collections.abc.MutableSet):
|
618
|
+
"""A set that holds stateful objects. Applying or extracting the set performs the action of every member of the set."""
|
619
|
+
|
620
|
+
def __init__(self):
|
621
|
+
super().__init__()
|
622
|
+
self._contents = set()
|
623
|
+
|
624
|
+
def extract(self, emulator: emulators.Emulator) -> None:
|
625
|
+
for stateful in self:
|
626
|
+
stateful.extract(emulator)
|
627
|
+
|
628
|
+
def apply(self, emulator: emulators.Emulator) -> None:
|
629
|
+
for stateful in self:
|
630
|
+
logger.debug(
|
631
|
+
f"applying state {stateful} of type {type(stateful)} to emulator {emulator}"
|
632
|
+
)
|
633
|
+
stateful.apply(emulator)
|
634
|
+
|
635
|
+
def __contains__(self, item):
|
636
|
+
return item in self._contents
|
637
|
+
|
638
|
+
def __iter__(self):
|
639
|
+
return self._contents.__iter__()
|
640
|
+
|
641
|
+
def __len__(self):
|
642
|
+
return len(self._contents)
|
643
|
+
|
644
|
+
def add(self, item):
|
645
|
+
self._contents.add(item)
|
646
|
+
|
647
|
+
def discard(self, item):
|
648
|
+
self._contents.discard(item)
|
649
|
+
|
650
|
+
def members(self, type):
|
651
|
+
return set(filter(lambda x: isinstance(x, type), self._contents))
|
652
|
+
|
653
|
+
|
654
|
+
class Machine(StatefulSet):
|
655
|
+
"""A container for all state needed to begin or resume emulation or
|
656
|
+
analysis), including CPU with register values, code, raw memory or
|
657
|
+
even stack and heap memory.
|
658
|
+
|
659
|
+
Machines have exit points which are instruction addresses that
|
660
|
+
when hit by an emulator will cause it to stop before any side
|
661
|
+
effects of that instruction are applied. Similarly, machines have
|
662
|
+
bounds which are address ranges. When an address outside of the
|
663
|
+
range is hit by the emulator that will cause it to stop before any
|
664
|
+
side effects are applied. Note that the start of the range is
|
665
|
+
included in the range, but the end is not.
|
666
|
+
|
667
|
+
"""
|
668
|
+
|
669
|
+
def __init__(self):
|
670
|
+
super().__init__()
|
671
|
+
self._bounds = utils.RangeCollection()
|
672
|
+
self._exit_points = set()
|
673
|
+
self._constraints = list()
|
674
|
+
|
675
|
+
def add_exit_point(self, address: int):
|
676
|
+
"""Add an exit point to the machine.
|
677
|
+
|
678
|
+
Arguments:
|
679
|
+
address: The address to exit on
|
680
|
+
|
681
|
+
"""
|
682
|
+
self._exit_points.add(address)
|
683
|
+
|
684
|
+
def get_exit_points(self) -> typing.Set[int]:
|
685
|
+
"""Gets the set of exit points for a machine.
|
686
|
+
|
687
|
+
Returns:
|
688
|
+
The set of exit point addresses.
|
689
|
+
"""
|
690
|
+
return self._exit_points
|
691
|
+
|
692
|
+
def add_bound(self, start: int, end: int):
|
693
|
+
"""Adds a bound to the machine
|
694
|
+
|
695
|
+
Arguments:
|
696
|
+
start: the start address of the bound (included in the bound)
|
697
|
+
end: the end address of the bound (excluded in the bound)
|
698
|
+
"""
|
699
|
+
self._bounds.add_range((start, end))
|
700
|
+
|
701
|
+
def get_bounds(self) -> typing.List[typing.Tuple[int, int]]:
|
702
|
+
"""Gets a list of bounds for the machine.
|
703
|
+
|
704
|
+
|
705
|
+
Returns:
|
706
|
+
The list of bounds.
|
707
|
+
"""
|
708
|
+
return list(self._bounds.ranges)
|
709
|
+
|
710
|
+
def add_constraint(self, expr: claripy.ast.bool.Bool) -> None:
|
711
|
+
"""Add a constraint to the environment
|
712
|
+
|
713
|
+
A constraint is an expression that
|
714
|
+
some emulators can use to limit the possible values
|
715
|
+
of unbound variables.
|
716
|
+
They will only consider execution states
|
717
|
+
where all constraints can evaluate to True.
|
718
|
+
|
719
|
+
Constraints must be Boolean expressions;
|
720
|
+
the easiest form is the equality or inequality
|
721
|
+
of two bitvector expressions.
|
722
|
+
|
723
|
+
You can get the variable representing
|
724
|
+
a labeled Value via its `to_symbolic()` method.
|
725
|
+
Note that Values with both a label and content
|
726
|
+
already have a constraint binding the label's
|
727
|
+
variable to the content.
|
728
|
+
|
729
|
+
Arguments:
|
730
|
+
expr: The constraint expression to add
|
731
|
+
"""
|
732
|
+
if not isinstance(expr, claripy.ast.bool.Bool):
|
733
|
+
raise TypeError(f"expr is a {type(expr)}, not a Boolean expression")
|
734
|
+
|
735
|
+
self._constraints.append(expr)
|
736
|
+
|
737
|
+
def get_constraints(self) -> typing.List[claripy.ast.bool.Bool]:
|
738
|
+
"""Retrieve all constraints applied to this machine.
|
739
|
+
|
740
|
+
Returns:
|
741
|
+
A list of constraint expressions
|
742
|
+
"""
|
743
|
+
return list(self._constraints)
|
744
|
+
|
745
|
+
def apply(self, emulator: emulators.Emulator) -> None:
|
746
|
+
for address in self._exit_points:
|
747
|
+
emulator.add_exit_point(address)
|
748
|
+
for start, end in self.get_bounds():
|
749
|
+
emulator.add_bound(start, end)
|
750
|
+
|
751
|
+
if isinstance(emulator, emulators.ConstrainedEmulator):
|
752
|
+
for expr in self._constraints:
|
753
|
+
emulator.add_constraint(expr)
|
754
|
+
|
755
|
+
return super().apply(emulator)
|
756
|
+
|
757
|
+
def extract(self, emulator: emulators.Emulator) -> None:
|
758
|
+
self._exit_points = emulator.get_exit_points()
|
759
|
+
for start, end in emulator.get_bounds():
|
760
|
+
self.add_bound(start, end)
|
761
|
+
|
762
|
+
if isinstance(emulator, emulators.ConstrainedEmulator):
|
763
|
+
self._constraints = emulator.get_constraints()
|
764
|
+
|
765
|
+
return super().extract(emulator)
|
766
|
+
|
767
|
+
def emulate(self, emulator: emulators.Emulator) -> Machine:
|
768
|
+
"""Emulate this machine with the given emulator.
|
769
|
+
|
770
|
+
Arguments:
|
771
|
+
emulator: An emulator instance on which this machine state should
|
772
|
+
run.
|
773
|
+
|
774
|
+
Returns:
|
775
|
+
The final system state after emulation.
|
776
|
+
"""
|
777
|
+
|
778
|
+
self.apply(emulator)
|
779
|
+
|
780
|
+
try:
|
781
|
+
emulator.run()
|
782
|
+
except exceptions.EmulationBounds:
|
783
|
+
pass
|
784
|
+
|
785
|
+
machine_copy = copy.deepcopy(self)
|
786
|
+
machine_copy.extract(emulator)
|
787
|
+
|
788
|
+
return machine_copy
|
789
|
+
|
790
|
+
def analyze(self, analysis: analyses.Analysis) -> None:
|
791
|
+
"""Run the given analysis on this machine.
|
792
|
+
|
793
|
+
Arguments:
|
794
|
+
analysis: The analysis to run.
|
795
|
+
"""
|
796
|
+
|
797
|
+
analysis.run(self)
|
798
|
+
|
799
|
+
def step(
|
800
|
+
self, emulator: emulators.Emulator
|
801
|
+
) -> typing.Generator[Machine, None, None]:
|
802
|
+
"""This is a generator that single steps the machine each time it is called.
|
803
|
+
|
804
|
+
Yields:
|
805
|
+
A new machine that is the previous one single stepped forward.
|
806
|
+
"""
|
807
|
+
self.apply(emulator)
|
808
|
+
|
809
|
+
while True:
|
810
|
+
try:
|
811
|
+
emulator.step()
|
812
|
+
machine_copy = copy.deepcopy(self)
|
813
|
+
machine_copy.extract(emulator)
|
814
|
+
yield machine_copy
|
815
|
+
except exceptions.EmulationBounds:
|
816
|
+
# import pdb
|
817
|
+
# pdb.set_trace()
|
818
|
+
print(
|
819
|
+
"emulation complete; encountered exit point or went out of bounds"
|
820
|
+
)
|
821
|
+
break
|
822
|
+
except Exception as e:
|
823
|
+
# import pdb
|
824
|
+
# pdb.set_trace()
|
825
|
+
print(f"emulation ended; raised exception {e}")
|
826
|
+
break
|
827
|
+
return None
|
828
|
+
|
829
|
+
def fuzz(
|
830
|
+
self,
|
831
|
+
emulator: emulators.Emulator,
|
832
|
+
input_callback: typing.Callable,
|
833
|
+
crash_callback: typing.Optional[typing.Callable] = None,
|
834
|
+
always_validate: bool = False,
|
835
|
+
iterations: int = 1,
|
836
|
+
) -> None:
|
837
|
+
"""Fuzz the machine using unicornafl.
|
838
|
+
|
839
|
+
Arguments:
|
840
|
+
emulator: Currently, must be the unicorn emulator
|
841
|
+
input_callback: A callback that applies an input to a machine
|
842
|
+
crash_callback: An optional callback that is given the unicorn state and can decide whether or not to record it as a crash. (See unicornafl documentation for more info)
|
843
|
+
always_validate: Whether to run the crash_callback on every run or only when unicorn returns an error.
|
844
|
+
iterations: The number of iterations to run before forking a new child
|
845
|
+
Returns:
|
846
|
+
Bytes for this value with the given byteorder.
|
847
|
+
"""
|
848
|
+
try:
|
849
|
+
import argparse
|
850
|
+
|
851
|
+
import unicornafl
|
852
|
+
except ImportError:
|
853
|
+
raise RuntimeError(
|
854
|
+
"missing `unicornafl` - afl++ must be installed manually from source"
|
855
|
+
)
|
856
|
+
|
857
|
+
arg_parser = argparse.ArgumentParser(description="AFL Harness")
|
858
|
+
arg_parser.add_argument(
|
859
|
+
"input_file", type=str, help="File path AFL will mutate"
|
860
|
+
)
|
861
|
+
args = arg_parser.parse_args()
|
862
|
+
|
863
|
+
if not isinstance(emulator, emulators.UnicornEmulator):
|
864
|
+
raise RuntimeError("you must use a unicorn emulator to fuzz")
|
865
|
+
|
866
|
+
self.apply(emulator)
|
867
|
+
|
868
|
+
unicornafl.uc_afl_fuzz(
|
869
|
+
uc=emulator.engine,
|
870
|
+
input_file=args.input_file,
|
871
|
+
place_input_callback=input_callback,
|
872
|
+
exits=emulator.get_exit_points(),
|
873
|
+
validate_crash_callback=crash_callback,
|
874
|
+
always_validate=always_validate,
|
875
|
+
persistent_iters=iterations,
|
876
|
+
)
|
877
|
+
|
878
|
+
def get_cpus(self):
|
879
|
+
"""Gets a list of :class:`~smallworld.state.cpus.cpu.CPU` attached to this machine.
|
880
|
+
|
881
|
+
Returns:
|
882
|
+
A list of objects that subclass :class:`~smallworld.state.cpus.cpu.CPU` attached to this machin.
|
883
|
+
"""
|
884
|
+
return [i for i in self if issubclass(type(i), state.cpus.cpu.CPU)]
|
885
|
+
|
886
|
+
def get_elfs(self):
|
887
|
+
return [i for i in self if issubclass(type(i), state.memory.ElfExecutable)]
|
888
|
+
|
889
|
+
def get_platforms(self):
|
890
|
+
"""Gets a set of platforms for the :class:`~smallworld.state.cpus.cpu.CPU` (s) attached to this machine.
|
891
|
+
|
892
|
+
Returns:
|
893
|
+
A set of platforms for the :class:`~smallworld.state.cpus.cpu.CPU` (s) attached to this machin.
|
894
|
+
|
895
|
+
"""
|
896
|
+
return set([i.get_platform() for i in self.get_cpus()])
|
897
|
+
|
898
|
+
def get_cpu(self):
|
899
|
+
"""Gets the :class:`~smallworld.state.cpus.cpu.CPU` attached to this machine. Throws an exception if the machine has more than one.
|
900
|
+
|
901
|
+
Raises:
|
902
|
+
ConfigurationError: if the machine has more than one :class:`~smallworld.state.cpus.cpu.CPU`
|
903
|
+
|
904
|
+
Returns:
|
905
|
+
The :class:`~smallworld.state.cpus.cpu.CPU` attached to this machine.
|
906
|
+
"""
|
907
|
+
cpus = self.get_cpus()
|
908
|
+
if len(cpus) != 1:
|
909
|
+
raise exceptions.ConfigurationError("You have more than one CPU")
|
910
|
+
return cpus[0]
|
911
|
+
|
912
|
+
def get_elf(self):
|
913
|
+
elfs = self.get_elfs()
|
914
|
+
if len(elfs) != 1:
|
915
|
+
raise exceptions.ConfigurationError("You have more than one ELF")
|
916
|
+
return elfs[0]
|
917
|
+
|
918
|
+
def get_platform(self):
|
919
|
+
"""Gets the platform of the :class:`~smallworld.state.cpus.cpu.CPU` (s) attached to this machine. Throws an exception if the machine has more than one.
|
920
|
+
|
921
|
+
Raises:
|
922
|
+
ConfigurationError: if the machine has more than one platform
|
923
|
+
|
924
|
+
Returns:
|
925
|
+
The platform for the :class:`~smallworld.state.cpus.cpu.CPU` (s) attached to this machine.
|
926
|
+
"""
|
927
|
+
platforms = self.get_platforms()
|
928
|
+
if len(platforms) != 1:
|
929
|
+
raise exceptions.ConfigurationError("You have more than one platform")
|
930
|
+
return platforms.pop()
|
931
|
+
|
932
|
+
def read_memory(self, address: int, size: int) -> typing.Optional[bytes]:
|
933
|
+
"""Read bytes out of memory if available.
|
934
|
+
|
935
|
+
Arguments:
|
936
|
+
address: start address of read.
|
937
|
+
size: number of bytes to read.
|
938
|
+
|
939
|
+
Returns:
|
940
|
+
the bytes read, or None if unavailable.
|
941
|
+
"""
|
942
|
+
for m in self:
|
943
|
+
if issubclass(type(m), state.memory.Memory):
|
944
|
+
for po, v in m.items():
|
945
|
+
if m.address + po <= address <= m.address + po + v._size:
|
946
|
+
c = m[po].get()
|
947
|
+
o = address - (m.address + po)
|
948
|
+
return c[o : o + size]
|
949
|
+
return None
|
950
|
+
|
951
|
+
|
952
|
+
__all__ = [
|
953
|
+
"Stateful",
|
954
|
+
"Value",
|
955
|
+
"IntegerValue",
|
956
|
+
"BytesValue",
|
957
|
+
"SymbolicValue",
|
958
|
+
"Register",
|
959
|
+
"RegisterAlias",
|
960
|
+
"FixedRegister",
|
961
|
+
"Machine",
|
962
|
+
]
|