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.
Files changed (166) hide show
  1. smallworld/__init__.py +35 -0
  2. smallworld/analyses/__init__.py +14 -0
  3. smallworld/analyses/analysis.py +88 -0
  4. smallworld/analyses/code_coverage.py +31 -0
  5. smallworld/analyses/colorizer.py +682 -0
  6. smallworld/analyses/colorizer_summary.py +100 -0
  7. smallworld/analyses/field_detection/__init__.py +14 -0
  8. smallworld/analyses/field_detection/field_analysis.py +536 -0
  9. smallworld/analyses/field_detection/guards.py +26 -0
  10. smallworld/analyses/field_detection/hints.py +133 -0
  11. smallworld/analyses/field_detection/malloc.py +211 -0
  12. smallworld/analyses/forced_exec/__init__.py +3 -0
  13. smallworld/analyses/forced_exec/forced_exec.py +87 -0
  14. smallworld/analyses/underlays/__init__.py +4 -0
  15. smallworld/analyses/underlays/basic.py +13 -0
  16. smallworld/analyses/underlays/underlay.py +31 -0
  17. smallworld/analyses/unstable/__init__.py +4 -0
  18. smallworld/analyses/unstable/angr/__init__.py +0 -0
  19. smallworld/analyses/unstable/angr/base.py +12 -0
  20. smallworld/analyses/unstable/angr/divergence.py +274 -0
  21. smallworld/analyses/unstable/angr/model.py +383 -0
  22. smallworld/analyses/unstable/angr/nwbt.py +63 -0
  23. smallworld/analyses/unstable/angr/typedefs.py +170 -0
  24. smallworld/analyses/unstable/angr/utils.py +25 -0
  25. smallworld/analyses/unstable/angr/visitor.py +315 -0
  26. smallworld/analyses/unstable/angr_nwbt.py +106 -0
  27. smallworld/analyses/unstable/code_coverage.py +54 -0
  28. smallworld/analyses/unstable/code_reachable.py +44 -0
  29. smallworld/analyses/unstable/control_flow_tracer.py +71 -0
  30. smallworld/analyses/unstable/pointer_finder.py +90 -0
  31. smallworld/arch/__init__.py +0 -0
  32. smallworld/arch/aarch64_arch.py +286 -0
  33. smallworld/arch/amd64_arch.py +86 -0
  34. smallworld/arch/i386_arch.py +44 -0
  35. smallworld/emulators/__init__.py +14 -0
  36. smallworld/emulators/angr/__init__.py +7 -0
  37. smallworld/emulators/angr/angr.py +1652 -0
  38. smallworld/emulators/angr/default.py +15 -0
  39. smallworld/emulators/angr/exceptions.py +7 -0
  40. smallworld/emulators/angr/exploration/__init__.py +9 -0
  41. smallworld/emulators/angr/exploration/bounds.py +27 -0
  42. smallworld/emulators/angr/exploration/default.py +17 -0
  43. smallworld/emulators/angr/exploration/terminate.py +22 -0
  44. smallworld/emulators/angr/factory.py +55 -0
  45. smallworld/emulators/angr/machdefs/__init__.py +35 -0
  46. smallworld/emulators/angr/machdefs/aarch64.py +292 -0
  47. smallworld/emulators/angr/machdefs/amd64.py +192 -0
  48. smallworld/emulators/angr/machdefs/arm.py +387 -0
  49. smallworld/emulators/angr/machdefs/i386.py +221 -0
  50. smallworld/emulators/angr/machdefs/machdef.py +138 -0
  51. smallworld/emulators/angr/machdefs/mips.py +184 -0
  52. smallworld/emulators/angr/machdefs/mips64.py +189 -0
  53. smallworld/emulators/angr/machdefs/ppc.py +101 -0
  54. smallworld/emulators/angr/machdefs/riscv.py +261 -0
  55. smallworld/emulators/angr/machdefs/xtensa.py +255 -0
  56. smallworld/emulators/angr/memory/__init__.py +7 -0
  57. smallworld/emulators/angr/memory/default.py +10 -0
  58. smallworld/emulators/angr/memory/fixups.py +43 -0
  59. smallworld/emulators/angr/memory/memtrack.py +105 -0
  60. smallworld/emulators/angr/scratch.py +43 -0
  61. smallworld/emulators/angr/simos.py +53 -0
  62. smallworld/emulators/angr/utils.py +70 -0
  63. smallworld/emulators/emulator.py +1013 -0
  64. smallworld/emulators/hookable.py +252 -0
  65. smallworld/emulators/panda/__init__.py +5 -0
  66. smallworld/emulators/panda/machdefs/__init__.py +28 -0
  67. smallworld/emulators/panda/machdefs/aarch64.py +93 -0
  68. smallworld/emulators/panda/machdefs/amd64.py +71 -0
  69. smallworld/emulators/panda/machdefs/arm.py +89 -0
  70. smallworld/emulators/panda/machdefs/i386.py +36 -0
  71. smallworld/emulators/panda/machdefs/machdef.py +86 -0
  72. smallworld/emulators/panda/machdefs/mips.py +94 -0
  73. smallworld/emulators/panda/machdefs/mips64.py +91 -0
  74. smallworld/emulators/panda/machdefs/ppc.py +79 -0
  75. smallworld/emulators/panda/panda.py +575 -0
  76. smallworld/emulators/unicorn/__init__.py +13 -0
  77. smallworld/emulators/unicorn/machdefs/__init__.py +28 -0
  78. smallworld/emulators/unicorn/machdefs/aarch64.py +310 -0
  79. smallworld/emulators/unicorn/machdefs/amd64.py +326 -0
  80. smallworld/emulators/unicorn/machdefs/arm.py +321 -0
  81. smallworld/emulators/unicorn/machdefs/i386.py +137 -0
  82. smallworld/emulators/unicorn/machdefs/machdef.py +117 -0
  83. smallworld/emulators/unicorn/machdefs/mips.py +202 -0
  84. smallworld/emulators/unicorn/unicorn.py +684 -0
  85. smallworld/exceptions/__init__.py +5 -0
  86. smallworld/exceptions/exceptions.py +85 -0
  87. smallworld/exceptions/unstable/__init__.py +1 -0
  88. smallworld/exceptions/unstable/exceptions.py +25 -0
  89. smallworld/extern/__init__.py +4 -0
  90. smallworld/extern/ctypes.py +94 -0
  91. smallworld/extern/unstable/__init__.py +1 -0
  92. smallworld/extern/unstable/ghidra.py +129 -0
  93. smallworld/helpers.py +107 -0
  94. smallworld/hinting/__init__.py +8 -0
  95. smallworld/hinting/hinting.py +214 -0
  96. smallworld/hinting/hints.py +427 -0
  97. smallworld/hinting/unstable/__init__.py +2 -0
  98. smallworld/hinting/utils.py +19 -0
  99. smallworld/instructions/__init__.py +18 -0
  100. smallworld/instructions/aarch64.py +20 -0
  101. smallworld/instructions/arm.py +18 -0
  102. smallworld/instructions/bsid.py +67 -0
  103. smallworld/instructions/instructions.py +258 -0
  104. smallworld/instructions/mips.py +21 -0
  105. smallworld/instructions/x86.py +100 -0
  106. smallworld/logging.py +90 -0
  107. smallworld/platforms.py +95 -0
  108. smallworld/py.typed +0 -0
  109. smallworld/state/__init__.py +6 -0
  110. smallworld/state/cpus/__init__.py +32 -0
  111. smallworld/state/cpus/aarch64.py +563 -0
  112. smallworld/state/cpus/amd64.py +676 -0
  113. smallworld/state/cpus/arm.py +630 -0
  114. smallworld/state/cpus/cpu.py +71 -0
  115. smallworld/state/cpus/i386.py +239 -0
  116. smallworld/state/cpus/mips.py +374 -0
  117. smallworld/state/cpus/mips64.py +372 -0
  118. smallworld/state/cpus/powerpc.py +229 -0
  119. smallworld/state/cpus/riscv.py +357 -0
  120. smallworld/state/cpus/xtensa.py +80 -0
  121. smallworld/state/memory/__init__.py +7 -0
  122. smallworld/state/memory/code.py +70 -0
  123. smallworld/state/memory/elf/__init__.py +3 -0
  124. smallworld/state/memory/elf/elf.py +564 -0
  125. smallworld/state/memory/elf/rela/__init__.py +32 -0
  126. smallworld/state/memory/elf/rela/aarch64.py +27 -0
  127. smallworld/state/memory/elf/rela/amd64.py +32 -0
  128. smallworld/state/memory/elf/rela/arm.py +51 -0
  129. smallworld/state/memory/elf/rela/i386.py +32 -0
  130. smallworld/state/memory/elf/rela/mips.py +45 -0
  131. smallworld/state/memory/elf/rela/ppc.py +45 -0
  132. smallworld/state/memory/elf/rela/rela.py +63 -0
  133. smallworld/state/memory/elf/rela/riscv64.py +27 -0
  134. smallworld/state/memory/elf/rela/xtensa.py +15 -0
  135. smallworld/state/memory/elf/structs.py +55 -0
  136. smallworld/state/memory/heap.py +85 -0
  137. smallworld/state/memory/memory.py +181 -0
  138. smallworld/state/memory/stack/__init__.py +31 -0
  139. smallworld/state/memory/stack/aarch64.py +22 -0
  140. smallworld/state/memory/stack/amd64.py +42 -0
  141. smallworld/state/memory/stack/arm.py +66 -0
  142. smallworld/state/memory/stack/i386.py +22 -0
  143. smallworld/state/memory/stack/mips.py +34 -0
  144. smallworld/state/memory/stack/mips64.py +34 -0
  145. smallworld/state/memory/stack/ppc.py +34 -0
  146. smallworld/state/memory/stack/riscv.py +22 -0
  147. smallworld/state/memory/stack/stack.py +127 -0
  148. smallworld/state/memory/stack/xtensa.py +34 -0
  149. smallworld/state/models/__init__.py +6 -0
  150. smallworld/state/models/mmio.py +186 -0
  151. smallworld/state/models/model.py +163 -0
  152. smallworld/state/models/posix.py +455 -0
  153. smallworld/state/models/x86/__init__.py +2 -0
  154. smallworld/state/models/x86/microsoftcdecl.py +35 -0
  155. smallworld/state/models/x86/systemv.py +240 -0
  156. smallworld/state/state.py +962 -0
  157. smallworld/state/unstable/__init__.py +0 -0
  158. smallworld/state/unstable/elf.py +393 -0
  159. smallworld/state/x86_registers.py +30 -0
  160. smallworld/utils.py +935 -0
  161. smallworld_re-1.0.0.dist-info/LICENSE.txt +21 -0
  162. smallworld_re-1.0.0.dist-info/METADATA +189 -0
  163. smallworld_re-1.0.0.dist-info/RECORD +166 -0
  164. smallworld_re-1.0.0.dist-info/WHEEL +5 -0
  165. smallworld_re-1.0.0.dist-info/entry_points.txt +2 -0
  166. 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
+ ]