angr 9.2.135__py3-none-manylinux2014_x86_64.whl → 9.2.137__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 (198) 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 +6 -4
  10. angr/analyses/calling_convention/fact_collector.py +10 -3
  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 +40 -68
  15. angr/analyses/cfg/cfg_emulated.py +1 -104
  16. angr/analyses/cfg/cfg_fast.py +90 -27
  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 +65 -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/class_identifier.py +1 -2
  26. angr/analyses/complete_calling_conventions.py +3 -0
  27. angr/analyses/congruency_check.py +2 -3
  28. angr/analyses/data_dep/data_dependency_analysis.py +2 -2
  29. angr/analyses/ddg.py +1 -4
  30. angr/analyses/decompiler/ail_simplifier.py +15 -5
  31. angr/analyses/decompiler/block_simplifier.py +2 -2
  32. angr/analyses/decompiler/ccall_rewriters/__init__.py +2 -0
  33. angr/analyses/decompiler/ccall_rewriters/amd64_ccalls.py +1 -1
  34. angr/analyses/decompiler/ccall_rewriters/x86_ccalls.py +69 -0
  35. angr/analyses/decompiler/clinic.py +119 -72
  36. angr/analyses/decompiler/condition_processor.py +2 -0
  37. angr/analyses/decompiler/decompiler.py +1 -0
  38. angr/analyses/decompiler/dephication/dephication_base.py +2 -0
  39. angr/analyses/decompiler/dephication/rewriting_engine.py +8 -6
  40. angr/analyses/decompiler/dephication/seqnode_dephication.py +10 -1
  41. angr/analyses/decompiler/optimization_passes/duplication_reverter/ail_merge_graph.py +2 -2
  42. angr/analyses/decompiler/optimization_passes/duplication_reverter/duplication_reverter.py +2 -2
  43. angr/analyses/decompiler/optimization_passes/ite_region_converter.py +1 -1
  44. angr/analyses/decompiler/optimization_passes/lowered_switch_simplifier.py +1 -1
  45. angr/analyses/decompiler/optimization_passes/return_duplicator_base.py +1 -2
  46. angr/analyses/decompiler/optimization_passes/stack_canary_simplifier.py +1 -1
  47. angr/analyses/decompiler/sequence_walker.py +6 -2
  48. angr/analyses/decompiler/ssailification/rewriting.py +11 -1
  49. angr/analyses/decompiler/ssailification/rewriting_engine.py +56 -19
  50. angr/analyses/decompiler/ssailification/ssailification.py +13 -3
  51. angr/analyses/decompiler/ssailification/traversal.py +28 -2
  52. angr/analyses/decompiler/ssailification/traversal_state.py +6 -1
  53. angr/analyses/decompiler/structured_codegen/c.py +44 -21
  54. angr/analyses/decompiler/structuring/phoenix.py +118 -15
  55. angr/analyses/decompiler/utils.py +113 -8
  56. angr/analyses/disassembly.py +5 -5
  57. angr/analyses/fcp/__init__.py +4 -0
  58. angr/analyses/fcp/fcp.py +429 -0
  59. angr/analyses/identifier/identify.py +1 -3
  60. angr/analyses/loopfinder.py +4 -3
  61. angr/analyses/patchfinder.py +1 -1
  62. angr/analyses/propagator/engine_base.py +4 -3
  63. angr/analyses/propagator/propagator.py +14 -53
  64. angr/analyses/reaching_definitions/function_handler.py +1 -1
  65. angr/analyses/reassembler.py +1 -2
  66. angr/analyses/s_liveness.py +5 -1
  67. angr/analyses/s_propagator.py +26 -7
  68. angr/analyses/s_reaching_definitions/s_rda_model.py +2 -1
  69. angr/analyses/s_reaching_definitions/s_rda_view.py +20 -1
  70. angr/analyses/s_reaching_definitions/s_reaching_definitions.py +11 -1
  71. angr/analyses/soot_class_hierarchy.py +1 -2
  72. angr/analyses/stack_pointer_tracker.py +29 -3
  73. angr/analyses/static_hooker.py +1 -2
  74. angr/analyses/typehoon/simple_solver.py +2 -2
  75. angr/analyses/variable_recovery/engine_ail.py +19 -7
  76. angr/analyses/variable_recovery/engine_base.py +16 -14
  77. angr/analyses/variable_recovery/engine_vex.py +2 -2
  78. angr/analyses/variable_recovery/variable_recovery_fast.py +23 -3
  79. angr/analyses/veritesting.py +4 -7
  80. angr/analyses/vfg.py +1 -1
  81. angr/analyses/vsa_ddg.py +1 -2
  82. angr/block.py +62 -22
  83. angr/callable.py +1 -3
  84. angr/calling_conventions.py +3 -3
  85. angr/codenode.py +5 -1
  86. angr/concretization_strategies/__init__.py +1 -83
  87. angr/concretization_strategies/any.py +2 -1
  88. angr/concretization_strategies/any_named.py +1 -1
  89. angr/concretization_strategies/base.py +81 -0
  90. angr/concretization_strategies/controlled_data.py +2 -1
  91. angr/concretization_strategies/eval.py +2 -1
  92. angr/concretization_strategies/logging.py +3 -1
  93. angr/concretization_strategies/max.py +2 -1
  94. angr/concretization_strategies/nonzero.py +2 -1
  95. angr/concretization_strategies/nonzero_range.py +2 -1
  96. angr/concretization_strategies/norepeats.py +2 -1
  97. angr/concretization_strategies/norepeats_range.py +2 -1
  98. angr/concretization_strategies/range.py +2 -1
  99. angr/concretization_strategies/signed_add.py +2 -1
  100. angr/concretization_strategies/single.py +2 -1
  101. angr/concretization_strategies/solutions.py +2 -1
  102. angr/concretization_strategies/unlimited_range.py +2 -1
  103. angr/engines/__init__.py +8 -5
  104. angr/engines/engine.py +3 -5
  105. angr/engines/failure.py +4 -5
  106. angr/engines/pcode/emulate.py +1 -1
  107. angr/engines/pcode/lifter.py +31 -18
  108. angr/engines/procedure.py +5 -7
  109. angr/engines/soot/expressions/__init__.py +20 -23
  110. angr/engines/soot/expressions/base.py +4 -4
  111. angr/engines/soot/expressions/invoke.py +1 -2
  112. angr/engines/soot/statements/__init__.py +10 -12
  113. angr/engines/soot/values/__init__.py +10 -12
  114. angr/engines/soot/values/arrayref.py +3 -3
  115. angr/engines/soot/values/instancefieldref.py +3 -2
  116. angr/engines/successors.py +18 -12
  117. angr/engines/syscall.py +4 -6
  118. angr/engines/unicorn.py +3 -2
  119. angr/engines/vex/claripy/ccall.py +8 -10
  120. angr/engines/vex/claripy/datalayer.py +4 -5
  121. angr/engines/vex/lifter.py +9 -6
  122. angr/exploration_techniques/__init__.py +0 -2
  123. angr/exploration_techniques/spiller.py +1 -3
  124. angr/exploration_techniques/stochastic.py +2 -3
  125. angr/factory.py +3 -9
  126. angr/flirt/build_sig.py +8 -15
  127. angr/knowledge_plugins/cfg/cfg_model.py +20 -17
  128. angr/knowledge_plugins/functions/function.py +70 -79
  129. angr/knowledge_plugins/functions/function_manager.py +8 -7
  130. angr/knowledge_plugins/functions/function_parser.py +1 -1
  131. angr/knowledge_plugins/functions/soot_function.py +21 -24
  132. angr/knowledge_plugins/propagations/propagation_model.py +4 -5
  133. angr/knowledge_plugins/propagations/states.py +0 -511
  134. angr/knowledge_plugins/variables/variable_manager.py +16 -10
  135. angr/procedures/libc/memcpy.py +4 -4
  136. angr/procedures/procedure_dict.py +3 -2
  137. angr/protos/__init__.py +2 -5
  138. angr/protos/cfg_pb2.py +21 -18
  139. angr/protos/function_pb2.py +17 -14
  140. angr/protos/primitives_pb2.py +44 -39
  141. angr/protos/variables_pb2.py +36 -31
  142. angr/protos/xrefs_pb2.py +15 -12
  143. angr/sim_procedure.py +15 -16
  144. angr/sim_variable.py +13 -1
  145. angr/simos/__init__.py +2 -0
  146. angr/simos/javavm.py +4 -6
  147. angr/simos/xbox.py +32 -0
  148. angr/state_plugins/__init__.py +0 -2
  149. angr/state_plugins/callstack.py +4 -4
  150. angr/state_plugins/cgc.py +3 -2
  151. angr/state_plugins/gdb.py +6 -5
  152. angr/state_plugins/globals.py +1 -2
  153. angr/state_plugins/heap/heap_brk.py +1 -2
  154. angr/state_plugins/history.py +10 -12
  155. angr/state_plugins/inspect.py +3 -5
  156. angr/state_plugins/libc.py +2 -2
  157. angr/state_plugins/log.py +8 -10
  158. angr/state_plugins/loop_data.py +1 -2
  159. angr/state_plugins/posix.py +7 -7
  160. angr/state_plugins/preconstrainer.py +2 -3
  161. angr/state_plugins/scratch.py +5 -8
  162. angr/state_plugins/sim_action.py +3 -3
  163. angr/state_plugins/solver.py +8 -3
  164. angr/state_plugins/symbolizer.py +5 -4
  165. angr/state_plugins/uc_manager.py +3 -3
  166. angr/state_plugins/unicorn_engine.py +5 -1
  167. angr/state_plugins/view.py +3 -5
  168. angr/storage/file.py +3 -5
  169. angr/storage/memory_mixins/address_concretization_mixin.py +2 -2
  170. angr/storage/memory_mixins/bvv_conversion_mixin.py +3 -3
  171. angr/storage/memory_mixins/clouseau_mixin.py +1 -3
  172. angr/storage/memory_mixins/name_resolution_mixin.py +1 -3
  173. angr/storage/memory_mixins/paged_memory/paged_memory_mixin.py +13 -15
  174. angr/storage/memory_mixins/paged_memory/pages/__init__.py +1 -22
  175. angr/storage/memory_mixins/paged_memory/pages/base.py +31 -0
  176. angr/storage/memory_mixins/paged_memory/pages/list_page.py +1 -1
  177. angr/storage/memory_mixins/paged_memory/pages/mv_list_page.py +1 -1
  178. angr/storage/memory_mixins/paged_memory/pages/ultra_page.py +2 -4
  179. angr/storage/memory_mixins/paged_memory/privileged_mixin.py +3 -4
  180. angr/storage/memory_mixins/regioned_memory/abstract_merger_mixin.py +4 -2
  181. angr/storage/memory_mixins/smart_find_mixin.py +1 -1
  182. angr/storage/memory_mixins/underconstrained_mixin.py +1 -1
  183. angr/storage/memory_mixins/unwrapper_mixin.py +1 -3
  184. angr/utils/enums_conv.py +28 -12
  185. angr/utils/segment_list.py +25 -22
  186. angr/utils/timing.py +18 -1
  187. angr/vaults.py +5 -6
  188. {angr-9.2.135.dist-info → angr-9.2.137.dist-info}/METADATA +7 -7
  189. {angr-9.2.135.dist-info → angr-9.2.137.dist-info}/RECORD +193 -191
  190. {angr-9.2.135.dist-info → angr-9.2.137.dist-info}/WHEEL +1 -1
  191. angr/analyses/propagator/outdated_definition_walker.py +0 -159
  192. angr/analyses/propagator/tmpvar_finder.py +0 -18
  193. angr/engines/concrete.py +0 -180
  194. angr/exploration_techniques/symbion.py +0 -80
  195. angr/state_plugins/concrete.py +0 -295
  196. {angr-9.2.135.dist-info → angr-9.2.137.dist-info}/LICENSE +0 -0
  197. {angr-9.2.135.dist-info → angr-9.2.137.dist-info}/entry_points.txt +0 -0
  198. {angr-9.2.135.dist-info → angr-9.2.137.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,429 @@
1
+ from __future__ import annotations
2
+ from typing import Any
3
+ from collections.abc import Callable
4
+ from collections import defaultdict
5
+
6
+ import networkx
7
+ import pyvex
8
+ import claripy
9
+
10
+ from angr.utils.bits import s2u
11
+ from angr.block import Block
12
+ from angr.analyses.analysis import Analysis
13
+ from angr.analyses import AnalysesHub
14
+ from angr.knowledge_plugins.functions import Function
15
+ from angr.codenode import BlockNode, HookNode
16
+ from angr.engines.light import SimEngineNostmtVEX, SimEngineLight, SpOffset, RegisterOffset
17
+ from angr.calling_conventions import SimStackArg, default_cc
18
+ from angr.analyses.propagator.vex_vars import VEXReg, VEXTmp
19
+
20
+
21
+ class SV:
22
+ """
23
+ SizedValue: A faster implementation of claripy.ast.BV.
24
+ """
25
+
26
+ __slots__ = ("bits", "value")
27
+
28
+ def __init__(self, value, bits):
29
+ self.value = value
30
+ self.bits = bits
31
+
32
+ def __eq__(self, other):
33
+ return isinstance(other, SV) and self.value == other.value and self.bits == other.bits
34
+
35
+ def __hash__(self):
36
+ return hash((self.value, self.bits))
37
+
38
+
39
+ class FCPState:
40
+ """
41
+ The abstract state for FastConstantPropagation.
42
+ """
43
+
44
+ __slots__ = (
45
+ "bp_value",
46
+ "callee_stored_regs",
47
+ "regs",
48
+ "simple_stack",
49
+ "sp_value",
50
+ "stack",
51
+ "tmps",
52
+ )
53
+
54
+ def __init__(self):
55
+ self.tmps = {}
56
+ self.simple_stack = {}
57
+
58
+ self.regs: dict[int, SV] = {}
59
+ self.stack: dict[int, SV] = {}
60
+ self.sp_value = 0
61
+ self.bp_value = 0
62
+
63
+ def register_read(self, offset, size_in_bytes: int) -> int | None:
64
+ if offset in self.regs:
65
+ v = self.regs[offset]
66
+ if v.bits == size_in_bytes * 8:
67
+ return v.value
68
+ return None
69
+
70
+ def register_written(self, offset: int, size_in_bytes: int, value: int | None):
71
+ if value is None:
72
+ to_remove = set()
73
+ for off, v in self.regs.items():
74
+ if (off <= offset < off + v.bits // 8) or (offset <= off < offset + size_in_bytes):
75
+ to_remove.add(off)
76
+ for off in to_remove:
77
+ del self.regs[off]
78
+ else:
79
+ self.regs[offset] = SV(value, size_in_bytes * 8)
80
+
81
+ def stack_read(self, offset: int, size_int_bytes: int) -> int | None:
82
+ if offset in self.stack:
83
+ v = self.stack[offset]
84
+ if v.bits == size_int_bytes * 8:
85
+ return v.value
86
+ return None
87
+
88
+ def stack_written(self, offset: int, size_int_bytes: int, value: int | None):
89
+ if value is None:
90
+ to_remove = set()
91
+ for off, v in self.stack.items():
92
+ if (off <= offset < off + v.bits // 8) or (offset <= off < offset + size_int_bytes):
93
+ to_remove.add(off)
94
+ for off in to_remove:
95
+ del self.stack[off]
96
+ else:
97
+ self.stack[offset] = SV(value, size_int_bytes * 8)
98
+
99
+ def copy(self, with_tmps: bool = False) -> FCPState:
100
+ new_state = FCPState()
101
+ new_state.stack = self.stack.copy()
102
+ new_state.regs = self.regs.copy()
103
+ new_state.sp_value = self.sp_value
104
+ new_state.bp_value = self.bp_value
105
+ new_state.simple_stack = self.simple_stack.copy()
106
+ if with_tmps:
107
+ new_state.tmps = self.tmps.copy()
108
+ return new_state
109
+
110
+
111
+ binop_handler = SimEngineNostmtVEX[FCPState, claripy.ast.BV, FCPState].binop_handler
112
+
113
+
114
+ class SimEngineFCPVEX(
115
+ SimEngineNostmtVEX[FCPState, SpOffset | RegisterOffset | int, None],
116
+ SimEngineLight[type[FCPState], SpOffset | RegisterOffset | int, Block, None],
117
+ ):
118
+ """
119
+ THe engine for FastConstantPropagation.
120
+ """
121
+
122
+ def __init__(self, project, bp_as_gpr: bool, replacements: dict[int, dict], load_callback: Callable | None = None):
123
+ self.bp_as_gpr = bp_as_gpr
124
+ self.replacements = replacements
125
+ self._load_callback = load_callback
126
+ self.base_state = None
127
+ super().__init__(project)
128
+
129
+ def _allow_loading(self, addr: int, size: int) -> bool:
130
+ if self._load_callback is None:
131
+ return True
132
+ return self._load_callback(addr, size)
133
+
134
+ def _process_block_end(self, stmt_result: list, whitelist: set[int] | None) -> None:
135
+ if self.block.vex.jumpkind == "Ijk_Call":
136
+ self.state.register_written(self.arch.ret_offset, self.arch.bytes, None)
137
+
138
+ def _top(self, bits: int):
139
+ return None
140
+
141
+ def _is_top(self, expr: Any) -> bool:
142
+ raise NotImplementedError
143
+
144
+ def _handle_conversion(self, from_size: int, to_size: int, signed: bool, operand: pyvex.IRExpr) -> Any:
145
+ return None
146
+
147
+ def _handle_stmt_Put(self, stmt):
148
+ v = self._expr(stmt.data)
149
+ if stmt.offset == self.arch.sp_offset and isinstance(v, SpOffset):
150
+ self.state.sp_value = v.offset
151
+ elif stmt.offset == self.arch.bp_offset and not self.bp_as_gpr and isinstance(v, SpOffset):
152
+ self.state.bp_value = v.offset
153
+ elif isinstance(v, int):
154
+ size = stmt.data.result_size(self.tyenv) // self.arch.byte_width
155
+ codeloc = self._codeloc()
156
+ self.state.register_written(stmt.offset, size, v)
157
+ if stmt.offset != self.arch.ip_offset:
158
+ self.replacements[codeloc][VEXReg(stmt.offset, size)] = v
159
+ else:
160
+ size = stmt.data.result_size(self.tyenv) // self.arch.byte_width
161
+ self.state.register_written(stmt.offset, size, None)
162
+
163
+ def _handle_stmt_Store(self, stmt: pyvex.IRStmt.Store):
164
+ addr = self._expr(stmt.addr)
165
+ if isinstance(addr, SpOffset):
166
+ data = self._expr(stmt.data)
167
+ if isinstance(data, int):
168
+ self.state.stack_written(addr.offset, stmt.data.result_size(self.tyenv) // self.arch.byte_width, data)
169
+ else:
170
+ self.state.stack_written(addr.offset, stmt.data.result_size(self.tyenv) // self.arch.byte_width, None)
171
+
172
+ def _handle_stmt_WrTmp(self, stmt: pyvex.IRStmt.WrTmp):
173
+ if isinstance(stmt.data, pyvex.IRExpr.Binop) and not (
174
+ stmt.data.op.startswith("Iop_Add")
175
+ or stmt.data.op.startswith("Iop_Sub")
176
+ or stmt.data.op.startswith("Iop_And")
177
+ ):
178
+ return
179
+ v = self._expr(stmt.data)
180
+ if v is not None:
181
+ self.state.tmps[stmt.tmp] = v
182
+ if isinstance(v, int):
183
+ codeloc = self._codeloc()
184
+ self.replacements[codeloc][VEXTmp(stmt.tmp)] = v
185
+
186
+ def _handle_expr_Const(self, expr: pyvex.IRExpr.Const):
187
+ return expr.con.value
188
+
189
+ def _handle_expr_GSPTR(self, expr):
190
+ return None
191
+
192
+ def _handle_expr_Get(self, expr) -> SpOffset | None:
193
+ if expr.offset == self.arch.sp_offset:
194
+ return SpOffset(self.arch.bits, self.state.sp_value, is_base=False)
195
+ if expr.offset == self.arch.bp_offset and not self.bp_as_gpr:
196
+ return SpOffset(self.arch.bits, self.state.bp_value, is_base=False)
197
+ size = expr.result_size(self.tyenv) // self.arch.byte_width
198
+ v = self.state.register_read(expr.offset, size)
199
+ if v is not None:
200
+ codeloc = self._codeloc()
201
+ self.replacements[codeloc][VEXReg(expr.offset, size)] = v
202
+ return v
203
+
204
+ def _handle_expr_GetI(self, expr):
205
+ return None
206
+
207
+ def _handle_expr_ITE(self, expr):
208
+ return None
209
+
210
+ def _handle_expr_Load(self, expr) -> int | SpOffset | None:
211
+ addr = self._expr(expr.addr)
212
+ if isinstance(addr, SpOffset):
213
+ return self.state.stack.get(addr.offset)
214
+ if isinstance(addr, int):
215
+ size = expr.result_size(self.tyenv) // self.arch.byte_width
216
+ if self._allow_loading(addr, size):
217
+ # Try loading from the state
218
+ if self.base_state is not None:
219
+ data = self.base_state.memory.load(addr, size, endness=expr.endness)
220
+ if not data.symbolic:
221
+ return data.args[0]
222
+ else:
223
+ try:
224
+ return self.project.loader.memory.unpack_word(addr, size=size, endness=expr.endness)
225
+ except KeyError:
226
+ pass
227
+ return None
228
+
229
+ def _handle_expr_RdTmp(self, expr):
230
+ return self.state.tmps.get(expr.tmp, None)
231
+
232
+ def _dummy_handler(self, expr): # pylint:disable=unused-argument,no-self-use
233
+ return None
234
+
235
+ _handle_expr_VECRET = _dummy_handler
236
+ _handle_expr_CCall = _dummy_handler
237
+ _handle_expr_Unop = _dummy_handler
238
+ _handle_expr_Triop = _dummy_handler
239
+
240
+ @binop_handler
241
+ def _handle_binop_Add(self, expr):
242
+ op0, op1 = self._expr(expr.args[0]), self._expr(expr.args[1])
243
+ if isinstance(op0, SpOffset) and isinstance(op1, int):
244
+ return SpOffset(op0.bits, s2u(op0.offset + op1, op0.bits), is_base=op0.is_base)
245
+ if isinstance(op1, SpOffset) and isinstance(op0, int):
246
+ return SpOffset(op1.bits, s2u(op1.offset + op0, op1.bits), is_base=op1.is_base)
247
+ if isinstance(op0, int) and isinstance(op1, int):
248
+ mask = (1 << expr.result_size(self.tyenv)) - 1
249
+ return (op0 + op1) & mask
250
+ return None
251
+
252
+ @binop_handler
253
+ def _handle_binop_Sub(self, expr):
254
+ op0, op1 = self._expr(expr.args[0]), self._expr(expr.args[1])
255
+ if isinstance(op0, SpOffset) and isinstance(op1, int):
256
+ return SpOffset(op0.bits, s2u(op0.offset - op1, op0.bits), is_base=op0.is_base)
257
+ if isinstance(op1, SpOffset) and isinstance(op0, int):
258
+ return SpOffset(op1.bits, s2u(op1.offset - op0, op1.bits), is_base=op1.is_base)
259
+ if isinstance(op0, int) and isinstance(op1, int):
260
+ mask = (1 << expr.result_size(self.tyenv)) - 1
261
+ return (op0 - op1) & mask
262
+ return None
263
+
264
+ @binop_handler
265
+ def _handle_binop_And(self, expr):
266
+ op0, op1 = self._expr(expr.args[0]), self._expr(expr.args[1])
267
+ if isinstance(op0, SpOffset):
268
+ return op0
269
+ if isinstance(op1, SpOffset):
270
+ return op1
271
+ if isinstance(op0, int) and isinstance(op1, int):
272
+ return op0 & op1
273
+ return None
274
+
275
+
276
+ class FastConstantPropagation(Analysis):
277
+ """
278
+ An extremely fast constant propagation analysis that finds function-wide constant values with potentially high
279
+ false negative rates.
280
+ """
281
+
282
+ def __init__(
283
+ self,
284
+ func: Function,
285
+ blocks: set[Block] | None = None,
286
+ vex_cross_insn_opt: bool = False,
287
+ load_callback: Callable | None = None,
288
+ ):
289
+ self.function = func
290
+ self._blocks = blocks
291
+ self._vex_cross_insn_opt = vex_cross_insn_opt
292
+ self._load_callback = load_callback
293
+
294
+ self.replacements = {}
295
+
296
+ self._analyze()
297
+
298
+ def _analyze(self):
299
+ # traverse the function graph, collect registers and stack variables that are written to
300
+ func_graph = self.function.graph
301
+ func_graph_with_callees = self.function.transition_graph
302
+ startpoint = self.function.startpoint
303
+ bp_as_gpr = self.function.info.get("bp_as_gpr", False)
304
+ replacements = defaultdict(dict)
305
+ engine = SimEngineFCPVEX(self.project, bp_as_gpr, replacements, load_callback=self._load_callback)
306
+ init_state = FCPState()
307
+ if self.project.arch.call_pushes_ret:
308
+ init_state.sp_value = self.project.arch.bytes
309
+ init_state.bp_value = init_state.sp_value
310
+
311
+ sorted_nodes = reversed(list(networkx.dfs_postorder_nodes(func_graph, startpoint)))
312
+ block_addrs = None
313
+ if self._blocks:
314
+ block_addrs = {b.addr for b in self._blocks}
315
+
316
+ states: dict[BlockNode, FCPState] = {}
317
+ for node in sorted_nodes:
318
+ preds = func_graph.predecessors(node)
319
+ input_states = [states[pred] for pred in preds if pred in states]
320
+ state = init_state.copy() if not input_states else self._merge_states(input_states)
321
+
322
+ if self._blocks and node.addr not in block_addrs:
323
+ # skip this block
324
+ states[node] = state
325
+ continue
326
+
327
+ # process the block
328
+ if isinstance(node, BlockNode) and node.size == 0:
329
+ continue
330
+ if isinstance(node, HookNode):
331
+ # attempt to convert it into a function
332
+ if self.kb.functions.contains_addr(node.addr):
333
+ node = self.kb.functions.get_by_addr(node.addr)
334
+ else:
335
+ continue
336
+ if isinstance(node, Function):
337
+ if node.calling_convention is not None and node.prototype is not None:
338
+ # consume args and overwrite the return register
339
+ self._handle_function(state, node)
340
+ continue
341
+
342
+ block = self.project.factory.block(node.addr, size=node.size, cross_insn_opt=self._vex_cross_insn_opt)
343
+ engine.process(state, block=block)
344
+
345
+ # if the node ends with a function call, call _handle_function
346
+ succs = list(func_graph_with_callees.successors(node))
347
+ if any(isinstance(succ, (Function, HookNode)) for succ in succs):
348
+ callee = next(succ for succ in succs if isinstance(succ, (Function, HookNode)))
349
+ if isinstance(callee, HookNode):
350
+ # attempt to convert it into a function
351
+ if self.kb.functions.contains_addr(callee.addr):
352
+ callee = self.kb.functions.get_by_addr(callee.addr)
353
+ else:
354
+ callee = None
355
+ state = self._handle_function(state, callee)
356
+
357
+ states[node] = state
358
+
359
+ self.replacements = replacements
360
+
361
+ @staticmethod
362
+ def _merge_states(states: list[FCPState]) -> FCPState:
363
+ state = FCPState()
364
+ to_drop = set()
365
+ common_keys = set.intersection(*[set(s.regs) for s in states])
366
+ for s in states:
367
+ for offset, value in s.regs.items():
368
+ if offset in common_keys:
369
+ if offset in state.regs:
370
+ if state.regs[offset] != value:
371
+ to_drop.add(offset)
372
+ else:
373
+ state.regs[offset] = value
374
+ for offset in to_drop:
375
+ del state.regs[offset]
376
+
377
+ to_drop = set()
378
+ common_keys = set.intersection(*[set(s.stack) for s in states])
379
+ for s in states:
380
+ for offset, value in s.stack.items():
381
+ if offset in common_keys:
382
+ if offset in state.stack:
383
+ if state.stack[offset] != value:
384
+ to_drop.add(offset)
385
+ else:
386
+ state.stack[offset] = value
387
+ for offset in to_drop:
388
+ del state.stack[offset]
389
+
390
+ for s in states:
391
+ state.sp_value = max(state.sp_value, s.sp_value)
392
+ state.bp_value = max(state.bp_value, s.bp_value)
393
+ return state
394
+
395
+ def _handle_function(self, state: FCPState, func: Function | None) -> FCPState:
396
+
397
+ if func is None or func.calling_convention is None:
398
+ cc = default_cc(self.project.arch.name)
399
+ else:
400
+ cc = func.calling_convention
401
+
402
+ out_state = state.copy()
403
+ if func is not None and func.prototype is not None:
404
+ arg_locs = None
405
+ try:
406
+ arg_locs = cc.arg_locs(func.prototype)
407
+ except (TypeError, ValueError):
408
+ arg_locs = None
409
+
410
+ if None in arg_locs:
411
+ arg_locs = None
412
+
413
+ if arg_locs is not None:
414
+ for arg_loc in arg_locs:
415
+ for loc in arg_loc.get_footprint():
416
+ if isinstance(loc, SimStackArg):
417
+ sp_value = out_state.sp_value
418
+ if sp_value is not None:
419
+ out_state.stack_read(sp_value + loc.stack_offset, loc.size)
420
+
421
+ # clobber caller-saved regs
422
+ for reg_name in cc.CALLER_SAVED_REGS:
423
+ offset = self.project.arch.registers[reg_name][0]
424
+ out_state.register_written(offset, self.project.arch.registers[reg_name][1], None)
425
+
426
+ return out_state
427
+
428
+
429
+ AnalysesHub.register_default("FastConstantPropagation", FastConstantPropagation)
@@ -10,7 +10,7 @@ from networkx import NetworkXError
10
10
  from .errors import IdentifierException
11
11
  from .functions import Functions
12
12
  from .runner import Runner
13
- from angr.analyses import Analysis
13
+ from angr.analyses import Analysis, AnalysesHub
14
14
  from angr import options
15
15
  from angr.errors import AngrError, SimSegfaultError, SimEngineError, SimMemoryError, SimError
16
16
 
@@ -822,6 +822,4 @@ class Identifier(Analysis):
822
822
  return symbolic_state
823
823
 
824
824
 
825
- from angr.analyses import AnalysesHub
826
-
827
825
  AnalysesHub.register_default("Identifier", Identifier)
@@ -1,9 +1,12 @@
1
1
  from __future__ import annotations
2
+
3
+ import contextlib
2
4
  import logging
3
5
 
4
6
  import networkx
7
+
8
+ from angr.analyses import AnalysesHub
5
9
  from . import Analysis
6
- import contextlib
7
10
 
8
11
  l = logging.getLogger(name=__name__)
9
12
 
@@ -165,6 +168,4 @@ class LoopFinder(Analysis):
165
168
  return outtop, outall
166
169
 
167
170
 
168
- from angr.analyses import AnalysesHub
169
-
170
171
  AnalysesHub.register_default("LoopFinder", LoopFinder)
@@ -34,7 +34,7 @@ class OverlappingFunctionsAnalysis(Analysis):
34
34
  func_max_addr = max((block.addr + block.size) for block in func.blocks)
35
35
  addr_to_func_max_addr[func.addr] = (func, func_max_addr)
36
36
 
37
- for idx, (addr, (func, max_addr)) in enumerate(addr_to_func_max_addr.items()):
37
+ for idx, (addr, (_func, max_addr)) in enumerate(addr_to_func_max_addr.items()):
38
38
  for other_addr in addr_to_func_max_addr.islice(idx + 1):
39
39
  if other_addr >= max_addr:
40
40
  break
@@ -19,13 +19,16 @@ l = logging.getLogger(name=__name__)
19
19
  class SimEnginePropagatorBaseMixin(
20
20
  Generic[StateType, DataType_co, BlockType], SimEngineLight[StateType, DataType_co, BlockType, StateType]
21
21
  ): # pylint:disable=abstract-method
22
+ """
23
+ The base class for the propagator VEX engine.
24
+ """
25
+
22
26
  def __init__(
23
27
  self,
24
28
  project: Project,
25
29
  stack_pointer_tracker=None,
26
30
  propagate_tmps=True,
27
31
  reaching_definitions: ReachingDefinitionsModel | None = None,
28
- immediate_stmt_removal: bool = False,
29
32
  bp_as_gpr: bool = False,
30
33
  ):
31
34
  super().__init__(project)
@@ -36,9 +39,7 @@ class SimEnginePropagatorBaseMixin(
36
39
  self._load_callback = None
37
40
  self._propagate_tmps: bool = propagate_tmps
38
41
  self._reaching_definitions = reaching_definitions
39
- self._immediate_stmt_removal = immediate_stmt_removal
40
42
  self.bp_as_gpr = bp_as_gpr
41
- self.stmts_to_remove = set()
42
43
 
43
44
  # Used in the AIL engine
44
45
  self._stack_pointer_tracker = stack_pointer_tracker
@@ -1,6 +1,6 @@
1
1
  # pylint:disable=isinstance-second-argument-not-valid-type
2
2
  from __future__ import annotations
3
- from typing import Any, TYPE_CHECKING
3
+ from typing import Any
4
4
  import logging
5
5
  import time
6
6
  import contextlib
@@ -12,16 +12,12 @@ import pyvex
12
12
  from angr.code_location import CodeLocation
13
13
  from angr.analyses import ForwardAnalysis, visitors
14
14
  from angr.knowledge_plugins.propagations.propagation_model import PropagationModel
15
- from angr.knowledge_plugins.propagations.prop_value import PropValue, Detail
16
- from angr.knowledge_plugins.propagations.states import PropagatorAILState, PropagatorVEXState, PropagatorState
15
+ from angr.knowledge_plugins.propagations.states import PropagatorVEXState, PropagatorState
17
16
  from angr import sim_options
18
17
  from angr.analyses import register_analysis
19
18
  from angr.analyses.analysis import Analysis
20
19
  from .engine_vex import SimEnginePropagatorVEX
21
20
 
22
- if TYPE_CHECKING:
23
- from angr.analyses.reaching_definitions.reaching_definitions import ReachingDefinitionsModel
24
-
25
21
 
26
22
  _l = logging.getLogger(name=__name__)
27
23
 
@@ -31,8 +27,7 @@ class PropagatorAnalysis(ForwardAnalysis, Analysis): # pylint:disable=abstract-
31
27
  PropagatorAnalysis implements copy propagation. It propagates values (either constant values or variables) and
32
28
  expressions inside a block or across a function.
33
29
 
34
- PropagatorAnalysis supports both VEX and AIL. The VEX propagator only performs constant propagation. The AIL
35
- propagator performs both constant propagation and copy propagation of depth-N expressions.
30
+ PropagatorAnalysis only supports VEX. For AIL, please use SPropagator.
36
31
 
37
32
  PropagatorAnalysis performs certain arithmetic operations between constants, including but are not limited to:
38
33
 
@@ -66,8 +61,6 @@ class PropagatorAnalysis(ForwardAnalysis, Analysis): # pylint:disable=abstract-
66
61
  gp: int | None = None,
67
62
  cache_results: bool = False,
68
63
  key_prefix: str | None = None,
69
- reaching_definitions: ReachingDefinitionsModel | None = None,
70
- immediate_stmt_removal: bool = False,
71
64
  profiling: bool = False,
72
65
  ):
73
66
  if block is None and func is not None:
@@ -95,11 +88,9 @@ class PropagatorAnalysis(ForwardAnalysis, Analysis): # pylint:disable=abstract-
95
88
  self._do_binops = do_binops
96
89
  self._store_tops = store_tops
97
90
  self._vex_cross_insn_opt = vex_cross_insn_opt
98
- self._immediate_stmt_removal = immediate_stmt_removal
99
91
  self._gp = gp
100
92
  self._prop_key_prefix = key_prefix
101
93
  self._cache_results = cache_results
102
- self._reaching_definitions = reaching_definitions
103
94
  self._initial_codeloc: CodeLocation
104
95
  self.stmts_to_remove: set[CodeLocation] = set()
105
96
  if self.flavor == "function":
@@ -167,7 +158,6 @@ class PropagatorAnalysis(ForwardAnalysis, Analysis): # pylint:disable=abstract-
167
158
  # pyright says pylint is wrong about this
168
159
  self._engine_vex = SimEnginePropagatorVEX( # pylint: disable=abstract-class-instantiated
169
160
  project=self.project,
170
- reaching_definitions=self._reaching_definitions,
171
161
  bp_as_gpr=bp_as_gpr,
172
162
  )
173
163
 
@@ -176,7 +166,6 @@ class PropagatorAnalysis(ForwardAnalysis, Analysis): # pylint:disable=abstract-
176
166
 
177
167
  # performance counters
178
168
  self._analyzed_states: int = 0
179
- self._analyzed_statements: int = 0
180
169
 
181
170
  self._analyze()
182
171
 
@@ -188,12 +177,12 @@ class PropagatorAnalysis(ForwardAnalysis, Analysis): # pylint:disable=abstract-
188
177
  elapsed = time.perf_counter_ns() / 1000000 - start
189
178
  if self.flavor == "function":
190
179
  _l.warning("%r:", self._function)
180
+ _l.warning(" Blocks: %d", len(self._function.block_addrs_set))
191
181
  else:
192
182
  _l.warning("%r:", self._block)
193
- _l.warning(" Time elapsed: %s milliseconds", elapsed)
183
+ _l.warning(" Time elapsed: %0.02f milliseconds", elapsed)
194
184
  _l.warning(" Cache used: %s", cache_used)
195
185
  _l.warning(" Analyzed states: %d", self._analyzed_states)
196
- _l.warning(" Analyzed statements: %d", self._analyzed_statements)
197
186
 
198
187
  @property
199
188
  def prop_key(self) -> tuple[str | None, str, int, bool, bool, bool]:
@@ -232,7 +221,6 @@ class PropagatorAnalysis(ForwardAnalysis, Analysis): # pylint:disable=abstract-
232
221
  def _initial_abstract_state(self, node):
233
222
  self._initial_state = PropagatorVEXState.initial_state(
234
223
  self.project,
235
- rda=self._reaching_definitions,
236
224
  only_consts=self._only_consts,
237
225
  gp=self._gp,
238
226
  do_binops=self._do_binops,
@@ -261,7 +249,6 @@ class PropagatorAnalysis(ForwardAnalysis, Analysis): # pylint:disable=abstract-
261
249
  if state is not self._initial_state:
262
250
  # make a copy of the state if it's not the initial state
263
251
  state = state.copy()
264
- state._equivalence.clear()
265
252
  state.init_replacements()
266
253
  else:
267
254
  # clear self._initial_state so that we *do not* run this optimization again!
@@ -284,10 +271,6 @@ class PropagatorAnalysis(ForwardAnalysis, Analysis): # pylint:disable=abstract-
284
271
  )
285
272
  state.filter_replacements()
286
273
 
287
- if self._immediate_stmt_removal:
288
- self.stmts_to_remove |= engine.stmts_to_remove
289
- engine.stmts_to_remove = set()
290
-
291
274
  self.model.node_iterations[block_key] += 1
292
275
  self.model.states[block_key] = state
293
276
  self.model.block_initial_reg_values.update(state.block_initial_reg_values)
@@ -297,35 +280,19 @@ class PropagatorAnalysis(ForwardAnalysis, Analysis): # pylint:disable=abstract-
297
280
  else:
298
281
  PropagatorState.merge_replacements(self.model.replacements, state._replacements)
299
282
 
300
- self.model.equivalence |= state._equivalence
301
-
302
283
  # TODO: Clear registers according to calling conventions
303
284
 
304
285
  if self.model.node_iterations[block_key] < self._max_iterations:
305
286
  return None, state
306
287
  return False, state
307
288
 
308
- def _process_input_state_for_successor(self, node, successor, input_state: PropagatorAILState | PropagatorVEXState):
309
- if self._only_consts:
310
- if isinstance(input_state, PropagatorAILState):
311
- key = node.addr, successor.addr
312
- if key in self.model.block_initial_reg_values:
313
- input_state: PropagatorAILState = input_state.copy()
314
- for reg_atom, reg_value in self.model.block_initial_reg_values[key]:
315
- input_state.store_register(
316
- reg_atom,
317
- PropValue(
318
- claripy.BVV(reg_value.value, reg_value.bits),
319
- offset_and_details={0: Detail(reg_atom.size, reg_value, self._initial_codeloc)},
320
- ),
321
- )
322
- return input_state
323
- elif isinstance(input_state, PropagatorVEXState):
324
- key = node.addr, successor.addr
325
- if key in self.model.block_initial_reg_values:
326
- input_state: PropagatorVEXState = input_state.copy()
327
- for reg_offset, reg_size, value in self.model.block_initial_reg_values[key]:
328
- input_state.store_register(reg_offset, reg_size, claripy.BVV(value, reg_size * 8))
289
+ def _process_input_state_for_successor(self, node, successor, input_state: PropagatorVEXState):
290
+ if self._only_consts and isinstance(input_state, PropagatorVEXState):
291
+ key = node.addr, successor.addr
292
+ if key in self.model.block_initial_reg_values:
293
+ input_state: PropagatorVEXState = input_state.copy()
294
+ for reg_offset, reg_size, value in self.model.block_initial_reg_values[key]:
295
+ input_state.store_register(reg_offset, reg_size, claripy.BVV(value, reg_size * 8))
329
296
  return input_state
330
297
 
331
298
  def _intra_analysis(self):
@@ -361,14 +328,8 @@ class PropagatorAnalysis(ForwardAnalysis, Analysis): # pylint:disable=abstract-
361
328
  for codeloc in list(self.model.replacements.keys()):
362
329
  filtered_rep = {}
363
330
  for k, v in self.model.replacements[codeloc].items():
364
- if isinstance(v, claripy.ast.Base):
365
- # claripy expressions
366
- if not PropagatorState.is_top(v):
367
- filtered_rep[k] = v
368
- else:
369
- # AIL expressions
370
- if not PropagatorAILState.is_top(v):
371
- filtered_rep[k] = v
331
+ if isinstance(v, claripy.ast.Base) and not PropagatorState.is_top(v):
332
+ filtered_rep[k] = v
372
333
  self.model.replacements[codeloc] = filtered_rep
373
334
 
374
335
  if self._cache_results:
@@ -379,7 +379,7 @@ class FunctionHandler:
379
379
  else hook_libname
380
380
  )
381
381
  type_collections = []
382
- if prototype_libname is not None:
382
+ if prototype_libname is not None and prototype_libname in SIM_LIBRARIES:
383
383
  prototype_lib = SIM_LIBRARIES[prototype_libname]
384
384
  if prototype_lib.type_collection_names:
385
385
  for typelib_name in prototype_lib.type_collection_names: