angr 9.2.135__py3-none-win_amd64.whl → 9.2.137__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 (199) 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/lib/angr_native.dll +0 -0
  136. angr/procedures/libc/memcpy.py +4 -4
  137. angr/procedures/procedure_dict.py +3 -2
  138. angr/protos/__init__.py +2 -5
  139. angr/protos/cfg_pb2.py +21 -18
  140. angr/protos/function_pb2.py +17 -14
  141. angr/protos/primitives_pb2.py +44 -39
  142. angr/protos/variables_pb2.py +36 -31
  143. angr/protos/xrefs_pb2.py +15 -12
  144. angr/sim_procedure.py +15 -16
  145. angr/sim_variable.py +13 -1
  146. angr/simos/__init__.py +2 -0
  147. angr/simos/javavm.py +4 -6
  148. angr/simos/xbox.py +32 -0
  149. angr/state_plugins/__init__.py +0 -2
  150. angr/state_plugins/callstack.py +4 -4
  151. angr/state_plugins/cgc.py +3 -2
  152. angr/state_plugins/gdb.py +6 -5
  153. angr/state_plugins/globals.py +1 -2
  154. angr/state_plugins/heap/heap_brk.py +1 -2
  155. angr/state_plugins/history.py +10 -12
  156. angr/state_plugins/inspect.py +3 -5
  157. angr/state_plugins/libc.py +2 -2
  158. angr/state_plugins/log.py +8 -10
  159. angr/state_plugins/loop_data.py +1 -2
  160. angr/state_plugins/posix.py +7 -7
  161. angr/state_plugins/preconstrainer.py +2 -3
  162. angr/state_plugins/scratch.py +5 -8
  163. angr/state_plugins/sim_action.py +3 -3
  164. angr/state_plugins/solver.py +8 -3
  165. angr/state_plugins/symbolizer.py +5 -4
  166. angr/state_plugins/uc_manager.py +3 -3
  167. angr/state_plugins/unicorn_engine.py +5 -1
  168. angr/state_plugins/view.py +3 -5
  169. angr/storage/file.py +3 -5
  170. angr/storage/memory_mixins/address_concretization_mixin.py +2 -2
  171. angr/storage/memory_mixins/bvv_conversion_mixin.py +3 -3
  172. angr/storage/memory_mixins/clouseau_mixin.py +1 -3
  173. angr/storage/memory_mixins/name_resolution_mixin.py +1 -3
  174. angr/storage/memory_mixins/paged_memory/paged_memory_mixin.py +13 -15
  175. angr/storage/memory_mixins/paged_memory/pages/__init__.py +1 -22
  176. angr/storage/memory_mixins/paged_memory/pages/base.py +31 -0
  177. angr/storage/memory_mixins/paged_memory/pages/list_page.py +1 -1
  178. angr/storage/memory_mixins/paged_memory/pages/mv_list_page.py +1 -1
  179. angr/storage/memory_mixins/paged_memory/pages/ultra_page.py +2 -4
  180. angr/storage/memory_mixins/paged_memory/privileged_mixin.py +3 -4
  181. angr/storage/memory_mixins/regioned_memory/abstract_merger_mixin.py +4 -2
  182. angr/storage/memory_mixins/smart_find_mixin.py +1 -1
  183. angr/storage/memory_mixins/underconstrained_mixin.py +1 -1
  184. angr/storage/memory_mixins/unwrapper_mixin.py +1 -3
  185. angr/utils/enums_conv.py +28 -12
  186. angr/utils/segment_list.py +25 -22
  187. angr/utils/timing.py +18 -1
  188. angr/vaults.py +5 -6
  189. {angr-9.2.135.dist-info → angr-9.2.137.dist-info}/METADATA +7 -7
  190. {angr-9.2.135.dist-info → angr-9.2.137.dist-info}/RECORD +194 -192
  191. {angr-9.2.135.dist-info → angr-9.2.137.dist-info}/WHEEL +1 -1
  192. angr/analyses/propagator/outdated_definition_walker.py +0 -159
  193. angr/analyses/propagator/tmpvar_finder.py +0 -18
  194. angr/engines/concrete.py +0 -180
  195. angr/exploration_techniques/symbion.py +0 -80
  196. angr/state_plugins/concrete.py +0 -295
  197. {angr-9.2.135.dist-info → angr-9.2.137.dist-info}/LICENSE +0 -0
  198. {angr-9.2.135.dist-info → angr-9.2.137.dist-info}/entry_points.txt +0 -0
  199. {angr-9.2.135.dist-info → angr-9.2.137.dist-info}/top_level.txt +0 -0
