angr 9.2.139__py3-none-manylinux2014_x86_64.whl → 9.2.141__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 (87) hide show
  1. angr/__init__.py +1 -1
  2. angr/analyses/calling_convention/calling_convention.py +136 -53
  3. angr/analyses/calling_convention/fact_collector.py +44 -18
  4. angr/analyses/calling_convention/utils.py +3 -1
  5. angr/analyses/cfg/cfg_base.py +13 -0
  6. angr/analyses/cfg/cfg_fast.py +11 -0
  7. angr/analyses/cfg/indirect_jump_resolvers/jumptable.py +9 -8
  8. angr/analyses/decompiler/ail_simplifier.py +115 -72
  9. angr/analyses/decompiler/callsite_maker.py +24 -11
  10. angr/analyses/decompiler/clinic.py +78 -43
  11. angr/analyses/decompiler/decompiler.py +18 -7
  12. angr/analyses/decompiler/expression_narrower.py +1 -1
  13. angr/analyses/decompiler/optimization_passes/const_prop_reverter.py +8 -7
  14. angr/analyses/decompiler/optimization_passes/duplication_reverter/duplication_reverter.py +3 -1
  15. angr/analyses/decompiler/optimization_passes/flip_boolean_cmp.py +21 -2
  16. angr/analyses/decompiler/optimization_passes/ite_region_converter.py +21 -13
  17. angr/analyses/decompiler/optimization_passes/lowered_switch_simplifier.py +84 -15
  18. angr/analyses/decompiler/optimization_passes/optimization_pass.py +92 -11
  19. angr/analyses/decompiler/optimization_passes/return_duplicator_base.py +53 -9
  20. angr/analyses/decompiler/peephole_optimizations/eager_eval.py +44 -7
  21. angr/analyses/decompiler/region_identifier.py +6 -4
  22. angr/analyses/decompiler/region_simplifiers/expr_folding.py +287 -122
  23. angr/analyses/decompiler/region_simplifiers/region_simplifier.py +31 -13
  24. angr/analyses/decompiler/ssailification/rewriting.py +23 -15
  25. angr/analyses/decompiler/ssailification/rewriting_engine.py +105 -24
  26. angr/analyses/decompiler/ssailification/ssailification.py +22 -14
  27. angr/analyses/decompiler/structured_codegen/c.py +73 -137
  28. angr/analyses/decompiler/structuring/dream.py +22 -18
  29. angr/analyses/decompiler/structuring/phoenix.py +158 -41
  30. angr/analyses/decompiler/structuring/recursive_structurer.py +1 -0
  31. angr/analyses/decompiler/structuring/structurer_base.py +37 -10
  32. angr/analyses/decompiler/structuring/structurer_nodes.py +4 -1
  33. angr/analyses/decompiler/utils.py +106 -21
  34. angr/analyses/deobfuscator/api_obf_finder.py +8 -5
  35. angr/analyses/deobfuscator/api_obf_type2_finder.py +18 -10
  36. angr/analyses/deobfuscator/string_obf_finder.py +105 -18
  37. angr/analyses/forward_analysis/forward_analysis.py +1 -1
  38. angr/analyses/propagator/top_checker_mixin.py +6 -6
  39. angr/analyses/reaching_definitions/__init__.py +2 -1
  40. angr/analyses/reaching_definitions/dep_graph.py +1 -12
  41. angr/analyses/reaching_definitions/engine_vex.py +36 -31
  42. angr/analyses/reaching_definitions/function_handler.py +15 -2
  43. angr/analyses/reaching_definitions/rd_state.py +1 -37
  44. angr/analyses/reaching_definitions/reaching_definitions.py +13 -24
  45. angr/analyses/s_propagator.py +6 -41
  46. angr/analyses/s_reaching_definitions/s_rda_model.py +7 -1
  47. angr/analyses/s_reaching_definitions/s_rda_view.py +43 -25
  48. angr/analyses/stack_pointer_tracker.py +36 -22
  49. angr/analyses/typehoon/simple_solver.py +45 -7
  50. angr/analyses/typehoon/typeconsts.py +18 -5
  51. angr/analyses/variable_recovery/engine_ail.py +1 -1
  52. angr/analyses/variable_recovery/engine_base.py +7 -5
  53. angr/analyses/variable_recovery/engine_vex.py +20 -4
  54. angr/block.py +69 -107
  55. angr/callable.py +14 -7
  56. angr/calling_conventions.py +30 -11
  57. angr/distributed/__init__.py +1 -1
  58. angr/engines/__init__.py +7 -8
  59. angr/engines/engine.py +1 -120
  60. angr/engines/failure.py +2 -2
  61. angr/engines/hook.py +2 -2
  62. angr/engines/light/engine.py +2 -2
  63. angr/engines/pcode/engine.py +2 -14
  64. angr/engines/procedure.py +2 -2
  65. angr/engines/soot/engine.py +2 -2
  66. angr/engines/soot/statements/switch.py +1 -1
  67. angr/engines/successors.py +124 -11
  68. angr/engines/syscall.py +2 -2
  69. angr/engines/unicorn.py +3 -3
  70. angr/engines/vex/heavy/heavy.py +3 -15
  71. angr/factory.py +12 -22
  72. angr/knowledge_plugins/key_definitions/atoms.py +8 -4
  73. angr/knowledge_plugins/key_definitions/live_definitions.py +41 -103
  74. angr/knowledge_plugins/variables/variable_manager.py +7 -5
  75. angr/sim_type.py +19 -17
  76. angr/simos/simos.py +3 -1
  77. angr/state_plugins/plugin.py +19 -4
  78. angr/storage/memory_mixins/memory_mixin.py +1 -1
  79. angr/storage/memory_mixins/paged_memory/pages/multi_values.py +10 -5
  80. angr/utils/ssa/__init__.py +119 -4
  81. angr/utils/types.py +48 -0
  82. {angr-9.2.139.dist-info → angr-9.2.141.dist-info}/METADATA +6 -6
  83. {angr-9.2.139.dist-info → angr-9.2.141.dist-info}/RECORD +87 -86
  84. {angr-9.2.139.dist-info → angr-9.2.141.dist-info}/LICENSE +0 -0
  85. {angr-9.2.139.dist-info → angr-9.2.141.dist-info}/WHEEL +0 -0
  86. {angr-9.2.139.dist-info → angr-9.2.141.dist-info}/entry_points.txt +0 -0
  87. {angr-9.2.139.dist-info → angr-9.2.141.dist-info}/top_level.txt +0 -0
@@ -2,7 +2,8 @@
2
2
  from __future__ import annotations
3
3
  import logging
4
4
  from typing import cast
5
- from collections.abc import Iterable
5
+
6
+ from collections.abc import Iterable, Sequence
6
7
  from collections import defaultdict
7
8
  import contextlib
8
9
 
@@ -82,7 +83,8 @@ class AllocHelper:
82
83
 
83
84
  def size(self):
84
85
  val = self.translate(self.ptr, claripy.BVV(0, len(self.ptr)))
85
- assert val.op == "BVV"
86
+ assert isinstance(val, claripy.ast.Base) and val.op == "BVV"
87
+ assert isinstance(val.args[0], int)
86
88
  return abs(val.args[0])
87
89
 
88
90
  @classmethod
@@ -130,6 +132,7 @@ def refine_locs_with_struct_type(
130
132
  arg_type = SimTypeInt(label=arg_type.label).with_arch(arch)
131
133
 
132
134
  if isinstance(arg_type, (SimTypeReg, SimTypeNum, SimTypeFloat)):
135
+ assert arg_type.size is not None
133
136
  seen_bytes = 0
134
137
  pieces = []
135
138
  while seen_bytes < arg_type.size // arch.byte_width:
@@ -147,20 +150,21 @@ def refine_locs_with_struct_type(
147
150
  piece.is_fp = True
148
151
  return piece
149
152
  if isinstance(arg_type, SimTypeFixedSizeArray):
153
+ assert arg_type.elem_type.size is not None and arg_type.length is not None
150
154
  # TODO explicit stride
151
- locs = [
155
+ locs_list = [
152
156
  refine_locs_with_struct_type(
153
157
  arch, locs, arg_type.elem_type, offset=offset + i * arg_type.elem_type.size // arch.byte_width
154
158
  )
155
159
  for i in range(arg_type.length)
156
160
  ]
157
- return SimArrayArg(locs)
161
+ return SimArrayArg(locs_list)
158
162
  if isinstance(arg_type, SimStruct):
159
- locs = {
163
+ locs_dict = {
160
164
  field: refine_locs_with_struct_type(arch, locs, field_ty, offset=offset + arg_type.offsets[field])
161
165
  for field, field_ty in arg_type.fields.items()
162
166
  }
163
- return SimStructArg(arg_type, locs)
167
+ return SimStructArg(arg_type, locs_dict)
164
168
  if isinstance(arg_type, SimUnion):
165
169
  # Treat a SimUnion as functionality equivalent to its longest member
166
170
  for member in arg_type.members.values():
@@ -574,8 +578,8 @@ class SimCC:
574
578
  # (if applicable) and the arguments. Probably zero.
575
579
  STACKARG_SP_DIFF = 0 # The amount of stack space reserved for the return address
576
580
  CALLER_SAVED_REGS: list[str] = [] # Caller-saved registers
577
- RETURN_ADDR: SimFunctionArgument = None # The location where the return address is stored, as a SimFunctionArgument
578
- RETURN_VAL: SimFunctionArgument = None # The location where the return value is stored, as a SimFunctionArgument
581
+ RETURN_ADDR: SimFunctionArgument # The location where the return address is stored, as a SimFunctionArgument
582
+ RETURN_VAL: SimFunctionArgument # The location where the return value is stored, as a SimFunctionArgument
579
583
  OVERFLOW_RETURN_VAL: SimFunctionArgument | None = (
580
584
  None # The second half of the location where a double-length return value is stored
581
585
  )
@@ -687,7 +691,7 @@ class SimCC:
687
691
  ty = ty.with_arch(self.arch)
688
692
  if isinstance(ty, (SimStruct, SimUnion, SimTypeFixedSizeArray)):
689
693
  raise AngrTypeError(
690
- f"{self} doesn't know how to return aggregate types. Consider overriding return_val to "
694
+ f"{self} doesn't know how to return aggregate types ({type(ty)}). Consider overriding return_val to "
691
695
  "implement its ABI logic"
692
696
  )
693
697
  if self.return_in_implicit_outparam(ty):
@@ -728,6 +732,7 @@ class SimCC:
728
732
  l.warning("Function argument type cannot be BOT. Treating it as a 32-bit int.")
729
733
  arg_type = SimTypeInt().with_arch(self.arch)
730
734
  is_fp = isinstance(arg_type, SimTypeFloat)
735
+ assert arg_type.size is not None
731
736
  size = arg_type.size // self.arch.byte_width
732
737
  try:
733
738
  arg = next(session.fp_iter) if is_fp else next(session.int_iter)
@@ -760,7 +765,7 @@ class SimCC:
760
765
  def is_fp_value(val):
761
766
  return (
762
767
  isinstance(val, (float, claripy.ast.FP))
763
- or (isinstance(val, claripy.ast.Base) and val.op.startswith("fp"))
768
+ or (isinstance(val, claripy.ast.Base) and val.op.startswith("fp")) # type: ignore
764
769
  or (isinstance(val, claripy.ast.Base) and val.op == "Reverse" and val.args[0].op.startswith("fp"))
765
770
  )
766
771
 
@@ -1130,7 +1135,7 @@ class SimCC:
1130
1135
 
1131
1136
  @staticmethod
1132
1137
  def find_cc(
1133
- arch: archinfo.Arch, args: list[SimFunctionArgument], sp_delta: int, platform: str = "Linux"
1138
+ arch: archinfo.Arch, args: Sequence[SimFunctionArgument], sp_delta: int, platform: str = "Linux"
1134
1139
  ) -> SimCC | None:
1135
1140
  """
1136
1141
  Pinpoint the best-fit calling convention and return the corresponding SimCC instance, or None if no fit is
@@ -1334,6 +1339,20 @@ class SimCCMicrosoftAMD64(SimCC):
1334
1339
  def return_val(self, ty, perspective_returned=False):
1335
1340
  if ty._arch is None:
1336
1341
  ty = ty.with_arch(self.arch)
1342
+
1343
+ # Unions are allocated according to the layout of the largest member
1344
+ if isinstance(ty, SimUnion):
1345
+ chosen = None
1346
+ size = None
1347
+ for subty in ty.members.values():
1348
+ if subty.size is not None and (size is None or size < subty.size):
1349
+ chosen = subty
1350
+ size = subty.size
1351
+ if chosen is None:
1352
+ # fallback to void*
1353
+ chosen = SimTypePointer(SimTypeBottom())
1354
+ return self.return_val(chosen, perspective_returned=perspective_returned)
1355
+
1337
1356
  if not isinstance(ty, SimStruct):
1338
1357
  return super().return_val(ty, perspective_returned)
1339
1358
 
@@ -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,27 +1,14 @@
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
14
  class SimEngine(Generic[StateType, ResultType], metaclass=abc.ABCMeta):
@@ -40,109 +27,3 @@ class SimEngine(Generic[StateType, ResultType], metaclass=abc.ABCMeta):
40
27
 
41
28
  def __setstate__(self, state):
42
29
  self.project = state[0]
43
-
44
-
45
- class SuccessorsMixin(SimEngine[HeavyState, SimSuccessors]):
46
- """
47
- A mixin for SimEngine which implements ``process`` to perform common operations related to symbolic execution
48
- and dispatches to a ``process_successors`` method to fill a SimSuccessors object with the results.
49
- """
50
-
51
- def __init__(self, project: angr.Project):
52
- super().__init__(project)
53
-
54
- self.successors: SimSuccessors | None = None
55
-
56
- def process(self, state: HeavyState, **kwargs) -> SimSuccessors: # pylint:disable=unused-argument
57
- """
58
- Perform execution with a state.
59
-
60
- You should only override this method in a subclass in order to provide the correct method signature and
61
- docstring. You should override the ``_process`` method to do your actual execution.
62
-
63
- :param state: The state with which to execute. This state will be copied before
64
- modification.
65
- :param inline: This is an inline execution. Do not bother copying the state.
66
- :param force_addr: Force execution to pretend that we're working at this concrete address
67
- :returns: A SimSuccessors object categorizing the execution's successor states
68
- """
69
- inline = kwargs.pop("inline", False)
70
- force_addr = kwargs.pop("force_addr", None)
71
-
72
- ip = state._ip
73
- addr = (
74
- (ip if isinstance(ip, SootAddressDescriptor) else state.solver.eval(ip))
75
- if force_addr is None
76
- else force_addr
77
- )
78
-
79
- # make a copy of the initial state for actual processing, if needed
80
- new_state = state.copy() if not inline and o.COPY_STATES in state.options else state
81
- # enforce this distinction
82
- old_state = state
83
- del state
84
- self.state = new_state
85
-
86
- # we have now officially begun the stepping process! now is where we "cycle" a state's
87
- # data - move the "present" into the "past" by pushing an entry on the history stack.
88
- # nuance: make sure to copy from the PREVIOUS state to the CURRENT one
89
- # to avoid creating a dead link in the history, messing up the statehierarchy
90
- new_state.register_plugin("history", old_state.history.make_child())
91
- new_state.history.recent_bbl_addrs.append(addr)
92
- if new_state.arch.unicorn_support:
93
- assert isinstance(addr, int)
94
- new_state.scratch.executed_pages_set = {addr & ~0xFFF}
95
-
96
- self.successors = SimSuccessors(addr, old_state)
97
-
98
- new_state._inspect(
99
- "engine_process", when=BP_BEFORE, sim_engine=self, sim_successors=self.successors, address=addr
100
- )
101
- self.successors = new_state._inspect_getattr("sim_successors", self.successors)
102
- try:
103
- self.process_successors(self.successors, **kwargs)
104
- except SimException as e:
105
- if o.EXCEPTION_HANDLING not in old_state.options:
106
- raise
107
- assert old_state.project is not None
108
- old_state.project.simos.handle_exception(self.successors, self, e)
109
-
110
- new_state._inspect("engine_process", when=BP_AFTER, sim_successors=self.successors, address=addr)
111
- self.successors = new_state._inspect_getattr("sim_successors", self.successors)
112
- assert self.successors is not None
113
-
114
- # downsizing
115
- if new_state.supports_inspect:
116
- new_state.inspect.downsize()
117
- # if not TRACK, clear actions on OLD state
118
- # if o.TRACK_ACTION_HISTORY not in old_state.options:
119
- # old_state.history.recent_events = []
120
-
121
- # fix up the descriptions...
122
- description = str(self.successors)
123
- l.info("Ticked state: %s", description)
124
- for succ in self.successors.all_successors:
125
- succ.history.recent_description = description
126
- for succ in self.successors.flat_successors:
127
- succ.history.recent_description = description
128
-
129
- return self.successors
130
-
131
- def process_successors(self, successors, **kwargs): # pylint:disable=unused-argument,no-self-use
132
- """
133
- Implement this function to fill out the SimSuccessors object with the results of stepping state.
134
-
135
- In order to implement a model where multiple mixins can potentially handle a request, a mixin may implement
136
- this method and then perform a super() call if it wants to pass on handling to the next mixin.
137
-
138
- Keep in mind python's method resolution order when composing multiple classes implementing this method.
139
- In short: left-to-right, depth-first, but deferring any base classes which are shared by multiple subclasses
140
- (the merge point of a diamond pattern in the inheritance graph) until the last point where they would be
141
- encountered in this depth-first search. For example, if you have classes A, B(A), C(B), D(A), E(C, D), then the
142
- method resolution order will be E, C, B, D, A.
143
-
144
- :param state: The state to manipulate
145
- :param successors: The successors object to fill out
146
- :param kwargs: Any extra arguments. Do not fail if you are passed unexpected arguments.
147
- """
148
- successors.processed = False # mark failure
angr/engines/failure.py CHANGED
@@ -3,13 +3,13 @@ from __future__ import annotations
3
3
  import logging
4
4
 
5
5
  from angr.errors import AngrExitError
6
- from .engine import SuccessorsMixin
6
+ from .successors import SuccessorsEngine
7
7
  from .procedure import ProcedureMixin
8
8
 
9
9
  l = logging.getLogger(name=__name__)
10
10
 
11
11
 
12
- class SimEngineFailure(SuccessorsMixin, ProcedureMixin):
12
+ class SimEngineFailure(SuccessorsEngine, ProcedureMixin):
13
13
  def process_successors(self, successors, **kwargs):
14
14
  state = self.state
15
15
  jumpkind = state.history.parent.jumpkind if state.history and state.history.parent else None
angr/engines/hook.py CHANGED
@@ -1,7 +1,7 @@
1
1
  from __future__ import annotations
2
2
  import logging
3
3
 
4
- from .engine import SuccessorsMixin
4
+ from .successors import SuccessorsEngine
5
5
  from .procedure import ProcedureMixin
6
6
  from archinfo.arch_soot import SootAddressDescriptor
7
7
 
@@ -9,7 +9,7 @@ l = logging.getLogger(name=__name__)
9
9
 
10
10
 
11
11
  # pylint: disable=abstract-method,unused-argument,arguments-differ
12
- class HooksMixin(SuccessorsMixin, ProcedureMixin):
12
+ class HooksMixin(SuccessorsEngine, ProcedureMixin):
13
13
  """
14
14
  A SimEngine mixin which adds a SimSuccessors handler which will look into the project's hooks and run the hook at
15
15
  the current address.
@@ -159,7 +159,7 @@ class SimEngineLightVEX(
159
159
 
160
160
  @staticmethod
161
161
  def binopv_handler(
162
- f: Callable[[T, int, int, pyvex.expr.Binop], DataType_co]
162
+ f: Callable[[T, int, int, pyvex.expr.Binop], DataType_co],
163
163
  ) -> Callable[[T, int, int, pyvex.expr.Binop], DataType_co]:
164
164
  f.binopv_handler = True
165
165
  return f
@@ -181,7 +181,7 @@ class SimEngineLightVEX(
181
181
 
182
182
  @staticmethod
183
183
  def dirty_handler(
184
- f: Callable[[T, pyvex.stmt.Dirty], StmtDataType]
184
+ f: Callable[[T, pyvex.stmt.Dirty], StmtDataType],
185
185
  ) -> Callable[[T, pyvex.stmt.Dirty], StmtDataType]:
186
186
  f.dirty_handler = True
187
187
  return f
@@ -5,7 +5,7 @@ import claripy
5
5
  import logging
6
6
 
7
7
  from angr.calling_conventions import DEFAULT_CC, default_cc, SimRegArg
8
- from angr.engines.engine import SuccessorsMixin, SimSuccessors
8
+ from angr.engines.successors import SuccessorsEngine, SimSuccessors
9
9
  from angr.misc.ux import once
10
10
  from angr.utils.constants import DEFAULT_STATEMENT
11
11
  from angr import sim_options as o
@@ -19,7 +19,7 @@ l = logging.getLogger(__name__)
19
19
 
20
20
 
21
21
  class HeavyPcodeMixin(
22
- SuccessorsMixin,
22
+ SuccessorsEngine,
23
23
  PcodeLifterEngineMixin,
24
24
  PcodeEmulatorMixin,
25
25
  ):
@@ -52,7 +52,6 @@ class HeavyPcodeMixin(
52
52
  self,
53
53
  successors: SimSuccessors,
54
54
  irsb: IRSB | None = None,
55
- insn_text: str | None = None,
56
55
  insn_bytes: bytes | None = None,
57
56
  thumb: bool = False,
58
57
  size: int | None = None,
@@ -67,21 +66,10 @@ class HeavyPcodeMixin(
67
66
  extra_stop_points=extra_stop_points,
68
67
  num_inst=num_inst,
69
68
  size=size,
70
- insn_text=insn_text,
71
69
  insn_bytes=insn_bytes,
72
70
  **kwargs,
73
71
  )
74
72
 
75
- if insn_text is not None:
76
- if insn_bytes is not None:
77
- raise errors.SimEngineError("You cannot provide both 'insn_bytes' and 'insn_text'!")
78
-
79
- insn_bytes = self.project.arch.asm(insn_text, addr=successors.addr, thumb=thumb)
80
- if insn_bytes is None:
81
- raise errors.AngrAssemblyError(
82
- "Assembling failed. Please make sure keystone is installed, and the assembly string is correct."
83
- )
84
-
85
73
  successors.sort = "IRSB"
86
74
  successors.description = "IRSB"
87
75
  self.state.history.recent_block_count = 1
angr/engines/procedure.py CHANGED
@@ -4,7 +4,7 @@ import logging
4
4
  from angr import sim_options as o
5
5
  from angr import errors
6
6
  from angr.state_plugins.inspect import BP_BEFORE, BP_AFTER
7
- from .engine import SuccessorsMixin
7
+ from .successors import SuccessorsEngine
8
8
 
9
9
 
10
10
  l = logging.getLogger(name=__name__)
@@ -58,7 +58,7 @@ class ProcedureMixin:
58
58
  successors.processed = True
59
59
 
60
60
 
61
- class ProcedureEngine(ProcedureMixin, SuccessorsMixin):
61
+ class ProcedureEngine(ProcedureMixin, SuccessorsEngine):
62
62
  """
63
63
  A SimEngine that you may use if you only care about processing SimProcedures. *Requires* the procedure
64
64
  kwarg to be passed to process.
@@ -15,7 +15,7 @@ from angr.errors import SimEngineError, SimTranslationError
15
15
  from cle import CLEError
16
16
  from angr.state_plugins.inspect import BP_AFTER, BP_BEFORE
17
17
  from angr.sim_type import SimTypeFunction, parse_type
18
- from angr.engines.engine import SuccessorsMixin
18
+ from angr.engines.successors import SuccessorsEngine
19
19
  from angr.engines.procedure import ProcedureMixin
20
20
  from .exceptions import BlockTerminationNotice, IncorrectLocationException
21
21
  from .statements import SimSootStmt_Return, SimSootStmt_ReturnVoid, translate_stmt
@@ -26,7 +26,7 @@ l = logging.getLogger("angr.engines.soot.engine")
26
26
  # pylint: disable=arguments-differ
27
27
 
28
28
 
29
- class SootMixin(SuccessorsMixin, ProcedureMixin):
29
+ class SootMixin(SuccessorsEngine, ProcedureMixin):
30
30
  """
31
31
  Execution engine based on Soot.
32
32
  """
@@ -25,7 +25,7 @@ class SwitchBase(SimSootStmt):
25
25
  jmp_condition = lookup_value == key_val
26
26
  self._add_jmp_target(jmp_target, jmp_condition)
27
27
  # add condition for the default target
28
- default_jmp_conditions += [(lookup_value != key_val)]
28
+ default_jmp_conditions += [lookup_value != key_val]
29
29
 
30
30
  # add default target
31
31
  default_jmp_target = self._get_bb_addr_from_instr(self.stmt.default_target)
@@ -1,23 +1,27 @@
1
1
  from __future__ import annotations
2
- from typing import TYPE_CHECKING
2
+
3
3
  import logging
4
4
 
5
5
  import claripy
6
-
7
6
  from archinfo.arch_soot import ArchSoot, SootAddressDescriptor
8
7
 
8
+ import angr
9
9
  from angr import sim_options as o
10
10
  from angr.calling_conventions import SYSCALL_CC
11
- from angr.errors import SimSolverModeError, AngrUnsupportedSyscallError, AngrSyscallError, SimValueError, SimUnsatError
12
- from angr.storage import DUMMY_SYMBOLIC_READ_VALUE
13
- from angr.state_plugins.inspect import BP_BEFORE, BP_AFTER
11
+ from angr.engines.engine import SimEngine
12
+ from angr.errors import (
13
+ AngrSyscallError,
14
+ AngrUnsupportedSyscallError,
15
+ SimException,
16
+ SimSolverModeError,
17
+ SimUnsatError,
18
+ SimValueError,
19
+ )
20
+ from angr.sim_state import SimState
14
21
  from angr.state_plugins.callstack import CallStack
22
+ from angr.state_plugins.inspect import BP_AFTER, BP_BEFORE
15
23
  from angr.state_plugins.sim_action_object import _raw_ast
16
-
17
-
18
- if TYPE_CHECKING:
19
- from angr import SimState
20
- from angr.engines.engine import HeavyState
24
+ from angr.storage import DUMMY_SYMBOLIC_READ_VALUE
21
25
 
22
26
 
23
27
  l = logging.getLogger(name=__name__)
@@ -300,7 +304,7 @@ class SimSuccessors:
300
304
  else:
301
305
  # The architecture doesn't have an ip_at_syscall register.
302
306
  # Nothing to do but hope vigorously.
303
- l.warning(f"Handling syscall on arch {state.arch.name:s} without ip_at_syscall register")
307
+ l.warning("Handling syscall on arch %s without ip_at_syscall register", state.arch.name)
304
308
 
305
309
  try:
306
310
  symbolic_syscall_num, concrete_syscall_nums = self._resolve_syscall(state)
@@ -539,3 +543,112 @@ class SimSuccessors:
539
543
  addrs = state.solver.eval_upto(ip, limit)
540
544
 
541
545
  return [(ip == addr, addr) for addr in addrs]
546
+
547
+
548
+ HeavyState = SimState[int | SootAddressDescriptor, claripy.ast.BV | SootAddressDescriptor]
549
+
550
+
551
+ class SuccessorsEngine(SimEngine[HeavyState, SimSuccessors]):
552
+ """
553
+ A mixin for SimEngine which implements ``process`` to perform common operations related to symbolic execution
554
+ and dispatches to a ``process_successors`` method to fill a SimSuccessors object with the results.
555
+ """
556
+
557
+ def __init__(self, project: angr.Project):
558
+ super().__init__(project)
559
+
560
+ self.successors: SimSuccessors | None = None
561
+
562
+ def process(self, state: HeavyState, **kwargs) -> SimSuccessors: # pylint:disable=unused-argument
563
+ """
564
+ Perform execution with a state.
565
+
566
+ You should only override this method in a subclass in order to provide the correct method signature and
567
+ docstring. You should override the ``_process`` method to do your actual execution.
568
+
569
+ :param state: The state with which to execute. This state will be copied before
570
+ modification.
571
+ :param inline: This is an inline execution. Do not bother copying the state.
572
+ :param force_addr: Force execution to pretend that we're working at this concrete address
573
+ :returns: A SimSuccessors object categorizing the execution's successor states
574
+ """
575
+ inline = kwargs.pop("inline", False)
576
+ force_addr = kwargs.pop("force_addr", None)
577
+
578
+ ip = state._ip
579
+ addr = (
580
+ (ip if isinstance(ip, SootAddressDescriptor) else state.solver.eval(ip))
581
+ if force_addr is None
582
+ else force_addr
583
+ )
584
+
585
+ # make a copy of the initial state for actual processing, if needed
586
+ new_state = state.copy() if not inline and o.COPY_STATES in state.options else state
587
+ # enforce this distinction
588
+ old_state = state
589
+ del state
590
+ self.state = new_state
591
+
592
+ # we have now officially begun the stepping process! now is where we "cycle" a state's
593
+ # data - move the "present" into the "past" by pushing an entry on the history stack.
594
+ # nuance: make sure to copy from the PREVIOUS state to the CURRENT one
595
+ # to avoid creating a dead link in the history, messing up the statehierarchy
596
+ new_state.register_plugin("history", old_state.history.make_child())
597
+ new_state.history.recent_bbl_addrs.append(addr)
598
+ if new_state.arch.unicorn_support:
599
+ assert isinstance(addr, int)
600
+ new_state.scratch.executed_pages_set = {addr & ~0xFFF}
601
+
602
+ self.successors = SimSuccessors(addr, old_state)
603
+
604
+ new_state._inspect(
605
+ "engine_process", when=BP_BEFORE, sim_engine=self, sim_successors=self.successors, address=addr
606
+ )
607
+ self.successors = new_state._inspect_getattr("sim_successors", self.successors)
608
+ try:
609
+ self.process_successors(self.successors, **kwargs)
610
+ except SimException as e:
611
+ if o.EXCEPTION_HANDLING not in old_state.options:
612
+ raise
613
+ assert old_state.project is not None
614
+ old_state.project.simos.handle_exception(self.successors, self, e)
615
+
616
+ new_state._inspect("engine_process", when=BP_AFTER, sim_successors=self.successors, address=addr)
617
+ self.successors = new_state._inspect_getattr("sim_successors", self.successors)
618
+ assert self.successors is not None
619
+
620
+ # downsizing
621
+ if new_state.supports_inspect:
622
+ new_state.inspect.downsize()
623
+ # if not TRACK, clear actions on OLD state
624
+ # if o.TRACK_ACTION_HISTORY not in old_state.options:
625
+ # old_state.history.recent_events = []
626
+
627
+ # fix up the descriptions...
628
+ description = str(self.successors)
629
+ l.info("Ticked state: %s", description)
630
+ for succ in self.successors.all_successors:
631
+ succ.history.recent_description = description
632
+ for succ in self.successors.flat_successors:
633
+ succ.history.recent_description = description
634
+
635
+ return self.successors
636
+
637
+ def process_successors(self, successors, **kwargs): # pylint:disable=unused-argument,no-self-use
638
+ """
639
+ Implement this function to fill out the SimSuccessors object with the results of stepping state.
640
+
641
+ In order to implement a model where multiple mixins can potentially handle a request, a mixin may implement
642
+ this method and then perform a super() call if it wants to pass on handling to the next mixin.
643
+
644
+ Keep in mind python's method resolution order when composing multiple classes implementing this method.
645
+ In short: left-to-right, depth-first, but deferring any base classes which are shared by multiple subclasses
646
+ (the merge point of a diamond pattern in the inheritance graph) until the last point where they would be
647
+ encountered in this depth-first search. For example, if you have classes A, B(A), C(B), D(A), E(C, D), then the
648
+ method resolution order will be E, C, B, D, A.
649
+
650
+ :param state: The state to manipulate
651
+ :param successors: The successors object to fill out
652
+ :param kwargs: Any extra arguments. Do not fail if you are passed unexpected arguments.
653
+ """
654
+ successors.processed = False # mark failure
angr/engines/syscall.py CHANGED
@@ -3,14 +3,14 @@ import logging
3
3
 
4
4
  import angr
5
5
  from angr.errors import AngrUnsupportedSyscallError
6
- from .engine import SuccessorsMixin
6
+ from .successors import SuccessorsEngine
7
7
  from .procedure import ProcedureMixin
8
8
 
9
9
  l = logging.getLogger(name=__name__)
10
10
 
11
11
 
12
12
  # pylint:disable=abstract-method,arguments-differ
13
- class SimEngineSyscall(SuccessorsMixin, ProcedureMixin):
13
+ class SimEngineSyscall(SuccessorsEngine, ProcedureMixin):
14
14
  """
15
15
  A SimEngine mixin which adds a successors handling step that checks if a syscall was just requested and if so
16
16
  handles it as a step.
angr/engines/unicorn.py CHANGED
@@ -8,7 +8,7 @@ import claripy
8
8
 
9
9
  import angr
10
10
  from angr.errors import SimIRSBError, SimIRSBNoDecodeError, SimValueError
11
- from .engine import SuccessorsMixin
11
+ from .successors import SuccessorsEngine
12
12
  from .vex.heavy.heavy import VEXEarlyExit
13
13
  from angr import sim_options as o
14
14
  from angr.misc.ux import once
@@ -21,7 +21,7 @@ from angr.utils.constants import DEFAULT_STATEMENT
21
21
  l = logging.getLogger(name=__name__)
22
22
 
23
23
 
24
- class SimEngineUnicorn(SuccessorsMixin):
24
+ class SimEngineUnicorn(SuccessorsEngine):
25
25
  """
26
26
  Concrete execution in the Unicorn Engine, a fork of qemu.
27
27
 
@@ -281,7 +281,7 @@ class SimEngineUnicorn(SuccessorsMixin):
281
281
  def _get_vex_block_details(self, block_addr, block_size):
282
282
  # Mostly based on the lifting code in HeavyVEXMixin
283
283
  # pylint:disable=no-member
284
- irsb = super().lift_vex(addr=block_addr, state=self.state, size=block_size)
284
+ irsb = self.project.factory.block(addr=block_addr, backup_state=self.state, size=block_size).vex
285
285
  if irsb.size == 0:
286
286
  if irsb.jumpkind == "Ijk_NoDecode":
287
287
  if not self.state.project.is_hooked(irsb.addr):