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,1652 @@
1
+ from __future__ import annotations
2
+
3
+ import logging
4
+ import typing
5
+
6
+ import angr
7
+ import claripy
8
+ import cle
9
+
10
+ from ... import exceptions, platforms, utils
11
+ from .. import emulator
12
+ from .default import configure_default_plugins, configure_default_strategy
13
+ from .factory import PatchedObjectFactory
14
+ from .machdefs import AngrMachineDef
15
+ from .simos import HookableSimOS
16
+
17
+ log = logging.getLogger(__name__)
18
+
19
+
20
+ class HookHandler(angr.SimProcedure):
21
+ """SimProcedure for implementing "finish" hooks.
22
+
23
+ This requires a callback as an extra kwarg.
24
+ """
25
+
26
+ def run(self, *args, callback, parent):
27
+ emu = ConcreteAngrEmulator(self.state, parent)
28
+ callback(emu)
29
+ return None
30
+
31
+
32
+ class AngrEmulator(
33
+ emulator.Emulator,
34
+ emulator.InstructionHookable,
35
+ emulator.FunctionHookable,
36
+ emulator.SyscallHookable,
37
+ emulator.MemoryReadHookable,
38
+ emulator.MemoryWriteHookable,
39
+ emulator.ConstrainedEmulator,
40
+ ):
41
+ """
42
+ Angr symbolic execution emulator
43
+
44
+ This is primarily designed to support symbolic execution,
45
+ although subclasses can configure angr however they like.
46
+
47
+ One challenge with symbolic execution is that
48
+ it doesn't work on a single machine state,
49
+ but rather multiple machine states representing
50
+ parallel execution paths.
51
+
52
+ As such, this interface doesn't yet fully support
53
+ all features of the base Emulator class;
54
+ it's not clear what reading or writing machine state
55
+ means when there's more than one state.
56
+ """
57
+
58
+ PAGE_SIZE = 4096
59
+
60
+ name = "angr-emulator"
61
+ description = "an emulator using angr as its backend"
62
+ version = "0.0"
63
+
64
+ def __init__(self, platform: platforms.Platform, preinit=None, init=None):
65
+ # Initializaed bit; tells us if angr state is initialized
66
+ self._initialized: bool = False
67
+
68
+ # Dirty bit; tells us if we can modify self.state
69
+ self._dirty: bool = False
70
+
71
+ # Linear mode bit; tells us if we're running in forced linear execution
72
+ self._linear: bool = False
73
+
74
+ # Plugin preset; tells us which plugin preset to use.
75
+ self._plugin_preset = "default"
76
+
77
+ # The machine definition;
78
+ # helps translate between angr and smallworld
79
+ self.machdef: AngrMachineDef = AngrMachineDef.for_platform(platform)
80
+
81
+ self.platform = platform
82
+ self.preinit = preinit
83
+ self.init = init
84
+
85
+ # Cached state modifications,
86
+ # to be applied during initialization
87
+ self._code: typing.List[typing.Tuple[int, bytes]] = list()
88
+ self._register_contents: typing.Dict[
89
+ str, typing.Union[int, claripy.ast.bv.BV]
90
+ ] = dict()
91
+ self._register_labels: typing.Dict[str, str] = dict()
92
+ self._memory_maps: typing.List[typing.Tuple[int, int]] = list()
93
+ self._memory_contents: typing.List[
94
+ typing.Tuple[int, typing.Union[bytes, claripy.ast.bv.BV]]
95
+ ] = list()
96
+ self._memory_labels: typing.List[typing.Tuple[int, int, str]] = list()
97
+ self._instr_hooks: typing.List[
98
+ typing.Tuple[int, typing.Callable[[emulator.Emulator], None]]
99
+ ] = list()
100
+ self._gb_instr_hook: typing.Optional[
101
+ typing.Callable[[emulator.Emulator], None]
102
+ ] = None
103
+ self._func_hooks: typing.List[
104
+ typing.Tuple[int, typing.Callable[[emulator.Emulator], None]]
105
+ ] = list()
106
+ self._syscall_hooks: typing.Dict[
107
+ int, typing.Callable[[emulator.Emulator], None]
108
+ ] = dict()
109
+ self._gb_syscall_hook: typing.Optional[
110
+ typing.Callable[[emulator.Emulator, int], None]
111
+ ] = None
112
+ self._read_hooks: typing.List[
113
+ typing.Tuple[
114
+ int,
115
+ int,
116
+ typing.Callable[
117
+ [emulator.Emulator, int, int, claripy.ast.bv.BV],
118
+ typing.Optional[claripy.ast.bv.BV],
119
+ ],
120
+ ]
121
+ ] = list()
122
+ self._gb_read_hook: typing.Optional[
123
+ typing.Callable[
124
+ [emulator.Emulator, int, int, claripy.ast.bv.BV],
125
+ typing.Optional[claripy.ast.bv.BV],
126
+ ]
127
+ ] = None
128
+ self._write_hooks: typing.List[
129
+ typing.Tuple[
130
+ int,
131
+ int,
132
+ typing.Callable[[emulator.Emulator, int, int, claripy.ast.bv.BV], None],
133
+ ]
134
+ ] = list()
135
+ self._gb_write_hook: typing.Optional[
136
+ typing.Callable[[emulator.Emulator, int, int, claripy.ast.bv.BV], None]
137
+ ] = None
138
+ self._code_bounds: typing.List[typing.Tuple[int, int]] = list()
139
+ self._exit_points: typing.Set[int] = set()
140
+ self._constraints: typing.List[claripy.ast.bool.Bool] = list()
141
+ self._extensions: typing.Dict[str, typing.Any] = dict()
142
+
143
+ def initialize(self):
144
+ """Initialize the emulator
145
+
146
+ To take advantage of CLE, we need to know about all code
147
+ before we initialize the angr state objects.
148
+ However, applying register and memory changes requires access
149
+ to the angr state object.
150
+
151
+ SmallWorld doesn't support ordering how state is applied,
152
+ so I need to collect it, then apply it once emulation starts.
153
+
154
+ This function is invoked automatically when you cycle the emulator,
155
+ but you're free to invoke it early if you want.
156
+ """
157
+
158
+ if self._initialized:
159
+ raise exceptions.ConfigurationError("Cannot reinitialize running emulator")
160
+
161
+ # Build a single, coherent byte stream out of our code
162
+ self._code.sort(key=lambda x: x[0])
163
+
164
+ code = utils.SparseIO()
165
+ segments = list()
166
+
167
+ if len(self._code) > 0:
168
+ code_ranges = utils.RangeCollection()
169
+ base_address = self._code[0][0]
170
+ for addr, data in self._code:
171
+ size = len(data)
172
+ code.seek(addr - base_address)
173
+ code.write(data)
174
+ code_ranges.add_range((addr, addr + size))
175
+ segments = list(
176
+ map(lambda x: (x[0] - base_address, x[0], x[1] - x[0]), code_ranges)
177
+ )
178
+
179
+ # Create an angr project using a blank byte stream,
180
+ # and registered as self-modifying so we can load more code later.
181
+ backend = cle.Blob(
182
+ None, code, is_main_bin=True, arch=self.machdef.angr_arch, segments=segments
183
+ )
184
+ loader = cle.Loader(backend)
185
+ self.proj: angr.Project = angr.Project(
186
+ loader,
187
+ engine=self.machdef.angr_engine,
188
+ selfmodifying_code=True,
189
+ simos=HookableSimOS,
190
+ )
191
+ # Override the default factory
192
+ self.proj.factory = PatchedObjectFactory(
193
+ self.proj, type(self.proj.factory.default_engine)
194
+ )
195
+
196
+ # Configure default plugin preset.
197
+ # Do this before creating the state, so the state inherits the correct preset.
198
+ # Also do this before preinit, so that analyses can inherit from the default.
199
+ configure_default_plugins(self)
200
+
201
+ # If preinit is specified, run it.
202
+ if self.preinit is not None:
203
+ self.preinit(self)
204
+
205
+ # Create a completely blank entry state
206
+ self.state = self.proj.factory.blank_state(
207
+ plugin_preset=self._plugin_preset,
208
+ add_options={
209
+ # angr.options.BYPASS_UNSUPPORTED_SYSCALL,
210
+ angr.options.SYMBOL_FILL_UNCONSTRAINED_REGISTERS,
211
+ angr.options.SYMBOL_FILL_UNCONSTRAINED_MEMORY,
212
+ },
213
+ remove_options={
214
+ angr.options.SIMPLIFY_CONSTRAINTS,
215
+ angr.options.SIMPLIFY_EXIT_GUARD,
216
+ angr.options.SIMPLIFY_EXIT_STATE,
217
+ angr.options.SIMPLIFY_EXIT_TARGET,
218
+ angr.options.SIMPLIFY_EXPRS,
219
+ angr.options.SIMPLIFY_MEMORY_READS,
220
+ angr.options.SIMPLIFY_MEMORY_WRITES,
221
+ angr.options.SIMPLIFY_REGISTER_READS,
222
+ angr.options.SIMPLIFY_REGISTER_WRITES,
223
+ },
224
+ )
225
+
226
+ # Create a simulation manager for our entry state
227
+ self.mgr = self.proj.factory.simulation_manager(self.state, save_unsat=True)
228
+
229
+ # Configure default simulation strategy.
230
+ configure_default_strategy(self)
231
+
232
+ # If we have an init runner, run it.
233
+ if self.init:
234
+ self.init(self)
235
+
236
+ # Mark this emulator as initialized
237
+ self._initialized = True
238
+
239
+ # Read back any changes that happened before initialization.
240
+ for name, content in self._register_contents.items():
241
+ self.write_register_content(name, content)
242
+
243
+ for name, label in self._register_labels.items():
244
+ self.write_register_label(name, label)
245
+
246
+ for addr, size in self._memory_maps:
247
+ self.map_memory(addr, size)
248
+
249
+ for addr, content in self._memory_contents:
250
+ self.write_memory_content(addr, content)
251
+
252
+ self._write_memory_label_bulk(self._memory_labels)
253
+
254
+ for addr, func in self._instr_hooks:
255
+ self.hook_instruction(addr, func)
256
+
257
+ if self._gb_instr_hook is not None:
258
+ self.hook_instructions(self._gb_instr_hook)
259
+
260
+ for addr, func in self._func_hooks:
261
+ self.hook_function(addr, func)
262
+
263
+ for num, func in self._syscall_hooks.items():
264
+ self.hook_syscall(num, func)
265
+
266
+ if self._gb_syscall_hook is not None:
267
+ self.hook_syscalls(self._gb_syscall_hook)
268
+
269
+ for start, end, func in self._read_hooks:
270
+ self.hook_memory_read_symbolic(start, end, func)
271
+
272
+ if self._gb_read_hook is not None:
273
+ self.hook_memory_reads_symbolic(self._gb_read_hook)
274
+
275
+ for start, end, func in self._write_hooks:
276
+ self.hook_memory_write_symbolic(start, end, func)
277
+
278
+ if self._gb_write_hook is not None:
279
+ self.hook_memory_writes_symbolic(self._gb_write_hook)
280
+
281
+ for start, end in self._code_bounds:
282
+ self.add_bound(start, end)
283
+
284
+ for addr in self._exit_points:
285
+ self.add_exit_point(addr)
286
+
287
+ for expr in self._constraints:
288
+ self.add_constraint(expr)
289
+
290
+ self.state.scratch.extensions |= self._extensions
291
+
292
+ def read_register_symbolic(self, name: str) -> claripy.ast.bv.BV:
293
+ if not self._initialized:
294
+ raise exceptions.ConfigurationError(
295
+ "Cannot read registers before initialization"
296
+ )
297
+
298
+ if self._dirty and not self._linear:
299
+ raise NotImplementedError(
300
+ "Reading registers not supported once execution begins."
301
+ )
302
+
303
+ if name == "pc":
304
+ name = self.machdef.pc_reg
305
+
306
+ (off, size) = self.machdef.angr_reg(name)
307
+ return self.state.registers.load(off, size)
308
+
309
+ def read_register_content(self, name: str) -> int:
310
+ out = self.read_register_symbolic(name)
311
+ if out.symbolic:
312
+ raise exceptions.SymbolicValueError(f"Register {name} is symbolic")
313
+ else:
314
+ return out.concrete_value
315
+
316
+ def read_register_type(self, name: str) -> typing.Optional[typing.Any]:
317
+ return None
318
+
319
+ def read_register_label(self, name: str) -> typing.Optional[str]:
320
+ if not self._initialized:
321
+ raise exceptions.ConfigurationError(
322
+ "Cannot read registers before initialization"
323
+ )
324
+
325
+ if self._dirty and not self._linear:
326
+ raise NotImplementedError(
327
+ "Reading register not supported once execution begins."
328
+ )
329
+
330
+ if name == "pc":
331
+ name = self.machdef.pc_reg
332
+
333
+ try:
334
+ # This considers all BVSs to be labeled values;
335
+ # if it has a name, we're giving it to you.
336
+ (off, size) = self.machdef.angr_reg(name)
337
+ out = self.state.registers.load(off, size)
338
+ if out.symbolic:
339
+ if out.op == "BVS":
340
+ # This is a "pure" label; we can return it.
341
+ return out.args[0]
342
+ else:
343
+ # This is a mixed expression; we can't return it
344
+ log.warn(f"Register {name} contains a symbolic expression")
345
+ raise exceptions.SymbolicValueError(
346
+ f"Register {name} contains a symbolic expression"
347
+ )
348
+ else:
349
+ # No propagated label
350
+ return None
351
+ except ValueError:
352
+ # TODO: Handle invalid registers more gracefully
353
+ return None
354
+
355
+ def write_register_content(
356
+ self, name: str, content: typing.Union[None, int, claripy.ast.bv.BV]
357
+ ) -> None:
358
+ if not self._initialized and content is not None:
359
+ if name == "pc":
360
+ name = self.machdef.pc_reg
361
+ # Test that the angr register exists
362
+ _, _ = self.machdef.angr_reg(name)
363
+ self._register_contents[name] = content
364
+ return
365
+ elif self._dirty and not self._linear:
366
+ raise NotImplementedError(
367
+ "Writing registers not supported once execution begins."
368
+ )
369
+ # This will replace any value - symbolic or otherwise - currently in the emulator.
370
+ # Since labels are treated as symbolic values, it must be called before
371
+ # write_register_label().
372
+
373
+ if name == "pc":
374
+ name = self.machdef.pc_reg
375
+ (off, size) = self.machdef.angr_reg(name)
376
+
377
+ if content is None:
378
+ v = claripy.BVS("UNINITIALIZED", size * 8)
379
+ elif isinstance(content, int):
380
+ v = claripy.BVV(content, size * 8)
381
+ elif isinstance(content, claripy.ast.bv.BV):
382
+ v = content
383
+ else:
384
+ raise TypeError(
385
+ f"Content for register {name} was {type(content)}; expected (None | int | claripy.ast.bv.BV)"
386
+ )
387
+
388
+ if v.size() != size * 8:
389
+ raise exceptions.ConfigurationError(
390
+ f"Register {name} has size {size}, but content has size {v.size() // 8}"
391
+ )
392
+
393
+ self.state.registers.store(off, v)
394
+
395
+ def write_register_type(
396
+ self, name: str, type: typing.Optional[typing.Any] = None
397
+ ) -> None:
398
+ pass
399
+
400
+ def write_register_label(
401
+ self, name: str, label: typing.Optional[str] = None
402
+ ) -> None:
403
+ if label is None:
404
+ return
405
+ elif not self._initialized:
406
+ if name == "pc":
407
+ name = self.machdef.pc_reg
408
+ # Test that the angr register exists
409
+ _, _ = self.machdef.angr_reg(name)
410
+ self._register_labels[name] = label
411
+ elif self._dirty and not self._linear:
412
+ raise NotImplementedError(
413
+ "Writing registers not supported once execution begins."
414
+ )
415
+ else:
416
+ if name == "pc":
417
+ name = self.machdef.pc_reg
418
+ (off, size) = self.machdef.angr_reg(name)
419
+
420
+ # This will bind whatever value is currently in the register
421
+ # to a symbol named after the label
422
+ # The same label will ALWAYS map to the same symbol!
423
+ # This can introduce unintended restrictions on exploration,
424
+ # or even a contradictory state.
425
+ s = claripy.BVS(label, size * 8, explicit_name=True)
426
+ v = self.state.registers.load(off, size)
427
+
428
+ if not self.state.solver.satisfiable(extra_constraints=[v == s]):
429
+ # Creating this binding will definitely cause a contradiction.
430
+ # Have you already used this label somewhere else?
431
+ raise exceptions.ConfigurationError(
432
+ f"Contradiction binding register {name} to label {label}"
433
+ )
434
+
435
+ # Passing the last check doesn't guarantee you're safe.
436
+ # There may be over-constraints. Please be careful.
437
+ self.state.registers.store(off, s)
438
+ self.state.solver.add(v == s)
439
+
440
+ def read_memory_content(self, address: int, size: int) -> bytes:
441
+ if not self._initialized:
442
+ raise exceptions.ConfigurationError(
443
+ "Cannot read memory before initialization"
444
+ )
445
+
446
+ if self._dirty and not self._linear:
447
+ raise NotImplementedError(
448
+ "Reading memory not supported once execution begins."
449
+ )
450
+ v = self.state.memory.load(address, size)
451
+ if v.symbolic:
452
+ log.warn(f"Memory at {hex(address)} ({size} bytes) is symbolic")
453
+ raise exceptions.SymbolicValueError(f"Memory at {hex(address)} is symbolic")
454
+
455
+ # Annoyingly, there isn't an easy way to convert BVV to bytes.
456
+ return bytes([v.get_byte(i).concrete_value for i in range(0, size)])
457
+
458
+ def read_memory_symbolic(self, address: int, size: int) -> claripy.ast.bv.BV:
459
+ if not self._initialized:
460
+ raise exceptions.ConfigurationError(
461
+ "Cannot read memory before initialization"
462
+ )
463
+
464
+ if self._dirty and not self._linear:
465
+ raise NotImplementedError(
466
+ "Reading memory not supported once execution begins."
467
+ )
468
+ return self.state.memory.load(address, size)
469
+
470
+ def read_memory_type(self, address: int, size: int) -> typing.Optional[typing.Any]:
471
+ return None
472
+
473
+ def read_memory_label(self, address: int, size: int) -> typing.Optional[str]:
474
+ if not self._initialized:
475
+ raise exceptions.ConfigurationError(
476
+ "Cannot read memory before initialization"
477
+ )
478
+
479
+ if self._dirty and not self._linear:
480
+ raise NotImplementedError(
481
+ "Writing memory not supported once execution begins."
482
+ )
483
+ v = self.state.memory.load(address, size)
484
+ if v.symbolic():
485
+ if v.op == "Extract":
486
+ # You got a piece of a possibly-labeled expression
487
+ # Try parsing the inner expression to see if it's a single symbol.
488
+ v = v.args[2]
489
+
490
+ if v.op == "BVS":
491
+ # You got a single symbol; I'll treat it as a label
492
+ return v.args[0]
493
+ else:
494
+ # You got a symbolic expression; I can't decode it further
495
+ log.warn(f"Memory at {hex(address)} ({size} bytes) is symbolic: {v}")
496
+ raise exceptions.SymbolicValueError(
497
+ f"Memory at {hex(address)} is symbolic"
498
+ )
499
+ else:
500
+ # Definitely no labels here
501
+ return None
502
+
503
+ def map_memory(self, address: int, size: int) -> None:
504
+ if not self._initialized:
505
+ self._memory_maps.append((address, size))
506
+ else:
507
+ if self._dirty and not self._linear:
508
+ raise NotImplementedError(
509
+ "Mapping memory not supported once execution begins."
510
+ )
511
+ # Unlike Unicorn, angr doesn't care about pages.
512
+ region = (address, address + size)
513
+ self.state.scratch.memory_map.add_range(region)
514
+
515
+ def get_memory_map(self) -> typing.List[typing.Tuple[int, int]]:
516
+ if not self._initialized:
517
+ return list(map(lambda x: (x[0], x[0] + x[1]), self._memory_maps))
518
+ if self._dirty and not self._linear:
519
+ raise NotImplementedError(
520
+ "Mapping memory not supported once execution begins."
521
+ )
522
+ return list(self.state.scratch.memory_map.ranges)
523
+
524
+ def write_memory_content(
525
+ self, address: int, content: typing.Union[bytes, claripy.ast.bv.BV]
526
+ ) -> None:
527
+ if not self._initialized:
528
+ self._memory_contents.append((address, content))
529
+ elif self._dirty and not self._linear:
530
+ raise NotImplementedError(
531
+ "Writing memory not supported once execution begins."
532
+ )
533
+ else:
534
+ log.info(f"Storing {len(content)} bytes at {hex(address)}")
535
+ if isinstance(content, bytes):
536
+ content = claripy.BVV(content)
537
+ self.state.memory.store(address, content, inspect=False)
538
+
539
+ def write_memory_type(
540
+ self, address: int, size: int, type: typing.Optional[typing.Any] = None
541
+ ) -> None:
542
+ pass
543
+
544
+ def write_memory_label(
545
+ self, address: int, size: int, label: typing.Optional[str] = None
546
+ ) -> None:
547
+ if label is None:
548
+ return
549
+ elif not self._initialized:
550
+ self._memory_labels.append((address, size, label))
551
+ elif self._dirty and not self._linear:
552
+ raise NotImplementedError(
553
+ "Writing memory not supported once execution begins."
554
+ )
555
+ else:
556
+ # This will bind whatever value is currently at this address
557
+ # to a symbol named after the label
558
+ #
559
+ # The same label will ALWAYS map to the same symbol!
560
+ # This can introduce unintended restrictions on exploration,
561
+ # or even a contradictory state.
562
+ #
563
+ # This will trigger angr's default value computation,
564
+ # which may cause spurious results in some analyses.
565
+ s = claripy.BVS(label, size * 8, explicit_name=True)
566
+ v = self.state.memory.load(address, size)
567
+ if not self.state.solver.satisfiable(extra_constraints=[v == s]):
568
+ # Creating this binding will definitely cause a contradiction.
569
+ # Have you already used this label somewhere else?
570
+ raise exceptions.ConfigurationError(
571
+ f"Contradiction binding memory at {hex(address)} to label {label}"
572
+ )
573
+
574
+ # Passing the last check doesn't mean you're safe.
575
+ # There may be over-constraints. Please be careful.
576
+ self.state.memory.store(address, s, inspect=False)
577
+ self.state.solver.add(v == s)
578
+
579
+ def _write_memory_label_bulk(
580
+ self, labels: typing.List[typing.Tuple[int, int, typing.Optional[str]]]
581
+ ) -> None:
582
+ # Variant of write_memory_label that applies multiple labels in one go.
583
+ # This addresses a performance bottleneck during initialization;
584
+ # it's not really intended for public use,
585
+ # as it lacks some of the safety checks.
586
+ cs = list()
587
+ for addr, size, label in labels:
588
+ if label is None:
589
+ continue
590
+ s = claripy.BVS(label, size * 8, explicit_name=True)
591
+ v = self.state.memory.load(addr, size)
592
+ self.state.memory.store(addr, s)
593
+ cs.append(s == v)
594
+ c = claripy.And(*cs)
595
+ self.state.solver.add(c)
596
+
597
+ def write_code(self, address: int, content: bytes):
598
+ if self._initialized:
599
+ self.write_memory_content(address, content)
600
+ else:
601
+ self._code.append((address, content))
602
+
603
+ def hook_instruction(
604
+ self, address: int, function: typing.Callable[[emulator.Emulator], None]
605
+ ) -> None:
606
+ if not self._initialized:
607
+ self._instr_hooks.append((address, function))
608
+
609
+ elif self._dirty and not self._linear:
610
+ raise NotImplementedError(
611
+ "Instruction hooking not supported once execution begins"
612
+ )
613
+
614
+ elif address in self.state.scratch.insn_bps:
615
+ raise exceptions.ConfigurationError(
616
+ "Instruction at address {hex(address)} is already hooked"
617
+ )
618
+
619
+ else:
620
+
621
+ def hook_handler(state):
622
+ emu = ConcreteAngrEmulator(state, self)
623
+ function(emu)
624
+
625
+ # An update to angr means some operations on `state`
626
+ # will clobber this variable, which causes bad problems.
627
+ # Unclobber it, just in case
628
+ state.inspect.action_attrs_set = True
629
+
630
+ bp = self.state.inspect.b(
631
+ "instruction",
632
+ when=angr.BP_BEFORE,
633
+ action=hook_handler,
634
+ instruction=address,
635
+ )
636
+ self.state.scratch.insn_bps[address] = bp
637
+
638
+ def unhook_instruction(self, address: int) -> None:
639
+ if not self._initialized:
640
+ self._instr_hooks = list(
641
+ filter(lambda x: x[0] != address, self._instr_hooks)
642
+ )
643
+
644
+ elif self._dirty and not self._linear:
645
+ raise NotImplementedError(
646
+ "Instruction hooking not supported once execution begins"
647
+ )
648
+
649
+ elif address not in self.state.scratch.insn_bps:
650
+ raise exceptions.ConfigurationError(
651
+ "Instruction at address {hex(address)} is not hooked"
652
+ )
653
+
654
+ else:
655
+ bp = self.state.scratch.insn_bps[address]
656
+ del self.state.scratch.insn_bps[address]
657
+
658
+ self.state.inspect.remove_breakpoint("instruction", bp)
659
+
660
+ def hook_instructions(
661
+ self, function: typing.Callable[[emulator.Emulator], None]
662
+ ) -> None:
663
+ if not self._initialized:
664
+ self._gb_instr_hook = function
665
+
666
+ elif self._dirty and not self._linear:
667
+ raise NotImplementedError(
668
+ "Global instruction hooking not supported once execution begins"
669
+ )
670
+
671
+ elif self.state.scratch.global_insn_bp is not None:
672
+ raise exceptions.ConfigurationError(
673
+ "Global instruction hook already registered"
674
+ )
675
+
676
+ else:
677
+
678
+ def hook_handler(state):
679
+ emu = ConcreteAngrEmulator(state, self)
680
+ function(emu)
681
+
682
+ # An update to angr means some operations on `state`
683
+ # will clobber this variable, which causes bad problems.
684
+ # Unclobber it, just in case
685
+ state.inspect.action_attrs_set = True
686
+
687
+ self.state.scratch.global_insn_bp = self.state.inspect.b(
688
+ "instruction", when=angr.BP_BEFORE, action=hook_handler
689
+ )
690
+
691
+ def unhook_instructions(self) -> None:
692
+ if not self._initialized:
693
+ self._gb_instruction_hook = None
694
+
695
+ elif self._dirty and not self._linear:
696
+ raise NotImplementedError(
697
+ "Global instruction unhooking not supported once execution begins"
698
+ )
699
+
700
+ elif self.state.scratch.global_insn_bp is None:
701
+ raise exceptions.ConfigurationError("No global instruction hook present")
702
+
703
+ else:
704
+ bp = self.state.scratch.global_insn_bp
705
+ self.state.scratch.global_insn_bp = None
706
+ self.state.inspect.remove_breakpoint("instruction", bp)
707
+
708
+ def hook_function(
709
+ self, address: int, function: typing.Callable[[emulator.Emulator], None]
710
+ ) -> None:
711
+ if not self._initialized:
712
+ self._func_hooks.append((address, function))
713
+
714
+ elif self._dirty and not self._linear:
715
+ raise NotImplementedError("Cannot hook functions once emulation starts")
716
+
717
+ else:
718
+ hook = HookHandler(callback=function, parent=self)
719
+ self.proj.hook(address, hook, 0)
720
+
721
+ self.map_memory(address, 1)
722
+ self.state.scratch.func_bps[address] = None
723
+
724
+ def unhook_function(self, address: int) -> None:
725
+ if not self._initialized:
726
+ self._func_hooks = list(filter(lambda x: x[0] != address, self._func_hooks))
727
+
728
+ elif self._dirty and not self._linear:
729
+ raise NotImplementedError("Cannot unhook functions once emulation starts")
730
+
731
+ else:
732
+ self.proj.unhook(address)
733
+
734
+ def hook_syscall(
735
+ self, number: int, function: typing.Callable[[emulator.Emulator], None]
736
+ ) -> None:
737
+ if not self._initialized:
738
+ self._syscall_hooks[number] = function
739
+ elif self._dirty and not self._linear:
740
+ raise NotImplementedError("Cannot hook syscalls once emulation starts")
741
+ elif number in self.state.scratch.syscall_funcs:
742
+ raise exceptions.ConfigurationError(
743
+ f"Already have a syscall hook for {number}"
744
+ )
745
+ else:
746
+
747
+ def syscall_handler(state):
748
+ function(ConcreteAngrEmulator(state, self))
749
+
750
+ self.state.scratch.syscall_funcs[number] = syscall_handler
751
+
752
+ def unhook_syscall(self, number: int) -> None:
753
+ if not self._initialized:
754
+ del self._syscall_hooks[number]
755
+
756
+ elif self._dirty and not self._linear:
757
+ raise NotImplementedError("Cannot unhook syscalls once emulation starts")
758
+
759
+ elif number not in self.state.scratch.syscall_funcs:
760
+ raise exceptions.ConfigurationError(f"No syscall hook for {number}")
761
+
762
+ else:
763
+ del self.state.scratch.syscall_funcs[number]
764
+
765
+ def hook_syscalls(
766
+ self, function: typing.Callable[[emulator.Emulator, int], None]
767
+ ) -> None:
768
+ if not self._initialized:
769
+ self._gb_syscall_hook = function
770
+
771
+ elif self._dirty and not self._linear:
772
+ raise NotImplementedError("Cannot hook syscalls once emulation starts")
773
+
774
+ elif self.state.scratch.global_syscall_func is not None:
775
+ raise exceptions.ConfigurationError("Already have a global syscall hook")
776
+
777
+ else:
778
+
779
+ def syscall_handler(state, number: int):
780
+ function(ConcreteAngrEmulator(state, self), number)
781
+
782
+ self.state.scratch.global_syscall_func = syscall_handler
783
+
784
+ def unhook_syscalls(self) -> None:
785
+ if self._dirty and not self._linear:
786
+ raise NotImplementedError("Cannot unhook syscalls once emulation starts")
787
+
788
+ if self.state.scratch.global_syscall_func is None:
789
+ raise exceptions.ConfigurationError("No global syscall hook registered")
790
+ self.state.scratch.global_syscall_func = None
791
+
792
+ def hook_memory_read_symbolic(
793
+ self,
794
+ start: int,
795
+ end: int,
796
+ function: typing.Callable[
797
+ [emulator.Emulator, int, int, claripy.ast.bv.BV],
798
+ typing.Optional[claripy.ast.bv.BV],
799
+ ],
800
+ ) -> None:
801
+ if not self._initialized:
802
+ self._read_hooks.append((start, end, function))
803
+
804
+ elif self._dirty and not self._linear:
805
+ raise NotImplementedError(
806
+ "Memory hooking not supported once execution begins"
807
+ )
808
+
809
+ elif (start, end) in self.state.scratch.mem_read_bps:
810
+ raise exceptions.ConfigurationError(
811
+ f"{hex(start)}-{hex(end)} already hooked for reads"
812
+ )
813
+
814
+ else:
815
+ # This uses angr's conditional breakpoint facility
816
+ def read_condition(state):
817
+ # The breakpoint condition.
818
+ # This needs to be a bit clever to detect reads at bound symbolic addresses.
819
+ # The actual address won't get concretized until later
820
+ read_start = state.inspect.mem_read_address
821
+ if not isinstance(read_start, int):
822
+ if read_start.symbolic:
823
+ try:
824
+ values = state.solver.eval_atmost(read_start, 1)
825
+ # Truly unbound address.
826
+ # Assume it won't collapse to our hook address
827
+ if len(values) < 1:
828
+ return False
829
+ read_start = values[0]
830
+ except angr.errors.SimUnsatError:
831
+ return False
832
+ except angr.errors.SimValueError:
833
+ return False
834
+ else:
835
+ read_start = read_start.concrete_value
836
+ state.inspect.mem_read_address = read_start
837
+ read_size = state.inspect.mem_read_length
838
+
839
+ if read_size is None:
840
+ return False
841
+ read_end = read_start + read_size
842
+
843
+ return start <= read_start and end >= read_end
844
+
845
+ def read_callback(state):
846
+ # The breakpoint action.
847
+ addr = state.inspect.mem_read_address
848
+ size = state.inspect.mem_read_length
849
+ expr = state.inspect.mem_read_expr
850
+ res = function(ConcreteAngrEmulator(state, self), addr, size, expr)
851
+
852
+ if res is None:
853
+ res = expr
854
+ state.inspect.mem_read_expr = res
855
+
856
+ # An update to angr means some operations on `state`
857
+ # will clobber this variable, which causes bad problems.
858
+ # Unclobber it, just in case
859
+ state.inspect.action_attrs_set = True
860
+
861
+ bp = self.state.inspect.b(
862
+ "mem_read",
863
+ when=angr.BP_AFTER,
864
+ condition=read_condition,
865
+ action=read_callback,
866
+ )
867
+ self.state.scratch.mem_read_bps[(start, end)] = bp
868
+
869
+ def hook_memory_read(
870
+ self,
871
+ start: int,
872
+ end: int,
873
+ function: typing.Callable[
874
+ [emulator.Emulator, int, int, bytes], typing.Optional[bytes]
875
+ ],
876
+ ):
877
+ # Most of the machinery is handled in hook_memory_read_symbolic
878
+ # We just need to concretize the inputs and claripy the outputs
879
+ def sym_callback(
880
+ emu: emulator.Emulator, addr: int, size: int, expr: claripy.ast.bv.BV
881
+ ) -> typing.Optional[claripy.ast.bv.BV]:
882
+ if not isinstance(emu, ConcreteAngrEmulator):
883
+ raise TypeError("Where'd you get your emu?")
884
+ if expr.symbolic:
885
+ # Have the solver handle binding resolution for us.
886
+ try:
887
+ values = emu.state.solver.eval_atmost(expr, 1)
888
+ if len(values) < 1:
889
+ raise exceptions.AnalysisError(f"No possible values fpr {expr}")
890
+ value = values[0].to_bytes(size, byteorder=self.byteorder)
891
+ log.info("Collapsed symbolic {expr} to {values[0]:x} for MMIO")
892
+ except angr.errors.SimUnsatError:
893
+ raise exceptions.AnalysisError(f"No possible values for {expr}")
894
+ except angr.errors.SimValueError:
895
+ value = b"\x00" * size
896
+ else:
897
+ value = expr.concrete_value.to_bytes(size, byteorder=self.byteorder)
898
+ res = function(emu, addr, size, value)
899
+ if res is not None:
900
+ res_expr = claripy.BVV(res)
901
+ if self.platform.byteorder == platforms.Byteorder.LITTLE:
902
+ # Fix byte order if needed.
903
+ # I don't know _why_ this is needed,
904
+ # but encoding the result as little-endian on a little-endian
905
+ # system produces the incorrect value in the machine state.
906
+ res_expr = claripy.Reverse(res_expr)
907
+ return res_expr
908
+ return res
909
+
910
+ self.hook_memory_read_symbolic(start, end, sym_callback)
911
+
912
+ def unhook_memory_read(self, start: int, end: int):
913
+ if not self._initialized:
914
+ self._read_hooks = list(
915
+ filter(lambda x: x[0] != start and x[1] != end, self._read_hooks)
916
+ )
917
+
918
+ elif self._dirty and not self._linear:
919
+ raise NotImplementedError(
920
+ "Memory hooking not supported once execution begins"
921
+ )
922
+
923
+ elif (start, end) not in self.state.scratch.mem_read_bps:
924
+ raise exceptions.ConfigurationError(
925
+ f"{hex(start)} - {hex(end)} is not hooked for reads"
926
+ )
927
+ else:
928
+ bp = self.state.scratch.mem_read_bps[(start, end)]
929
+ del self.state.scratch.mem_read_bps[(start, end)]
930
+ self.state.inspect.remove_breakpoint("mem_read", bp=bp)
931
+
932
+ def hook_memory_reads_symbolic(
933
+ self,
934
+ function: typing.Callable[
935
+ [emulator.Emulator, int, int, claripy.ast.bv.BV],
936
+ typing.Optional[claripy.ast.bv.BV],
937
+ ],
938
+ ) -> None:
939
+ if not self._initialized:
940
+ self._gb_read_hook = function
941
+
942
+ elif self._dirty and not self._linear:
943
+ raise NotImplementedError(
944
+ "Memory hooking not supported once execution begins"
945
+ )
946
+
947
+ elif self.state.scratch.global_read_bp is not None:
948
+ raise exceptions.ConfigurationError(
949
+ "Global memory read hook already present"
950
+ )
951
+
952
+ else:
953
+
954
+ def read_callback(state):
955
+ # the breakpoint action.
956
+ addr = state.inspect.mem_read_address
957
+ if not isinstance(addr, int):
958
+ addr = addr.concrete_value
959
+ size = state.inspect.mem_read_length
960
+ expr = state.inspect.mem_read_expr
961
+
962
+ res = function(ConcreteAngrEmulator(state, self), addr, size, expr)
963
+
964
+ if self.platform.byteorder == platforms.byteorder.LITTLE:
965
+ # fix byte order if needed.
966
+ # i don't know _why_ this is needed,
967
+ # but encoding the result as little-endian on a little-endian
968
+ # system produces the incorrect value in the machine state.
969
+ res = claripy.Reverse(res)
970
+ state.inspect.mem_read_expr = res
971
+
972
+ # An update to angr means some operations on `state`
973
+ # will clobber this variable, which causes bad problems.
974
+ # Unclobber it, just in case
975
+ state.inspect.action_attrs_set = True
976
+
977
+ bp = self.state.inspect.b(
978
+ "mem_read",
979
+ when=angr.BP_AFTER,
980
+ action=read_callback,
981
+ )
982
+ self.state.scratch.global_read_bp = bp
983
+
984
+ def hook_memory_reads(
985
+ self,
986
+ function: typing.Callable[
987
+ [emulator.Emulator, int, int, bytes], typing.Optional[bytes]
988
+ ],
989
+ ):
990
+ # Symbolic version does most of the lifting;
991
+ # we just need to translate the values
992
+ def sym_callback(
993
+ emu: emulator.Emulator, addr: int, size: int, expr: claripy.ast.bv.BV
994
+ ) -> typing.Optional[claripy.ast.bv.BV]:
995
+ if not isinstance(emu, ConcreteAngrEmulator):
996
+ raise TypeError("Where'd you get your emu?")
997
+ if expr.symbolic:
998
+ # Have the solver handle binding resolution for us.
999
+ try:
1000
+ values = emu.state.solver.eval_atmost(expr, 1)
1001
+ if len(values) < 1:
1002
+ raise exceptions.AnalysisError(f"No possible values fpr {expr}")
1003
+ value = values[0].to_bytes(size, byteorder=self.byteorder)
1004
+ log.info("Collapsed symbolic {expr} to {values[0]:x} for MMIO")
1005
+ except angr.errors.SimUnsatError:
1006
+ raise exceptions.AnalysisError(f"No possible values for {expr}")
1007
+ except angr.errors.SimValueError:
1008
+ raise exceptions.AnalysisError(
1009
+ f"Unbound value for MMIO write to {hex(addr)}: {expr}"
1010
+ )
1011
+ else:
1012
+ value = expr.concrete_value.to_bytes(size, byteorder=self.byteorder)
1013
+ res = function(emu, addr, size, value)
1014
+ if res is not None:
1015
+ return claripy.BVV(res)
1016
+ return res
1017
+
1018
+ self.hook_memory_reads_symbolic(sym_callback)
1019
+
1020
+ def unhook_memory_reads(self) -> None:
1021
+ if not self._initialized:
1022
+ self._gb_read_hook = None
1023
+
1024
+ elif self._dirty and not self._linear:
1025
+ raise NotImplementedError(
1026
+ "Memory unhooking not supported once execution begins"
1027
+ )
1028
+
1029
+ elif self.state.scratch.global_read_bp is not None:
1030
+ raise exceptions.ConfigurationError("Global memory read hook not present")
1031
+
1032
+ else:
1033
+ bp = self.state.scratch.global_read_bp
1034
+ self.state.scratch.global_read_bp = None
1035
+ self.state.inspect.remove_breakpoint("mem_read", bp)
1036
+
1037
+ def hook_memory_write_symbolic(
1038
+ self,
1039
+ start: int,
1040
+ end: int,
1041
+ function: typing.Callable[
1042
+ [emulator.Emulator, int, int, claripy.ast.bv.BV], None
1043
+ ],
1044
+ ) -> None:
1045
+ if not self._initialized:
1046
+ self._write_hooks.append((start, end, function))
1047
+
1048
+ elif self._dirty and not self._linear:
1049
+ raise NotImplementedError(
1050
+ "Memory hooking not supported once execution begins"
1051
+ )
1052
+
1053
+ elif (start, end) in self.state.scratch.mem_write_bps:
1054
+ raise exceptions.ConfigurationError(
1055
+ f"{hex(start)} - {hex(end)} already hooked for writes"
1056
+ )
1057
+
1058
+ else:
1059
+
1060
+ def write_condition(state):
1061
+ write_start = state.inspect.mem_write_address
1062
+ if not isinstance(write_start, int):
1063
+ if write_start.symbolic:
1064
+ # Need to use the solver to resolve this.
1065
+ try:
1066
+ values = state.solver.eval_atmost(write_start, 1)
1067
+ if len(values) < 1:
1068
+ return False
1069
+ write_start = values[0]
1070
+ except angr.errors.SimUnsatError:
1071
+ return False
1072
+ except angr.errors.SimValueError:
1073
+ return False
1074
+ else:
1075
+ write_start = write_start.concrete_value
1076
+ # Populate concrete value back to the inspect struct
1077
+ state.inspect.mem_write_address = write_start
1078
+ write_size = state.inspect.mem_write_length
1079
+
1080
+ if write_size is None:
1081
+ # Some ISAs don't populate mem_write_length.
1082
+ # Infer length from value
1083
+ write_size = len(state.inspect.mem_write_expr) // 8
1084
+ # Populate concrete value back to the inspect struct
1085
+ state.inspect.mem_write_length = write_size
1086
+ write_end = write_start + write_size
1087
+
1088
+ return start <= write_start and end >= write_end
1089
+
1090
+ def write_callback(state):
1091
+ addr = state.inspect.mem_write_address
1092
+ size = state.inspect.mem_write_length
1093
+ expr = state.inspect.mem_write_expr
1094
+
1095
+ if size is None:
1096
+ size = len(expr)
1097
+
1098
+ function(ConcreteAngrEmulator(state, self), addr, size, expr)
1099
+
1100
+ # An update to angr means some operations on `state`
1101
+ # will clobber this variable, which causes bad problems.
1102
+ # Unclobber it, just in case
1103
+ state.inspect.action_attrs_set = True
1104
+
1105
+ bp = self.state.inspect.b(
1106
+ "mem_write",
1107
+ when=angr.BP_BEFORE,
1108
+ condition=write_condition,
1109
+ action=write_callback,
1110
+ )
1111
+ self.state.scratch.mem_write_bps[(start, end)] = bp
1112
+
1113
+ def hook_memory_write(
1114
+ self,
1115
+ start: int,
1116
+ end: int,
1117
+ function: typing.Callable[[emulator.Emulator, int, int, bytes], None],
1118
+ ) -> None:
1119
+ def sym_callback(
1120
+ emu: emulator.Emulator, addr: int, size: int, expr: claripy.ast.bv.BV
1121
+ ) -> None:
1122
+ if not isinstance(emu, ConcreteAngrEmulator):
1123
+ raise TypeError("Where'd you get your emu?")
1124
+ if expr.symbolic:
1125
+ # Have the solver handle binding resolution for us.
1126
+ try:
1127
+ values = emu.state.solver.eval_atmost(expr, 1)
1128
+ if len(values) < 1:
1129
+ raise exceptions.AnalysisError(f"No possible values fpr {expr}")
1130
+ value = values[0].to_bytes(size, byteorder=self.byteorder)
1131
+ log.info("Collapsed symbolic {expr} to {values[0]:x} for MMIO")
1132
+ except angr.errors.SimUnsatError:
1133
+ raise exceptions.AnalysisError(f"No possible values for {expr}")
1134
+ except angr.errors.SimValueError:
1135
+ raise exceptions.AnalysisError(
1136
+ f"Unbound value for MMIO write to {hex(addr)}: {expr}"
1137
+ )
1138
+ else:
1139
+ value = expr.concrete_value.to_bytes(size, byteorder=self.byteorder)
1140
+ function(emu, addr, size, value)
1141
+
1142
+ self.hook_memory_write_symbolic(start, end, sym_callback)
1143
+
1144
+ def unhook_memory_write(self, start: int, end: int) -> None:
1145
+ if not self._initialized:
1146
+ self._write_hooks = list(
1147
+ filter(lambda x: x[0] != start or x[1] != end, self._write_hooks)
1148
+ )
1149
+
1150
+ elif self._dirty and not self._linear:
1151
+ raise NotImplementedError(
1152
+ "Memory hooking not supported once execution begins"
1153
+ )
1154
+
1155
+ elif (start, end) not in self.state.scratch.mem_write_bps:
1156
+ raise exceptions.ConfigurationError(
1157
+ f"{hex(start)} - {hex(end)} is not hooked for writes"
1158
+ )
1159
+
1160
+ else:
1161
+ bp = self.state.scratch.mem_write_bps[(start, end)]
1162
+ del self.state.scratch.mem_write_bps[(start, end)]
1163
+ self.state.inspect.remove_breakpoint("mem_write", bp=bp)
1164
+
1165
+ def hook_memory_writes_symbolic(
1166
+ self,
1167
+ function: typing.Callable[
1168
+ [emulator.Emulator, int, int, claripy.ast.bv.BV], None
1169
+ ],
1170
+ ) -> None:
1171
+ if not self._initialized:
1172
+ self._gb_write_hook = function
1173
+
1174
+ elif self._dirty and not self._linear:
1175
+ raise NotImplementedError(
1176
+ "Memory hooking not supported once execution begins"
1177
+ )
1178
+
1179
+ elif self.state.scratch.global_write_bp is not None:
1180
+ raise exceptions.ConfigurationError(
1181
+ "Global memory write hook already present"
1182
+ )
1183
+
1184
+ else:
1185
+
1186
+ def write_callback(state):
1187
+ addr = state.inspect.mem_write_address
1188
+ if not isinstance(addr, int):
1189
+ addr = addr.concrete_value
1190
+ size = state.inspect.mem_write_length
1191
+ expr = state.inspect.mem_write_expr
1192
+ function(ConcreteAngrEmulator(state, self), addr, size, expr)
1193
+
1194
+ # An update to angr means some operations on `state`
1195
+ # will clobber this variable, which causes bad problems.
1196
+ # Unclobber it, just in case
1197
+ state.inspect.action_attrs_set = True
1198
+
1199
+ bp = self.state.inspect.b(
1200
+ "mem_write",
1201
+ when=angr.BP_BEFORE,
1202
+ action=write_callback,
1203
+ )
1204
+
1205
+ self.state.scratch.global_write_bp = bp
1206
+
1207
+ def hook_memory_writes(
1208
+ self, function: typing.Callable[[emulator.Emulator, int, int, bytes], None]
1209
+ ) -> None:
1210
+ def sym_callback(
1211
+ emu: emulator.Emulator, addr: int, size: int, expr: claripy.ast.bv.BV
1212
+ ) -> None:
1213
+ if not isinstance(emu, ConcreteAngrEmulator):
1214
+ raise TypeError("Where'd you get your emu?")
1215
+ if expr.symbolic:
1216
+ # Have the solver handle binding resolution for us.
1217
+ try:
1218
+ values = emu.state.solver.eval_atmost(expr, 1)
1219
+ if len(values) < 1:
1220
+ raise exceptions.AnalysisError(f"No possible values fpr {expr}")
1221
+ value = values[0].to_bytes(size, byteorder=self.byteorder)
1222
+ log.info("Collapsed symbolic {expr} to {values[0]:x} for MMIO")
1223
+ except angr.errors.SimUnsatError:
1224
+ raise exceptions.AnalysisError(f"No possible values for {expr}")
1225
+ except angr.errors.SimValueError:
1226
+ raise exceptions.AnalysisError(
1227
+ f"Unbound value for MMIO write to {hex(addr)}: {expr}"
1228
+ )
1229
+ else:
1230
+ value = expr.concrete_value.to_bytes(size, byteorder=self.byteorder)
1231
+ function(emu, addr, size, value)
1232
+
1233
+ self.hook_memory_writes_symbolic(sym_callback)
1234
+
1235
+ def unhook_memory_writes(self) -> None:
1236
+ if self._dirty and not self._linear:
1237
+ raise NotImplementedError(
1238
+ "Memory unhooking not supported once execution begins"
1239
+ )
1240
+ if self.state.scratch.global_write_bp is not None:
1241
+ raise exceptions.ConfigurationError("Global memory write hook not present")
1242
+
1243
+ bp = self.state.scratch.global_read_bp
1244
+ self.state.scratch.global_read_bp = None
1245
+ self.state.inspect.remove_breakpoint("mem_write", bp)
1246
+
1247
+ def _step(self, single_insn: bool):
1248
+ """Common routine for all step functions.
1249
+
1250
+ This is rather verbose, so let's only write it once.
1251
+
1252
+ Arguments:
1253
+ single_insn: True to step one instruction. False to step to the end of the block
1254
+ """
1255
+ # If we have not been initialized, initialize ourself
1256
+ if not self._initialized:
1257
+ self.initialize()
1258
+
1259
+ # As soon as we start executing, disable value access
1260
+ self._dirty = True
1261
+ if self._linear:
1262
+ if self.state._ip.concrete_value not in self.state.scratch.func_bps:
1263
+ disas = self.state.block(opt_level=0).disassembly
1264
+ if disas is not None and len(disas.insns) > 0:
1265
+ log.info(f"Stepping through {disas.insns[0]}")
1266
+ else:
1267
+ # Capstone only supports a subset of the instructions supported by LibVEX.
1268
+ # I can only disassemble what I can disassemble.
1269
+ log.info(f"Stepping through {self.state._ip} (untranslatable!)")
1270
+ else:
1271
+ log.info(f"Stepping through {self.state._ip} (hook)")
1272
+
1273
+ # Step execution once, however the user asked for it.
1274
+ if single_insn:
1275
+ # Not all architectures support single-step execution.
1276
+ # In particular, angr can't lift delay slot ISAs one instruction at a time,
1277
+ # since it has to lift the instruction and the slot as one unit.
1278
+ if not self.machdef.supports_single_step:
1279
+ raise exceptions.ConfigurationError(
1280
+ f"AngrEmulator does not support single-instruction stepping for {self.platform}"
1281
+ )
1282
+ num_inst = 1
1283
+ else:
1284
+ num_inst = None
1285
+ self.mgr.step(
1286
+ num_inst=num_inst,
1287
+ successor_func=self.machdef.successors,
1288
+ thumb=self.machdef.is_thumb,
1289
+ )
1290
+
1291
+ # Test for exceptional states
1292
+ if len(self.mgr.errored) > 0:
1293
+ raise exceptions.EmulationError(
1294
+ self.mgr.errored[0].error
1295
+ ) from self.mgr.errored[0].error
1296
+
1297
+ # Handle linear execution mode
1298
+ if self._linear:
1299
+ if len(self.mgr.active) > 1:
1300
+ log.warn("Path diverged! Detailes stored in simulation manager.")
1301
+ raise exceptions.EmulationStop("Path diverged in linear mode")
1302
+ elif len(self.mgr.active) > 0:
1303
+ self.state = self.mgr.active[0]
1304
+ elif len(self.mgr.deadended) > 0:
1305
+ self.state = self.mgr.deadended[0]
1306
+ elif len(self.mgr.unconstrained) > 0:
1307
+ self.state = self.mgr.unconstrained[0]
1308
+ else:
1309
+ raise exceptions.AnalysisError(
1310
+ "No states in expected stashes for linear execution"
1311
+ )
1312
+
1313
+ # Filter out exited or invalid states
1314
+ self.mgr.move(
1315
+ from_stash="active",
1316
+ to_stash="unconstrained",
1317
+ filter_func=lambda x: (x._ip.symbolic),
1318
+ )
1319
+
1320
+ def filter_func(state):
1321
+ ip = state._ip.concrete_value
1322
+ if (
1323
+ not self.state.scratch.bounds.is_empty()
1324
+ and not self.state.scratch.bounds.contains_value(ip)
1325
+ ):
1326
+ return True
1327
+ if not self.state.scratch.memory_map.contains_value(ip):
1328
+ return True
1329
+ if ip in self.state.scratch.exit_points:
1330
+ return True
1331
+ return False
1332
+
1333
+ self.mgr.move(
1334
+ from_stash="active", to_stash="deadended", filter_func=filter_func
1335
+ )
1336
+
1337
+ # Stop if we're out of active states
1338
+ if len(self.mgr.active) == 0:
1339
+ raise exceptions.EmulationStop()
1340
+
1341
+ def step_instruction(self) -> None:
1342
+ self._step(True)
1343
+
1344
+ def step_block(self) -> None:
1345
+ self._step(False)
1346
+
1347
+ def step(self) -> None:
1348
+ self._step(True)
1349
+
1350
+ def run(self):
1351
+ log.info("Starting angr run")
1352
+ try:
1353
+ # Continue stepping as long as we have steps.
1354
+ while True:
1355
+ self._step(False)
1356
+ except exceptions.EmulationStop:
1357
+ return
1358
+
1359
+ def visit_states(
1360
+ self,
1361
+ function: typing.Callable[[emulator.Emulator], None],
1362
+ stash: str = "active",
1363
+ ) -> None:
1364
+ """Visit every state in the selected frontier
1365
+
1366
+ This lets you work around the fact that most operations
1367
+ only work at emulation start, or in linear mode.
1368
+ """
1369
+ if not self._initialized:
1370
+ raise NotImplementedError("Cannot visit states before initialization")
1371
+ for s in self.mgr.stashes[stash]:
1372
+ function(ConcreteAngrEmulator(s, self))
1373
+
1374
+ def enable_linear(self):
1375
+ """Enable linear execution
1376
+
1377
+ This doesn't actually concretize anything;
1378
+ it just kills execution when it hits an unconstrained branch.
1379
+ """
1380
+ if self._dirty:
1381
+ raise NotImplementedError(
1382
+ "Enabling linear mode not supported once execution begins"
1383
+ )
1384
+ self._linear = True
1385
+ log.warn("Linear execution mode enabled")
1386
+
1387
+ def get_bounds(self) -> typing.List[typing.Tuple[int, int]]:
1388
+ if not self._initialized:
1389
+ return list(self._code_bounds)
1390
+
1391
+ if self._dirty and not self._linear:
1392
+ raise NotImplementedError(
1393
+ "Accessing bounds not supported once execution begins"
1394
+ )
1395
+ return list(self.state.scratch.bounds.ranges)
1396
+
1397
+ def add_bound(self, start: int, end: int) -> None:
1398
+ if not self._initialized:
1399
+ self._code_bounds.append((start, end))
1400
+
1401
+ elif self._dirty and not self._linear:
1402
+ raise NotImplementedError(
1403
+ "Accessing bounds not supported once execution begins"
1404
+ )
1405
+
1406
+ else:
1407
+ self.state.scratch.bounds.add_range((start, end))
1408
+
1409
+ def remove_bound(self, start: int, end: int) -> None:
1410
+ if not self._initialized:
1411
+ self._code_bounds = list(
1412
+ filter(lambda x: x[0] != start or x[1] != end, self._code_bounds)
1413
+ )
1414
+
1415
+ elif self._dirty and not self._linear:
1416
+ raise NotImplementedError(
1417
+ "Accessing bounds not supported once execution begins"
1418
+ )
1419
+
1420
+ else:
1421
+ self.state.scratch.bounds.remove_range((start, end))
1422
+
1423
+ def get_exit_points(self) -> typing.Set[int]:
1424
+ if not self._initialized:
1425
+ return set(self._exit_points)
1426
+
1427
+ if self._dirty and not self._linear:
1428
+ raise NotImplementedError(
1429
+ "Accessing exit points not supported once execution begins"
1430
+ )
1431
+ return set(self.state.scratch.exit_points)
1432
+
1433
+ def add_exit_point(self, address: int) -> None:
1434
+ if not self._initialized:
1435
+ self._exit_points.add(address)
1436
+ elif self._dirty and not self._linear:
1437
+ raise NotImplementedError(
1438
+ "Accessing exit points not supported once execution begins"
1439
+ )
1440
+ else:
1441
+ self.state.scratch.exit_points.add(address)
1442
+
1443
+ def remove_exit_point(self, address: int) -> None:
1444
+ if not self._initialized:
1445
+ self._exit_points.remove(address)
1446
+
1447
+ elif self._dirty and not self._linear:
1448
+ raise NotImplementedError(
1449
+ "Accessing exit points not supported once execution begins"
1450
+ )
1451
+
1452
+ else:
1453
+ self.state.scratch.exit_points.remove(address)
1454
+
1455
+ def add_constraint(self, expr: claripy.ast.bool.Bool) -> None:
1456
+ if not self._initialized:
1457
+ self._constraints.append(expr)
1458
+ elif self._dirty and not self._linear:
1459
+ raise NotImplementedError(
1460
+ "Accessing constraints not supported once execution begins"
1461
+ )
1462
+ else:
1463
+ self.state.solver.add(expr)
1464
+
1465
+ def get_constraints(self) -> typing.List[claripy.ast.bool.Bool]:
1466
+ if not self._initialized:
1467
+ return list(self._constraints)
1468
+ elif self._dirty and not self._linear:
1469
+ raise NotImplementedError(
1470
+ "Accessing constraints not supported once execution begins"
1471
+ )
1472
+ else:
1473
+ return list(self.state.solver.constraints)
1474
+
1475
+ def satisfiable(
1476
+ self,
1477
+ extra_constraints: typing.List[claripy.ast.bool.Bool] = [],
1478
+ ) -> bool:
1479
+ if not self._initialized:
1480
+ raise exceptions.ConfigurationError(
1481
+ "Solver not available before initialization"
1482
+ )
1483
+ elif self._dirty and not self._linear:
1484
+ raise NotImplementedError(
1485
+ "Accessing solver not supported once execution begins"
1486
+ )
1487
+ return self.state.solver.satisfiable(extra_constraints=extra_constraints)
1488
+
1489
+ def eval_atmost(self, expr: claripy.ast.bv.BV, most: int) -> typing.List[int]:
1490
+ if not self._initialized:
1491
+ raise exceptions.ConfigurationError(
1492
+ "Solver not available before initialization"
1493
+ )
1494
+ elif self._dirty and not self._linear:
1495
+ raise NotImplementedError(
1496
+ "Accessing solver not supported once execution begins"
1497
+ )
1498
+ try:
1499
+ return self.state.solver.eval_atmost(expr, most)
1500
+ except angr.errors.SimUnsatError:
1501
+ raise exceptions.UnsatError("No solutions")
1502
+ except angr.errors.SimValueError:
1503
+ raise exceptions.SymbolicValueError("Too many solutions")
1504
+
1505
+ def eval_atleast(self, expr: claripy.ast.bv.BV, least: int) -> typing.List[int]:
1506
+ if not self._initialized:
1507
+ raise exceptions.ConfigurationError(
1508
+ "Solver not available before initialization"
1509
+ )
1510
+ elif self._dirty and not self._linear:
1511
+ raise NotImplementedError(
1512
+ "Accessing solver not supported once execution begins"
1513
+ )
1514
+ try:
1515
+ return self.state.solver.eval_atleast(expr, least)
1516
+ except angr.errors.SimUnsatError:
1517
+ raise exceptions.UnsatError("No solutions")
1518
+ except angr.errors.SimValueError:
1519
+ raise exceptions.SymbolicValueError("Not enough solutions")
1520
+
1521
+ def add_extension(self, name: str, ext: typing.Any) -> None:
1522
+ """Add extra data to the machine state.
1523
+
1524
+ This allows users to tie data structures
1525
+ to the emulator's execution states,
1526
+ allowing path-specific data tracking.
1527
+
1528
+ Only one extension with a given name can be tied
1529
+ to a given state. Usually, extensions are assigned
1530
+ before emulation starts, but it's perfectly
1531
+ possible to assign them later, either in linear mode,
1532
+ or through the emulator stub provided to a hook.
1533
+
1534
+ All extensions must be compatible with `copy.deepcopy()`.
1535
+
1536
+ Arguments:
1537
+ name: ID string used to retrieve the extension
1538
+ ext: The extension
1539
+
1540
+ Raises:
1541
+ KeyError: `name` is already taken
1542
+ """
1543
+
1544
+ if not self._initialized:
1545
+ if name in self._extensions:
1546
+ raise KeyError(f"Extension with name {name} exists")
1547
+ self._extensions[name] = ext
1548
+ elif self._dirty and not self._linear:
1549
+ raise NotImplementedError("Cannot extend state once execution has begun")
1550
+ else:
1551
+ if name in self.state.scratch.extensions:
1552
+ raise KeyError(f"Extension with name {name} exists")
1553
+ self.state.scratch.extensions[name] = ext
1554
+
1555
+ def get_extension(self, name: str) -> typing.Any:
1556
+ """Fetch extra data from the machine state
1557
+
1558
+ Arguments:
1559
+ name: ID string used to retrieve the extension
1560
+
1561
+ Returns:
1562
+ Whatever was assigned to `name` using `add_extension`
1563
+
1564
+ Raises:
1565
+ KeyError: If no extension exists tied to `name`
1566
+ """
1567
+ if not self._initialized:
1568
+ return self._extensions[name]
1569
+ elif self._dirty and not self._linear:
1570
+ raise NotImplementedError("Cannot extend state once execution has begun")
1571
+ else:
1572
+ return self.state.scratch.extensions[name]
1573
+
1574
+ @property
1575
+ def byteorder(self) -> typing.Literal["big", "little"]:
1576
+ """Get the byteorder string for this platform
1577
+
1578
+ Returns:
1579
+ 'big' or 'little', appropriately-typed as literals.
1580
+ """
1581
+ if self.platform.byteorder == platforms.Byteorder.BIG:
1582
+ return "big"
1583
+ elif self.platform.byteorder == platforms.Byteorder.LITTLE:
1584
+ return "little"
1585
+ else:
1586
+ raise ValueError("Unsupported byteorder!")
1587
+
1588
+ def __repr__(self):
1589
+ if self._initialized:
1590
+ return f"Angr ({self.mgr})"
1591
+ else:
1592
+ return "Angr (Uninitialized)"
1593
+
1594
+
1595
+ class ConcreteAngrEmulator(AngrEmulator):
1596
+ """
1597
+ "Emulator" for angr state access
1598
+
1599
+ The primary AngrEmulator class is designed for symbolic execution;
1600
+ it represents many states being explored in parallel.
1601
+ As such, the notion of accessing state doesn't work; which state do you want?
1602
+
1603
+ For hook evaluation, we do need an emulator-like object
1604
+ that can access a single state. Hence, this wrapper class.
1605
+
1606
+ NOTE: This is NOT a full emulator!
1607
+ Think of it more as a view onto an AngrEmulator instance.
1608
+ If you want to explore a single path using angr,
1609
+ use AngrEmulator and call enable_linear() before exploration.
1610
+ """
1611
+
1612
+ @property
1613
+ def PAGE_SIZE(self):
1614
+ # Page Size should be inherited from the parent.
1615
+ return self.pagesize
1616
+
1617
+ def __init__(self, state: angr.SimState, parent: AngrEmulator):
1618
+ # Do NOT call the superclass constructor.
1619
+ # It initializes the angr project, and we've already done that.
1620
+ self._initialized: bool = True
1621
+ self._dirty: bool = False
1622
+ self._linear: bool = False
1623
+
1624
+ # Force-initialize relevant state from the parent
1625
+ self.platform: platforms.Platform = parent.platform
1626
+ self.proj: angr.Project = parent.proj
1627
+ self.state: angr.SimState = state
1628
+ self.machdef: AngrMachineDef = parent.machdef
1629
+ self.pagesize: int = parent.PAGE_SIZE
1630
+
1631
+ # Function hooking is not supported;
1632
+ # it relies on state global to the angr project, not individual states.
1633
+ def hook_function(
1634
+ self, address: int, function: typing.Callable[[emulator.Emulator], None]
1635
+ ) -> None:
1636
+ raise NotImplementedError("Function hooking not supported inside a hook.")
1637
+
1638
+ def unhook_function(self, address: int) -> None:
1639
+ raise NotImplementedError("Function hooking not supported inside a hook.")
1640
+
1641
+ # Execution is not supported; this is not a complete emulator.
1642
+ def run(self) -> None:
1643
+ raise NotImplementedError("Running not supported inside a hook.")
1644
+
1645
+ def _step(self, single_insn: bool) -> None:
1646
+ raise NotImplementedError("Stepping not supported inside a hook.")
1647
+
1648
+ def __repr__(self):
1649
+ return f"Angr Hook ({self.state})"
1650
+
1651
+
1652
+ __all__ = ["AngrEmulator"]