@@ -17,7 +17,6 @@ from .manual_mergepoint import ManualMergepoint
17
17
  from .tech_builder import TechniqueBuilder
18
18
  from .stochastic import StochasticSearch
19
19
  from .unique import UniqueSearch
20
- from .symbion import Symbion
21
20
  from .memory_watcher import MemoryWatcher
22
21
  from .bucketizer import Bucketizer
23
22
  from .local_loop_seer import LocalLoopSeer
@@ -45,7 +44,6 @@ __all__ = (
45
44
  "StochasticSearch",
46
45
  "StubStasher",
47
46
  "Suggestions",
48
- "Symbion",
49
47
  "TechniqueBuilder",
50
48
  "Threading",
51
49
  "Timeout",
@@ -4,6 +4,7 @@ from __future__ import annotations
4
4
  import contextlib
5
5
  import logging
6
6
 
7
+ from angr import vaults
7
8
  from .base import ExplorationTechnique
8
9
 
9
10
 
@@ -277,6 +278,3 @@ class Spiller(ExplorationTechnique):
277
278
  @staticmethod
278
279
  def state_priority(state):
279
280
  return id(state)
280
-
281
-
282
- from angr import vaults
@@ -44,13 +44,12 @@ class StochasticSearch(ExplorationTechnique):
44
44
  assert len(states) >= 2
45
45
  total_weight = sum(self.affinity[s.addr] for s in states)
46
46
  selected = self._random.uniform(0, total_weight)
47
- i = 0
48
47
  for i, state in enumerate(states):
49
48
  weight = self.affinity[state.addr]
50
49
  if selected < weight:
51
- break
50
+ return states[i]
52
51
  selected -= weight
53
- return states[i]
52
+ return states[len(states) - 1]
54
53
 
55
54
  simgr.stashes[stash] = [weighted_pick(simgr.stashes[stash])]
56
55
 
angr/factory.py CHANGED
@@ -11,7 +11,7 @@ from .sim_state import SimState
11
11
  from .calling_conventions import default_cc, SimRegArg, SimStackArg, PointerWrapper, SimCCUnknown
12
12
  from .callable import Callable
13
13
  from .errors import AngrAssemblyError, AngrError
14
- from .engines import UberEngine, ProcedureEngine, SimEngineConcrete
14
+ from .engines import UberEngine, ProcedureEngine
15
15
  from .sim_type import SimTypeFunction, SimTypeInt
16
16
  from .codenode import HookNode, SyscallNode
17
17
  from .block import Block, SootBlock
@@ -39,7 +39,6 @@ class AngrObjectFactory:
39
39
  project: Project
40
40
  default_engine_factory: type[SimEngine]
41
41
  procedure_engine: ProcedureEngine
42
- concrete_engine: SimEngineConcrete | None
43
42
  _default_cc: type[SimCC] | None
44
43
 
45
44
  # We use thread local storage to cache engines on a per-thread basis
@@ -66,16 +65,11 @@ class AngrObjectFactory:
66
65
  )
67
66
  self.procedure_engine = ProcedureEngine(project)
68
67
 
69
- if project.concrete_target:
70
- self.concrete_engine = SimEngineConcrete(project)
71
- else:
72
- self.concrete_engine = None
73
-
74
68
  def __getstate__(self):
75
- return self.project, self.default_engine_factory, self.procedure_engine, self.concrete_engine, self._default_cc
69
+ return self.project, self.default_engine_factory, self.procedure_engine, self._default_cc
76
70
 
77
71
  def __setstate__(self, state):
78
- self.project, self.default_engine_factory, self.procedure_engine, self.concrete_engine, self._default_cc = state
72
+ self.project, self.default_engine_factory, self.procedure_engine, self._default_cc = state
79
73
  self._tls = threading.local()
