angr 9.2.139__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 (68) hide show
  1. angr/__init__.py +1 -1
  2. angr/analyses/calling_convention/calling_convention.py +48 -21
  3. angr/analyses/cfg/cfg_base.py +13 -0
  4. angr/analyses/cfg/cfg_fast.py +11 -0
  5. angr/analyses/decompiler/ail_simplifier.py +67 -52
  6. angr/analyses/decompiler/clinic.py +68 -43
  7. angr/analyses/decompiler/decompiler.py +17 -7
  8. angr/analyses/decompiler/expression_narrower.py +1 -1
  9. angr/analyses/decompiler/optimization_passes/const_prop_reverter.py +8 -7
  10. angr/analyses/decompiler/optimization_passes/ite_region_converter.py +21 -13
  11. angr/analyses/decompiler/optimization_passes/optimization_pass.py +16 -10
  12. angr/analyses/decompiler/optimization_passes/return_duplicator_base.py +2 -2
  13. angr/analyses/decompiler/region_simplifiers/expr_folding.py +259 -108
  14. angr/analyses/decompiler/region_simplifiers/region_simplifier.py +27 -12
  15. angr/analyses/decompiler/structuring/dream.py +21 -17
  16. angr/analyses/decompiler/structuring/phoenix.py +152 -40
  17. angr/analyses/decompiler/structuring/recursive_structurer.py +1 -0
  18. angr/analyses/decompiler/structuring/structurer_base.py +36 -10
  19. angr/analyses/decompiler/structuring/structurer_nodes.py +4 -1
  20. angr/analyses/decompiler/utils.py +60 -1
  21. angr/analyses/deobfuscator/api_obf_finder.py +8 -5
  22. angr/analyses/deobfuscator/api_obf_type2_finder.py +18 -10
  23. angr/analyses/deobfuscator/string_obf_finder.py +105 -18
  24. angr/analyses/forward_analysis/forward_analysis.py +1 -1
  25. angr/analyses/propagator/top_checker_mixin.py +6 -6
  26. angr/analyses/reaching_definitions/__init__.py +2 -1
  27. angr/analyses/reaching_definitions/dep_graph.py +1 -12
  28. angr/analyses/reaching_definitions/engine_vex.py +36 -31
  29. angr/analyses/reaching_definitions/function_handler.py +15 -2
  30. angr/analyses/reaching_definitions/rd_state.py +1 -37
  31. angr/analyses/reaching_definitions/reaching_definitions.py +13 -24
  32. angr/analyses/s_propagator.py +6 -41
  33. angr/analyses/s_reaching_definitions/s_rda_model.py +7 -1
  34. angr/analyses/stack_pointer_tracker.py +36 -22
  35. angr/analyses/typehoon/simple_solver.py +45 -7
  36. angr/analyses/typehoon/typeconsts.py +18 -5
  37. angr/analyses/variable_recovery/engine_base.py +7 -5
  38. angr/block.py +69 -107
  39. angr/callable.py +14 -7
  40. angr/calling_conventions.py +15 -1
  41. angr/distributed/__init__.py +1 -1
  42. angr/engines/__init__.py +7 -8
  43. angr/engines/engine.py +1 -120
  44. angr/engines/failure.py +2 -2
  45. angr/engines/hook.py +2 -2
  46. angr/engines/light/engine.py +2 -2
  47. angr/engines/pcode/engine.py +2 -14
  48. angr/engines/procedure.py +2 -2
  49. angr/engines/soot/engine.py +2 -2
  50. angr/engines/soot/statements/switch.py +1 -1
  51. angr/engines/successors.py +124 -11
  52. angr/engines/syscall.py +2 -2
  53. angr/engines/unicorn.py +3 -3
  54. angr/engines/vex/heavy/heavy.py +3 -15
  55. angr/factory.py +4 -19
  56. angr/knowledge_plugins/key_definitions/atoms.py +8 -4
  57. angr/knowledge_plugins/key_definitions/live_definitions.py +41 -103
  58. angr/sim_type.py +19 -17
  59. angr/state_plugins/plugin.py +19 -4
  60. angr/storage/memory_mixins/memory_mixin.py +1 -1
  61. angr/storage/memory_mixins/paged_memory/pages/multi_values.py +10 -5
  62. angr/utils/ssa/__init__.py +119 -4
  63. {angr-9.2.139.dist-info → angr-9.2.140.dist-info}/METADATA +6 -6
  64. {angr-9.2.139.dist-info → angr-9.2.140.dist-info}/RECORD +68 -68
  65. {angr-9.2.139.dist-info → angr-9.2.140.dist-info}/LICENSE +0 -0
  66. {angr-9.2.139.dist-info → angr-9.2.140.dist-info}/WHEEL +0 -0
  67. {angr-9.2.139.dist-info → angr-9.2.140.dist-info}/entry_points.txt +0 -0
  68. {angr-9.2.139.dist-info → angr-9.2.140.dist-info}/top_level.txt +0 -0
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
@@ -687,7 +687,7 @@ class SimCC:
687
687
  ty = ty.with_arch(self.arch)
