angr 9.2.138__py3-none-manylinux2014_x86_64.whl → 9.2.140__py3-none-manylinux2014_x86_64.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.

Potentially problematic release.


This version of angr might be problematic. Click here for more details.

Files changed (100) hide show
  1. angr/__init__.py +1 -1
  2. angr/analyses/calling_convention/calling_convention.py +48 -21
  3. angr/analyses/calling_convention/fact_collector.py +59 -12
  4. angr/analyses/calling_convention/utils.py +2 -2
  5. angr/analyses/cfg/cfg_base.py +13 -0
  6. angr/analyses/cfg/cfg_fast.py +23 -4
  7. angr/analyses/decompiler/ail_simplifier.py +79 -53
  8. angr/analyses/decompiler/block_simplifier.py +0 -2
  9. angr/analyses/decompiler/callsite_maker.py +80 -14
  10. angr/analyses/decompiler/clinic.py +99 -80
  11. angr/analyses/decompiler/condition_processor.py +2 -2
  12. angr/analyses/decompiler/decompiler.py +19 -7
  13. angr/analyses/decompiler/dephication/rewriting_engine.py +16 -7
  14. angr/analyses/decompiler/expression_narrower.py +1 -1
  15. angr/analyses/decompiler/optimization_passes/__init__.py +3 -0
  16. angr/analyses/decompiler/optimization_passes/condition_constprop.py +149 -0
  17. angr/analyses/decompiler/optimization_passes/const_prop_reverter.py +8 -7
  18. angr/analyses/decompiler/optimization_passes/deadblock_remover.py +12 -3
  19. angr/analyses/decompiler/optimization_passes/inlined_string_transformation_simplifier.py +1 -1
  20. angr/analyses/decompiler/optimization_passes/ite_region_converter.py +21 -13
  21. angr/analyses/decompiler/optimization_passes/optimization_pass.py +21 -12
  22. angr/analyses/decompiler/optimization_passes/return_duplicator_base.py +17 -9
  23. angr/analyses/decompiler/optimization_passes/return_duplicator_high.py +7 -10
  24. angr/analyses/decompiler/peephole_optimizations/eager_eval.py +12 -1
  25. angr/analyses/decompiler/peephole_optimizations/remove_redundant_conversions.py +61 -25
  26. angr/analyses/decompiler/peephole_optimizations/remove_redundant_shifts.py +50 -1
  27. angr/analyses/decompiler/presets/fast.py +2 -0
  28. angr/analyses/decompiler/presets/full.py +2 -0
  29. angr/analyses/decompiler/region_simplifiers/expr_folding.py +259 -108
  30. angr/analyses/decompiler/region_simplifiers/region_simplifier.py +28 -9
  31. angr/analyses/decompiler/ssailification/rewriting_engine.py +20 -2
  32. angr/analyses/decompiler/ssailification/traversal_engine.py +4 -3
  33. angr/analyses/decompiler/structured_codegen/c.py +10 -3
  34. angr/analyses/decompiler/structuring/dream.py +28 -19
  35. angr/analyses/decompiler/structuring/phoenix.py +253 -89
  36. angr/analyses/decompiler/structuring/recursive_structurer.py +1 -0
  37. angr/analyses/decompiler/structuring/structurer_base.py +121 -46
  38. angr/analyses/decompiler/structuring/structurer_nodes.py +6 -1
  39. angr/analyses/decompiler/utils.py +60 -1
  40. angr/analyses/deobfuscator/api_obf_finder.py +13 -5
  41. angr/analyses/deobfuscator/api_obf_type2_finder.py +166 -0
  42. angr/analyses/deobfuscator/string_obf_finder.py +105 -18
  43. angr/analyses/forward_analysis/forward_analysis.py +1 -1
  44. angr/analyses/propagator/top_checker_mixin.py +6 -6
  45. angr/analyses/reaching_definitions/__init__.py +2 -1
  46. angr/analyses/reaching_definitions/dep_graph.py +1 -12
  47. angr/analyses/reaching_definitions/engine_vex.py +36 -31
  48. angr/analyses/reaching_definitions/function_handler.py +15 -2
  49. angr/analyses/reaching_definitions/rd_state.py +1 -37
  50. angr/analyses/reaching_definitions/reaching_definitions.py +13 -24
  51. angr/analyses/s_propagator.py +129 -87
  52. angr/analyses/s_reaching_definitions/s_rda_model.py +7 -1
  53. angr/analyses/s_reaching_definitions/s_rda_view.py +2 -2
  54. angr/analyses/s_reaching_definitions/s_reaching_definitions.py +3 -1
  55. angr/analyses/stack_pointer_tracker.py +36 -22
  56. angr/analyses/typehoon/simple_solver.py +45 -7
  57. angr/analyses/typehoon/typeconsts.py +18 -5
  58. angr/analyses/variable_recovery/engine_ail.py +1 -1
  59. angr/analyses/variable_recovery/engine_base.py +62 -67
  60. angr/analyses/variable_recovery/engine_vex.py +1 -1
  61. angr/analyses/variable_recovery/irsb_scanner.py +2 -2
  62. angr/block.py +69 -107
  63. angr/callable.py +14 -7
  64. angr/calling_conventions.py +81 -10
  65. angr/distributed/__init__.py +1 -1
  66. angr/engines/__init__.py +7 -8
  67. angr/engines/engine.py +3 -138
  68. angr/engines/failure.py +2 -2
  69. angr/engines/hook.py +2 -2
  70. angr/engines/light/engine.py +5 -10
  71. angr/engines/pcode/emulate.py +2 -2
  72. angr/engines/pcode/engine.py +2 -14
  73. angr/engines/pcode/lifter.py +2 -2
  74. angr/engines/procedure.py +2 -2
  75. angr/engines/soot/engine.py +2 -2
  76. angr/engines/soot/statements/switch.py +1 -1
  77. angr/engines/successors.py +123 -17
  78. angr/engines/syscall.py +2 -2
  79. angr/engines/unicorn.py +3 -3
  80. angr/engines/vex/heavy/heavy.py +3 -15
  81. angr/engines/vex/lifter.py +2 -2
  82. angr/engines/vex/light/light.py +2 -2
  83. angr/factory.py +4 -19
  84. angr/knowledge_plugins/cfg/cfg_model.py +3 -2
  85. angr/knowledge_plugins/key_definitions/atoms.py +8 -4
  86. angr/knowledge_plugins/key_definitions/live_definitions.py +41 -103
  87. angr/knowledge_plugins/labels.py +2 -2
  88. angr/knowledge_plugins/obfuscations.py +1 -0
  89. angr/knowledge_plugins/xrefs/xref_manager.py +4 -0
  90. angr/sim_type.py +19 -17
  91. angr/state_plugins/plugin.py +19 -4
  92. angr/storage/memory_mixins/memory_mixin.py +1 -1
  93. angr/storage/memory_mixins/paged_memory/pages/multi_values.py +10 -5
  94. angr/utils/ssa/__init__.py +119 -4
  95. {angr-9.2.138.dist-info → angr-9.2.140.dist-info}/METADATA +6 -6
  96. {angr-9.2.138.dist-info → angr-9.2.140.dist-info}/RECORD +100 -98
  97. {angr-9.2.138.dist-info → angr-9.2.140.dist-info}/LICENSE +0 -0
  98. {angr-9.2.138.dist-info → angr-9.2.140.dist-info}/WHEEL +0 -0
  99. {angr-9.2.138.dist-info → angr-9.2.140.dist-info}/entry_points.txt +0 -0
  100. {angr-9.2.138.dist-info → angr-9.2.140.dist-info}/top_level.txt +0 -0