80
74
 
81
75
  @property
angr/flirt/build_sig.py CHANGED
@@ -22,9 +22,7 @@ def get_basic_info(ar_path: str) -> dict[str, str]:
22
22
  """
23
23
 
24
24
  with tempfile.TemporaryDirectory() as tempdirname:
25
- cwd = os.getcwd()
26
- os.chdir(tempdirname)
27
- subprocess.call(["ar", "x", ar_path])
25
+ subprocess.call(["ar", "x", ar_path], cwd=tempdirname)
28
26
 
29
27
  # Load arch and OS information from the first .o file
30
28
  o_files = [f for f in os.listdir(".") if f.endswith(".o")]
@@ -32,8 +30,8 @@ def get_basic_info(ar_path: str) -> dict[str, str]:
32
30
  proj = angr.Project(o_files[0], auto_load_libs=False)
33
31
  arch_name = proj.arch.name.lower()
34
32
  os_name = proj.simos.name.lower()
35
-
36
- os.chdir(cwd)
33
+ else:
34
+ raise ValueError("No .o files found in the archive.")
37
35
 
38
36
  return {
39
37
  "arch": arch_name,
@@ -64,9 +62,7 @@ def get_unique_strings(ar_path: str) -> list[str]:
64
62
  # extract the archive file into a temporary directory
65
63
  all_strings = set()
66
64
  with tempfile.TemporaryDirectory() as tempdirname:
67
- cwd = os.getcwd()
68
- os.chdir(tempdirname)
69
- subprocess.call(["ar", "x", ar_path])
65
+ subprocess.call(["ar", "x", ar_path], cwd=tempdirname)
70
66
 
71
67
  for filename in os.listdir("."):
72
68
  if filename.endswith(".o"):
@@ -93,8 +89,6 @@ def get_unique_strings(ar_path: str) -> list[str]:
93
89
  non_symbol_strings.add(s)
94
90
  all_strings |= non_symbol_strings
95
91
 
96
- os.chdir(cwd)
97
-
98
92
  grouped_strings = defaultdict(set)
99
93
  for s in all_strings:
100
94
  grouped_strings[s[:5]].add(s)
@@ -138,7 +132,7 @@ def process_exc_file(exc_path: str):
138
132
 
139
133
  TODO: Add caller-callee-based de-duplication.
140
134
  """
141
- with open(exc_path) as f:
135
+ with open(exc_path, encoding="utf-8") as f:
142
136
  data = f.read()
143
137
  lines = data.split("\n")
144
138
 
@@ -184,7 +178,7 @@ def process_exc_file(exc_path: str):
184
178
  g[the_chosen_one] = "+" + line
185
179
 
186
180
  # output
187
- with open(exc_path, "w") as f:
181
+ with open(exc_path, "w", encoding="utf-8") as f:
188
182
  for g in groups.values():
189
183
  for line in g.values():
190
184
  f.write(line + "\n")
@@ -273,8 +267,7 @@ def main():
273
267
  basename = os.path.basename(ar_path)
274
268
 
275
269
  # sanitize basename since otherwise sigmake is not happy with it
276
- if basename.endswith(".a"):
277
- basename = basename[:-2]
270
+ basename = basename.removesuffix(".a")
278
271
  basename = basename.replace("+", "plus")
279
272
 
280
273
  # sanitize signame as well
@@ -292,7 +285,7 @@ def main():
292
285
 
293
286
  assert not has_collision
294
287
 
295
- with open(meta_path, "w") as f:
288
+ with open(meta_path, "w", encoding="utf-8") as f:
296
289
  metadata = {
297
290
  "unique_strings": unique_strings,
298
291
  }
@@ -6,10 +6,10 @@ import logging
6
6
  from typing import TYPE_CHECKING
7
7
  from collections.abc import Callable
8
8
  from collections import defaultdict
9
- import bisect
10
9
  import string
11
10
 
12
11
  import networkx
12
+ from sortedcontainers import SortedList
13
13
 
14
14
  import cle
15
15
 
