angr 9.2.134__py3-none-macosx_11_0_arm64.whl → 9.2.136__py3-none-macosx_11_0_arm64.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 (174) hide show
  1. angr/__init__.py +1 -1
  2. angr/analyses/__init__.py +5 -8
  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/__init__.py +6 -0
  10. angr/analyses/{calling_convention.py → calling_convention/calling_convention.py} +32 -64
  11. angr/analyses/calling_convention/fact_collector.py +502 -0
  12. angr/analyses/calling_convention/utils.py +57 -0
  13. angr/analyses/cdg.py +1 -2
  14. angr/analyses/cfg/cfb.py +1 -3
  15. angr/analyses/cfg/cfg.py +2 -2
  16. angr/analyses/cfg/cfg_base.py +37 -35
  17. angr/analyses/cfg/cfg_emulated.py +1 -1
  18. angr/analyses/cfg/cfg_fast.py +62 -15
  19. angr/analyses/cfg/cfg_fast_soot.py +1 -1
  20. angr/analyses/cfg/indirect_jump_resolvers/__init__.py +2 -0
  21. angr/analyses/cfg/indirect_jump_resolvers/const_resolver.py +46 -10
  22. angr/analyses/cfg/indirect_jump_resolvers/default_resolvers.py +5 -1
  23. angr/analyses/cfg/indirect_jump_resolvers/jumptable.py +50 -14
  24. angr/analyses/cfg/indirect_jump_resolvers/memload_resolver.py +81 -0
  25. angr/analyses/cfg/indirect_jump_resolvers/propagator_utils.py +24 -5
  26. angr/analyses/cfg/indirect_jump_resolvers/x86_pe_iat.py +2 -5
  27. angr/analyses/complete_calling_conventions.py +32 -3
  28. angr/analyses/congruency_check.py +2 -3
  29. angr/analyses/data_dep/data_dependency_analysis.py +2 -2
  30. angr/analyses/ddg.py +1 -4
  31. angr/analyses/decompiler/ail_simplifier.py +3 -4
  32. angr/analyses/decompiler/clinic.py +42 -7
  33. angr/analyses/decompiler/optimization_passes/duplication_reverter/ail_merge_graph.py +2 -2
  34. angr/analyses/decompiler/optimization_passes/duplication_reverter/duplication_reverter.py +2 -2
  35. angr/analyses/decompiler/optimization_passes/ite_region_converter.py +1 -1
  36. angr/analyses/decompiler/optimization_passes/lowered_switch_simplifier.py +1 -1
  37. angr/analyses/decompiler/optimization_passes/register_save_area_simplifier.py +0 -6
  38. angr/analyses/decompiler/optimization_passes/stack_canary_simplifier.py +2 -7
  39. angr/analyses/decompiler/optimization_passes/switch_default_case_duplicator.py +0 -6
  40. angr/analyses/decompiler/optimization_passes/win_stack_canary_simplifier.py +0 -6
  41. angr/analyses/decompiler/structuring/phoenix.py +1 -1
  42. angr/analyses/disassembly.py +5 -5
  43. angr/analyses/fcp/__init__.py +4 -0
  44. angr/analyses/fcp/fcp.py +429 -0
  45. angr/analyses/identifier/identify.py +1 -3
  46. angr/analyses/loopfinder.py +4 -3
  47. angr/analyses/patchfinder.py +1 -1
  48. angr/analyses/propagator/engine_base.py +4 -3
  49. angr/analyses/propagator/propagator.py +14 -53
  50. angr/analyses/reassembler.py +1 -2
  51. angr/analyses/s_propagator.py +1 -3
  52. angr/analyses/soot_class_hierarchy.py +1 -2
  53. angr/analyses/stack_pointer_tracker.py +18 -2
  54. angr/analyses/static_hooker.py +1 -2
  55. angr/analyses/typehoon/simple_solver.py +2 -2
  56. angr/analyses/variable_recovery/engine_vex.py +5 -0
  57. angr/analyses/variable_recovery/variable_recovery_fast.py +1 -2
  58. angr/analyses/veritesting.py +4 -7
  59. angr/analyses/vfg.py +1 -1
  60. angr/analyses/vsa_ddg.py +1 -2
  61. angr/block.py +3 -2
  62. angr/callable.py +1 -3
  63. angr/calling_conventions.py +15 -7
  64. angr/codenode.py +5 -1
  65. angr/concretization_strategies/__init__.py +1 -83
  66. angr/concretization_strategies/any.py +2 -1
  67. angr/concretization_strategies/any_named.py +1 -1
  68. angr/concretization_strategies/base.py +81 -0
  69. angr/concretization_strategies/controlled_data.py +2 -1
  70. angr/concretization_strategies/eval.py +2 -1
  71. angr/concretization_strategies/logging.py +3 -1
  72. angr/concretization_strategies/max.py +2 -1
  73. angr/concretization_strategies/nonzero.py +2 -1
  74. angr/concretization_strategies/nonzero_range.py +2 -1
  75. angr/concretization_strategies/norepeats.py +2 -1
  76. angr/concretization_strategies/norepeats_range.py +2 -1
  77. angr/concretization_strategies/range.py +2 -1
  78. angr/concretization_strategies/signed_add.py +2 -1
  79. angr/concretization_strategies/single.py +2 -1
  80. angr/concretization_strategies/solutions.py +2 -1
  81. angr/concretization_strategies/unlimited_range.py +2 -1
  82. angr/engines/__init__.py +8 -5
  83. angr/engines/engine.py +3 -5
  84. angr/engines/failure.py +4 -5
  85. angr/engines/procedure.py +5 -7
  86. angr/engines/soot/expressions/__init__.py +22 -23
  87. angr/engines/soot/expressions/base.py +4 -4
  88. angr/engines/soot/expressions/invoke.py +1 -2
  89. angr/engines/soot/statements/__init__.py +9 -10
  90. angr/engines/soot/values/__init__.py +9 -10
  91. angr/engines/soot/values/arrayref.py +3 -3
  92. angr/engines/soot/values/instancefieldref.py +3 -2
  93. angr/engines/successors.py +7 -6
  94. angr/engines/syscall.py +4 -6
  95. angr/engines/unicorn.py +3 -2
  96. angr/engines/vex/claripy/ccall.py +8 -10
  97. angr/engines/vex/claripy/datalayer.py +4 -5
  98. angr/exploration_techniques/__init__.py +0 -2
  99. angr/exploration_techniques/spiller.py +1 -3
  100. angr/exploration_techniques/stochastic.py +2 -3
  101. angr/factory.py +3 -9
  102. angr/knowledge_plugins/cfg/cfg_model.py +20 -17
  103. angr/knowledge_plugins/functions/function.py +74 -77
  104. angr/knowledge_plugins/functions/function_manager.py +14 -7
  105. angr/knowledge_plugins/functions/function_parser.py +1 -1
  106. angr/knowledge_plugins/functions/soot_function.py +16 -16
  107. angr/knowledge_plugins/propagations/propagation_model.py +4 -5
  108. angr/knowledge_plugins/propagations/states.py +0 -511
  109. angr/lib/angr_native.dylib +0 -0
  110. angr/procedures/libc/memcpy.py +4 -4
  111. angr/procedures/procedure_dict.py +3 -2
  112. angr/protos/__init__.py +2 -5
  113. angr/protos/cfg_pb2.py +21 -18
  114. angr/protos/function_pb2.py +17 -14
  115. angr/protos/primitives_pb2.py +44 -39
  116. angr/protos/variables_pb2.py +36 -31
  117. angr/protos/xrefs_pb2.py +15 -12
  118. angr/sim_procedure.py +15 -16
  119. angr/sim_variable.py +13 -1
  120. angr/simos/__init__.py +2 -0
  121. angr/simos/javavm.py +4 -6
  122. angr/simos/xbox.py +32 -0
  123. angr/state_plugins/__init__.py +0 -2
  124. angr/state_plugins/callstack.py +4 -4
  125. angr/state_plugins/cgc.py +3 -2
  126. angr/state_plugins/gdb.py +6 -5
  127. angr/state_plugins/globals.py +1 -2
  128. angr/state_plugins/heap/heap_brk.py +1 -2
  129. angr/state_plugins/history.py +10 -12
  130. angr/state_plugins/inspect.py +3 -5
  131. angr/state_plugins/libc.py +2 -2
  132. angr/state_plugins/log.py +8 -10
  133. angr/state_plugins/loop_data.py +1 -2
  134. angr/state_plugins/posix.py +7 -7
  135. angr/state_plugins/preconstrainer.py +2 -3
  136. angr/state_plugins/scratch.py +5 -8
  137. angr/state_plugins/sim_action.py +3 -3
  138. angr/state_plugins/solver.py +8 -3
  139. angr/state_plugins/symbolizer.py +5 -4
  140. angr/state_plugins/uc_manager.py +3 -3
  141. angr/state_plugins/unicorn_engine.py +5 -1
  142. angr/state_plugins/view.py +3 -5
  143. angr/storage/file.py +3 -5
  144. angr/storage/memory_mixins/address_concretization_mixin.py +2 -2
  145. angr/storage/memory_mixins/bvv_conversion_mixin.py +3 -3
  146. angr/storage/memory_mixins/clouseau_mixin.py +1 -3
  147. angr/storage/memory_mixins/name_resolution_mixin.py +1 -3
  148. angr/storage/memory_mixins/paged_memory/paged_memory_mixin.py +13 -15
  149. angr/storage/memory_mixins/paged_memory/pages/__init__.py +1 -22
  150. angr/storage/memory_mixins/paged_memory/pages/base.py +31 -0
  151. angr/storage/memory_mixins/paged_memory/pages/list_page.py +1 -1
  152. angr/storage/memory_mixins/paged_memory/pages/mv_list_page.py +1 -1
  153. angr/storage/memory_mixins/paged_memory/pages/ultra_page.py +2 -4
  154. angr/storage/memory_mixins/paged_memory/privileged_mixin.py +3 -4
  155. angr/storage/memory_mixins/regioned_memory/abstract_merger_mixin.py +4 -2
  156. angr/storage/memory_mixins/smart_find_mixin.py +1 -1
  157. angr/storage/memory_mixins/underconstrained_mixin.py +1 -1
  158. angr/storage/memory_mixins/unwrapper_mixin.py +1 -3
  159. angr/utils/bits.py +13 -0
  160. angr/utils/enums_conv.py +28 -12
  161. angr/utils/segment_list.py +25 -22
  162. angr/utils/timing.py +18 -1
  163. angr/vaults.py +5 -6
  164. {angr-9.2.134.dist-info → angr-9.2.136.dist-info}/METADATA +6 -6
  165. {angr-9.2.134.dist-info → angr-9.2.136.dist-info}/RECORD +169 -165
  166. {angr-9.2.134.dist-info → angr-9.2.136.dist-info}/WHEEL +1 -1
  167. angr/analyses/propagator/outdated_definition_walker.py +0 -159
  168. angr/analyses/propagator/tmpvar_finder.py +0 -18
  169. angr/engines/concrete.py +0 -180
  170. angr/exploration_techniques/symbion.py +0 -80
  171. angr/state_plugins/concrete.py +0 -295
  172. {angr-9.2.134.dist-info → angr-9.2.136.dist-info}/LICENSE +0 -0
  173. {angr-9.2.134.dist-info → angr-9.2.136.dist-info}/entry_points.txt +0 -0
  174. {angr-9.2.134.dist-info → angr-9.2.136.dist-info}/top_level.txt +0 -0
@@ -1570,13 +1570,13 @@ class CFGBase(Analysis):
1570
1570
  # TODO: Is it required that PLT stubs are always aligned by 16? If so, on what architectures and platforms is it
1571
1571
  # TODO: enforced?
1572
1572
 
1573
- tmp_functions = self.kb.functions.copy()
1573
+ tmp_functions = self.kb.functions
1574
1574
 
1575
1575
  for function in tmp_functions.values():
1576
1576
  function.mark_nonreturning_calls_endpoints()
1577
1577
  if function.returning is False:
1578
1578
  # remove all FakeRet edges that are related to this function
1579
- func_node = self.model.get_any_node(function.addr)
1579
+ func_node = self.model.get_any_node(function.addr, force_fastpath=True)
1580
1580
  if func_node is not None:
1581
1581
  callsite_nodes = [
1582
1582
  src
@@ -1588,8 +1588,8 @@ class CFGBase(Analysis):
1588
1588
  if data.get("jumpkind", None) == "Ijk_FakeRet":
1589
1589
  self.graph.remove_edge(callsite_node, dst)
1590
1590
 
1591
- # Clear old functions dict
1592
- self.kb.functions.clear()
1591
+ # Clear old functions dict by creating a new function manager
1592
+ self.kb.functions = FunctionManager(self.kb)
1593
1593
 
1594
1594
  blockaddr_to_function = {}
1595
1595
  traversed_cfg_nodes = set()
@@ -1602,7 +1602,7 @@ class CFGBase(Analysis):
1602
1602
  if jumpkind == "Ijk_Call" or jumpkind.startswith("Ijk_Sys"):
1603
1603
  function_nodes.add(dst)
1604
1604
 
1605
- entry_node = self.model.get_any_node(self._binary.entry)
1605
+ entry_node = self.model.get_any_node(self._binary.entry, force_fastpath=True)
1606
1606
  if entry_node is not None:
1607
1607
  function_nodes.add(entry_node)
1608
1608
 
@@ -1663,7 +1663,7 @@ class CFGBase(Analysis):
1663
1663
  secondary_function_nodes = set()
1664
1664
  # add all function chunks ("functions" that are not called from anywhere)
1665
1665
  for func_addr in tmp_functions:
1666
- node = self.model.get_any_node(func_addr)
1666
+ node = self.model.get_any_node(func_addr, force_fastpath=True)
1667
1667
  if node is None:
1668
1668
  continue
1669
1669
  if node.addr not in blockaddr_to_function:
@@ -1992,8 +1992,8 @@ class CFGBase(Analysis):
1992
1992
  if not transition_found:
1993
1993
  continue
1994
1994
 
1995
- cfgnode_0 = self.model.get_any_node(block_node.addr)
1996
- cfgnode_1 = self.model.get_any_node(addr_1)
1995
+ cfgnode_0 = self.model.get_any_node(block_node.addr, force_fastpath=True)
1996
+ cfgnode_1 = self.model.get_any_node(addr_1, force_fastpath=True)
1997
1997
 
1998
1998
  if cfgnode_0 is None or cfgnode_1 is None:
1999
1999
  continue
@@ -2089,7 +2089,7 @@ class CFGBase(Analysis):
2089
2089
  continue
2090
2090
  if func_addr in jumptable_entries:
2091
2091
  # is there any call edge pointing to it?
2092
- func_node = self.get_any_node(func_addr)
2092
+ func_node = self.get_any_node(func_addr, force_fastpath=True)
2093
2093
  if func_node is not None:
2094
2094
  in_edges = self.graph.in_edges(func_node, data=True)
2095
2095
  has_transition_pred = None
@@ -2128,7 +2128,7 @@ class CFGBase(Analysis):
2128
2128
  else:
2129
2129
  is_syscall = self.project.simos.is_syscall_addr(addr)
2130
2130
 
2131
- n = self.model.get_any_node(addr, is_syscall=is_syscall)
2131
+ n = self.model.get_any_node(addr, is_syscall=is_syscall, force_fastpath=True)
2132
2132
  node = addr if n is None else self._to_snippet(n)
2133
2133
 
2134
2134
  if isinstance(addr, SootAddressDescriptor):
@@ -2304,7 +2304,7 @@ class CFGBase(Analysis):
2304
2304
  src_function = self._addr_to_function(src_addr, blockaddr_to_function, known_functions)
2305
2305
 
2306
2306
  if src_addr not in src_function.block_addrs_set:
2307
- n = self.model.get_any_node(src_addr)
2307
+ n = self.model.get_any_node(src_addr, force_fastpath=True)
2308
2308
  node = src_addr if n is None else self._to_snippet(n)
2309
2309
  self.kb.functions._add_node(src_function.addr, node)
2310
2310
 
@@ -2315,7 +2315,7 @@ class CFGBase(Analysis):
2315
2315
  jumpkind = data["jumpkind"]
2316
2316
 
2317
2317
  if jumpkind == "Ijk_Ret":
2318
- n = self.model.get_any_node(src_addr)
2318
+ n = self.model.get_any_node(src_addr, force_fastpath=True)
2319
2319
  from_node = src_addr if n is None else self._to_snippet(n)
2320
2320
  self.kb.functions._add_return_from(src_function.addr, from_node, None)
2321
2321
 
@@ -2334,7 +2334,7 @@ class CFGBase(Analysis):
2334
2334
  # It must be calling a function
2335
2335
  dst_function = self._addr_to_function(dst_addr, blockaddr_to_function, known_functions)
2336
2336
 
2337
- n = self.model.get_any_node(src_addr)
2337
+ n = self.model.get_any_node(src_addr, force_fastpath=True)
2338
2338
  if n is None:
2339
2339
  src_snippet = self._to_snippet(addr=src_addr, base_state=self._base_state)
2340
2340
  else:
@@ -2349,21 +2349,12 @@ class CFGBase(Analysis):
2349
2349
  else:
2350
2350
  fakeret_node = self._one_fakeret_node(all_edges)
2351
2351
 
2352
- fakeret_snippet = None if fakeret_node is None else self._to_snippet(cfg_node=fakeret_node)
2353
-
2354
2352
  if isinstance(dst_addr, SootAddressDescriptor):
2355
2353
  dst_addr = dst_addr.method
2356
2354
 
2357
- self.kb.functions._add_call_to(
2358
- src_function.addr,
2359
- src_snippet,
2360
- dst_addr,
2361
- fakeret_snippet,
2362
- syscall=is_syscall,
2363
- ins_addr=ins_addr,
2364
- stmt_idx=stmt_idx,
2365
- )
2366
-
2355
+ # determining the returning target
2356
+ return_to_outside = False
2357
+ returning_snippet = None
2367
2358
  if dst_function.returning and fakeret_node is not None:
2368
2359
  returning_target = src.addr + src.size
2369
2360
  if returning_target not in blockaddr_to_function:
@@ -2372,9 +2363,9 @@ class CFGBase(Analysis):
2372
2363
  else:
2373
2364
  self._addr_to_function(returning_target, blockaddr_to_function, known_functions)
2374
2365
 
2375
- to_outside = blockaddr_to_function[returning_target] is not src_function
2366
+ return_to_outside = blockaddr_to_function[returning_target] is not src_function
2376
2367
 
2377
- n = self.model.get_any_node(returning_target)
2368
+ n = self.model.get_any_node(returning_target, force_fastpath=True)
2378
2369
  if n is None:
2379
2370
  try:
2380
2371
  returning_snippet = self._to_snippet(addr=returning_target, base_state=self._base_state)
@@ -2384,17 +2375,28 @@ class CFGBase(Analysis):
2384
2375
  else:
2385
2376
  returning_snippet = self._to_snippet(cfg_node=n)
2386
2377
 
2387
- if returning_snippet is not None:
2388
- self.kb.functions._add_fakeret_to(
2389
- src_function.addr, src_snippet, returning_snippet, confirmed=True, to_outside=to_outside
2390
- )
2378
+ self.kb.functions._add_call_to(
2379
+ src_function.addr,
2380
+ src_snippet,
2381
+ dst_addr,
2382
+ retn_node=returning_snippet,
2383
+ syscall=is_syscall,
2384
+ ins_addr=ins_addr,
2385
+ stmt_idx=stmt_idx,
2386
+ return_to_outside=return_to_outside,
2387
+ )
2388
+
2389
+ if returning_snippet is not None:
2390
+ self.kb.functions._add_fakeret_to(
2391
+ src_function.addr, src_snippet, returning_snippet, confirmed=True, to_outside=return_to_outside
2392
+ )
2391
2393
 
2392
2394
  elif jumpkind in ("Ijk_Boring", "Ijk_InvalICache", "Ijk_Exception"):
2393
2395
  # convert src_addr and dst_addr to CodeNodes
2394
- n = self.model.get_any_node(src_addr)
2396
+ n = self.model.get_any_node(src_addr, force_fastpath=True)
2395
2397
  src_node = src_addr if n is None else self._to_snippet(cfg_node=n)
2396
2398
 
2397
- n = self.model.get_any_node(dst_addr)
2399
+ n = self.model.get_any_node(dst_addr, force_fastpath=True)
2398
2400
  dst_node = dst_addr if n is None else self._to_snippet(cfg_node=n)
2399
2401
 
2400
2402
  if self._skip_unmapped_addrs:
@@ -2460,10 +2462,10 @@ class CFGBase(Analysis):
2460
2462
 
2461
2463
  elif jumpkind == "Ijk_FakeRet":
2462
2464
  # convert src_addr and dst_addr to CodeNodes
2463
- n = self.model.get_any_node(src_addr)
2465
+ n = self.model.get_any_node(src_addr, force_fastpath=True)
2464
2466
  src_node = src_addr if n is None else self._to_snippet(n)
2465
2467
 
2466
- n = self.model.get_any_node(dst_addr)
2468
+ n = self.model.get_any_node(dst_addr, force_fastpath=True)
2467
2469
  dst_node = dst_addr if n is None else self._to_snippet(n)
2468
2470
 
2469
2471
  if dst_addr not in blockaddr_to_function:
@@ -2083,7 +2083,7 @@ class CFGEmulated(ForwardAnalysis, CFGBase): # pylint: disable=abstract-method
2083
2083
  if src_node_key is None:
2084
2084
  if dst_node is None:
2085
2085
  raise ValueError("Either src_node_key or dst_node_key must be specified.")
2086
- self.kb.functions.function(dst_node.function_address, create=True)._register_nodes(True, dst_codenode)
2086
+ self.kb.functions.function(dst_node.function_address, create=True)._register_node(True, dst_codenode)
2087
2087
  return
2088
2088
 
2089
2089
  src_node = self._graph_get_node(src_node_key, terminator_for_nonexistent_node=True)
@@ -135,9 +135,9 @@ class PendingJobs:
135
135
  A collection of pending jobs during CFG recovery.
136
136
  """
137
137
 
138
- def __init__(self, functions, deregister_job_callback):
138
+ def __init__(self, kb, deregister_job_callback):
139
139
  self._jobs = OrderedDict() # A mapping between function addresses and lists of pending jobs
140
- self._functions = functions
140
+ self._kb = kb
141
141
  self._deregister_job_callback = deregister_job_callback
142
142
 
143
143
  self._returning_functions = set()
@@ -145,6 +145,10 @@ class PendingJobs:
145
145
  # consecutive calls to cleanup().
146
146
  self._job_count = 0
147
147
 
148
+ @property
149
+ def _functions(self):
150
+ return self._kb.functions
151
+
148
152
  def __len__(self):
149
153
  return self._job_count
150
154
 
@@ -1316,7 +1320,7 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
1316
1320
  self._known_thunks = self._find_thunks()
1317
1321
 
1318
1322
  # Initialize variables used during analysis
1319
- self._pending_jobs: PendingJobs = PendingJobs(self.functions, self._deregister_analysis_job)
1323
+ self._pending_jobs: PendingJobs = PendingJobs(self.kb, self._deregister_analysis_job)
1320
1324
  self._traced_addresses: set[int] = {a for a, n in self._nodes_by_addr.items() if n}
1321
1325
  self._function_returns = defaultdict(set)
1322
1326
 
@@ -1498,6 +1502,18 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
1498
1502
  pass
1499
1503
 
1500
1504
  def _function_completed(self, func_addr: int):
1505
+ if self.project.arch.name == "AMD64":
1506
+ # determine if the function is __rust_probestack
1507
+ func = self.kb.functions.get_by_addr(func_addr) if self.kb.functions.contains_addr(func_addr) else None
1508
+ if func is not None and len(func.block_addrs_set) == 3:
1509
+ block_bytes = {func.get_block(block_addr).bytes for block_addr in func.block_addrs_set}
1510
+ if block_bytes == {
1511
+ b"UH\x89\xe5I\x89\xc3I\x81\xfb\x00\x10\x00\x00v\x1c",
1512
+ b"H\x81\xec\x00\x10\x00\x00H\x85d$\x08I\x81\xeb\x00\x10\x00\x00I\x81\xfb\x00\x10\x00\x00w\xe4",
1513
+ b"L)\xdcH\x85d$\x08H\x01\xc4\xc9\xc3",
1514
+ }:
1515
+ func.info["is_rust_probestack"] = True
1516
+
1501
1517
  if self._collect_data_ref and self.project is not None and ":" in self.project.arch.name:
1502
1518
  # this is a pcode arch - use Clinic to recover data references
1503
1519
 
@@ -1744,6 +1760,7 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
1744
1760
  # Clean up
1745
1761
  self._traced_addresses = None
1746
1762
  self._lifter_deregister_readonly_regions()
1763
+ self._function_returns = None
1747
1764
 
1748
1765
  self._finish_progress()
1749
1766
 
@@ -1825,19 +1842,18 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
1825
1842
  break
1826
1843
 
1827
1844
  # special handling: some binaries do not have SecurityCookie set, but still contain _security_init_cookie
1828
- if security_init_cookie_found is False:
1845
+ if security_init_cookie_found is False and self.functions.contains_addr(self.project.entry):
1829
1846
  start_func = self.functions.get_by_addr(self.project.entry)
1830
- if start_func is not None:
1831
- for callee in start_func.transition_graph:
1832
- if (
1833
- isinstance(callee, Function)
1834
- and not security_init_cookie_found
1835
- and is_function_likely_security_init_cookie(callee)
1836
- ):
1837
- security_init_cookie_found = True
1838
- callee.is_default_name = False
1839
- callee.name = "_security_init_cookie"
1840
- break
1847
+ for callee in start_func.transition_graph:
1848
+ if (
1849
+ isinstance(callee, Function)
1850
+ and not security_init_cookie_found
1851
+ and is_function_likely_security_init_cookie(callee)
1852
+ ):
1853
+ security_init_cookie_found = True
1854
+ callee.is_default_name = False
1855
+ callee.name = "_security_init_cookie"
1856
+ break
1841
1857
 
1842
1858
  def _post_process_string_references(self) -> None:
1843
1859
  """
@@ -4765,6 +4781,37 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
4765
4781
  func = self.kb.functions.get_by_addr(func_addr)
4766
4782
  func.info["get_pc"] = "ebx"
4767
4783
 
4784
+ # determine if the function uses ebp as a general purpose register or not
4785
+ if addr == func_addr or 0 < addr - func_addr <= 0x20:
4786
+ ebp_as_gpr = True
4787
+ cap = self._lift(addr, size=cfg_node.size).capstone
4788
+ for insn in cap.insns:
4789
+ if (
4790
+ insn.mnemonic == "mov"
4791
+ and len(insn.operands) == 2
4792
+ and insn.operands[0].type == capstone.x86.X86_OP_REG
4793
+ and insn.operands[1].type == capstone.x86.X86_OP_REG
4794
+ ):
4795
+ if (
4796
+ insn.operands[0].reg == capstone.x86.X86_REG_EBP
4797
+ and insn.operands[1].reg == capstone.x86.X86_REG_ESP
4798
+ ):
4799
+ ebp_as_gpr = False
4800
+ break
4801
+ elif (
4802
+ insn.mnemonic == "lea"
4803
+ and len(insn.operands) == 2
4804
+ and insn.operands[0].type == capstone.x86.X86_OP_REG
4805
+ and insn.operands[1].type == capstone.x86.X86_OP_MEM
4806
+ ) and (
4807
+ insn.operands[0].reg == capstone.x86.X86_REG_EBP
4808
+ and insn.operands[1].mem.base == capstone.x86.X86_REG_ESP
4809
+ ):
4810
+ ebp_as_gpr = False
4811
+ break
4812
+ func = self.kb.functions.get_by_addr(func_addr)
4813
+ func.info["bp_as_gpr"] = ebp_as_gpr
4814
+
4768
4815
  elif self.project.arch.name == "AMD64":
4769
4816
  # determine if the function uses rbp as a general purpose register or not
4770
4817
  if addr == func_addr or 0 < addr - func_addr <= 0x20:
@@ -50,7 +50,7 @@ class CFGFastSoot(CFGFast):
50
50
  self._initialize_cfg()
51
51
 
52
52
  # Initialize variables used during analysis
53
- self._pending_jobs = PendingJobs(self.functions, self._deregister_analysis_job)
53
+ self._pending_jobs = PendingJobs(self.kb, self._deregister_analysis_job)
54
54
  self._traced_addresses = set()
55
55
  self._changed_functions = set()
56
56
  self._updated_nonreturning_functions = set()
@@ -9,6 +9,7 @@ from .amd64_elf_got import AMD64ElfGotResolver
9
9
  from .arm_elf_fast import ArmElfFastResolver
10
10
  from .const_resolver import ConstantResolver
11
11
  from .amd64_pe_iat import AMD64PeIatResolver
12
+ from .memload_resolver import MemoryLoadResolver
12
13
 
13
14
 
14
15
  __all__ = (
@@ -17,6 +18,7 @@ __all__ = (
17
18
  "ArmElfFastResolver",
18
19
  "ConstantResolver",
19
20
  "JumpTableResolver",
21
+ "MemoryLoadResolver",
20
22
  "MipsElfFastResolver",
21
23
  "MipsElfGotResolver",
22
24
  "X86ElfPicPltResolver",
@@ -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