angr/block.py CHANGED
@@ -130,19 +130,23 @@ class Block(Serializable):
130
130
  BLOCK_MAX_SIZE = 4096
131
131
 
132
132
  __slots__ = [
133
+ "_backup_state",
133
134
  "_bytes",
134
135
  "_capstone",
135
136
  "_collect_data_refs",
136
137
  "_const_prop",
137
138
  "_cross_insn_opt",
138
139
  "_disassembly",
140
+ "_extra_stop_points",
139
141
  "_initial_regs",
140
142
  "_instruction_addrs",
141
143
  "_instructions",
142
144
  "_load_from_ro_regions",
145
+ "_max_size",
143
146
  "_opt_level",
144
147
  "_project",
145
148
  "_strict_block_end",
149
+ "_traceflags",
146
150
  "_vex",
147
151
  "_vex_nostmt",
148
152
  "addr",
@@ -155,11 +159,10 @@ class Block(Serializable):
155
159
  self,
156
160
  addr,
157
161
  project=None,
158
- arch: Arch = None,
162
+ arch: Arch | None = None,
159
163
  size=None,
160
164
  max_size=None,
161
165
  byte_string=None,
162
- vex=None,
163
166
  thumb=False,
164
167
  backup_state=None,
165
168
  extra_stop_points=None,
@@ -174,14 +177,11 @@ class Block(Serializable):
174
177
  initial_regs=None,
175
178
  skip_stmts=False,
176
179
  ):
177
- # set up arch
178
- self.arch: Arch
179
- if project is not None:
180
+ if arch is not None:
181
+ self.arch = arch
182
+ elif project is not None:
180
183
  self.arch = project.arch
181
184
  else:
182
- self.arch = arch
183
-
184
- if self.arch is None:
185
185
  raise ValueError('Either "project" or "arch" has to be specified.')