@@ -81,7 +81,7 @@ class CFGModel(Serializable):
81
81
  # CFGNodes dict indexed by block ID. Don't serialize
82
82
  self._nodes: dict[int, CFGNode] = {}
83
83
  # addresses of CFGNodes to speed up get_any_node(..., anyaddr=True). Don't serialize
84
- self._node_addrs: list[int] = []
84
+ self._node_addrs: SortedList[int] | None = None
85
85
 
86
86
  self.normalized = False
87
87
 
@@ -137,7 +137,8 @@ class CFGModel(Serializable):
137
137
  edge.dst_ea = dst.addr
138
138
  for k, v in data.items():
139
139
  if k == "jumpkind":
140
- edge.jumpkind = cfg_jumpkind_to_pb(v)
140
+ jk = cfg_jumpkind_to_pb(v)
141
+ edge.jumpkind = primitives_pb2.Edge.UnknownJumpkind if jk is None else jk
141
142
  elif k == "ins_addr":
142
143
  edge.ins_addr = v if v is not None else 0xFFFF_FFFF_FFFF_FFFF
143
144
  elif k == "stmt_idx":
@@ -176,7 +177,7 @@ class CFGModel(Serializable):
176
177
  "The resulting graph may be broken."
177
178
  )
178
179
 
179
- model._node_addrs = sorted(model._nodes_by_addr.keys())
180
+ model._node_addrs = None
180
181
 
181
182
  # edges
182
183
  for edge_pb2 in cmsg.edges:
@@ -219,6 +220,9 @@ class CFGModel(Serializable):
219
220
 
220
221
  return model
221
222
 
223
+ def _build_node_addr_index(self):
224
+ self._node_addrs = SortedList(iter(k for k, lst in self._nodes_by_addr.items() if lst))
225
+
222
226
  #
223
227
  # Node insertion and removal
224
228
  #
@@ -227,12 +231,8 @@ class CFGModel(Serializable):
227
231
  self._nodes[block_id] = node
228
232
  self._nodes_by_addr[node.addr].append(node)
229
233
 
230
- if isinstance(node.addr, int):
231
- pos = bisect.bisect_left(self._node_addrs, node.addr)
232
- if pos >= len(self._node_addrs):
233
- self._node_addrs.append(node.addr)
234
- elif self._node_addrs[pos] != node.addr:
235
- self._node_addrs.insert(pos, node.addr)
234
+ if self._node_addrs is not None and isinstance(node.addr, int) and node.addr not in self._node_addrs:
235
+ self._node_addrs.add(node.addr)
236
236
 
237
237
  def remove_node(self, block_id: int, node: CFGNode) -> None:
