angr 9.2.142__py3-none-manylinux2014_x86_64.whl → 9.2.144__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 (61) hide show
  1. angr/__init__.py +1 -1
  2. angr/analyses/calling_convention/calling_convention.py +22 -10
  3. angr/analyses/calling_convention/fact_collector.py +72 -14
  4. angr/analyses/cfg/cfg_base.py +7 -2
  5. angr/analyses/cfg/cfg_emulated.py +13 -4
  6. angr/analyses/cfg/cfg_fast.py +21 -60
  7. angr/analyses/cfg/indirect_jump_resolvers/__init__.py +2 -0
  8. angr/analyses/cfg/indirect_jump_resolvers/const_resolver.py +12 -1
  9. angr/analyses/cfg/indirect_jump_resolvers/constant_value_manager.py +107 -0
  10. angr/analyses/cfg/indirect_jump_resolvers/default_resolvers.py +2 -1
  11. angr/analyses/cfg/indirect_jump_resolvers/jumptable.py +6 -102
  12. angr/analyses/cfg/indirect_jump_resolvers/syscall_resolver.py +92 -0
  13. angr/analyses/complete_calling_conventions.py +18 -5
  14. angr/analyses/decompiler/ail_simplifier.py +95 -65
  15. angr/analyses/decompiler/clinic.py +162 -68
  16. angr/analyses/decompiler/decompiler.py +4 -4
  17. angr/analyses/decompiler/optimization_passes/base_ptr_save_simplifier.py +1 -1
  18. angr/analyses/decompiler/optimization_passes/condition_constprop.py +49 -14
  19. angr/analyses/decompiler/optimization_passes/ite_region_converter.py +8 -0
  20. angr/analyses/decompiler/optimization_passes/optimization_pass.py +5 -5
  21. angr/analyses/decompiler/optimization_passes/return_duplicator_base.py +5 -0
  22. angr/analyses/decompiler/peephole_optimizations/__init__.py +2 -0
  23. angr/analyses/decompiler/peephole_optimizations/a_sub_a_shr_const_shr_const.py +37 -0
  24. angr/analyses/decompiler/peephole_optimizations/simplify_pc_relative_loads.py +15 -1
  25. angr/analyses/decompiler/sequence_walker.py +8 -0
  26. angr/analyses/decompiler/ssailification/rewriting_engine.py +2 -0
  27. angr/analyses/decompiler/ssailification/ssailification.py +10 -2
  28. angr/analyses/decompiler/ssailification/traversal_engine.py +17 -2
  29. angr/analyses/decompiler/structured_codegen/c.py +25 -4
  30. angr/analyses/decompiler/utils.py +13 -0
  31. angr/analyses/disassembly.py +3 -3
  32. angr/analyses/fcp/fcp.py +1 -4
  33. angr/analyses/s_propagator.py +40 -29
  34. angr/analyses/s_reaching_definitions/s_rda_model.py +45 -36
  35. angr/analyses/s_reaching_definitions/s_rda_view.py +6 -3
  36. angr/analyses/s_reaching_definitions/s_reaching_definitions.py +41 -42
  37. angr/analyses/typehoon/dfa.py +13 -3
  38. angr/analyses/typehoon/typehoon.py +60 -18
  39. angr/analyses/typehoon/typevars.py +11 -7
  40. angr/analyses/variable_recovery/engine_ail.py +19 -23
  41. angr/analyses/variable_recovery/engine_base.py +26 -30
  42. angr/analyses/variable_recovery/variable_recovery_fast.py +17 -21
  43. angr/calling_conventions.py +18 -8
  44. angr/knowledge_plugins/functions/function.py +29 -15
  45. angr/knowledge_plugins/key_definitions/constants.py +2 -2
  46. angr/knowledge_plugins/key_definitions/liveness.py +4 -4
  47. angr/lib/angr_native.so +0 -0
  48. angr/procedures/definitions/linux_kernel.py +5 -0
  49. angr/state_plugins/unicorn_engine.py +24 -8
  50. angr/storage/memory_mixins/paged_memory/page_backer_mixins.py +1 -2
  51. angr/storage/memory_mixins/paged_memory/pages/mv_list_page.py +2 -2
  52. angr/utils/doms.py +40 -33
  53. angr/utils/graph.py +26 -20
  54. angr/utils/ssa/__init__.py +21 -14
  55. angr/utils/ssa/vvar_uses_collector.py +2 -2
  56. {angr-9.2.142.dist-info → angr-9.2.144.dist-info}/METADATA +11 -8
  57. {angr-9.2.142.dist-info → angr-9.2.144.dist-info}/RECORD +61 -58
  58. {angr-9.2.142.dist-info → angr-9.2.144.dist-info}/WHEEL +1 -1
  59. {angr-9.2.142.dist-info → angr-9.2.144.dist-info}/LICENSE +0 -0
  60. {angr-9.2.142.dist-info → angr-9.2.144.dist-info}/entry_points.txt +0 -0
  61. {angr-9.2.142.dist-info → angr-9.2.144.dist-info}/top_level.txt +0 -0
@@ -1,11 +1,12 @@
1
1
  # pylint:disable=wrong-import-position,wrong-import-order
2
2
  from __future__ import annotations
3
3
  import enum
4
- from typing import TYPE_CHECKING, Any, Literal, cast
4
+ from typing import TYPE_CHECKING, Literal, cast
5
5
  from collections.abc import Sequence
6
6
  from collections import defaultdict, OrderedDict
7
7
  import logging
8
8
  import functools
9
+ import contextlib
9
10
 
10
11
  import pyvex
11
12
  import claripy
@@ -15,7 +16,6 @@ from claripy.annotation import UninitializedAnnotation
15
16
  from angr import sim_options as o
16
17
  from angr import BP, BP_BEFORE, BP_AFTER
17
18
  from angr.misc.ux import once
18
- from angr.code_location import CodeLocation
19
19
  from angr.concretization_strategies import SimConcretizationStrategyAny
20
20
  from angr.knowledge_plugins.cfg import IndirectJump, IndirectJumpType
21
21
  from angr.engines.vex.claripy import ccall
@@ -26,13 +26,11 @@ from angr.annocfg import AnnotatedCFG
26
26
  from angr.exploration_techniques.slicecutor import Slicecutor
27
27
  from angr.exploration_techniques.local_loop_seer import LocalLoopSeer
28
28
  from angr.exploration_techniques.explorer import Explorer
29
- from angr.project import Project
30
29
  from angr.utils.constants import DEFAULT_STATEMENT
31
- from angr.analyses.propagator.vex_vars import VEXReg
32
30
  from angr.analyses.propagator.top_checker_mixin import ClaripyDataVEXEngineMixin
33
31
  from angr.engines.vex.claripy.datalayer import value
34
32
  from .resolver import IndirectJumpResolver
35
- from .propagator_utils import PropagatorLoadCallback
33
+ from .constant_value_manager import ConstantValueManager
36
34
 
37
35
  try:
38
36
  from angr.engines import pcode
@@ -40,7 +38,6 @@ except ImportError:
40
38
  pcode = None
41
39
 
42
40
  if TYPE_CHECKING:
43
- from angr import SimState
44
41
  from angr.knowledge_plugins import Function
45
42
 
46
43
  l = logging.getLogger(name=__name__)
@@ -133,101 +130,6 @@ class JumpTargetBaseAddr:
133
130
  return self.base_addr is not None
134
131
 
135
132
 
136
- #
137
- # Constant register resolving support
138
- #
139
-
140
-
141
- class ConstantValueManager:
142
- """
143
- Manages the loading of registers who hold constant values.
144
- """
145
-
146
- __slots__ = (
147
- "func",
148
- "indirect_jump_addr",
149
- "kb",
150
- "mapping",
151
- "project",
152
- )
153
-
154
- def __init__(self, project: Project, kb, func: Function, ij_addr: int):
155
- self.project = project
156
- self.kb = kb
157
- self.func = func
158
- self.indirect_jump_addr = ij_addr
159
-
160
- self.mapping: dict[Any, dict[Any, claripy.ast.Base]] | None = None
161
-
162
- def reg_read_callback(self, state: SimState):
163
- if self.mapping is None:
164
- self._build_mapping()
165
- assert self.mapping is not None
166
-
167
- codeloc = CodeLocation(state.scratch.bbl_addr, state.scratch.stmt_idx, ins_addr=state.scratch.ins_addr)
168
- if codeloc in self.mapping:
169
- reg_read_offset = state.inspect.reg_read_offset
170
- if isinstance(reg_read_offset, claripy.ast.BV) and reg_read_offset.op == "BVV":
171
- reg_read_offset = reg_read_offset.args[0]
172
- variable = VEXReg(reg_read_offset, state.inspect.reg_read_length)
173
- if variable in self.mapping[codeloc]:
174
- v = self.mapping[codeloc][variable]
175
- if isinstance(v, int):
176
- v = claripy.BVV(v, state.inspect.reg_read_length * state.arch.byte_width)
177
- state.inspect.reg_read_expr = v
178
-
179
- def _build_mapping(self):
180
- # constant propagation
181
- l.debug("JumpTable: Propagating for %r at %#x.", self.func, self.indirect_jump_addr)
182
-
183
- # determine blocks to run FCP on
184
-
185
- # - include at most three levels of superblock successors from the entrypoint
186
- self.mapping = {}
187
- startpoint = self.func.startpoint
188
- if startpoint is None:
189
- return
190
-
191
- blocks = set()
192
- succ_and_levels = [(startpoint, 0)]
193
- while succ_and_levels:
194
- new_succs = []
195
- for node, level in succ_and_levels:
196
- if node in blocks:
197
- continue
198
- blocks.add(node)
199
- if node.addr == self.indirect_jump_addr:
200
- # stop at the indirect jump block
201
- continue
202
- for _, succ, data in self.func.graph.out_edges(node, data=True):
203
- new_level = level if data.get("type") == "fake_return" else level + 1
204
- if new_level <= 3:
205
- new_succs.append((succ, new_level))
206
- succ_and_levels = new_succs
207
-
208
- # - include at most six levels of predecessors from the indirect jump block
209
- ij_block = self.func.get_node(self.indirect_jump_addr)
210
- preds = [ij_block]
211
- for _ in range(6):
212
- new_preds = []
213
- for node in preds:
214
- if node in blocks:
215
- continue
216
- blocks.add(node)
217
- new_preds += list(self.func.graph.predecessors(node))
218
- preds = new_preds
219
- if not preds:
220
- break
221
-
222
- prop = self.project.analyses.FastConstantPropagation(
223
- self.func,
224
- blocks=blocks,
225
- vex_cross_insn_opt=True,
226
- load_callback=PropagatorLoadCallback(self.project).propagator_load_callback,
227
- )
228
- self.mapping = prop.replacements
229
-
230
-
231
133
  #
232
134
  # Jump table pre-check
233
135
  #
@@ -1798,7 +1700,9 @@ class JumpTableResolver(IndirectJumpResolver):
1798
1700
  # swap the two tmps
1799
1701
  jump_base_addr.tmp, jump_base_addr.tmp_1 = jump_base_addr.tmp_1, jump_base_addr.tmp
1800
1702
  # Load the concrete base address
1801
- jump_base_addr.base_addr = state.solver.eval(state.scratch.temps[jump_base_addr.tmp_1])
1703
+ with contextlib.suppress(SimError):
1704
+ # silently eat the claripy exception
1705
+ jump_base_addr.base_addr = state.solver.eval(state.scratch.temps[jump_base_addr.tmp_1])
1802
1706
  else:
1803
1707
  # We do not support the cases where the base address involves more than one addition.
1804
1708
  # One such case exists in libc-2.27.so shipped with Ubuntu x86 where esi is used as the address of the
@@ -0,0 +1,92 @@
1
+ from __future__ import annotations
2
+ import contextlib
3
+ from typing import TYPE_CHECKING
4
+ import logging
5
+
6
+ from angr import sim_options as o
7
+ from angr import BP, BP_AFTER
8
+ from angr.errors import (
9
+ AngrUnsupportedSyscallError,
10
+ SimOperationError,
11
+ SimError,
12
+ )
13
+
14
+ from .resolver import IndirectJumpResolver
15
+ from .constant_value_manager import ConstantValueManager
16
+
17
+ if TYPE_CHECKING:
18
+ from angr import Block
19
+ from angr.engines import SimSuccessors
20
+ from angr.sim_state import SimState
21
+ from angr.sim_procedure import SimProcedure
22
+
23
+
24
+ _l = logging.getLogger(name=__name__)
25
+
26
+
27
+ class SyscallResolver(IndirectJumpResolver):
28
+ """
29
+ Resolve syscalls to SimProcedures.
30
+ """
31
+
32
+ def __init__(self, project):
33
+ super().__init__(project, timeless=True)
34
+
35
+ def filter(self, cfg, addr, func_addr, block, jumpkind):
36
+ return jumpkind.startswith("Ijk_Sys")
37
+
38
+ def resolve( # pylint:disable=unused-argument
39
+ self, cfg, addr: int, func_addr: int, block: Block, jumpkind: str, func_graph_complete: bool = True, **kwargs
40
+ ):
41
+ stub = self._resolve_syscall_to_stub(cfg, addr, func_addr, block)
42
+ return (True, [stub.addr]) if stub else (False, [])
43
+
44
+ def _resolve_syscall_to_stub(self, cfg, addr: int, func_addr: int, block: Block) -> SimProcedure | None:
45
+ if not cfg.functions.contains_addr(func_addr):
46
+ return None
47
+ func = cfg.functions.get_by_addr(func_addr)
48
+
49
+ cv_manager = ConstantValueManager(self.project, cfg.kb, func, addr)
50
+ constant_value_reg_read_bp = BP(when=BP_AFTER, enabled=True, action=cv_manager.reg_read_callback)
51
+
52
+ state = self.project.factory.blank_state(
53
+ mode="fastpath",
54
+ addr=block.addr,
55
+ add_options={o.SYMBOL_FILL_UNCONSTRAINED_MEMORY, o.SYMBOL_FILL_UNCONSTRAINED_REGISTERS},
56
+ )
57
+ state.inspect.add_breakpoint("reg_read", constant_value_reg_read_bp)
58
+
59
+ successors = self._simulate_block_with_resilience(state)
60
+ if successors:
61
+ state = self._get_syscall_state_from_successors(successors)
62
+ if state:
63
+ with contextlib.suppress(AngrUnsupportedSyscallError):
64
+ return self.project.simos.syscall(state)
65
+ return None
66
+
67
+ def _simulate_block_with_resilience(self, state: SimState) -> SimSuccessors | None:
68
+ """
69
+ Execute a basic block with "On Error Resume Next". Give up when there is no way moving forward.
70
+ """
71
+
72
+ stmt_idx = 0
73
+ successors = None # make PyCharm's linting happy
74
+
75
+ while True:
76
+ try:
77
+ successors = self.project.factory.successors(state, skip_stmts=stmt_idx)
78
+ break
79
+ except SimOperationError:
80
+ stmt_idx += 1
81
+ continue
82
+ except SimError:
83
+ return None
84
+
85
+ return successors
86
+
87
+ @staticmethod
88
+ def _get_syscall_state_from_successors(successors: SimSuccessors) -> SimState | None:
89
+ for state in successors.flat_successors:
90
+ if state.history.jumpkind and state.history.jumpkind.startswith("Ijk_Sys"):
91
+ return state
92
+ return None
@@ -63,7 +63,7 @@ class CompleteCallingConventionsAnalysis(Analysis):
63
63
  max_function_size: int | None = None,
