angr 9.2.135__py3-none-win_amd64.whl → 9.2.136__py3-none-win_amd64.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 (166) hide show
  1. angr/__init__.py +1 -1
  2. angr/analyses/__init__.py +3 -7
  3. angr/analyses/analysis.py +4 -0
  4. angr/analyses/backward_slice.py +1 -2
  5. angr/analyses/binary_optimizer.py +3 -4
  6. angr/analyses/bindiff.py +4 -6
  7. angr/analyses/boyscout.py +1 -3
  8. angr/analyses/callee_cleanup_finder.py +4 -4
  9. angr/analyses/calling_convention/calling_convention.py +4 -3
  10. angr/analyses/calling_convention/fact_collector.py +0 -1
  11. angr/analyses/cdg.py +1 -2
  12. angr/analyses/cfg/cfb.py +1 -3
  13. angr/analyses/cfg/cfg.py +2 -2
  14. angr/analyses/cfg/cfg_base.py +37 -35
  15. angr/analyses/cfg/cfg_emulated.py +1 -1
  16. angr/analyses/cfg/cfg_fast.py +62 -15
  17. angr/analyses/cfg/cfg_fast_soot.py +1 -1
  18. angr/analyses/cfg/indirect_jump_resolvers/__init__.py +2 -0
  19. angr/analyses/cfg/indirect_jump_resolvers/const_resolver.py +46 -10
  20. angr/analyses/cfg/indirect_jump_resolvers/default_resolvers.py +5 -1
  21. angr/analyses/cfg/indirect_jump_resolvers/jumptable.py +50 -14
  22. angr/analyses/cfg/indirect_jump_resolvers/memload_resolver.py +81 -0
  23. angr/analyses/cfg/indirect_jump_resolvers/propagator_utils.py +24 -5
  24. angr/analyses/cfg/indirect_jump_resolvers/x86_pe_iat.py +2 -5
  25. angr/analyses/congruency_check.py +2 -3
  26. angr/analyses/data_dep/data_dependency_analysis.py +2 -2
  27. angr/analyses/ddg.py +1 -4
  28. angr/analyses/decompiler/ail_simplifier.py +3 -4
  29. angr/analyses/decompiler/clinic.py +42 -7
  30. angr/analyses/decompiler/optimization_passes/duplication_reverter/ail_merge_graph.py +2 -2
  31. angr/analyses/decompiler/optimization_passes/duplication_reverter/duplication_reverter.py +2 -2
  32. angr/analyses/decompiler/optimization_passes/ite_region_converter.py +1 -1
  33. angr/analyses/decompiler/optimization_passes/lowered_switch_simplifier.py +1 -1
  34. angr/analyses/decompiler/optimization_passes/stack_canary_simplifier.py +1 -1
  35. angr/analyses/decompiler/structuring/phoenix.py +1 -1
  36. angr/analyses/disassembly.py +5 -5
  37. angr/analyses/fcp/__init__.py +4 -0
  38. angr/analyses/fcp/fcp.py +429 -0
  39. angr/analyses/identifier/identify.py +1 -3
  40. angr/analyses/loopfinder.py +4 -3
  41. angr/analyses/patchfinder.py +1 -1
  42. angr/analyses/propagator/engine_base.py +4 -3
  43. angr/analyses/propagator/propagator.py +14 -53
  44. angr/analyses/reassembler.py +1 -2
  45. angr/analyses/s_propagator.py +1 -3
  46. angr/analyses/soot_class_hierarchy.py +1 -2
  47. angr/analyses/stack_pointer_tracker.py +18 -2
  48. angr/analyses/static_hooker.py +1 -2
  49. angr/analyses/typehoon/simple_solver.py +2 -2
  50. angr/analyses/variable_recovery/variable_recovery_fast.py +1 -2
  51. angr/analyses/veritesting.py +4 -7
  52. angr/analyses/vfg.py +1 -1
  53. angr/analyses/vsa_ddg.py +1 -2
  54. angr/block.py +3 -2
  55. angr/callable.py +1 -3
  56. angr/calling_conventions.py +3 -3
  57. angr/codenode.py +5 -1
  58. angr/concretization_strategies/__init__.py +1 -83
  59. angr/concretization_strategies/any.py +2 -1
  60. angr/concretization_strategies/any_named.py +1 -1
  61. angr/concretization_strategies/base.py +81 -0
  62. angr/concretization_strategies/controlled_data.py +2 -1
  63. angr/concretization_strategies/eval.py +2 -1
  64. angr/concretization_strategies/logging.py +3 -1
  65. angr/concretization_strategies/max.py +2 -1
  66. angr/concretization_strategies/nonzero.py +2 -1
  67. angr/concretization_strategies/nonzero_range.py +2 -1
  68. angr/concretization_strategies/norepeats.py +2 -1
  69. angr/concretization_strategies/norepeats_range.py +2 -1
  70. angr/concretization_strategies/range.py +2 -1
  71. angr/concretization_strategies/signed_add.py +2 -1
  72. angr/concretization_strategies/single.py +2 -1
  73. angr/concretization_strategies/solutions.py +2 -1
  74. angr/concretization_strategies/unlimited_range.py +2 -1
  75. angr/engines/__init__.py +8 -5
  76. angr/engines/engine.py +3 -5
  77. angr/engines/failure.py +4 -5
  78. angr/engines/procedure.py +5 -7
  79. angr/engines/soot/expressions/__init__.py +22 -23
  80. angr/engines/soot/expressions/base.py +4 -4
  81. angr/engines/soot/expressions/invoke.py +1 -2
  82. angr/engines/soot/statements/__init__.py +9 -10
  83. angr/engines/soot/values/__init__.py +9 -10
  84. angr/engines/soot/values/arrayref.py +3 -3
  85. angr/engines/soot/values/instancefieldref.py +3 -2
  86. angr/engines/successors.py +7 -6
  87. angr/engines/syscall.py +4 -6
  88. angr/engines/unicorn.py +3 -2
  89. angr/engines/vex/claripy/ccall.py +8 -10
  90. angr/engines/vex/claripy/datalayer.py +4 -5
  91. angr/exploration_techniques/__init__.py +0 -2
  92. angr/exploration_techniques/spiller.py +1 -3
  93. angr/exploration_techniques/stochastic.py +2 -3
  94. angr/factory.py +3 -9
  95. angr/knowledge_plugins/cfg/cfg_model.py +20 -17
  96. angr/knowledge_plugins/functions/function.py +70 -73
  97. angr/knowledge_plugins/functions/function_manager.py +8 -7
  98. angr/knowledge_plugins/functions/function_parser.py +1 -1
  99. angr/knowledge_plugins/functions/soot_function.py +16 -16
  100. angr/knowledge_plugins/propagations/propagation_model.py +4 -5
  101. angr/knowledge_plugins/propagations/states.py +0 -511
  102. angr/lib/angr_native.dll +0 -0
  103. angr/procedures/libc/memcpy.py +4 -4
  104. angr/procedures/procedure_dict.py +3 -2
  105. angr/protos/__init__.py +2 -5
  106. angr/protos/cfg_pb2.py +21 -18
  107. angr/protos/function_pb2.py +17 -14
  108. angr/protos/primitives_pb2.py +44 -39
  109. angr/protos/variables_pb2.py +36 -31
  110. angr/protos/xrefs_pb2.py +15 -12
  111. angr/sim_procedure.py +15 -16
  112. angr/sim_variable.py +13 -1
  113. angr/simos/__init__.py +2 -0
  114. angr/simos/javavm.py +4 -6
  115. angr/simos/xbox.py +32 -0
  116. angr/state_plugins/__init__.py +0 -2
  117. angr/state_plugins/callstack.py +4 -4
  118. angr/state_plugins/cgc.py +3 -2
  119. angr/state_plugins/gdb.py +6 -5
  120. angr/state_plugins/globals.py +1 -2
  121. angr/state_plugins/heap/heap_brk.py +1 -2
  122. angr/state_plugins/history.py +10 -12
  123. angr/state_plugins/inspect.py +3 -5
  124. angr/state_plugins/libc.py +2 -2
  125. angr/state_plugins/log.py +8 -10
  126. angr/state_plugins/loop_data.py +1 -2
  127. angr/state_plugins/posix.py +7 -7
  128. angr/state_plugins/preconstrainer.py +2 -3
  129. angr/state_plugins/scratch.py +5 -8
  130. angr/state_plugins/sim_action.py +3 -3
  131. angr/state_plugins/solver.py +8 -3
  132. angr/state_plugins/symbolizer.py +5 -4
  133. angr/state_plugins/uc_manager.py +3 -3
  134. angr/state_plugins/unicorn_engine.py +5 -1
  135. angr/state_plugins/view.py +3 -5
  136. angr/storage/file.py +3 -5
  137. angr/storage/memory_mixins/address_concretization_mixin.py +2 -2
  138. angr/storage/memory_mixins/bvv_conversion_mixin.py +3 -3
  139. angr/storage/memory_mixins/clouseau_mixin.py +1 -3
  140. angr/storage/memory_mixins/name_resolution_mixin.py +1 -3
  141. angr/storage/memory_mixins/paged_memory/paged_memory_mixin.py +13 -15
  142. angr/storage/memory_mixins/paged_memory/pages/__init__.py +1 -22
  143. angr/storage/memory_mixins/paged_memory/pages/base.py +31 -0
  144. angr/storage/memory_mixins/paged_memory/pages/list_page.py +1 -1
  145. angr/storage/memory_mixins/paged_memory/pages/mv_list_page.py +1 -1
  146. angr/storage/memory_mixins/paged_memory/pages/ultra_page.py +2 -4
  147. angr/storage/memory_mixins/paged_memory/privileged_mixin.py +3 -4
  148. angr/storage/memory_mixins/regioned_memory/abstract_merger_mixin.py +4 -2
  149. angr/storage/memory_mixins/smart_find_mixin.py +1 -1
  150. angr/storage/memory_mixins/underconstrained_mixin.py +1 -1
  151. angr/storage/memory_mixins/unwrapper_mixin.py +1 -3
  152. angr/utils/enums_conv.py +28 -12
  153. angr/utils/segment_list.py +25 -22
  154. angr/utils/timing.py +18 -1
  155. angr/vaults.py +5 -6
  156. {angr-9.2.135.dist-info → angr-9.2.136.dist-info}/METADATA +6 -6
  157. {angr-9.2.135.dist-info → angr-9.2.136.dist-info}/RECORD +161 -160
  158. {angr-9.2.135.dist-info → angr-9.2.136.dist-info}/WHEEL +1 -1
  159. angr/analyses/propagator/outdated_definition_walker.py +0 -159
  160. angr/analyses/propagator/tmpvar_finder.py +0 -18
  161. angr/engines/concrete.py +0 -180
  162. angr/exploration_techniques/symbion.py +0 -80
  163. angr/state_plugins/concrete.py +0 -295
  164. {angr-9.2.135.dist-info → angr-9.2.136.dist-info}/LICENSE +0 -0
  165. {angr-9.2.135.dist-info → angr-9.2.136.dist-info}/entry_points.txt +0 -0
  166. {angr-9.2.135.dist-info → angr-9.2.136.dist-info}/top_level.txt +0 -0
@@ -75,7 +75,31 @@ class ConstantResolver(IndirectJumpResolver):
75
75
 
76
76
  vex_block = block.vex
77
77
  if isinstance(vex_block.next, pyvex.expr.RdTmp):
78
- # what does the jump rely on? slice it back and see
78
+ tmp_stmt_idx, tmp_ins_addr = self._find_tmp_write_stmt_and_ins(vex_block, vex_block.next.tmp)
79
+ if tmp_stmt_idx is None or tmp_ins_addr is None:
80
+ return False, []
81
+
82
+ # first check: is it jumping to a target loaded from memory? if so, it should have been resolved by
83
+ # MemoryLoadResolver.
84
+ stmt = vex_block.statements[tmp_stmt_idx]
85
+ assert isinstance(stmt, pyvex.IRStmt.WrTmp)
86
+ if (
87
+ isinstance(stmt.data, pyvex.IRExpr.Load)
88
+ and isinstance(stmt.data.addr, pyvex.IRExpr.Const)
89
+ and stmt.data.result_size(vex_block.tyenv) == self.project.arch.bits
90
+ ):
91
+ # well, if MemoryLoadResolver hasn't resolved it, we can try to resolve it here, or bail early because
92
+ # ConstantResolver won't help.
93
+ load_addr = stmt.data.addr.con.value
94
+ try:
95
+ value = self.project.loader.memory.unpack_word(load_addr, size=self.project.arch.bytes)
96
+ if isinstance(value, int) and self._is_target_valid(cfg, value):
97
+ return True, [value]
98
+ except KeyError:
99
+ pass
100
+ return False, []
101
+
102
+ # second check: what does the jump rely on? slice it back and see
79
103
  b = Blade(
80
104
  cfg.graph,
81
105
  addr,
@@ -104,26 +128,25 @@ class ConstantResolver(IndirectJumpResolver):
104
128
  block = self.project.factory.block(pred_addr, cross_insn_opt=True).vex
105
129
  if stmt_idx != DEFAULT_STATEMENT:
106
130
  stmt = block.statements[stmt_idx]
107
- if isinstance(stmt, pyvex.IRStmt.WrTmp) and isinstance(stmt.data, pyvex.IRExpr.Load):
131
+ if (
132
+ isinstance(stmt, pyvex.IRStmt.WrTmp)
133
+ and isinstance(stmt.data, pyvex.IRExpr.Load)
134
+ and not isinstance(stmt.data.addr, pyvex.IRExpr.Const)
135
+ ):
108
136
  # loading from memory - unsupported
109
137
  return False, []
110
138
  break
111
139
 
112
140
  _l.debug("ConstantResolver: Propagating for %r at %#x.", func, addr)
113
- prop = self.project.analyses.Propagator(
114
- func=func,
115
- only_consts=True,
116
- do_binops=True,
141
+ prop = self.project.analyses.FastConstantPropagation(
142
+ func,
117
143
  vex_cross_insn_opt=False,
118
- completed_funcs=cfg._completed_functions,
119
144
  load_callback=PropagatorLoadCallback(self.project).propagator_load_callback,
120
- cache_results=True,
121
- key_prefix="cfg_intermediate",
122
145
  )
123
146
 
124
147
  replacements = prop.replacements
125
148
  if replacements:
126
- block_loc = CodeLocation(block.addr, None)
149
+ block_loc = CodeLocation(block.addr, tmp_stmt_idx, ins_addr=tmp_ins_addr)
127
150
  tmp_var = vex_vars.VEXTmp(vex_block.next.tmp)
128
151
 
129
152
  if exists_in_replacements(replacements, block_loc, tmp_var):
@@ -135,5 +158,18 @@ class ConstantResolver(IndirectJumpResolver):
135
158
  and self._is_target_valid(cfg, resolved_tmp.args[0])
136
159
  ):
137
160
  return True, [resolved_tmp.args[0]]
161
+ if isinstance(resolved_tmp, int) and self._is_target_valid(cfg, resolved_tmp):
162
+ return True, [resolved_tmp]
138
163
 
139
164
  return False, []
165
+
166
+ @staticmethod
167
+ def _find_tmp_write_stmt_and_ins(vex_block, tmp: int) -> tuple[int | None, int | None]:
168
+ stmt_idx = None
169
+ for idx, stmt in enumerate(reversed(vex_block.statements)):
170
+ if isinstance(stmt, pyvex.IRStmt.IMark) and stmt_idx is not None:
171
+ ins_addr = stmt.addr + stmt.delta
172
+ return stmt_idx, ins_addr
173
+ if isinstance(stmt, pyvex.IRStmt.WrTmp) and stmt.tmp == tmp:
174
+ stmt_idx = len(vex_block.statements) - idx - 1
175
+ return None, None
@@ -1,5 +1,6 @@
1
1
  from __future__ import annotations
2
2
  import cle
3
+ from angr.analyses.cfg.indirect_jump_resolvers import MemoryLoadResolver
3
4
 
4
5
  from . import MipsElfFastResolver
5
6
  from . import X86ElfPicPltResolver
@@ -19,6 +20,9 @@ DEFAULT_RESOLVERS = {
19
20
  cle.PE: [
20
21
  X86PeIatResolver,
21
22
  ],
23
+ cle.XBE: [
24
+ X86PeIatResolver,
25
+ ],
22
26
  },
23
27
  "AMD64": {
24
28
  cle.MetaELF: [
@@ -54,7 +58,7 @@ DEFAULT_RESOLVERS = {
54
58
  ArmElfFastResolver,
55
59
  ]
56
60
  },
57
- "ALL": [JumpTableResolver, ConstantResolver],
61
+ "ALL": [MemoryLoadResolver, JumpTableResolver, ConstantResolver],
58
62
  }
59
63
 
60
64
 
@@ -10,6 +10,7 @@ import functools
10
10
  import pyvex
11
11
  import claripy
12
12
  from archinfo.arch_arm import is_arm_arch
13
+ from claripy.annotation import UninitializedAnnotation
13
14
 
14
15
  from angr import sim_options as o
15
16
  from angr import BP, BP_BEFORE, BP_AFTER
@@ -144,20 +145,22 @@ class ConstantValueManager:
144
145
 
145
146
  __slots__ = (
146
147
  "func",
148
+ "indirect_jump_addr",
147
149
  "kb",
148
150
  "mapping",
149
151
  "project",
150
152
  )
151
153
 
152
- def __init__(self, project: Project, kb, func: Function):
154
+ def __init__(self, project: Project, kb, func: Function, ij_addr: int):
153
155
  self.project = project
154
156
  self.kb = kb
155
157
  self.func = func
158
+ self.indirect_jump_addr = ij_addr
156
159
 
157
160
  self.mapping: dict[Any, dict[Any, claripy.ast.Base]] | None = None
158
161
 
159
162
  def reg_read_callback(self, state: SimState):
160
- if not self.mapping:
163
+ if self.mapping is None:
161
164
  self._build_mapping()
162
165
  assert self.mapping is not None
163
166
 
@@ -168,18 +171,54 @@ class ConstantValueManager:
168
171
  reg_read_offset = reg_read_offset.args[0]
169
172
  variable = VEXReg(reg_read_offset, state.inspect.reg_read_length)
170
173
  if variable in self.mapping[codeloc]:
171
- state.inspect.reg_read_expr = self.mapping[codeloc][variable]
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
172
178
 
173
179
  def _build_mapping(self):
174
180
  # constant propagation
175
- l.debug("JumpTable: Propagating for %r.", self.func)
176
- prop = self.project.analyses[PropagatorAnalysis].prep()(
177
- func=self.func,
178
- only_consts=True,
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 successors from the entrypoint
186
+ startpoint = self.func.startpoint
187
+ blocks = set()
188
+ succs = [startpoint]
189
+ for _ in range(3):
190
+ new_succs = []
191
+ for node in succs:
192
+ if node in blocks:
193
+ continue
194
+ blocks.add(node)
195
+ if node.addr == self.indirect_jump_addr:
196
+ # stop at the indirect jump block
197
+ continue
198
+ new_succs += list(self.func.graph.successors(node))
199
+ succs = new_succs
200
+ if not succs:
201
+ break
202
+
203
+ # - include at most six levels of predecessors from the indirect jump block
204
+ ij_block = self.func.get_node(self.indirect_jump_addr)
205
+ preds = [ij_block]
206
+ for _ in range(6):
207
+ new_preds = []
208
+ for node in preds:
209
+ if node in blocks:
210
+ continue
211
+ blocks.add(node)
212
+ new_preds += list(self.func.graph.predecessors(node))
213
+ preds = new_preds
214
+ if not preds:
215
+ break
216
+
217
+ prop = self.project.analyses.FastConstantPropagation(
218
+ self.func,
219
+ blocks=blocks,
179
220
  vex_cross_insn_opt=True,
180
221
  load_callback=PropagatorLoadCallback(self.project).propagator_load_callback,
181
- cache_results=True,
182
- key_prefix="cfg_intermediate",
183
222
  )
184
223
  self.mapping = prop.replacements
185
224
 
@@ -853,7 +892,7 @@ class JumpTableResolver(IndirectJumpResolver):
853
892
  potential_call_table = jumpkind == "Ijk_Call" or self._sp_moved_up(block) or len(func.block_addrs_set) <= 5
854
893
  # we only perform full-function propagation for jump tables or call tables in really small functions
855
894
  if not potential_call_table or len(func.block_addrs_set) <= 5:
856
- cv_manager = ConstantValueManager(self.project, cfg.kb, func)
895
+ cv_manager = ConstantValueManager(self.project, cfg.kb, func, addr)
857
896
  else:
858
897
  cv_manager = None
859
898
 
@@ -2131,7 +2170,7 @@ class JumpTableResolver(IndirectJumpResolver):
2131
2170
  read_addr = state.inspect.mem_read_address
2132
2171
  cond = state.inspect.mem_read_condition
2133
2172
 
2134
- if not isinstance(read_addr, int) and read_addr.uninitialized and cond is None:
2173
+ if not isinstance(read_addr, int) and read_addr.has_annotation_type(UninitializedAnnotation) and cond is None:
2135
2174
  # if this AST has been initialized before, just use the cached addr
2136
2175
  cached_addr = self._cached_memread_addrs.get(read_addr, None)
2137
2176
  if cached_addr is not None:
@@ -2402,6 +2441,3 @@ class JumpTableResolver(IndirectJumpResolver):
2402
2441
  if load_stmt_ids:
2403
2442
  return [load_stmt_ids[-1]]
2404
2443
  return []
2405
-
2406
-
2407
- from angr.analyses.propagator import PropagatorAnalysis
@@ -0,0 +1,81 @@
1
+ from __future__ import annotations
2
+ import logging
3
+
4
+ import pyvex
5
+
6
+ from .resolver import IndirectJumpResolver
7
+
8
+ _l = logging.getLogger(name=__name__)
9
+
10
+
11
+ class MemoryLoadResolver(IndirectJumpResolver):
12
+ """
13
+ Resolve an indirect jump that looks like the following::
14
+
15
+ .text:
16
+ call off_3314A8
17
+
18
+ .data:
19
+ off_3314A8 dd offset sub_1E426F
20
+
21
+ This indirect jump resolver may not be the best solution for all cases (e.g., when the .data section can be
22
+ intentionally altered by the binary itself).
23
+ """
24
+
25
+ def __init__(self, project):
26
+ super().__init__(project, timeless=True)
27
+
28
+ def filter(self, cfg, addr, func_addr, block, jumpkind):
29
+ return jumpkind in {"Ijk_Boring", "Ijk_Call"}
30
+
31
+ def resolve( # pylint:disable=unused-argument
32
+ self,
33
+ cfg,
34
+ addr: int,
35
+ func_addr: int,
36
+ block: pyvex.IRSB,
37
+ jumpkind: str,
38
+ func_graph_complete: bool = True,
39
+ **kwargs,
40
+ ):
41
+ """
42
+ :param cfg: CFG with specified function
43
+ :param addr: Address of indirect jump
44
+ :param func_addr: Address of function of indirect jump
45
+ :param block: Block of indirect jump (Block object)
46
+ :param jumpkind: VEX jumpkind (Ijk_Boring or Ijk_Call)
47
+ :return: Bool tuple with replacement address
48
+ """
49
+ vex_block = block
50
+ if isinstance(vex_block.next, pyvex.expr.RdTmp):
51
+ tmp_stmt_idx, tmp_ins_addr = self._find_tmp_write_stmt_and_ins(vex_block, vex_block.next.tmp)
52
+ if tmp_stmt_idx is None or tmp_ins_addr is None:
53
+ return False, []
54
+
55
+ stmt = vex_block.statements[tmp_stmt_idx]
56
+ assert isinstance(stmt, pyvex.IRStmt.WrTmp)
57
+ if (
58
+ isinstance(stmt.data, pyvex.IRExpr.Load)
59
+ and isinstance(stmt.data.addr, pyvex.IRExpr.Const)
60
+ and stmt.data.result_size(vex_block.tyenv) == self.project.arch.bits
61
+ ):
62
+ load_addr = stmt.data.addr.con.value
63
+ try:
64
+ value = self.project.loader.memory.unpack_word(load_addr, size=self.project.arch.bytes)
65
+ if isinstance(value, int) and self._is_target_valid(cfg, value):
66
+ return True, [value]
67
+ except KeyError:
68
+ return False, []
69
+
70
+ return False, []
71
+
72
+ @staticmethod
73
+ def _find_tmp_write_stmt_and_ins(vex_block, tmp: int) -> tuple[int | None, int | None]:
74
+ stmt_idx = None
75
+ for idx, stmt in enumerate(reversed(vex_block.statements)):
76
+ if isinstance(stmt, pyvex.IRStmt.IMark) and stmt_idx is not None:
77
+ ins_addr = stmt.addr + stmt.delta
78
+ return stmt_idx, ins_addr
79
+ if isinstance(stmt, pyvex.IRStmt.WrTmp) and stmt.tmp == tmp:
80
+ stmt_idx = len(vex_block.statements) - idx - 1
81
+ return None, None
@@ -10,7 +10,7 @@ class PropagatorLoadCallback:
10
10
  def __init__(self, project):
11
11
  self.project = project
12
12
 
13
- def propagator_load_callback(self, addr, size) -> bool: # pylint:disable=unused-argument
13
+ def propagator_load_callback(self, addr: claripy.ast.BV | int, size: int) -> bool: # pylint:disable=unused-argument
14
14
  # only allow loading if the address falls into a read-only region
15
15
  if isinstance(addr, claripy.ast.BV) and addr.op == "BVV":
16
16
  addr_v = addr.args[0]
@@ -19,9 +19,28 @@ class PropagatorLoadCallback:
19
19
  else:
20
20
  return False
21
21
  section = self.project.loader.find_section_containing(addr_v)
22
+ segment = None
22
23
  if section is not None:
23
- return section.is_readable and not section.is_writable
24
- segment = self.project.loader.find_segment_containing(addr_v)
25
- if segment is not None:
26
- return segment.is_readable and not segment.is_writable
24
+ if section.is_readable and not section.is_writable:
25
+ # read-only section
26
+ return True
27
+ else:
28
+ segment = self.project.loader.find_segment_containing(addr_v)
29
+ if segment is not None and segment.is_readable and not segment.is_writable:
30
+ # read-only segment
31
+ return True
32
+
33
+ if (size == self.project.arch.bytes and (section is not None and section.is_readable)) or (
34
+ segment is not None and segment.is_readable
35
+ ):
36
+ # memory is mapped and readable. does it contain a valid address?
37
+ try:
38
+ target_addr = self.project.loader.memory.unpack_word(
39
+ addr_v, size=size, endness=self.project.arch.memory_endness
40
+ )
41
+ if target_addr >= 0x1000 and self.project.loader.find_object_containing(target_addr) is not None:
42
+ return True
43
+ except KeyError:
44
+ return False
45
+
27
46
  return False
@@ -3,7 +3,6 @@ import logging
3
3
 
4
4
  from capstone.x86_const import X86_OP_MEM
5
5
 
6
- from angr.simos import SimWindows
7
6
  from .resolver import IndirectJumpResolver
8
7
 
9
8
  l = logging.getLogger(name=__name__)
@@ -11,16 +10,14 @@ l = logging.getLogger(name=__name__)
11
10
 
12
11
  class X86PeIatResolver(IndirectJumpResolver):
13
12
  """
14
- A timeless indirect jump resolver for IAT in x86 PEs.
13
+ A timeless indirect jump resolver for IAT in x86 PEs and xbes.
15
14
  """
16
15
 
17
16
  def __init__(self, project):
18
17
  super().__init__(project, timeless=True)
19
18
 
20
19
  def filter(self, cfg, addr, func_addr, block, jumpkind):
21
- if not isinstance(self.project.simos, SimWindows):
22
- return False
23
- if jumpkind != "Ijk_Call":
20
+ if jumpkind not in {"Ijk_Call", "Ijk_Boring"}: # both call and jmp
24
21
  return False
25
22
 
26
23
  insns = self.project.factory.block(addr).capstone.insns
@@ -3,6 +3,8 @@ import logging
3
3
 
4
4
  import claripy
5
5
 
6
+ from angr.errors import AngrIncongruencyError
7
+ from angr.analyses import AnalysesHub
6
8
  from . import Analysis
7
9
 
8
10
  l = logging.getLogger(name=__name__)
@@ -372,7 +374,4 @@ class CongruencyCheck(Analysis):
372
374
  return True
373
375
 
374
376
 
375
- from angr.errors import AngrIncongruencyError
376
- from angr.analyses import AnalysesHub
377
-
378
377
  AnalysesHub.register_default("CongruencyCheck", CongruencyCheck)
@@ -9,7 +9,7 @@ from typing import TYPE_CHECKING
9
9
  from networkx import DiGraph
10
10
 
11
11
  import claripy
12
- from claripy.ast.bv import BV
12
+ from claripy.ast import BV
13
13
  from .dep_nodes import DepNodeTypes, ConstantDepNode, MemDepNode, VarDepNode, RegDepNode, TmpDepNode
14
14
  from .sim_act_location import SimActLocation, DEFAULT_LOCATION, ParsedInstruction
15
15
  from angr.analyses import Analysis
@@ -173,7 +173,7 @@ class DataDependencyGraphAnalysis(Analysis):
173
173
  self,
174
174
  type_: int,
175
175
  sim_act: SimActionData,
176
- val: tuple[BV, int],
176
+ val: tuple[claripy.ast.BV, int],
177
177
  *constructor_params,
178
178
  ) -> BaseDepNode:
179
179
  """
angr/analyses/ddg.py CHANGED
@@ -6,8 +6,7 @@ import claripy
6
6
  import networkx
7
7
  import pyvex
8
8
 
9
- from . import Analysis
10
-
9
+ from angr.analyses import Analysis, AnalysesHub
11
10
  from angr.code_location import CodeLocation
12
11
  from angr.errors import SimSolverModeError, SimUnsatError, AngrDDGError
13
12
  from angr.sim_variable import (
@@ -1668,6 +1667,4 @@ class DDG(Analysis):
1668
1667
  return sources
1669
1668
 
1670
1669
 
1671
- from angr.analyses import AnalysesHub
1672
-
1673
1670
  AnalysesHub.register_default("DDG", DDG)
@@ -216,7 +216,7 @@ class AILSimplifier(Analysis):
216
216
  self._reaching_definitions = rd
217
217
  return rd
218
218
 
219
- def _compute_propagation(self, immediate_stmt_removal: bool = False) -> SPropagatorAnalysis:
219
+ def _compute_propagation(self) -> SPropagatorAnalysis:
220
220
  # Propagate expressions or return the existing result
221
221
  if self._propagator is not None:
222
222
  return self._propagator
@@ -225,7 +225,6 @@ class AILSimplifier(Analysis):
225
225
  func_graph=self.func_graph,
226
226
  # gp=self._gp,
227
227
  only_consts=self._only_consts,
228
- immediate_stmt_removal=immediate_stmt_removal,
229
228
  )
230
229
  self._propagator = prop
231
230
  return prop
@@ -354,7 +353,7 @@ class AILSimplifier(Analysis):
354
353
  ):
355
354
  repeat = False
356
355
  narrowable_phivarids = set()
357
- for def_vvarid, (_, narrow_info) in narrowing_candidates.items():
356
+ for def_vvarid in narrowing_candidates:
358
357
  if def_vvarid in blacklist_varids:
359
358
  continue
360
359
  if def_vvarid in rd.phi_vvar_ids:
@@ -591,7 +590,7 @@ class AILSimplifier(Analysis):
591
590
  """
592
591
 
593
592
  # propagator
594
- propagator = self._compute_propagation(immediate_stmt_removal=True)
593
+ propagator = self._compute_propagation()
595
594
  replacements = propagator.replacements
596
595
 
597
596
  # take replacements and rebuild the corresponding blocks
@@ -527,6 +527,9 @@ class Clinic(Analysis):
527
527
  # TODO: Totally remove this dict
528
528
  self._blocks_by_addr_and_size = None
529
529
 
530
+ # Rust-specific; only call this on Rust binaries when we can identify language and compiler
531
+ ail_graph = self._rewrite_rust_probestack_call(ail_graph)
532
+
530
533
  # Make call-sites
531
534
  self._update_progress(50.0, text="Making callsites")
532
535
  _, stackarg_offsets, removed_vvar_ids = self._make_callsites(ail_graph, stack_pointer_tracker=spt)
@@ -1010,13 +1013,15 @@ class Clinic(Analysis):
1010
1013
  if node is None:
1011
1014
  continue
1012
1015
  successors = self._cfg.get_successors(node, excluding_fakeret=True, jumpkind="Ijk_Call")
1013
- if len(successors) == 1 and not isinstance(
1014
- self.project.hooked_by(successors[0].addr), UnresolvableCallTarget
1015
- ):
1016
- # found a single successor - replace the last statement
1017
- new_last_stmt = last_stmt.copy()
1018
- new_last_stmt.target = ailment.Expr.Const(None, None, successors[0].addr, last_stmt.target.bits)
1019
- block.statements[-1] = new_last_stmt
1016
+ if len(successors) == 1:
1017
+ succ_addr = successors[0].addr
1018
+ if not self.project.is_hooked(succ_addr) or not isinstance(
1019
+ self.project.hooked_by(successors[0].addr), UnresolvableCallTarget
1020
+ ):
1021
+ # found a single successor - replace the last statement
1022
+ new_last_stmt = last_stmt.copy()
1023
+ new_last_stmt.target = ailment.Expr.Const(None, None, successors[0].addr, last_stmt.target.bits)
1024
+ block.statements[-1] = new_last_stmt
1020
1025
 
1021
1026
  elif isinstance(last_stmt, ailment.Stmt.Jump) and not isinstance(last_stmt.target, ailment.Expr.Const):
1022
1027
  # indirect jump
@@ -2732,6 +2737,36 @@ class Clinic(Analysis):
2732
2737
 
2733
2738
  return extra_regs
2734
2739
 
2740
+ def _rewrite_rust_probestack_call(self, ail_graph):
2741
+ for node in ail_graph:
2742
+ if not node.statements or ail_graph.out_degree[node] != 1:
2743
+ continue
2744
+ last_stmt = node.statements[-1]
2745
+ if isinstance(last_stmt, ailment.Stmt.Call) and isinstance(last_stmt.target, ailment.Expr.Const):
2746
+ func = (
2747
+ self.project.kb.functions.get_by_addr(last_stmt.target.value)
2748
+ if self.project.kb.functions.contains_addr(last_stmt.target.value)
2749
+ else None
2750
+ )
2751
+ if func is not None and func.info.get("is_rust_probestack", False) is True:
2752
+ # get rid of this call
2753
+ node.statements = node.statements[:-1]
2754
+ if self.project.arch.call_pushes_ret and node.statements:
2755
+ last_stmt = node.statements[-1]
2756
+ succ = next(iter(ail_graph.successors(node)))
2757
+ if (
2758
+ isinstance(last_stmt, ailment.Stmt.Store)
2759
+ and isinstance(last_stmt.addr, ailment.Expr.StackBaseOffset)
2760
+ and isinstance(last_stmt.addr.offset, int)
2761
+ and last_stmt.addr.offset < 0
2762
+ and isinstance(last_stmt.data, ailment.Expr.Const)
2763
+ and last_stmt.data.value == succ.addr
2764
+ ):
2765
+ # remove the statement that pushes the return address
2766
+ node.statements = node.statements[:-1]
2767
+ break
2768
+ return ail_graph
2769
+
2735
2770
  def _rewrite_alloca(self, ail_graph):
2736
2771
  # pylint:disable=too-many-boolean-expressions
2737
2772
  alloca_node = None
@@ -304,7 +304,7 @@ class AILMergeGraph:
304
304
  end_pair_map = {}
305
305
  end_pairs = set()
306
306
  merge_to_end_pair = {}
307
- for split, sblocks in self.merge_blocks_to_originals.items():
307
+ for sblocks in self.merge_blocks_to_originals.values():
308
308
  for sblock in sblocks:
309
309
  if isinstance(sblock, AILBlockSplit) and sblock.original in self.merge_blocks_to_originals:
310
310
  deletable_blocks.add(sblock.original)
@@ -429,7 +429,7 @@ class AILMergeGraph:
429
429
  self.original_split_blocks[updated] = self.original_split_blocks[k]
430
430
  del self.original_split_blocks[k]
431
431
 
432
- for k, v in self.original_split_blocks.items():
432
+ for v in self.original_split_blocks.values():
433
433
  for sblock in v:
434
434
  for attr in ["up_split", "match_split", "down_split"]:
435
435
  if getattr(sblock, attr) == original:
@@ -167,7 +167,7 @@ class DuplicationReverter(StructuringOptimizationPass):
167
167
 
168
168
  def _reinsert_merged_candidate(self, ail_merge_graph: AILMergeGraph, candidate: tuple[Block, Block]) -> bool:
169
169
  og_succs, og_preds = {}, {}
170
- for block, original_blocks in ail_merge_graph.original_blocks.items():
170
+ for original_blocks in ail_merge_graph.original_blocks.values():
171
171
  # collect all the old edges
172
172
  for og_block in original_blocks:
173
173
  og_succs[og_block] = list(self.write_graph.successors(og_block))
@@ -364,7 +364,7 @@ class DuplicationReverter(StructuringOptimizationPass):
364
364
  new_nodes[node] = new_node
365
365
 
366
366
  # fixup every single jump target (before adding them to the graph)
367
- for src, dst, data in graph.edges(data=True):
367
+ for src, dst in graph.edges():
368
368
  new_src = new_nodes[src]
369
369
  new_dst = new_nodes[dst]
370
370
  if new_dst is not dst:
@@ -263,7 +263,7 @@ class ITERegionConverter(OptimizationPass):
263
263
 
264
264
  # is this the statement that we are looking for?
265
265
  found_true_src_vvar, found_false_src_vvar = False, False
266
- for src, vvar in stmt.src.src_and_vvars:
266
+ for _src, vvar in stmt.src.src_and_vvars:
267
267
  if vvar is not None:
268
268
  if vvar.varid == true_stmt_dst.varid:
269
269
  found_true_src_vvar = True
@@ -557,7 +557,7 @@ class LoweredSwitchSimplifier(StructuringOptimizationPass):
557
557
  varhash_to_caselists[v].append((cases, extra_cmp_nodes))
558
558
 
559
559
  for v, caselists in list(varhash_to_caselists.items()):
560
- for idx, (cases, redundant_nodes) in list(enumerate(caselists)):
560
+ for idx, (cases, _redundant_nodes) in list(enumerate(caselists)):
561
561
  # filter: each case value should only appear once
562
562
  if len({case.value for case in cases}) != len(cases):
563
563
  caselists[idx] = None
@@ -144,7 +144,7 @@ class StackCanarySimplifier(OptimizationPass):
144
144
  nodes_to_process.append((pred, canary_check_stmt_idx, stack_chk_fail_caller, ret_node))
145
145
 
146
146
  # Awesome. Now patch this function.
147
- for pred, canary_check_stmt_idx, stack_chk_fail_caller, ret_node in nodes_to_process:
147
+ for pred, _canary_check_stmt_idx, stack_chk_fail_caller, ret_node in nodes_to_process:
148
148
  # Patch the pred so that it jumps to the one that is not stack_chk_fail_caller
149
149
  pred_copy = pred.copy()
150
150
  pred_copy.statements[-1] = ailment.Stmt.Jump(
@@ -1536,7 +1536,7 @@ class PhoenixStructurer(StructurerBase):
1536
1536
  full_graph.remove_edge(head, out_dst)
1537
1537
 
1538
1538
  # fix full_graph if needed: remove successors that are no longer needed
1539
- for out_src, out_dst in out_edges[1:]:
1539
+ for _out_src, out_dst in out_edges[1:]:
1540
1540
  if out_dst in full_graph and out_dst not in graph and full_graph.in_degree[out_dst] == 0:
1541
1541
  full_graph.remove_node(out_dst)
1542
1542
  if out_dst in self._region.successors:
@@ -1,22 +1,24 @@
1
1
  from __future__ import annotations
2
+
3
+ import contextlib
2
4
  import logging
3
5
  from collections import defaultdict
4
- from typing import Union, Any
5
6
  from collections.abc import Sequence
7
+ from typing import Union, Any
6
8
 
7
9
  import pyvex
8
10
  import archinfo
9
- from angr.knowledge_plugins import Function
10
11
 
11
12
  from . import Analysis
12
13
 
14
+ from angr.analyses import AnalysesHub
13
15
  from angr.errors import AngrTypeError
16
+ from angr.knowledge_plugins import Function
14
17
  from angr.utils.library import get_cpp_function_name
15
18
  from angr.utils.formatting import ansi_color_enabled, ansi_color, add_edge_to_buffer
16
19
  from angr.block import DisassemblerInsn, CapstoneInsn, SootBlockNode
17
20
  from angr.codenode import BlockNode
18
21
  from .disassembly_utils import decode_instruction
19
- import contextlib
20
22
 
21
23
  try:
22
24
  from angr.engines import pcode
@@ -1295,6 +1297,4 @@ class Disassembly(Analysis):
1295
1297
  return "\n".join(buf)
1296
1298
 
1297
1299
 
1298
- from angr.analyses import AnalysesHub
1299
-
1300
1300
  AnalysesHub.register_default("Disassembly", Disassembly)
@@ -0,0 +1,4 @@
1
+ from __future__ import annotations
2
+ from .fcp import FastConstantPropagation
3
+
4
+ __all__ = ["FastConstantPropagation"]