688
688
  if isinstance(ty, (SimStruct, SimUnion, SimTypeFixedSizeArray)):
689
689
  raise AngrTypeError(
690
- 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 "
691
691
  "implement its ABI logic"
692
692
  )
693
693
  if self.return_in_implicit_outparam(ty):
@@ -1334,6 +1334,20 @@ class SimCCMicrosoftAMD64(SimCC):
1334
1334
  def return_val(self, ty, perspective_returned=False):
1335
1335
  if ty._arch is None:
1336
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
+
1337
1351
  if not isinstance(ty, SimStruct):
1338
1352
  return super().return_val(ty, perspective_returned)
1339
1353
 
@@ -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):
@@ -3,7 +3,7 @@ import logging
3
3
  import claripy
4
4
  import pyvex
5
5
 
6
- from angr.engines.engine import SuccessorsMixin
6
+ from angr.engines.successors import SuccessorsEngine
7
7
  from angr.engines.vex.light import VEXMixin
8
8
  from angr.engines.vex.lifter import VEXLifter
9
9
  from angr.engines.vex.claripy.datalayer import ClaripyDataMixin, symbol
@@ -57,7 +57,7 @@ class SimStateStorageMixin(VEXMixin):
57
57
 
58
58
 
59
59
  # pylint:disable=arguments-differ
60
- class HeavyVEXMixin(SuccessorsMixin, ClaripyDataMixin, SimStateStorageMixin, VEXMixin, VEXLifter):
60
+ class HeavyVEXMixin(SuccessorsEngine, ClaripyDataMixin, SimStateStorageMixin, VEXMixin, VEXLifter):
61
61
  """
62
62
  Execution engine based on VEX, Valgrind's IR.
63
63
 
@@ -83,7 +83,6 @@ class HeavyVEXMixin(SuccessorsMixin, ClaripyDataMixin, SimStateStorageMixin, VEX
83
83
  self,
84
84
  successors,
85
85
  irsb=None,
86
- insn_text=None,
87
86
  insn_bytes=None,
88
87
  thumb=False,
89
88
  size=None,
@@ -99,21 +98,10 @@ class HeavyVEXMixin(SuccessorsMixin, ClaripyDataMixin, SimStateStorageMixin, VEX
99
98
  extra_stop_points=extra_stop_points,
100
99
  num_inst=num_inst,
101
100
  size=size,
102
- insn_text=insn_text,
103
101
  insn_bytes=insn_bytes,
104
102
  **kwargs,
105
103
  )
106
104
 
107
- if insn_text is not None:
108
- if insn_bytes is not None:
109
- raise errors.SimEngineError("You cannot provide both 'insn_bytes' and 'insn_text'!")
110
-
111
- insn_bytes = self.project.arch.asm(insn_text, addr=successors.addr, thumb=thumb)
112
- if insn_bytes is None:
113
- raise errors.AngrAssemblyError(
114
- "Assembling failed. Please make sure keystone is installed, and the assembly string is correct."
115
- )
116
-
117
105
  successors.sort = "IRSB"
118
106
  successors.description = "IRSB"
119
107
  self.state.history.recent_block_count = 1
@@ -137,9 +125,9 @@ class HeavyVEXMixin(SuccessorsMixin, ClaripyDataMixin, SimStateStorageMixin, VEX
137
125
 
138
126
  if irsb is None:
139
127
  irsb = self.lift_vex(
128
+ insn_bytes=insn_bytes,
140
129
  addr=addr,
141
130
  state=self.state,
142
- insn_bytes=insn_bytes,
143
131
  thumb=thumb,
144
132
  size=size,
145
133
  num_inst=num_inst,