238
238
  """
@@ -250,10 +250,8 @@ class CFGModel(Serializable):
250
250
  if not self._nodes_by_addr[node.addr]:
251
251
  del self._nodes_by_addr[node.addr]
252
252
 
253
- if isinstance(node.addr, int):
254
- pos = bisect.bisect_left(self._node_addrs, node.addr)
255
- if pos < len(self._node_addrs) and self._node_addrs[pos] == node.addr:
256
- self._node_addrs.pop(pos)
253
+ if self._node_addrs is not None and isinstance(node.addr, int) and node.addr in self._node_addrs:
254
+ self._node_addrs.remove(node.addr)
257
255
 
258
256
  #
259
257
  # CFG View
@@ -294,17 +292,22 @@ class CFGModel(Serializable):
294
292
  # fastpath: directly look in the nodes list
295
293
  if not anyaddr or addr in self._nodes_by_addr:
296
294
  try:
297
- return self._nodes_by_addr[addr][0]
298
- except (KeyError, IndexError):
295
+ if is_syscall is None:
296
+ return self._nodes_by_addr[addr][0]
297
+ return next(iter(node for node in self._nodes_by_addr[addr] if node.is_syscall == is_syscall))
298
+ except (KeyError, IndexError, StopIteration):
299
299
  pass
300
300
 
301
301
  if force_fastpath:
302
302
  return None
303
303
 
304
304
  if isinstance(addr, int):
305
+ if self._node_addrs is None:
306
+ self._build_node_addr_index()
307
+
305
308
  # slower path
306
309
  # find all potential addresses that the block may cover
307
- pos = bisect.bisect_left(self._node_addrs, max(addr - VEX_IRSB_MAX_SIZE, 0))
310
+ pos = self._node_addrs.bisect_left(max(addr - VEX_IRSB_MAX_SIZE, 0))
308
311
 
309
312
  is_cfgemulated = self.ident == "CFGEmulated"
310
313
 
@@ -1,11 +1,14 @@
1
+ # pylint:disable=too-many-boolean-expressions
1
2
  from __future__ import annotations
2
3
  import os
3
4
  import logging
4
- import networkx
5
5
  import itertools
6
6
  from collections import defaultdict
7
7
  from collections.abc import Iterable
8
+ import contextlib
9
+ from typing import overload
8
10
 
11
+ import networkx
9
12
  from itanium_demangler import parse
10
13
 
11
14
  from cle.backends.symbol import Symbol
@@ -13,7 +16,6 @@ from archinfo.arch_arm import get_real_address_if_arm
13
16
  import claripy
14
17
 
15
18
  from angr.knowledge_plugins.cfg.memory_data import MemoryDataSort
16
-
17
19
  from angr.codenode import CodeNode, BlockNode, HookNode, SyscallNode
18
20
  from angr.serializable import Serializable
19
21
  from angr.errors import AngrValueError, SimEngineError, SimMemoryError
@@ -22,14 +24,12 @@ from angr.procedures.definitions import SimSyscallLibrary
22
24
  from angr.protos import function_pb2
23
25
  from angr.calling_conventions import DEFAULT_CC, default_cc
24
26
  from angr.misc.ux import deprecated
25
- from .function_parser import FunctionParser
26
-
27
- l = logging.getLogger(name=__name__)
28
-
29
27
  from angr.sim_type import SimTypeFunction, parse_defns
30
28
  from angr.calling_conventions import SimCC
31
29
  from angr.project import Project
32
- import contextlib
30
+ from .function_parser import FunctionParser
31
+
32
+ l = logging.getLogger(name=__name__)
33
33
 
34
34
 
35
35
  class Function(Serializable):
@@ -41,7 +41,6 @@ class Function(Serializable):
41
41
  "_addr_to_block_node",
42
42
  "_argument_registers",
43
43
  "_argument_stack_variables",
44
- "_block_cache",
45
44
  "_block_sizes",
46
45
  "_call_sites",
47
46
  "_callout_sites",
@@ -71,13 +70,10 @@ class Function(Serializable):
71
70
  "is_simprocedure",
72
71
  "is_syscall",
73
72
  "normalized",
74
- "prepared_registers",
75
- "prepared_stack_variables",
76
73
  "previous_names",
77
74
  "prototype",
78
75
  "prototype_libname",
79
76
  "ran_cca",
80
- "registers_read_afterwards",
81
77
  "retaddr_on_stack",
82
78
  "sp_delta",
83
79
  "startpoint",
@@ -150,15 +146,11 @@ class Function(Serializable):
150
146
  self.is_prototype_guessed: bool = True
151
147
  # Whether this function returns or not. `None` means it's not determined yet
152
148
  self._returning = None
153
- self.prepared_registers = set()
154
- self.prepared_stack_variables = set()
155
- self.registers_read_afterwards = set()
156
149
 
157
150
  self._addr_to_block_node = {} # map addresses to nodes. it's a cache of blocks. if a block is removed from the
158
151
  # function, it may not be removed from _addr_to_block_node. if you want to list
159
152
  # all blocks of a function, access .blocks.
160
153
  self._block_sizes = {} # map addresses to block sizes
161
- self._block_cache = {} # a cache of real, hard data Block objects
162
154
  self._local_blocks = {} # a dict of all blocks inside the function
163
155
  self._local_block_addrs = set() # a set of addresses of all blocks inside the function
164
156
 
@@ -374,13 +366,6 @@ class Function(Serializable):
374
366
  :param byte_string:
375
367
  :return:
376
368
  """
377
- if addr in self._block_cache:
378
- b = self._block_cache[addr]
379
- if size is None or b.size == size:
380
- return b
381
- # size seems to be updated. remove this cached entry from the block cache
382
- del self._block_cache[addr]
383
-
384
369
  if size is None and addr in self.block_addrs:
385
370
  # we know the size
386
371
  size = self._block_sizes[addr]
@@ -389,7 +374,6 @@ class Function(Serializable):
389
374
  if size is None:
390
375
  # update block_size dict
391
376
  self._block_sizes[addr] = block.size
392
- self._block_cache[addr] = block
393
377
  return block
394
378
 
395
379
  # compatibility
@@ -440,7 +424,7 @@ class Function(Serializable):
440
424
 
441
425
  @classmethod
442
426
  def _get_cmsg(cls):
443
- return function_pb2.Function()
427
+ return function_pb2.Function() # pylint:disable=no-member
444
428
 
445
429
  def serialize_to_cmessage(self):
446
430
  return FunctionParser.serialize(self)
@@ -613,7 +597,6 @@ class Function(Serializable):
613
597
  d["_local_transition_graph"] = None
614
598
  d["_project"] = None
615
599
  d["_function_manager"] = None
616
- d["_block_cache"] = {}
617
600
  return d
618
601
 
619
602
  @property
@@ -642,7 +625,7 @@ class Function(Serializable):
642
625
 
643
626
  @property
644
627
  def size(self):
645
- return sum([b.size for b in self.blocks])
628
+ return sum(b.size for b in self.blocks)
646
629
 
647
630
  @property
648
631
  def binary(self):
@@ -675,7 +658,7 @@ class Function(Serializable):
675
658
  dec = self.project.analyses.Decompiler(self, cfg=self._function_manager._kb.cfgs.get_most_accurate())
676
659
  return dec.codegen.text
677
660
 
678
- def add_jumpout_site(self, node):
661
+ def add_jumpout_site(self, node: CodeNode):
679
662
  """
680
663
  Add a custom jumpout site.
681
664
 
@@ -683,11 +666,11 @@ class Function(Serializable):
683
666
  :return: None
684
667
  """
685
668
 
686
- self._register_nodes(True, node)
669
+ node = self._register_node(True, node)
687
670
  self._jumpout_sites.add(node)
688
671
  self._add_endpoint(node, "transition")
689
672
 
690
- def add_retout_site(self, node):
673
+ def add_retout_site(self, node: CodeNode):
691
674
  """
692
675
  Add a custom retout site.
693
676
 
@@ -703,7 +686,7 @@ class Function(Serializable):
703
686
  :return: None
704
687
  """
705
688
 
706
- self._register_nodes(True, node)
689
+ node = self._register_node(True, node)
707
690
  self._retout_sites.add(node)
708
691
  self._add_endpoint(node, "return")
709
692
 
@@ -785,7 +768,6 @@ class Function(Serializable):
785
768
  return None
786
769
 
787
770
  def _clear_transition_graph(self):
788
- self._block_cache = {}
789
771
  self._block_sizes = {}
790
772
  self._addr_to_block_node = {}
791
773
  self._local_blocks = {}
@@ -812,11 +794,13 @@ class Function(Serializable):
812
794
 
813
795
  # it's confirmed. register the node if needed
814
796
  if "outside" not in data or data["outside"] is False:
815
- self._register_nodes(True, dst)
797
+ dst = self._register_node(True, dst)
816
798
 
817
799
  self.transition_graph[src][dst]["confirmed"] = True
818
800
 
819
- def _transit_to(self, from_node, to_node, outside=False, ins_addr=None, stmt_idx=None, is_exception=False):
801
+ def _transit_to(
802
+ self, from_node: CodeNode, to_node, outside=False, ins_addr=None, stmt_idx=None, is_exception=False
803
+ ):
820
804
  """
821
805
  Registers an edge between basic blocks in this function's transition graph.
822
806
  Arguments are CodeNode objects.
@@ -830,16 +814,15 @@ class Function(Serializable):
830
814
  """
831
815
 
832
816
  if outside:
833
- self._register_nodes(True, from_node)
817
+ from_node = self._register_node(True, from_node)
834
818
  if to_node is not None:
835
- self._register_nodes(False, to_node)
819
+ to_node = self._register_node(False, to_node)
836
820
 
837
821
  self._jumpout_sites.add(from_node)
838
822
  else:
823
+ from_node = self._register_node(True, from_node)
839
824
  if to_node is not None:
840
- self._register_nodes(True, from_node, to_node)
841
- else:
842
- self._register_nodes(True, from_node)
825
+ to_node = self._register_node(True, to_node)
843
826
 
844
827
  type_ = "transition" if not is_exception else "exception"
845
828
  if to_node is not None:
@@ -871,19 +854,22 @@ class Function(Serializable):
871
854
  :type ins_addr: int or None
872
855
  """
873
856
 
874
- self._register_nodes(True, from_node)
857
+ from_node = self._register_node(True, from_node)
875
858
 
876
859
  if to_func.is_syscall:
877
860
  self.transition_graph.add_edge(from_node, to_func, type="syscall", stmt_idx=stmt_idx, ins_addr=ins_addr)
878
861
  else:
879
862
  self.transition_graph.add_edge(from_node, to_func, type="call", stmt_idx=stmt_idx, ins_addr=ins_addr)
880
863
  if ret_node is not None:
864
+ ret_node = self._register_node(return_to_outside is False, ret_node)
881
865
  self._fakeret_to(from_node, ret_node, to_outside=return_to_outside)
882
866
 
883
867
  self._local_transition_graph = None
884
868
 
885
869
  def _fakeret_to(self, from_node, to_node, confirmed=None, to_outside=False):
886
- self._register_nodes(True, from_node)
870
+ from_node = self._register_node(True, from_node)
871
+ if confirmed:
872
+ to_node = self._register_node(not to_outside, to_node)
887
873
 
888
874
  if confirmed is None:
889
875
  self.transition_graph.add_edge(from_node, to_node, type="fake_return", outside=to_outside)
@@ -891,8 +877,6 @@ class Function(Serializable):
891
877
  self.transition_graph.add_edge(
892
878
  from_node, to_node, type="fake_return", confirmed=confirmed, outside=to_outside
893
879
  )
894
- if confirmed:
895
- self._register_nodes(not to_outside, to_node)
896
880
 
897
881
  self._local_transition_graph = None
898
882
 
@@ -910,45 +894,56 @@ class Function(Serializable):
910
894
  self._local_transition_graph = None
911
895
 
912
896
  def _update_local_blocks(self, node: CodeNode):
913
- self._local_blocks[node.addr] = node
914
- self._local_block_addrs.add(node.addr)
897
+ if node.addr not in self._local_blocks or self._local_blocks[node.addr] != node:
898
+ self._local_blocks[node.addr] = node
899
+ self._local_block_addrs.add(node.addr)
915
900
 
916
901
  def _update_addr_to_block_cache(self, node: BlockNode):
917
902
  if node.addr not in self._addr_to_block_node:
918
903
  self._addr_to_block_node[node.addr] = node
919
904
 
920
- def _register_nodes(self, is_local, *nodes):
921
- if not isinstance(is_local, bool):
922
- raise AngrValueError('_register_nodes(): the "is_local" parameter must be a bool')
923
-
924
- for node in nodes:
925
- if node.addr not in self:
926
- # only add each node once
927
- self.transition_graph.add_node(node)
928
-
929
- if not isinstance(node, CodeNode):
930
- continue
931
- node._graph = self.transition_graph
932
- if self._block_sizes.get(node.addr, 0) == 0:
933
- self._block_sizes[node.addr] = node.size
934
- if node.addr == self.addr and (self.startpoint is None or not self.startpoint.is_hook):
935
- self.startpoint = node
936
- if is_local and node.addr not in self._local_blocks:
937
- self._update_local_blocks(node)
938
- # add BlockNodes to the addr_to_block_node cache if not already there
939
- if isinstance(node, BlockNode):
940
- self._update_addr_to_block_cache(node)
941
- # else:
942
- # # checks that we don't have multiple block nodes at a single address
943
- # assert node == self._addr_to_block_node[node.addr]
944
-
945
- def _add_return_site(self, return_site):
905
+ @overload
906
+ def _register_node(self, is_local: bool, node: CodeNode) -> CodeNode: ...
907
+
908
+ @overload
909
+ def _register_node(self, is_local: bool, node: Function) -> Function: ...
910
+
911
+ def _register_node(self, is_local: bool, node: CodeNode | Function) -> CodeNode | Function:
912
+ # if the node already exists and is the same, we reuse the existing node
913
+ if is_local and self._local_blocks.get(node.addr, None) == node:
914
+ return self._local_blocks[node.addr]
915
+
916
+ if node.addr not in self and node not in self.transition_graph:
917
+ # only add each node to the graph once
918
+ self.transition_graph.add_node(node)
919
+
920
+ if not isinstance(node, CodeNode):
921
+ # function and other things bail here
922
+ return node
923
+
924
+ # this is either a new node or a different node at the same address
925
+ node._graph = self.transition_graph
926
+ if self._block_sizes.get(node.addr, 0) == 0:
927
+ self._block_sizes[node.addr] = node.size
928
+ if node.addr == self.addr and (self.startpoint is None or not self.startpoint.is_hook):
929
+ self.startpoint = node
930
+ if is_local and node.addr not in self._local_blocks:
931
+ self._update_local_blocks(node)
932
+ # add BlockNodes to the addr_to_block_node cache if not already there
933
+ if isinstance(node, BlockNode):
934
+ self._update_addr_to_block_cache(node)
935
+ # else:
936
+ # # checks that we don't have multiple block nodes at a single address
937
+ # assert node == self._addr_to_block_node[node.addr]
938
+ return node
939
+
940
+ def _add_return_site(self, return_site: CodeNode):
946
941
  """
