angr 9.2.160__cp310-abi3-manylinux2014_x86_64.whl → 9.2.161__cp310-abi3-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 (31) hide show
  1. angr/__init__.py +4 -1
  2. angr/analyses/decompiler/ail_simplifier.py +81 -1
  3. angr/analyses/decompiler/block_simplifier.py +7 -5
  4. angr/analyses/decompiler/clinic.py +1 -0
  5. angr/analyses/decompiler/peephole_optimizations/__init__.py +4 -4
  6. angr/analyses/decompiler/peephole_optimizations/eager_eval.py +53 -0
  7. angr/analyses/decompiler/peephole_optimizations/modulo_simplifier.py +89 -0
  8. angr/analyses/decompiler/peephole_optimizations/{const_mull_a_shift.py → optimized_div_simplifier.py} +139 -25
  9. angr/analyses/decompiler/peephole_optimizations/remove_redundant_bitmasks.py +18 -9
  10. angr/analyses/decompiler/structuring/phoenix.py +19 -32
  11. angr/analyses/s_reaching_definitions/s_rda_model.py +1 -0
  12. angr/analyses/s_reaching_definitions/s_reaching_definitions.py +5 -2
  13. angr/analyses/variable_recovery/engine_base.py +9 -1
  14. angr/analyses/variable_recovery/variable_recovery_base.py +30 -2
  15. angr/analyses/variable_recovery/variable_recovery_fast.py +11 -2
  16. angr/emulator.py +143 -0
  17. angr/engines/concrete.py +66 -0
  18. angr/engines/icicle.py +66 -30
  19. angr/exploration_techniques/driller_core.py +2 -2
  20. angr/project.py +7 -0
  21. angr/rustylib.abi3.so +0 -0
  22. angr/sim_type.py +16 -8
  23. angr/utils/graph.py +20 -1
  24. angr/utils/ssa/__init__.py +3 -3
  25. {angr-9.2.160.dist-info → angr-9.2.161.dist-info}/METADATA +5 -5
  26. {angr-9.2.160.dist-info → angr-9.2.161.dist-info}/RECORD +30 -28
  27. angr/analyses/decompiler/peephole_optimizations/a_sub_a_div_const_mul_const.py +0 -57
  28. {angr-9.2.160.dist-info → angr-9.2.161.dist-info}/WHEEL +0 -0
  29. {angr-9.2.160.dist-info → angr-9.2.161.dist-info}/entry_points.txt +0 -0
  30. {angr-9.2.160.dist-info → angr-9.2.161.dist-info}/licenses/LICENSE +0 -0
  31. {angr-9.2.160.dist-info → angr-9.2.161.dist-info}/top_level.txt +0 -0
@@ -11,7 +11,7 @@ import networkx
11
11
  import claripy
12
12
  from angr.ailment.block import Block
13
13
  from angr.ailment.statement import Statement, ConditionalJump, Jump, Label, Return
14
- from angr.ailment.expression import Const, UnaryOp, MultiStatementExpression
14
+ from angr.ailment.expression import Const, UnaryOp, MultiStatementExpression, BinaryOp
15
15
 
16
16
  from angr.utils.graph import GraphUtils
17
17
  from angr.utils.ail import is_phi_assignment, is_head_controlled_loop_block
@@ -2174,20 +2174,17 @@ class PhoenixStructurer(StructurerBase):
2174
2174
  if r is not None:
2175
2175
  left, left_cond, right, left_right_cond, succ = r
2176
2176
  # create the condition node
2177
- memo = {}
2177
+ left_cond_expr = self.cond_proc.convert_claripy_bool_ast(left_cond)
2178
+ left_cond_expr_neg = UnaryOp(None, "Not", left_cond_expr, ins_addr=start_node.addr)
2179
+ left_right_cond_expr = self.cond_proc.convert_claripy_bool_ast(left_right_cond)
2178
2180
  if not self._is_single_statement_block(left):
2179
2181
  if not self._should_use_multistmtexprs(left):
2180
2182
  return False
2181
2183
  # create a MultiStatementExpression for left_right_cond
2182
2184
  stmts = self._build_multistatementexpr_statements(left)
2183
2185
  assert stmts is not None
2184
- mstmt_expr = MultiStatementExpression(
2185
- None, stmts, self.cond_proc.convert_claripy_bool_ast(left_right_cond), ins_addr=left.addr
2186
- )
2187
- memo[left_right_cond._hash] = mstmt_expr
2188
- cond = self.cond_proc.convert_claripy_bool_ast(
2189
- claripy.Or(claripy.Not(left_cond), left_right_cond), memo=memo
2190
- )
2186
+ left_right_cond_expr = MultiStatementExpression(None, stmts, left_right_cond_expr, ins_addr=left.addr)
2187
+ cond = BinaryOp(None, "LogicalOr", [left_cond_expr_neg, left_right_cond_expr], ins_addr=start_node.addr)
2191
2188
  cond_jump = ConditionalJump(
2192
2189
  None,
2193
2190
  cond,
@@ -2212,18 +2209,16 @@ class PhoenixStructurer(StructurerBase):
2212
2209
  if r is not None:
2213
2210
  left, left_cond, right, right_left_cond, else_node = r
2214
2211
  # create the condition node
2215
- memo = {}
2212
+ left_cond_expr = self.cond_proc.convert_claripy_bool_ast(left_cond)
2213
+ right_left_cond_expr = self.cond_proc.convert_claripy_bool_ast(right_left_cond)
2216
2214
  if not self._is_single_statement_block(right):
2217
2215
  if not self._should_use_multistmtexprs(right):
2218
2216
  return False
2219
2217
  # create a MultiStatementExpression for left_right_cond
2220
2218
  stmts = self._build_multistatementexpr_statements(right)
2221
2219
  assert stmts is not None
2222
- mstmt_expr = MultiStatementExpression(
2223
- None, stmts, self.cond_proc.convert_claripy_bool_ast(right_left_cond), ins_addr=left.addr
2224
- )
2225
- memo[right_left_cond._hash] = mstmt_expr
2226
- cond = self.cond_proc.convert_claripy_bool_ast(claripy.Or(left_cond, right_left_cond), memo=memo)
2220
+ right_left_cond_expr = MultiStatementExpression(None, stmts, right_left_cond_expr, ins_addr=left.addr)
2221
+ cond = BinaryOp(None, "LogicalOr", [left_cond_expr, right_left_cond_expr], ins_addr=start_node.addr)
2227
2222
  cond_jump = ConditionalJump(
2228
2223
  None,
2229
2224
  cond,
@@ -2248,20 +2243,17 @@ class PhoenixStructurer(StructurerBase):
2248
2243
  if r is not None:
2249
2244
  left, left_cond, succ, left_succ_cond, right = r
2250
2245
  # create the condition node
2251
- memo = {}
2246
+ left_cond_expr = self.cond_proc.convert_claripy_bool_ast(left_cond)
2247
+ left_succ_cond_expr = self.cond_proc.convert_claripy_bool_ast(left_succ_cond)
2252
2248
  if not self._is_single_statement_block(left):
2253
2249
  if not self._should_use_multistmtexprs(left):
2254
2250
  return False
2255
2251
  # create a MultiStatementExpression for left_right_cond
2256
2252
  stmts = self._build_multistatementexpr_statements(left)
2257
2253
  assert stmts is not None
2258
- mstmt_expr = MultiStatementExpression(
2259
- None, stmts, self.cond_proc.convert_claripy_bool_ast(left_succ_cond), ins_addr=left.addr
2260
- )
2261
- memo[left_succ_cond._hash] = mstmt_expr
2262
- cond = self.cond_proc.convert_claripy_bool_ast(
2263
- claripy.And(left_cond, claripy.Not(left_succ_cond)), memo=memo
2264
- )
2254
+ left_succ_cond_expr = MultiStatementExpression(None, stmts, left_succ_cond_expr, ins_addr=left.addr)
2255
+ left_succ_cond_expr_neg = UnaryOp(None, "Not", left_succ_cond_expr, ins_addr=start_node.addr)
2256
+ cond = BinaryOp(None, "LogicalAnd", [left_cond_expr, left_succ_cond_expr_neg], ins_addr=start_node.addr)
2265
2257
  cond_jump = ConditionalJump(
2266
2258
  None,
2267
2259
  cond,
@@ -2285,21 +2277,16 @@ class PhoenixStructurer(StructurerBase):
2285
2277
  if r is not None:
2286
2278
  left, left_cond, right, right_left_cond, else_node = r
2287
2279
  # create the condition node
2288
- memo = {}
2280
+ left_cond_expr = self.cond_proc.convert_claripy_bool_ast(left_cond)
2281
+ left_right_cond_expr = self.cond_proc.convert_claripy_bool_ast(right_left_cond)
2289
2282
  if not self._is_single_statement_block(left):
2290
2283
  if not self._should_use_multistmtexprs(left):
2291
2284
  return False
2292
2285
  # create a MultiStatementExpression for left_right_cond
2293
2286
  stmts = self._build_multistatementexpr_statements(left)
2294
2287
  assert stmts is not None
2295
- mstmt_expr = MultiStatementExpression(
2296
- None, stmts, self.cond_proc.convert_claripy_bool_ast(right_left_cond), ins_addr=left.addr
2297
- )
2298
- memo[right_left_cond._hash] = mstmt_expr
2299
- cond = self.cond_proc.convert_claripy_bool_ast(
2300
- claripy.And(left_cond, right_left_cond),
2301
- memo=memo,
2302
- )
2288
+ left_right_cond_expr = MultiStatementExpression(None, stmts, left_right_cond_expr, ins_addr=left.addr)
2289
+ cond = BinaryOp(None, "LogicalAnd", [left_cond_expr, left_right_cond_expr], ins_addr=start_node.addr)
2303
2290
  cond_jump = ConditionalJump(
2304
2291
  None,
2305
2292
  cond,
@@ -25,6 +25,7 @@ class SRDAModel:
25
25
  self.all_tmp_definitions: dict[CodeLocation, dict[atoms.Tmp, int]] = defaultdict(dict)
26
26
  self.all_tmp_uses: dict[CodeLocation, dict[atoms.Tmp, set[tuple[Tmp, int]]]] = defaultdict(dict)
27
27
  self.phi_vvar_ids: set[int] = set()
28
+ self.phivarid_to_varids_with_unknown: dict[int, set[int | None]] = {}
28
29
  self.phivarid_to_varids: dict[int, set[int]] = {}
29
30
  self.vvar_uses_by_loc: dict[CodeLocation, list[int]] = {}
30
31
 
@@ -63,7 +63,7 @@ class SReachingDefinitionsAnalysis(Analysis):
63
63
  case _:
64
64
  raise NotImplementedError
65
65
 
66
- phi_vvars: dict[int, set[int]] = {}
66
+ phi_vvars: dict[int, set[int | None]] = {}
67
67
  # find all vvar definitions
68
68
  vvar_deflocs = get_vvar_deflocs(blocks.values(), phi_vvars=phi_vvars)
69
69
  # find all explicit vvar uses
@@ -87,7 +87,10 @@ class SReachingDefinitionsAnalysis(Analysis):
87
87
  self.model.phi_vvar_ids = set(phi_vvars)
88
88
  self.model.phivarid_to_varids = {}
89
89
  for vvar_id, src_vvars in phi_vvars.items():
90
- self.model.phivarid_to_varids[vvar_id] = src_vvars
90
+ self.model.phivarid_to_varids_with_unknown[vvar_id] = src_vvars
91
+ self.model.phivarid_to_varids[vvar_id] = ( # type: ignore
92
+ {vvar_id for vvar_id in src_vvars if vvar_id is not None} if None in src_vvars else src_vvars
93
+ )
91
94
 
92
95
  if self.mode == "function":
93
96
 
@@ -525,6 +525,10 @@ class SimEngineVRBase(
525
525
  def _store_to_stack(
526
526
  self, stack_offset, data: RichR[claripy.ast.BV | claripy.ast.FP], size, offset=0, atom=None, endness=None
527
527
  ):
528
+ """
529
+ Store data to a stack location. We limit the size of the data to store to 256 bytes for performance reasons.
530
+ """
531
+
528
532
  if atom is None:
529
533
  existing_vars = self.state.variable_manager[self.func_addr].find_variables_by_stmt(
530
534
  self.block.addr, self.stmt_idx, "memory"
@@ -550,7 +554,11 @@ class SimEngineVRBase(
550
554
  variable, variable_offset = next(iter(existing_vars))
551
555
 
552
556
  if isinstance(stack_offset, int):
553
- expr = self.state.annotate_with_variables(data.data, [(variable_offset, variable)])
557
+ expr = data.data
558
+ if isinstance(expr, claripy.ast.Bits) and expr.size() > 1024:
559
+ # we don't write more than 256 bytes to the stack at a time for performance reasons
560
+ expr = expr[expr.size() - 1 : expr.size() - 1024]
561
+ expr = self.state.annotate_with_variables(expr, [(variable_offset, variable)])
554
562
  stack_addr = self.state.stack_addr_from_offset(stack_offset)
555
563
  self.state.stack_region.store(stack_addr, expr, endness=endness)
556
564
 
@@ -5,6 +5,8 @@ from collections.abc import Generator, Iterable
5
5
  import logging
6
6
  from collections import defaultdict
7
7
 
8
+ import networkx
9
+
8
10
  import archinfo
9
11
  import claripy
10
12
  from claripy.annotation import Annotation
@@ -86,8 +88,18 @@ class VariableRecoveryBase(Analysis):
86
88
  The base class for VariableRecovery and VariableRecoveryFast.
87
89
  """
88
90
 
89
- def __init__(self, func, max_iterations, store_live_variables: bool, vvar_to_vvar: dict[int, int] | None = None):
91
+ def __init__(
92
+ self,
93
+ func,
94
+ max_iterations,
95
+ store_live_variables: bool,
96
+ vvar_to_vvar: dict[int, int] | None = None,
97
+ func_graph: networkx.DiGraph | None = None,
98
+ entry_node_addr: int | tuple[int, int | None] | None = None,
99
+ ):
90
100
  self.function = func
101
+ self.func_graph = func_graph
102
+ self.entry_node_addr = entry_node_addr
91
103
  self.variable_manager = self.kb.variables
92
104
 
93
105
  self._max_iterations = max_iterations
@@ -120,7 +132,23 @@ class VariableRecoveryBase(Analysis):
120
132
 
121
133
  def initialize_dominance_frontiers(self):
122
134
  # Computer the dominance frontier for each node in the graph
123
- df = self.project.analyses.DominanceFrontier(self.function)
135
+ func_entry = None
136
+ if self.func_graph is not None:
137
+ entry_node_addr = self.entry_node_addr if self.entry_node_addr is not None else self.function.addr
138
+ assert entry_node_addr is not None
139
+ if isinstance(entry_node_addr, int):
140
+ func_entry = next(iter(node for node in self.func_graph if node.addr == entry_node_addr))
141
+ elif isinstance(entry_node_addr, tuple):
142
+ func_entry = next(
143
+ iter(
144
+ node
145
+ for node in self.func_graph
146
+ if node.addr == entry_node_addr[0] and node.idx == entry_node_addr[1]
147
+ )
148
+ )
149
+ else:
150
+ raise TypeError(f"Unsupported entry node address type: {type(entry_node_addr)}")
151
+ df = self.project.analyses.DominanceFrontier(self.function, func_graph=self.func_graph, entry=func_entry)
124
152
  self._dominance_frontiers = defaultdict(set)
125
153
  for b0, domfront in df.frontiers.items():
126
154
  for d in domfront:
@@ -237,6 +237,7 @@ class VariableRecoveryFast(ForwardAnalysis, VariableRecoveryBase): # pylint:dis
237
237
  self,
238
238
  func: Function | str | int,
239
239
  func_graph: networkx.DiGraph | None = None,
240
+ entry_node_addr: int | tuple[int, int | None] | None = None,
240
241
  max_iterations: int = 2,
241
242
  low_priority=False,
242
243
  track_sp=True,
@@ -259,10 +260,18 @@ class VariableRecoveryFast(ForwardAnalysis, VariableRecoveryBase): # pylint:dis
259
260
  function_graph_visitor = visitors.FunctionGraphVisitor(func, graph=func_graph)
260
261
 
261
262
  # Make sure the function is not empty
262
- if not func.block_addrs_set or func.startpoint is None:
263
+ if (not func.block_addrs_set or func.startpoint is None) and not func_graph:
263
264
  raise AngrVariableRecoveryError(f"Function {func!r} is empty.")
264
265
 
265
- VariableRecoveryBase.__init__(self, func, max_iterations, store_live_variables, vvar_to_vvar=vvar_to_vvar)
266
+ VariableRecoveryBase.__init__(
267
+ self,
268
+ func,
269
+ max_iterations,
270
+ store_live_variables,
271
+ vvar_to_vvar=vvar_to_vvar,
272
+ func_graph=func_graph_with_calls,
273
+ entry_node_addr=entry_node_addr,
274
+ )
266
275
  ForwardAnalysis.__init__(
267
276
  self, order_jobs=True, allow_merging=True, allow_widening=False, graph_visitor=function_graph_visitor
268
277
  )
angr/emulator.py ADDED
@@ -0,0 +1,143 @@
1
+ from __future__ import annotations
2
+
3
+ import logging
4
+ from enum import Enum
5
+
6
+ from angr.engines.concrete import ConcreteEngine, HeavyConcreteState
7
+ from angr.errors import AngrError
8
+
9
+
10
+ log = logging.getLogger(name=__name__)
11
+
12
+
13
+ class EmulatorException(AngrError):
14
+ """Base class for exceptions raised by the Emulator."""
15
+
16
+
17
+ class EngineException(EmulatorException):
18
+ """Exception raised when the emulator encounters an unhandlable error in the engine."""
19
+
20
+
21
+ class StateDivergedException(EmulatorException):
22
+ """Exception raised when an engine returns multiple successors."""
23
+
24
+
25
+ class EmulatorStopReason(Enum):
26
+ """
27
+ Enum representing the reason for stopping the emulator.
28
+ """
29
+
30
+ INSTRUCTION_LIMIT = "instruction_limit"
31
+ BREAKPOINT = "breakpoint"
32
+ NO_SUCCESSORS = "no_successors"
33
+ MEMORY_ERROR = "memory_error"
34
+ FAILURE = "failure"
35
+ EXIT = "exit"
36
+
37
+
38
+ class Emulator:
39
+ """
40
+ Emulator is a utility that adapts an angr `ConcreteEngine` to a more
41
+ user-friendly interface for concrete execution. It only supports concrete
42
+ execution and requires a ConcreteEngine.
43
+
44
+ Saftey: This class is not thread-safe. It should only be used in a
45
+ single-threaded context. It can be safely shared between multiple threads,
46
+ provided that only one thread is using it at a time.
47
+ """
48
+
49
+ _engine: ConcreteEngine
50
+ _state: HeavyConcreteState
51
+
52
+ def __init__(self, engine: ConcreteEngine, init_state: HeavyConcreteState):
53
+ """
54
+ :param engine: The `ConcreteEngine` to use for emulation.
55
+ :param init_state: The initial state to use for emulation.
56
+ """
57
+ self._engine = engine
58
+ self._state = init_state
59
+
60
+ @property
61
+ def state(self) -> HeavyConcreteState:
62
+ """
63
+ The current state of the emulator.
64
+ """
65
+ return self._state
66
+
67
+ @property
68
+ def breakpoints(self) -> set[int]:
69
+ """
70
+ The set of currently set breakpoints.
71
+ """
72
+ return self._engine.get_breakpoints()
73
+
74
+ def add_breakpoint(self, addr: int) -> None:
75
+ """
76
+ Add a breakpoint at the given address.
77
+
78
+ :param addr: The address to set the breakpoint at.
79
+ """
80
+ self._engine.add_breakpoint(addr)
81
+
82
+ def remove_breakpoint(self, addr: int) -> None:
83
+ """
84
+ Remove a breakpoint at the given address, if present.
85
+
86
+ :param addr: The address to remove the breakpoint from.
87
+ """
88
+ self._engine.remove_breakpoint(addr)
89
+
90
+ def run(self, num_inst: int | None = None) -> EmulatorStopReason:
91
+ """
92
+ Execute the emulator.
93
+ """
94
+ completed_engine_execs = 0
95
+ num_inst_executed: int = 0
96
+ while self._state.history.jumpkind != "Ijk_Exit":
97
+ # Check if there is a breakpoint at the current address
98
+ if completed_engine_execs > 0 and self._state.addr in self._engine.get_breakpoints():
99
+ return EmulatorStopReason.BREAKPOINT
100
+
101
+ # Check if we've already executed the requested number of instructions
102
+ if num_inst is not None and num_inst_executed >= num_inst:
103
+ return EmulatorStopReason.INSTRUCTION_LIMIT
104
+
105
+ # Calculate remaining instructions for this engine execution
106
+ remaining_inst: int | None = None
107
+ if num_inst is not None:
108
+ remaining_inst = num_inst - num_inst_executed
109
+
110
+ # Run the engine to get successors
111
+ try:
112
+ successors = self._engine.process(self._state, num_inst=remaining_inst)
113
+ except EngineException as e:
114
+ raise EngineException(f"Engine encountered an error: {e}") from e
115
+
116
+ # Handle cases with an unexpected number of successors
117
+ if len(successors.successors) == 0:
118
+ return EmulatorStopReason.NO_SUCCESSORS
119
+ if len(successors.successors) > 1:
120
+ log.warning("Concrete engine returned multiple successors")
121
+
122
+ # Set the state before raising further exceptions
123
+ self._state = successors.successors[0]
124
+
125
+ # Track the number of instructions executed using the state's history
126
+ if self._state.history.recent_instruction_count > 0:
127
+ num_inst_executed += self._state.history.recent_instruction_count
128
+
129
+ if successors.successors[0].history.jumpkind == "Ijk_SigSEGV":
130
+ return EmulatorStopReason.MEMORY_ERROR
131
+
132
+ completed_engine_execs += 1
133
+
134
+ return EmulatorStopReason.EXIT
135
+
136
+
137
+ __all__ = (
138
+ "Emulator",
139
+ "EmulatorException",
140
+ "EmulatorStopReason",
141
+ "EngineException",
142
+ "StateDivergedException",
143
+ )
@@ -0,0 +1,66 @@
1
+ from __future__ import annotations
2
+
3
+ import logging
4
+ import typing
5
+ from abc import abstractmethod, ABCMeta
6
+ from typing_extensions import override
7
+
8
+ import claripy
9
+ from angr.engines.successors import SimSuccessors, SuccessorsEngine
10
+ from angr.sim_state import SimState
11
+
12
+ log = logging.getLogger(__name__)
13
+
14
+
15
+ HeavyConcreteState = SimState[int, int]
16
+
17
+
18
+ class ConcreteEngine(SuccessorsEngine, metaclass=ABCMeta):
19
+ """
20
+ ConcreteEngine extends SuccessorsEngine and adds APIs for managing breakpoints.
21
+ """
22
+
23
+ @abstractmethod
24
+ def get_breakpoints(self) -> set[int]:
25
+ """Return the set of currently set breakpoints."""
26
+
27
+ @abstractmethod
28
+ def add_breakpoint(self, addr: int) -> None:
29
+ """Add a breakpoint at the given address."""
30
+
31
+ @abstractmethod
32
+ def remove_breakpoint(self, addr: int) -> None:
33
+ """Remove a breakpoint at the given address, if present."""
34
+
35
+ @abstractmethod
36
+ def process_concrete(self, state: HeavyConcreteState, num_inst: int | None = None) -> HeavyConcreteState:
37
+ """
38
+ Process the concrete state and return a HeavyState object.
39
+
40
+ :param state: The concrete state to process.
41
+ :return: A HeavyState object representing the processed state.
42
+ """
43
+
44
+ @override
45
+ def process_successors(
46
+ self, successors: SimSuccessors, *, num_inst: int | None = None, **kwargs: dict[str, typing.Any]
47
+ ):
48
+ if len(kwargs) > 0:
49
+ log.warning("ConcreteEngine.process_successors received unknown kwargs: %s", kwargs)
50
+
51
+ # TODO: Properly error here when the state is not a HeavyConcreteState
52
+ # Alternatively, we could make SimSuccessors generic over the state type too
53
+ concrete_state = typing.cast(HeavyConcreteState, self.state)
54
+
55
+ concrete_successor = self.process_concrete(concrete_state, num_inst=num_inst)
56
+ successors.add_successor(
57
+ concrete_successor,
58
+ concrete_successor.ip,
59
+ claripy.true(),
60
+ concrete_successor.history.jumpkind,
61
+ add_guard=False,
62
+ )
63
+ successors.processed = True
64
+
65
+
66
+ __all__ = ["ConcreteEngine", "HeavyConcreteState"]
angr/engines/icicle.py CHANGED
@@ -5,17 +5,16 @@ from __future__ import annotations
5
5
  import logging
6
6
  import os
7
7
  from dataclasses import dataclass
8
+ from typing_extensions import override
8
9
 
9
- import claripy
10
10
  import pypcode
11
11
  from archinfo import Arch, Endness
12
12
 
13
+ from angr.engines.concrete import ConcreteEngine, HeavyConcreteState
13
14
  from angr.engines.failure import SimEngineFailure
14
15
  from angr.engines.hook import HooksMixin
15
- from angr.engines.successors import SuccessorsEngine
16
16
  from angr.engines.syscall import SimEngineSyscall
17
17
  from angr.rustylib.icicle import Icicle, VmExit, ExceptionCode
18
- from angr.sim_state import SimState
19
18
 
20
19
  log = logging.getLogger(__name__)
21
20
 
@@ -30,12 +29,13 @@ class IcicleStateTranslationData:
30
29
  to an angr state.
31
30
  """
32
31
 
33
- base_state: SimState
32
+ base_state: HeavyConcreteState
34
33
  registers: set[str]
35
34
  writable_pages: set[int]
35
+ initial_cpu_icount: int
36
36
 
37
37
 
38
- class IcicleEngine(SuccessorsEngine):
38
+ class IcicleEngine(ConcreteEngine):
39
39
  """
40
40
  An angr engine that uses Icicle to execute concrete states. The purpose of
41
41
  this implementation is to provide a high-performance concrete execution
@@ -55,6 +55,16 @@ class IcicleEngine(SuccessorsEngine):
55
55
  intends to provide a more complete set of features, such as hooks and syscalls.
56
56
  """
57
57
 
58
+ breakpoints: set[int]
59
+
60
+ def __init__(self, *args, **kwargs):
61
+ """
62
+ Initialize the IcicleEngine. This sets up the breakpoints set and
63
+ initializes the parent class.
64
+ """
65
+ super().__init__(*args, **kwargs)
66
+ self.breakpoints = set()
67
+
58
68
  @staticmethod
59
69
  def __make_icicle_arch(arch: Arch) -> str | None:
60
70
  """
@@ -81,7 +91,7 @@ class IcicleEngine(SuccessorsEngine):
81
91
  return IcicleEngine.__is_arm(icicle_arch) and addr & 1 == 1
82
92
 
83
93
  @staticmethod
84
- def __get_pages(state: SimState) -> set[int]:
94
+ def __get_pages(state: HeavyConcreteState) -> set[int]:
85
95
  """
86
96
  Unfortunately, the memory model doesn't have a way to get all pages.
87
97
  Instead, we can get all of the backers from the loader, then all of the
@@ -104,7 +114,7 @@ class IcicleEngine(SuccessorsEngine):
104
114
  return pages
105
115
 
106
116
  @staticmethod
107
- def __convert_angr_state_to_icicle(state: SimState) -> tuple[Icicle, IcicleStateTranslationData]:
117
+ def __convert_angr_state_to_icicle(state: HeavyConcreteState) -> tuple[Icicle, IcicleStateTranslationData]:
108
118
  icicle_arch = IcicleEngine.__make_icicle_arch(state.arch)
109
119
  if icicle_arch is None:
110
120
  raise ValueError("Unsupported architecture")
@@ -161,12 +171,15 @@ class IcicleEngine(SuccessorsEngine):
161
171
  base_state=state,
162
172
  registers=copied_registers,
163
173
  writable_pages=writable_pages,
174
+ initial_cpu_icount=emu.cpu_icount,
164
175
  )
165
176
 
166
177
  return (emu, translation_data)
167
178
 
168
179
  @staticmethod
169
- def __convert_icicle_state_to_angr(emu: Icicle, translation_data: IcicleStateTranslationData) -> SimState:
180
+ def __convert_icicle_state_to_angr(
181
+ emu: Icicle, translation_data: IcicleStateTranslationData, status: VmExit
182
+ ) -> HeavyConcreteState:
170
183
  state = translation_data.base_state.copy()
171
184
 
172
185
  # 1. Copy the register values
@@ -181,20 +194,8 @@ class IcicleEngine(SuccessorsEngine):
181
194
  addr = page_num * state.memory.page_size
182
195
  state.memory.store(addr, emu.mem_read(addr, state.memory.page_size))
183
196
 
184
- return state
185
-
186
- def process_successors(self, successors, *, num_inst=0, **kwargs):
187
- if len(kwargs) > 0:
188
- log.warning("IcicleEngine.process_successors received unknown kwargs: %s", kwargs)
189
-
190
- emu, translation_data = self.__convert_angr_state_to_icicle(self.state)
191
-
192
- if num_inst > 0:
193
- emu.icount_limit = num_inst
194
-
195
- status = emu.run() # pylint: ignore=assignment-from-no-return (pylint bug)
197
+ # 3. Set history.jumpkind
196
198
  exc = emu.exception_code
197
-
198
199
  if status == VmExit.UnhandledException:
199
200
  if exc in (
200
201
  ExceptionCode.ReadUnmapped,
@@ -203,22 +204,57 @@ class IcicleEngine(SuccessorsEngine):
203
204
  ExceptionCode.WritePerm,
204
205
  ExceptionCode.ExecViolation,
205
206
  ):
206
- jumpkind = "Ijk_SigSEGV"
207
+ state.history.jumpkind = "Ijk_SigSEGV"
207
208
  elif exc == ExceptionCode.Syscall:
208
- jumpkind = "Ijk_Syscall"
209
+ state.history.jumpkind = "Ijk_Syscall"
209
210
  elif exc == ExceptionCode.Halt:
210
- jumpkind = "Ijk_Exit"
211
+ state.history.jumpkind = "Ijk_Exit"
211
212
  elif exc == ExceptionCode.InvalidInstruction:
212
- jumpkind = "Ijk_NoDecode"
213
+ state.history.jumpkind = "Ijk_NoDecode"
213
214
  else:
214
- jumpkind = "Ijk_EmFail"
215
+ state.history.jumpkind = "Ijk_EmFail"
215
216
  else:
216
- jumpkind = "Ijk_Boring"
217
+ state.history.jumpkind = "Ijk_Boring"
218
+
219
+ # 4. Set history.recent_instruction_count
220
+ state.history.recent_instruction_count = emu.cpu_icount - translation_data.initial_cpu_icount
221
+
222
+ return state
223
+
224
+ @override
225
+ def get_breakpoints(self) -> set[int]:
226
+ """Return the set of currently set breakpoints."""
227
+ return self.breakpoints
228
+
229
+ @override
230
+ def add_breakpoint(self, addr: int) -> None:
231
+ """Add a breakpoint at the given address."""
232
+ self.breakpoints.add(addr)
233
+
234
+ @override
235
+ def remove_breakpoint(self, addr: int) -> None:
236
+ """Remove a breakpoint at the given address, if present."""
237
+ self.breakpoints.discard(addr)
238
+
239
+ @override
240
+ def process_concrete(self, state: HeavyConcreteState, num_inst: int | None = None) -> HeavyConcreteState:
241
+ emu, translation_data = self.__convert_angr_state_to_icicle(state)
242
+
243
+ # Set breakpoints, skip the current PC. This assumes that if running
244
+ # with a breakpoint at the current PC, then the user has already done
245
+ # the necessary handling and is resuming execution.
246
+ for addr in self.breakpoints:
247
+ if emu.pc != addr:
248
+ emu.add_breakpoint(addr)
249
+
250
+ # Set the instruction count limit
251
+ if num_inst is not None and num_inst > 0:
252
+ emu.icount_limit = num_inst
217
253
 
218
- successor_state = IcicleEngine.__convert_icicle_state_to_angr(emu, translation_data)
219
- successors.add_successor(successor_state, successor_state.ip, claripy.true(), jumpkind, add_guard=False)
254
+ # Run it
255
+ status = emu.run()
220
256
 
221
- successors.processed = True
257
+ return IcicleEngine.__convert_icicle_state_to_angr(emu, translation_data, status)
222
258
 
223
259
 
224
260
  class UberIcicleEngine(SimEngineFailure, SimEngineSyscall, HooksMixin, IcicleEngine):
@@ -94,7 +94,7 @@ class DrillerCore(ExplorationTechnique):
94
94
  @staticmethod
95
95
  def _has_false(state):
96
96
  # Check if the state is unsat even if we remove preconstraints.
97
- if state.scratch.guard.identical(claripy.false()):
97
+ if claripy.is_false(state.scratch.guard):
98
98
  return True
99
99
 
100
- return any(c.identical(claripy.false()) for c in state.solver.constraints)
100
+ return any(claripy.is_false(c) for c in state.solver.constraints)
angr/project.py CHANGED
@@ -297,11 +297,18 @@ class Project:
297
297
 
298
298
  # Step 1: get the set of libraries we are allowed to use to resolve unresolved symbols
299
299
  missing_libs = []
300
+ missing_wincore_dlls = False
300
301
  for lib_name in self.loader.missing_dependencies:
301
302
  try:
302
303
  missing_libs.extend(SIM_LIBRARIES[lib_name])
303
304
  except KeyError:
304
305
  l.info("There are no simprocedures for missing library %s :(", lib_name)
306
+ if lib_name.startswith("api-ms-win-"):
307
+ missing_wincore_dlls = True
308
+ if missing_wincore_dlls and "kernel32.dll" not in self.loader.missing_dependencies:
309
+ # some of the missing api-ms-win-*.dll libraries are actually provided by kernel32.dll
310
+ missing_libs.extend(SIM_LIBRARIES["kernel32.dll"])
311
+
305
312
  # additionally provide libraries we _have_ loaded as a fallback fallback
306
313
  # this helps in the case that e.g. CLE picked up a linux arm libc to satisfy an android arm binary
307
314
  for lib in self.loader.all_objects:
angr/rustylib.abi3.so CHANGED
Binary file