186
186
 
187
187
  if project is not None and backup_state is None and project.kb.patches.values():
@@ -195,63 +195,23 @@ class Block(Serializable):
195
195
  else:
196
196
  thumb = False
197
197
 
198
- self._project: Project | None = project
199
- self.thumb = thumb
198
+ self._project = project
200
199
  self.addr = addr
200
+ self._backup_state = backup_state
201
+ self.thumb = thumb
201
202
  self._opt_level = opt_level
202
- self._initial_regs: list[tuple[int, int, int]] | None = (
203
- initial_regs if (collect_data_refs or const_prop) else None
204
- )
203
+ self._initial_regs = initial_regs if (collect_data_refs or const_prop) else None
204
+ self._traceflags = traceflags
205
+ self._extra_stop_points = extra_stop_points
206
+ self._max_size = max_size if max_size is not None else self.BLOCK_MAX_SIZE
205
207
 
206
208
  if self._project is None and byte_string is None:
207
209
  raise ValueError('"byte_string" has to be specified if "project" is not provided.')
208
210
 
209
- if size is None:
210
- if byte_string is not None:
211
- size = len(byte_string)
212
- elif vex is not None:
213
- size = vex.size
214
- else:
215
- if self._initial_regs:
216
- self.set_initial_regs()
217
- clemory = None
218
- if project is not None:
219
- clemory = (
220
- project.loader.memory_ro_view
221
- if project.loader.memory_ro_view is not None
222
- else project.loader.memory
223
- )
224
- vex = self._vex_engine.lift_vex(
225
- clemory=clemory,
226
- state=backup_state,
227
- insn_bytes=byte_string,
228
- addr=addr,
229
- size=max_size,
230
- thumb=thumb,
231
- extra_stop_points=extra_stop_points,
232
- opt_level=opt_level,
233
- num_inst=num_inst,
234
- traceflags=traceflags,
235
- strict_block_end=strict_block_end,
236
- collect_data_refs=collect_data_refs,
237
- load_from_ro_regions=load_from_ro_regions,
238
- const_prop=const_prop,
239
- cross_insn_opt=cross_insn_opt,
240
- skip_stmts=skip_stmts,
241
- )
242
- if self._initial_regs:
243
- self.reset_initial_regs()
244
- size = vex.size
245
-
246
- if skip_stmts:
247
- self._vex = None
248
- self._vex_nostmt = vex
249
- else:
250
- self._vex = vex
251
- self._vex_nostmt = None
211
+ self._vex = None
212
+ self._vex_nostmt = None
252
213
  self._disassembly = None
253
214
  self._capstone = None
254
- self.size = size
255
215
  self._collect_data_refs = collect_data_refs
256
216
  self._strict_block_end = strict_block_end
257
217
  self._cross_insn_opt = cross_insn_opt
@@ -261,6 +221,23 @@ class Block(Serializable):
261
221
  self._instructions: int | None = num_inst
262
222
  self._instruction_addrs: list[int] = []
263
223
 
224
+ self._bytes = byte_string
225
+ self.size = size
226
+
227
+ if size is None:
228
+ if byte_string is not None:
229
+ size = len(byte_string)
230
+ else:
231
+ vex = self._lift_nocache(skip_stmts)
232
+ size = vex.size
233
+
234
+ if skip_stmts:
235
+ self._vex_nostmt = vex
236
+ else:
237
+ self._vex = vex
238
+
239
+ self.size = size
240
+
264
241
  if skip_stmts:
265
242
  self._parse_vex_info(self._vex_nostmt)
266
243
  else:
@@ -343,50 +320,7 @@ class Block(Serializable):
343
320
  raise ValueError("Project is not set")
344
321
  return self._project.factory.default_engine # type:ignore
345
322
 
346
- @property
347
- def vex(self) -> IRSB | PcodeIRSB:
348
- if not self._vex:
349
- if self._initial_regs:
350
- self.set_initial_regs()
351
- clemory = None
352
- if self._project is not None:
353
- clemory = (
354
- self._project.loader.memory_ro_view
355
- if self._project.loader.memory_ro_view is not None
356
- else self._project.loader.memory
357
- )
358
- self._vex = self._vex_engine.lift_vex(
359
- clemory=clemory,
360
- insn_bytes=self._bytes,
361
- addr=self.addr,
362
- thumb=self.thumb,
363
- size=self.size,
364
- num_inst=self._instructions,
365
- opt_level=self._opt_level,
366
- arch=self.arch,
367
- collect_data_refs=self._collect_data_refs,
368
- strict_block_end=self._strict_block_end,
369
- cross_insn_opt=self._cross_insn_opt,
370
- load_from_ro_regions=self._load_from_ro_regions,
371
- const_prop=self._const_prop,
372
- )
373
- if self._initial_regs:
374
- self.reset_initial_regs()
375
- self._parse_vex_info(self._vex)
376
-
377
- assert self._vex is not None
378
- return self._vex
379
-
380
- @property
381
- def vex_nostmt(self):
382
- if self._vex_nostmt:
383
- return self._vex_nostmt
384
-
385
- if self._vex:
386
- return self._vex
387
-
388
- if self._initial_regs:
389
- self.set_initial_regs()
323
+ def _lift_nocache(self, skip_stmts: bool) -> IRSB | PcodeIRSB:
390
324
  clemory = None