64
64
  workers: int = 0,
65
65
  cc_callback: Callable | None = None,
66
- prioritize_func_addrs: Iterable[int] | None = None,
66
+ prioritize_func_addrs: list[int] | set[int] | None = None,
67
67
  skip_other_funcs: bool = False,
68
68
  auto_start: bool = True,
69
69
  func_graphs: dict[int, networkx.DiGraph] | None = None,
@@ -130,9 +130,20 @@ class CompleteCallingConventionsAnalysis(Analysis):
130
130
  Infer calling conventions for all functions in the current project.
131
131
  """
132
132
 
133
- # get an ordering of functions based on the call graph
134
- # note that the call graph is a multi-digraph. we convert it to a digraph to speed up topological sort
135
- directed_callgraph = networkx.DiGraph(self.kb.functions.callgraph)
133
+ # special case: if both _prioritize_func_addrs and _skip_other_funcs are set, we only need to sort part of
134
+ # the call graph; even better, if there is only one function set, we don't need to sort the call graph at all!
135
+ if self._prioritize_func_addrs and self._skip_other_funcs:
136
+ if len(self._prioritize_func_addrs) == 1:
137
+ self._func_addrs = list(self._prioritize_func_addrs)
138
+ self._total_funcs = 1
139
+ return
140
+ directed_callgraph = networkx.DiGraph(self.kb.functions.callgraph)
141
+ directed_callgraph = directed_callgraph.subgraph(self._prioritize_func_addrs)
142
+ else:
143
+ # get an ordering of functions based on the call graph
144
+ # note that the call graph is a multi-digraph. we convert it to a digraph to speed up topological sort
145
+ directed_callgraph = networkx.DiGraph(self.kb.functions.callgraph)
146
+ assert isinstance(directed_callgraph, networkx.DiGraph)
136
147
  sorted_funcs = GraphUtils.quasi_topological_sort_nodes(directed_callgraph)
137
148
 
138
149
  total_funcs = 0
@@ -148,7 +159,7 @@ class CompleteCallingConventionsAnalysis(Analysis):
148
159
  continue
149
160
 
150
161
  if self._max_function_size is not None:
151
- func_size = sum(block.size for block in func.blocks)
162
+ func_size = sum(block.size for block in func.blocks if block.size is not None)
152
163
  if func_size > self._max_function_size:
153
164
  _l.info(
154
165
  "Skipping variable recovery for %r since its size (%d) is greater than the cutoff "
@@ -189,6 +200,7 @@ class CompleteCallingConventionsAnalysis(Analysis):
189
200
 
190
201
  def work(self):
191
202
  total_funcs = self._total_funcs
203
+ assert total_funcs is not None
192
204
  if self._workers == 0:
193
205
  self._update_progress(0)
194
206
  for idx, func_addr in enumerate(self._func_addrs):
@@ -211,6 +223,7 @@ class CompleteCallingConventionsAnalysis(Analysis):
211
223
  self._finish_progress()
212
224
 
213
225
  else:
226
+ assert self._remaining_funcs is not None and self._func_queue is not None
214
227
  self._remaining_funcs.value = len(self._func_addrs)
215
228
 
216
229
  # generate a call tree (obviously, it's acyclic)