947
942
  Registers a basic block as a site for control flow to return from this function.
948
943
 
949
- :param CodeNode return_site: The block node that ends with a return.
944
+ :param return_site: The block node that ends with a return.
950
945
  """
951
- self._register_nodes(True, return_site)
946
+ return_site = self._register_node(True, return_site)
952
947
 
953
948
  self._ret_sites.add(return_site)
954
949
  # A return site must be an endpoint of the function - you cannot continue execution of the current function
@@ -1285,8 +1280,8 @@ class Function(Serializable):
1285
1280
  """
1286
1281
  Draw the graph and save it to a PNG file.
1287
1282
  """
1288
- import matplotlib.pyplot as pyplot # pylint: disable=import-error
1289
- from networkx.drawing.nx_agraph import graphviz_layout # pylint: disable=import-error
1283
+ import matplotlib.pyplot as pyplot # pylint: disable=import-error,import-outside-toplevel
1284
+ from networkx.drawing.nx_agraph import graphviz_layout # pylint: disable=import-error,import-outside-toplevel
1290
1285
 
1291
1286
  tmp_graph = networkx.classes.digraph.DiGraph()
1292
1287
  for from_block, to_block in self.transition_graph.edges():
@@ -1416,11 +1411,8 @@ class Function(Serializable):
1416
1411
  self._local_blocks[n.addr] = new_node
1417
1412
 
1418
1413
  # update block_cache and block_sizes
1419
- if (n.addr in self._block_cache and self._block_cache[n.addr].size != new_node.size) or (
1420
- n.addr in self._block_sizes and self._block_sizes[n.addr] != new_node.size
1421
- ):
1414
+ if n.addr in self._block_sizes and self._block_sizes[n.addr] != new_node.size:
1422
1415
  # the cache needs updating
1423
- self._block_cache.pop(n.addr, None)
1424
1416
  self._block_sizes[n.addr] = new_node.size
1425
1417
 
1426
1418
  for p, _, data in original_predecessors:
@@ -1680,7 +1672,6 @@ class Function(Serializable):
1680
1672
  func.startpoint = self.startpoint
1681
1673
  func._addr_to_block_node = self._addr_to_block_node.copy()
1682
1674
  func._block_sizes = self._block_sizes.copy()
1683
- func._block_cache = self._block_cache.copy()
1684
1675
  func._local_blocks = self._local_blocks.copy()
1685
1676
  func._local_block_addrs = self._local_block_addrs.copy()
1686
1677
  func.info = self.info.copy()