391
325
  if self._project is not None:
392
326
  clemory = (
@@ -394,25 +328,53 @@ class Block(Serializable):
394
328
  if self._project.loader.memory_ro_view is not None
395
329
  else self._project.loader.memory
396
330
  )
397
- self._vex_nostmt = self._vex_engine.lift_vex(
331
+
332
+ if self._initial_regs:
333
+ self.set_initial_regs()
334
+
335
+ vex = self._vex_engine.lift_vex(
336
+ addr=self.addr,
337
+ state=self._backup_state,
398
338
  clemory=clemory,
399
339
  insn_bytes=self._bytes,
400
- addr=self.addr,
401
- thumb=self.thumb,
340
+ arch=self.arch,
402
341
  size=self.size,
403
342
  num_inst=self._instructions,
343
+ traceflags=self._traceflags,
344
+ thumb=self.thumb,
345
+ extra_stop_points=self._extra_stop_points,
404
346
  opt_level=self._opt_level,
405
- arch=self.arch,
406
- skip_stmts=True,
407
- collect_data_refs=self._collect_data_refs,
408
347
  strict_block_end=self._strict_block_end,
348
+ skip_stmts=skip_stmts,
349
+ collect_data_refs=self._collect_data_refs,
409
350
  cross_insn_opt=self._cross_insn_opt,
410
351
  load_from_ro_regions=self._load_from_ro_regions,
411
352
  const_prop=self._const_prop,
412
353
  )
354
+
413
355
  if self._initial_regs:
414
356
  self.reset_initial_regs()
357
+
358
+ return vex
359
+
360
+ @property
361
+ def vex(self) -> IRSB | PcodeIRSB:
362
+ if not self._vex:
363
+ self._vex = self._lift_nocache(False)
364
+ self._parse_vex_info(self._vex)
365
+
366
+ return self._vex
367
+
368
+ @property
369
+ def vex_nostmt(self):
370
+ if self._vex_nostmt:
371
+ return self._vex_nostmt
372
+ if self._vex:
373
+ return self._vex
374
+
375
+ self._vex_nostmt = self._lift_nocache(True)
415
376
  self._parse_vex_info(self._vex_nostmt)
377
+
416
378
  return self._vex_nostmt
417
379
 
418
380
  @property
angr/callable.py CHANGED
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
  import pycparser
3
3
 
4
+ from .sim_manager import SimulationManager
4
5
  from .errors import AngrCallableError, AngrCallableMultistateError
5
6
  from .calling_conventions import default_cc, SimCC
6
7
 
@@ -28,6 +29,7 @@ class Callable:
28
29
  cc=None,
29
30
  add_options=None,
30
31
  remove_options=None,
32
+ step_limit: int | None = None,
31
33
  ):
32
34
  """
33
35
  :param project: The project to operate on
@@ -60,6 +62,7 @@ class Callable:
60
62
  self._func_ty = prototype
61
63
  self._add_options = add_options if add_options else set()
62
64
  self._remove_options = remove_options if remove_options else set()
65
+ self._step_limit = step_limit
63
66
 
64
67
  self.result_path_group = None
65
68
  self.result_state = None
@@ -95,16 +98,12 @@ class Callable:
95
98
  remove_options=self._remove_options,
96
99
  )
97
100
 
98
- def step_func(pg):
99
- pg2 = pg.prune()
100
- if len(pg2.active) > 1:
101
- raise AngrCallableMultistateError("Execution split on symbolic condition!")
102
- return pg2
103
-
104
101
  caller = self._project.factory.simulation_manager(state)
105
- caller.run(step_func=step_func if self._concrete_only else None).unstash(from_stash="deadended")
102
+ caller.run(step_func=self._step_func).unstash(from_stash="deadended")
106
103
  caller.prune(filter_func=lambda pt: pt.addr == self._deadend_addr)
107
104
 
105
+ if "step_limited" in caller.stashes:
106
+ caller.stash(from_stash="step_limited", to_stash="active")
108
107
  if len(caller.active) == 0:
109
108
  raise AngrCallableError("No paths returned from function")
110
109
 
@@ -159,3 +158,11 @@ class Callable:
159
158
  raise AngrCallableError(f"Unsupported expression type {type(expr)}.")
160
159
 
161
160
  return self.__call__(*args)
161
+
162
+ def _step_func(self, pg: SimulationManager):
163
+ pg2 = pg.prune()
164
+ if self._concrete_only and len(pg2.active) > 1:
165
+ raise AngrCallableMultistateError("Execution split on symbolic condition!")
166
+ if self._step_limit:
167
+ pg2.stash(filter_func=lambda p: p.history.depth >= self._step_limit, to_stash="step_limited")
168
+ return pg2
@@ -11,6 +11,7 @@ import archinfo
11
11
  from archinfo import RegisterName
12
12
  from unique_log_filter import UniqueLogFilter
13
13
 
14
+ import angr
14
15
  from .errors import AngrTypeError
15
16
  from .sim_type import (
16
17
  SimType,
@@ -33,7 +34,6 @@ from .sim_type import (
33
34
  SimTypeReference,
34
35
  )
35
36
  from .state_plugins.sim_action_object import SimActionObject
36
- from .engines.soot.engine import SootMixin
37
37
 
38
38
  l = logging.getLogger(name=__name__)
39
39
  l.addFilter(UniqueLogFilter())
@@ -307,7 +307,7 @@ class SimRegArg(SimFunctionArgument):
307
307
  def __hash__(self):
308
308
  return hash((self.size, self.reg_name, self.reg_offset))
309
309
 
310
- def check_offset(self, arch):
310
+ def check_offset(self, arch) -> int:
311
311
  return arch.registers[self.reg_name][0] + self.reg_offset
312
312
 
313
313
  def set_value(self, state, value, **kwargs): # pylint: disable=unused-argument,arguments-differ
@@ -582,7 +582,12 @@ class SimCC:
582
582
  FP_RETURN_VAL: SimFunctionArgument | None = (
583
583
  None # The location where floating-point argument return values are stored
584
584
  )
585
- ARCH = None # The archinfo.Arch class that this CC must be used for, if relevant
585
+ ARCH: type[archinfo.Arch] | None = (
586
+ None # The archinfo.Arch class for which this CC is most likely relevant, if related
587
+ )
588
+ # archinfo.Arch classes for which this CC is relevant, in addition to self.ARCH.
589
+ # you should access cls.arches() to get a list of all arches for which this CC is relevant
590
+ EXTRA_ARCHES: tuple[type[archinfo.Arch], ...] = ()
586
591
  CALLEE_CLEANUP = False # Whether the callee has to deallocate the stack space for the arguments
587
592
 
588
593
  STACK_ALIGNMENT = 1 # the alignment requirement of the stack pointer at function start BEFORE call
@@ -682,7 +687,7 @@ class SimCC:
682
687
  ty = ty.with_arch(self.arch)
683
688
  if isinstance(ty, (SimStruct, SimUnion, SimTypeFixedSizeArray)):
684
689
  raise AngrTypeError(
685
- f"{self} doesn't know how to return aggregate types. Consider overriding return_val to "
690
+ f"{self} doesn't know how to return aggregate types ({type(ty)}). Consider overriding return_val to "
686
691
  "implement its ABI logic"
687
692
  )
688
693
  if self.return_in_implicit_outparam(ty):
@@ -1082,8 +1087,8 @@ class SimCC:
1082
1087
 
1083
1088
  @classmethod
1084
1089
  def _match(cls, arch, args: list, sp_delta):
1085
- if cls.ARCH is not None and not isinstance(
1086
- arch, cls.ARCH
1090
+ if (
1091
+ cls.arches() is not None and ":" not in arch.name and not isinstance(arch, cls.arches())
1087
1092
  ): # pylint:disable=isinstance-second-argument-not-valid-type
1088
1093
  return False
1089
1094
  if sp_delta != cls.STACKARG_SP_DIFF:
@@ -1149,6 +1154,12 @@ class SimCC:
1149
1154
  return cc_cls(arch)
1150
1155
  return None
1151
1156
 
1157
+ @classmethod
1158
+ def arches(cls) -> tuple[type[archinfo.Arch], ...]:
1159
+ if cls.ARCH is not None:
1160
+ return (cls.ARCH, *cls.EXTRA_ARCHES)
1161
+ return cls.EXTRA_ARCHES
1162
+
1152
1163
  def get_arg_info(self, state, prototype):
1153
1164
  """
1154
1165
  This is just a simple wrapper that collects the information from various locations
@@ -1323,6 +1334,20 @@ class SimCCMicrosoftAMD64(SimCC):
1323
1334
  def return_val(self, ty, perspective_returned=False):
1324
1335
  if ty._arch is None:
1325
1336
  ty = ty.with_arch(self.arch)
1337
+
1338
+ # Unions are allocated according to the layout of the largest member
1339
+ if isinstance(ty, SimUnion):
1340
+ chosen = None
1341
+ size = None
1342
+ for subty in ty.members.values():
1343
+ if subty.size is not None and (size is None or size < subty.size):
1344
+ chosen = subty
1345
+ size = subty.size
1346
+ if chosen is None:
1347
+ # fallback to void*
1348
+ chosen = SimTypePointer(SimTypeBottom())
1349
+ return self.return_val(chosen, perspective_returned=perspective_returned)
1350
+
1326
1351
  if not isinstance(ty, SimStruct):
1327
1352
  return super().return_val(ty, perspective_returned)
1328
1353
 
@@ -1444,7 +1469,7 @@ class SimCCSystemVAMD64(SimCC):
1444
1469
 
1445
1470
  @classmethod
1446
1471
  def _match(cls, arch, args, sp_delta):
1447
- if cls.ARCH is not None and not isinstance(arch, cls.ARCH):
1472
+ if cls.ARCH is not None and ":" not in arch.name and not isinstance(arch, cls.ARCH):
1448
1473
  return False
1449
1474
  # if sp_delta != cls.STACKARG_SP_DIFF:
1450
1475
  # return False
@@ -1789,8 +1814,54 @@ class SimCCARMHF(SimCCARM):
1789
1814
  FP_RETURN_VAL = SimRegArg("s0", 32)
1790
1815
  CALLER_SAVED_REGS = []
1791
1816
  RETURN_ADDR = SimRegArg("lr", 4)
1792
- RETURN_VAL = SimRegArg("r0", 4) # TODO Return val can also include reg r1
1817
+ RETURN_VAL = SimRegArg("r0", 4)
1818
+ OVERFLOW_RETURN_VAL = SimRegArg("r1", 4)
1793
1819
  ARCH = archinfo.ArchARMHF
1820
+ EXTRA_ARCHES = (archinfo.ArchARMCortexM,)
1821
+
1822
+ def next_arg(self, session, arg_type):
1823
+ if isinstance(arg_type, (SimTypeArray, SimTypeFixedSizeArray)): # hack
1824
+ arg_type = SimTypePointer(arg_type.elem_type).with_arch(self.arch)
1825
+ state = session.getstate()
1826
+ classification = self._classify(arg_type)
1827
+ try:
1828
+ mapped_classes = []
1829
+ for cls in classification:
1830
+ if cls == "DOUBLEP":
1831
+ if session.getstate()[1] % 2 == 1: # doubles must start on an even register
1832
+ next(session.int_iter)
1833
+
1834
+ if session.getstate()[1] == len(self.ARG_REGS) - 2:
1835
+ mapped_classes.append(next(session.int_iter))
1836
+ mapped_classes.append(next(session.both_iter))
1837
+ else:
1838
+ try:
1839
+ mapped_classes.append(next(session.int_iter))
1840
+ mapped_classes.append(next(session.int_iter))
1841
+ except StopIteration:
1842
+ mapped_classes.append(next(session.both_iter))
1843
+ mapped_classes.append(next(session.both_iter))
1844
+ elif cls == "NO_CLASS":
1845
+ raise NotImplementedError("Bug. Report to @rhelmot")
1846
+ elif cls == "MEMORY":
1847
+ mapped_classes.append(next(session.both_iter))
1848
+ elif cls == "INTEGER":
1849
+ try:
1850
+ mapped_classes.append(next(session.int_iter))
1851
+ except StopIteration:
1852
+ mapped_classes.append(next(session.both_iter))
1853
+ elif cls == "SINGLEP":
1854
+ try:
1855
+ mapped_classes.append(next(session.fp_iter))
1856
+ except StopIteration:
1857
+ mapped_classes.append(next(session.both_iter))
1858
+ else:
1859
+ raise NotImplementedError("Bug. Report to @rhelmot")
1860
+ except StopIteration:
1861
+ session.setstate(state)
1862
+ mapped_classes = [next(session.both_iter) for _ in classification]
1863
+
1864
+ return refine_locs_with_struct_type(self.arch, mapped_classes, arg_type)
1794
1865
 
1795
1866
 
1796
1867
  class SimCCARMLinuxSyscall(SimCCSyscall):
@@ -2122,7 +2193,7 @@ class SimCCSoot(SimCC):
2122
2193
  ARG_REGS = []
2123
2194
 
2124
2195
  def setup_callsite(self, state, ret_addr, args, prototype, stack_base=None, alloc_base=None, grow_like_stack=True):
2125
- SootMixin.setup_callsite(state, args, ret_addr)
2196
+ angr.engines.SootMixin.setup_callsite(state, args, ret_addr)
2126
2197
 
2127
2198
  @staticmethod
2128
2199
  def guess_prototype(args, prototype=None):
@@ -2226,7 +2297,7 @@ DEFAULT_CC: dict[str, dict[str, type[SimCC]]] = {
2226
2297
  "X86": {"Linux": SimCCCdecl, "CGC": SimCCCdecl, "Win32": SimCCMicrosoftCdecl},
2227
2298
  "ARMEL": {"Linux": SimCCARM},
2228
2299
  "ARMHF": {"Linux": SimCCARMHF},
2229
- "ARMCortexM": {"Linux": SimCCARM},
2300
+ "ARMCortexM": {"Linux": SimCCARMHF},
2230
2301
  "MIPS32": {"Linux": SimCCO32},
2231
2302
  "MIPS64": {"Linux": SimCCN64},
2232
2303
  "PPC32": {"Linux": SimCCPowerPC},
@@ -1,4 +1,4 @@
1
- """ angr.distributed provides a simple implementation for conducting
1
+ """angr.distributed provides a simple implementation for conducting
2
2
  long-running symbolic-execution-based tasks.
3
3
  """
4
4
 
angr/engines/__init__.py CHANGED
@@ -1,15 +1,14 @@
1
1
  from __future__ import annotations
2
2
 
3
- from .successors import SimSuccessors
4
- from .engine import SimEngine, SuccessorsMixin
5
-
6
- from .vex import HeavyVEXMixin, TrackActionsMixin, SimInspectMixin, HeavyResilienceMixin, SuperFastpathMixin
7
- from .procedure import ProcedureMixin, ProcedureEngine
8
- from .unicorn import SimEngineUnicorn
3
+ from .engine import SimEngine
9
4
  from .failure import SimEngineFailure
10
- from .syscall import SimEngineSyscall
11
5
  from .hook import HooksMixin
6
+ from .procedure import ProcedureEngine, ProcedureMixin
12
7
  from .soot import SootMixin
8
+ from .successors import SimSuccessors, SuccessorsEngine
9
+ from .syscall import SimEngineSyscall
10
+ from .unicorn import SimEngineUnicorn
11
+ from .vex import HeavyResilienceMixin, HeavyVEXMixin, SimInspectMixin, SuperFastpathMixin, TrackActionsMixin
13
12
 
14
13
 
15
14
  class UberEngine(
@@ -47,7 +46,7 @@ __all__ = [
47
46
  "SimInspectMixin",
48
47
  "SimSuccessors",
49
48
  "SootMixin",
50
- "SuccessorsMixin",
49
+ "SuccessorsEngine",
51
50
  "SuperFastpathMixin",
52
51
  "TrackActionsMixin",
53
52
  "UberEngine",
angr/engines/engine.py CHANGED
@@ -1,33 +1,19 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import Generic, TypeVar
4
3
  import abc
5
- import logging
4
+ from typing import Generic, TypeVar
6
5
 
7
- import claripy
8
- from archinfo.arch_soot import SootAddressDescriptor
9
6
 
10
7
  import angr
11
- from angr.sim_state import SimState
12
- from angr import sim_options as o
13
- from angr.errors import SimException
14
- from angr.state_plugins.inspect import BP_AFTER, BP_BEFORE
15
- from .successors import SimSuccessors
16
-
17
-
18
- l = logging.getLogger(name=__name__)
19
-
20
8
 
21
9
  StateType = TypeVar("StateType")
22
10
  ResultType = TypeVar("ResultType")
23
11
  DataType_co = TypeVar("DataType_co", covariant=True)
24
- HeavyState = SimState[int | SootAddressDescriptor, claripy.ast.BV | SootAddressDescriptor]
25
12
 
26
13
 
27
- class SimEngineBase(Generic[StateType]):
14
+ class SimEngine(Generic[StateType, ResultType], metaclass=abc.ABCMeta):
28
15
  """
29
- Even more basey of a base class for SimEngine. Used as a base by mixins which want access to the project but for
30
- which having method `process` (contained in `SimEngine`) doesn't make sense
16
+ A SimEngine is a type which understands how to perform execution on a state.
31
17
  """
32
18
 
33
19
  state: StateType
@@ -41,124 +27,3 @@ class SimEngineBase(Generic[StateType]):
41
27
 
42
28
  def __setstate__(self, state):
43
29
  self.project = state[0]
44
-
45
-
46
- class SimEngine(Generic[StateType, ResultType], SimEngineBase[StateType], metaclass=abc.ABCMeta):
47
- """
48
- A SimEngine is a class which understands how to perform execution on a state. This is a base class.
49
- """
50
-
51
- @abc.abstractmethod
52
- def process(self, state: StateType, **kwargs) -> ResultType:
53
- """
54
- The main entry point for an engine. Should take a state and return a result.
55
-
56
- :param state: The state to proceed from
57
- :return: The result. Whatever you want ;)
58
- """
59
-
60
-
61
- class SuccessorsMixin(SimEngine[HeavyState, SimSuccessors]):
62
- """
63
- A mixin for SimEngine which implements ``process`` to perform common operations related to symbolic execution
64
- and dispatches to a ``process_successors`` method to fill a SimSuccessors object with the results.
65
- """
66
-
67
- def __init__(self, project: angr.Project):
68
- super().__init__(project)
69
-
70
- self.successors: SimSuccessors | None = None
71
-
72
- def process(self, state: HeavyState, **kwargs) -> SimSuccessors: # pylint:disable=unused-argument
73
- """
74
- Perform execution with a state.
75
-
76
- You should only override this method in a subclass in order to provide the correct method signature and
77
- docstring. You should override the ``_process`` method to do your actual execution.
78
-
79
- :param state: The state with which to execute. This state will be copied before
80
- modification.
81
- :param inline: This is an inline execution. Do not bother copying the state.
82
- :param force_addr: Force execution to pretend that we're working at this concrete address
83
- :returns: A SimSuccessors object categorizing the execution's successor states
84
- """
85
- inline = kwargs.pop("inline", False)
86
- force_addr = kwargs.pop("force_addr", None)
87
-
88
- ip = state._ip
89
- addr = (
90
- (ip if isinstance(ip, SootAddressDescriptor) else state.solver.eval(ip))
91
- if force_addr is None
92
- else force_addr
93
- )
94
-
95
- # make a copy of the initial state for actual processing, if needed
96
- new_state = state.copy() if not inline and o.COPY_STATES in state.options else state
97
- # enforce this distinction
98
- old_state = state
99
- del state
100
- self.state = new_state
101
-
102
- # we have now officially begun the stepping process! now is where we "cycle" a state's
103
- # data - move the "present" into the "past" by pushing an entry on the history stack.
104
- # nuance: make sure to copy from the PREVIOUS state to the CURRENT one
105
- # to avoid creating a dead link in the history, messing up the statehierarchy
106
- new_state.register_plugin("history", old_state.history.make_child())
107
- new_state.history.recent_bbl_addrs.append(addr)
108
- if new_state.arch.unicorn_support:
109
- assert isinstance(addr, int)
110
- new_state.scratch.executed_pages_set = {addr & ~0xFFF}
111
-
112
- self.successors = SimSuccessors(addr, old_state)
113
-
114
- new_state._inspect(
115
- "engine_process", when=BP_BEFORE, sim_engine=self, sim_successors=self.successors, address=addr
116
- )
117
- self.successors = new_state._inspect_getattr("sim_successors", self.successors)
118
- try:
119
- self.process_successors(self.successors, **kwargs)
120
- except SimException as e:
121
- if o.EXCEPTION_HANDLING not in old_state.options:
122
- raise
123
- assert old_state.project is not None
124
- old_state.project.simos.handle_exception(self.successors, self, e)
125
-
126
- new_state._inspect("engine_process", when=BP_AFTER, sim_successors=self.successors, address=addr)
127
- self.successors = new_state._inspect_getattr("sim_successors", self.successors)
128
- assert self.successors is not None
129
-
130
- # downsizing
131
- if new_state.supports_inspect:
132
- new_state.inspect.downsize()
133
- # if not TRACK, clear actions on OLD state
134
- # if o.TRACK_ACTION_HISTORY not in old_state.options:
135
- # old_state.history.recent_events = []
136
-
137
- # fix up the descriptions...
138
- description = str(self.successors)
139
- l.info("Ticked state: %s", description)
140
- for succ in self.successors.all_successors:
141
- succ.history.recent_description = description
142
- for succ in self.successors.flat_successors:
143
- succ.history.recent_description = description
144
-
145
- return self.successors
146
-
147
- def process_successors(self, successors, **kwargs): # pylint:disable=unused-argument,no-self-use
148
- """
149
- Implement this function to fill out the SimSuccessors object with the results of stepping state.
150
-
151
- In order to implement a model where multiple mixins can potentially handle a request, a mixin may implement
152
- this method and then perform a super() call if it wants to pass on handling to the next mixin.
153
-
154
- Keep in mind python's method resolution order when composing multiple classes implementing this method.
155
- In short: left-to-right, depth-first, but deferring any base classes which are shared by multiple subclasses
156
- (the merge point of a diamond pattern in the inheritance graph) until the last point where they would be
157
- encountered in this depth-first search. For example, if you have classes A, B(A), C(B), D(A), E(C, D), then the
158
- method resolution order will be E, C, B, D, A.
159
-
160
- :param state: The state to manipulate
161
- :param successors: The successors object to fill out
162
- :param kwargs: Any extra arguments. Do not fail if you are passed unexpected arguments.
163
- """
164
- successors.processed = False # mark failure