angr 9.2.135__py3-none-macosx_11_0_arm64.whl → 9.2.137__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 (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.dylib +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
@@ -1,5 +1,6 @@
1
1
  # pylint:disable=superfluous-parens,too-many-boolean-expressions,line-too-long
2
2
  from __future__ import annotations
3
+ from typing import TYPE_CHECKING
3
4
  import itertools
4
5
  import logging
5
6
  import math
@@ -52,6 +53,10 @@ from .cfg_arch_options import CFGArchOptions
52
53
  from .cfg_base import CFGBase
53
54
  from .indirect_jump_resolvers.jumptable import JumpTableResolver
54
55
 
56
+ if TYPE_CHECKING:
57
+ from angr.block import Block
58
+ from angr.engines.pcode.lifter import IRSB as PcodeIRSB
59
+
55
60
 
56
61
  VEX_IRSB_MAX_SIZE = 400
57
62
 
@@ -135,9 +140,9 @@ class PendingJobs:
135
140
  A collection of pending jobs during CFG recovery.
136
141
  """
137
142
 
138
- def __init__(self, functions, deregister_job_callback):
143
+ def __init__(self, kb, deregister_job_callback):
139
144
  self._jobs = OrderedDict() # A mapping between function addresses and lists of pending jobs
140
- self._functions = functions
145
+ self._kb = kb
141
146
  self._deregister_job_callback = deregister_job_callback
142
147
 
143
148
  self._returning_functions = set()
@@ -145,6 +150,10 @@ class PendingJobs:
145
150
  # consecutive calls to cleanup().
146
151
  self._job_count = 0
147
152
 
153
+ @property
154
+ def _functions(self):
155
+ return self._kb.functions
156
+
148
157
  def __len__(self):
149
158
  return self._job_count
150
159
 
@@ -821,10 +830,10 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
821
830
  #
822
831
  # Variables used during analysis
823
832
  #
824
- self._pending_jobs = None
825
- self._traced_addresses = None
833
+ self._pending_jobs = None # type:ignore
834
+ self._traced_addresses = None # type:ignore
826
835
  self._function_returns = None
827
- self._function_exits = None
836
+ self._function_exits = None # type:ignore
828
837
  self._gp_value: int | None = None
829
838
  self._ro_region_cdata_cache: list | None = None
830
839
  self._job_ctr = 0
@@ -868,7 +877,7 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
868
877
  if size is None:
869
878
  size = len(data)
870
879
 
871
- data = bytes(pyvex.ffi.buffer(data, size))
880
+ data = bytes(pyvex.ffi.buffer(data, size)) # type:ignore
872
881
  for x in range(256):
873
882
  p_x = float(data.count(x)) / size
874
883
  if p_x > 0:
@@ -1225,7 +1234,9 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
1225
1234
  # umm now it's probably code
1226
1235
  break
1227
1236
 
1228
- instr_alignment = self._initial_state.arch.instruction_alignment
1237
+ instr_alignment = self.project.arch.instruction_alignment
1238
+ if not instr_alignment:
1239
+ instr_alignment = 1
1229
1240
  if start_addr % instr_alignment > 0:
1230
1241
  # occupy those few bytes
1231
1242
  size = instr_alignment - (start_addr % instr_alignment)
@@ -1282,7 +1293,7 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
1282
1293
  nodecode_bytes_ratio = (
1283
1294
  0.0 if self._next_addr is None else self._nodecode_bytes_ratio(self._next_addr, self._nodecode_window_size)
1284
1295
  )
1285
- if nodecode_bytes_ratio >= self._nodecode_threshold:
1296
+ if nodecode_bytes_ratio >= self._nodecode_threshold and self._next_addr is not None:
1286
1297
  next_allowed_addr = self._next_addr + self._nodecode_step
1287
1298
  else:
1288
1299
  next_allowed_addr = 0
@@ -1309,6 +1320,9 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
1309
1320
  return job.addr
1310
1321
 
1311
1322
  def _pre_analysis(self):
1323
+ # Create a read-only memory view in loader for faster data loading
1324
+ self.project.loader.gen_ro_memview()
1325
+
1312
1326
  # Call _initialize_cfg() before self.functions is used.
1313
1327
  self._initialize_cfg()
1314
1328
 
@@ -1316,7 +1330,7 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
1316
1330
  self._known_thunks = self._find_thunks()
1317
1331
 
1318
1332
  # Initialize variables used during analysis
1319
- self._pending_jobs: PendingJobs = PendingJobs(self.functions, self._deregister_analysis_job)
1333
+ self._pending_jobs: PendingJobs = PendingJobs(self.kb, self._deregister_analysis_job)
1320
1334
  self._traced_addresses: set[int] = {a for a, n in self._nodes_by_addr.items() if n}
1321
1335
  self._function_returns = defaultdict(set)
1322
1336
 
@@ -1498,6 +1512,18 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
1498
1512
  pass
1499
1513
 
1500
1514
  def _function_completed(self, func_addr: int):
1515
+ if self.project.arch.name == "AMD64":
1516
+ # determine if the function is __rust_probestack
1517
+ func = self.kb.functions.get_by_addr(func_addr) if self.kb.functions.contains_addr(func_addr) else None
1518
+ if func is not None and len(func.block_addrs_set) == 3:
1519
+ block_bytes = {func.get_block(block_addr).bytes for block_addr in func.block_addrs_set}
1520
+ if block_bytes == {
1521
+ b"UH\x89\xe5I\x89\xc3I\x81\xfb\x00\x10\x00\x00v\x1c",
1522
+ b"H\x81\xec\x00\x10\x00\x00H\x85d$\x08I\x81\xeb\x00\x10\x00\x00I\x81\xfb\x00\x10\x00\x00w\xe4",
1523
+ b"L)\xdcH\x85d$\x08H\x01\xc4\xc9\xc3",
1524
+ }:
1525
+ func.info["is_rust_probestack"] = True
1526
+
1501
1527
  if self._collect_data_ref and self.project is not None and ":" in self.project.arch.name:
1502
1528
  # this is a pcode arch - use Clinic to recover data references
1503
1529
 
@@ -1741,9 +1767,13 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
1741
1767
 
1742
1768
  CFGBase._post_analysis(self)
1743
1769
 
1770
+ # drop the read-only memory view in loader
1771
+ self.project.loader.discard_ro_memview()
1772
+
1744
1773
  # Clean up
1745
1774
  self._traced_addresses = None
1746
1775
  self._lifter_deregister_readonly_regions()
1776
+ self._function_returns = None
1747
1777
 
1748
1778
  self._finish_progress()
1749
1779
 
@@ -1825,19 +1855,18 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
1825
1855
  break
1826
1856
 
1827
1857
  # special handling: some binaries do not have SecurityCookie set, but still contain _security_init_cookie
1828
- if security_init_cookie_found is False:
1858
+ if security_init_cookie_found is False and self.functions.contains_addr(self.project.entry):
1829
1859
  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
1860
+ for callee in start_func.transition_graph:
1861
+ if (
1862
+ isinstance(callee, Function)
1863
+ and not security_init_cookie_found
1864
+ and is_function_likely_security_init_cookie(callee)
1865
+ ):
1866
+ security_init_cookie_found = True
1867
+ callee.is_default_name = False
1868
+ callee.name = "_security_init_cookie"
1869
+ break
1841
1870
 
1842
1871
  def _post_process_string_references(self) -> None:
1843
1872
  """
@@ -2910,7 +2939,7 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
2910
2939
  if v is not None:
2911
2940
  tmps[stmt.tmp] = v
2912
2941
 
2913
- elif type(stmt.data) in (pyvex.IRExpr.Binop,): # pylint: disable=unidiomatic-typecheck
2942
+ elif type(stmt.data) is pyvex.IRExpr.Binop: # pylint: disable=unidiomatic-typecheck
2914
2943
  # rip-related addressing
2915
2944
  if stmt.data.op in ("Iop_Add32", "Iop_Add64"):
2916
2945
  if all(type(arg) is pyvex.expr.Const for arg in stmt.data.args):
@@ -4396,8 +4425,8 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
4396
4425
 
4397
4426
  # Let's try to create the pyvex IRSB directly, since it's much faster
4398
4427
  nodecode = False
4399
- irsb = None
4400
- lifted_block = None
4428
+ irsb: pyvex.IRSB | PcodeIRSB | None = None
4429
+ lifted_block: Block = None # type:ignore
4401
4430
  try:
4402
4431
  lifted_block = self._lift(
4403
4432
  addr,
@@ -4411,10 +4440,13 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
4411
4440
  except SimTranslationError:
4412
4441
  nodecode = True
4413
4442
 
4414
- irsb_string: bytes = lifted_block.bytes[: irsb.size] if irsb is not None else lifted_block.bytes
4443
+ irsb_string: bytes = b""
4444
+ lifted_block_bytes = lifted_block.bytes if lifted_block.bytes is not None else b""
4445
+ if lifted_block is not None:
4446
+ irsb_string = lifted_block_bytes[: irsb.size] if irsb is not None else lifted_block_bytes
4415
4447
 
4416
4448
  # special logic during the complete scanning phase
4417
- if cfg_job.job_type == CFGJobType.COMPLETE_SCANNING and is_arm_arch(self.project.arch):
4449
+ if cfg_job.job_type == CFGJobType.COMPLETE_SCANNING and is_arm_arch(self.project.arch) and irsb is not None:
4418
4450
  # it's way too easy to incorrectly disassemble THUMB code contains 0x4f as ARM code svc?? #????
4419
4451
  # if we get a single block that getting decoded to svc?? under ARM mode, we treat it as nodecode
4420
4452
  if (
@@ -4452,7 +4484,7 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
4452
4484
  except SimTranslationError:
4453
4485
  nodecode = True
4454
4486
 
4455
- irsb_string: bytes = lifted_block.bytes[: irsb.size] if irsb is not None else lifted_block.bytes
4487
+ irsb_string = lifted_block.bytes[: irsb.size] if irsb is not None else lifted_block.bytes
4456
4488
 
4457
4489
  if not (nodecode or irsb.size == 0 or irsb.jumpkind == "Ijk_NoDecode"):
4458
4490
  # it is decodeable
@@ -4765,6 +4797,37 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
4765
4797
  func = self.kb.functions.get_by_addr(func_addr)
4766
4798
  func.info["get_pc"] = "ebx"
4767
4799
 
4800
+ # determine if the function uses ebp as a general purpose register or not
4801
+ if addr == func_addr or 0 < addr - func_addr <= 0x20:
4802
+ ebp_as_gpr = True
4803
+ cap = self._lift(addr, size=cfg_node.size).capstone
4804
+ for insn in cap.insns:
4805
+ if (
4806
+ insn.mnemonic == "mov"
4807
+ and len(insn.operands) == 2
4808
+ and insn.operands[0].type == capstone.x86.X86_OP_REG
4809
+ and insn.operands[1].type == capstone.x86.X86_OP_REG
4810
+ ):
4811
+ if (
4812
+ insn.operands[0].reg == capstone.x86.X86_REG_EBP
4813
+ and insn.operands[1].reg == capstone.x86.X86_REG_ESP
4814
+ ):
4815
+ ebp_as_gpr = False
4816
+ break
4817
+ elif (
4818
+ insn.mnemonic == "lea"
4819
+ and len(insn.operands) == 2
4820
+ and insn.operands[0].type == capstone.x86.X86_OP_REG
4821
+ and insn.operands[1].type == capstone.x86.X86_OP_MEM
4822
+ ) and (
4823
+ insn.operands[0].reg == capstone.x86.X86_REG_EBP
4824
+ and insn.operands[1].mem.base == capstone.x86.X86_REG_ESP
4825
+ ):
4826
+ ebp_as_gpr = False
4827
+ break
4828
+ func = self.kb.functions.get_by_addr(func_addr)
4829
+ func.info["bp_as_gpr"] = ebp_as_gpr
4830
+
4768
4831
  elif self.project.arch.name == "AMD64":
4769
4832
  # determine if the function uses rbp as a general purpose register or not
4770
4833
  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
 
@@ -466,6 +505,21 @@ class JumpTableProcessor(
466
505
  return self._top(expr.result_size(self.tyenv))
467
506
  return v
468
507
 
508
+ @binop_handler
509
+ def _handle_binop_And(self, expr):
510
+ arg0 = self._expr(expr.args[0])
511
+ if (
512
+ isinstance(arg0, claripy.ast.BV)
513
+ and self._is_registeroffset(arg0)
514
+ and isinstance(expr.args[1], pyvex.IRExpr.Const)
515
+ ):
516
+ mask_value = expr.args[1].con.value
517
+ if mask_value in {1, 3, 7, 15, 31, 63, 127, 255}:
518
+ # 1cbbf108f44c8f4babde546d26425ca5340dccf878d306b90eb0fbec2f83ab51:0x40bd1b
519
+ self.state.is_jumptable = True
520
+ return arg0 & mask_value
521
+ return self._top(expr.result_size(self.tyenv))
522
+
469
523
  @binop_handler
470
524
  def _handle_binop_CmpLE(self, expr):
471
525
  return self._handle_Comparison(*expr.args)
@@ -853,7 +907,7 @@ class JumpTableResolver(IndirectJumpResolver):
853
907
  potential_call_table = jumpkind == "Ijk_Call" or self._sp_moved_up(block) or len(func.block_addrs_set) <= 5
854
908
  # we only perform full-function propagation for jump tables or call tables in really small functions
855
909
  if not potential_call_table or len(func.block_addrs_set) <= 5:
856
- cv_manager = ConstantValueManager(self.project, cfg.kb, func)
910
+ cv_manager = ConstantValueManager(self.project, cfg.kb, func, addr)
857
911
  else:
858
912
  cv_manager = None
859
913
 
@@ -2131,7 +2185,7 @@ class JumpTableResolver(IndirectJumpResolver):
2131
2185
  read_addr = state.inspect.mem_read_address
2132
2186
  cond = state.inspect.mem_read_condition
2133
2187
 
2134
- if not isinstance(read_addr, int) and read_addr.uninitialized and cond is None:
2188
+ if not isinstance(read_addr, int) and read_addr.has_annotation_type(UninitializedAnnotation) and cond is None:
2135
2189
  # if this AST has been initialized before, just use the cached addr
2136
2190
  cached_addr = self._cached_memread_addrs.get(read_addr, None)
2137
2191
  if cached_addr is not None:
@@ -2402,6 +2456,3 @@ class JumpTableResolver(IndirectJumpResolver):
2402
2456
  if load_stmt_ids:
2403
2457
  return [load_stmt_ids[-1]]
2404
2458
  return []
2405
-
2406
-
2407
- from angr.analyses.propagator import PropagatorAnalysis
@@ -0,0 +1,81 @@
1
+ from __future__ import annotations
2
+ import logging
3
+
4
+ import pyvex
5
+
6
+ from .resolver import IndirectJumpResolver
7
+
8
+ _l = logging.getLogger(name=__name__)
9
+
10
+
11
+ class MemoryLoadResolver(IndirectJumpResolver):
12
+ """
13
+ Resolve an indirect jump that looks like the following::
14
+
15
+ .text:
16
+ call off_3314A8
17
+
18
+ .data:
19
+ off_3314A8 dd offset sub_1E426F
20
+
21
+ This indirect jump resolver may not be the best solution for all cases (e.g., when the .data section can be
22
+ intentionally altered by the binary itself).
23
+ """
24
+
25
+ def __init__(self, project):
26
+ super().__init__(project, timeless=True)
27
+
28
+ def filter(self, cfg, addr, func_addr, block, jumpkind):
29
+ return jumpkind in {"Ijk_Boring", "Ijk_Call"}
30
+
31
+ def resolve( # pylint:disable=unused-argument
32
+ self,
33
+ cfg,
34
+ addr: int,
35
+ func_addr: int,
36
+ block: pyvex.IRSB,
37
+ jumpkind: str,
38
+ func_graph_complete: bool = True,
39
+ **kwargs,
40
+ ):
41
+ """
42
+ :param cfg: CFG with specified function
43
+ :param addr: Address of indirect jump
44
+ :param func_addr: Address of function of indirect jump
45
+ :param block: Block of indirect jump (Block object)
46
+ :param jumpkind: VEX jumpkind (Ijk_Boring or Ijk_Call)
47
+ :return: Bool tuple with replacement address
48
+ """
49
+ vex_block = block
50
+ if isinstance(vex_block.next, pyvex.expr.RdTmp):
51
+ tmp_stmt_idx, tmp_ins_addr = self._find_tmp_write_stmt_and_ins(vex_block, vex_block.next.tmp)
52
+ if tmp_stmt_idx is None or tmp_ins_addr is None:
53
+ return False, []
54
+
55
+ stmt = vex_block.statements[tmp_stmt_idx]
56
+ assert isinstance(stmt, pyvex.IRStmt.WrTmp)
57
+ if (
58
+ isinstance(stmt.data, pyvex.IRExpr.Load)
59
+ and isinstance(stmt.data.addr, pyvex.IRExpr.Const)
60
+ and stmt.data.result_size(vex_block.tyenv) == self.project.arch.bits
61
+ ):
62
+ load_addr = stmt.data.addr.con.value
63
+ try:
64
+ value = self.project.loader.memory.unpack_word(load_addr, size=self.project.arch.bytes)
65
+ if isinstance(value, int) and self._is_target_valid(cfg, value):
66
+ return True, [value]
67
+ except KeyError:
68
+ return False, []
69
+
70
+ return False, []
71
+
72
+ @staticmethod
73
+ def _find_tmp_write_stmt_and_ins(vex_block, tmp: int) -> tuple[int | None, int | None]:
74
+ stmt_idx = None
75
+ for idx, stmt in enumerate(reversed(vex_block.statements)):
76
+ if isinstance(stmt, pyvex.IRStmt.IMark) and stmt_idx is not None:
77
+ ins_addr = stmt.addr + stmt.delta
78
+ return stmt_idx, ins_addr
79
+ if isinstance(stmt, pyvex.IRStmt.WrTmp) and stmt.tmp == tmp:
80
+ stmt_idx = len(vex_block.statements) - idx - 1
81
+ return None, None
@@ -10,7 +10,7 @@ class PropagatorLoadCallback:
10
10
  def __init__(self, project):
11
11
  self.project = project
12
12
 
13
- def propagator_load_callback(self, addr, size) -> bool: # pylint:disable=unused-argument
13
+ def propagator_load_callback(self, addr: claripy.ast.BV | int, size: int) -> bool: # pylint:disable=unused-argument
14
14
  # only allow loading if the address falls into a read-only region
15
15
  if isinstance(addr, claripy.ast.BV) and addr.op == "BVV":
16
16
  addr_v = addr.args[0]
@@ -19,9 +19,28 @@ class PropagatorLoadCallback:
19
19
  else:
20
20
  return False
21
21
  section = self.project.loader.find_section_containing(addr_v)
22
+ segment = None
22
23
  if section is not None:
23
- return section.is_readable and not section.is_writable
24
- segment = self.project.loader.find_segment_containing(addr_v)
25
- if segment is not None:
26
- return segment.is_readable and not segment.is_writable
24
+ if section.is_readable and not section.is_writable:
25
+ # read-only section
26
+ return True
27
+ else:
28
+ segment = self.project.loader.find_segment_containing(addr_v)
29
+ if segment is not None and segment.is_readable and not segment.is_writable:
30
+ # read-only segment
31
+ return True
32
+
33
+ if (size == self.project.arch.bytes and (section is not None and section.is_readable)) or (
34
+ segment is not None and segment.is_readable
35
+ ):
36
+ # memory is mapped and readable. does it contain a valid address?
37
+ try:
38
+ target_addr = self.project.loader.memory.unpack_word(
39
+ addr_v, size=size, endness=self.project.arch.memory_endness
40
+ )
41
+ if target_addr >= 0x1000 and self.project.loader.find_object_containing(target_addr) is not None:
42
+ return True
43
+ except KeyError:
44
+ return False
45
+
27
46
  return False
@@ -3,7 +3,6 @@ import logging
3
3
 
4
4
  from capstone.x86_const import X86_OP_MEM
5
5
 
6
- from angr.simos import SimWindows
7
6
  from .resolver import IndirectJumpResolver
8
7
 
9
8
  l = logging.getLogger(name=__name__)
@@ -11,16 +10,14 @@ l = logging.getLogger(name=__name__)
11
10
 
12
11
  class X86PeIatResolver(IndirectJumpResolver):
13
12
  """
14
- A timeless indirect jump resolver for IAT in x86 PEs.
13
+ A timeless indirect jump resolver for IAT in x86 PEs and xbes.
15
14
  """
16
15
 
17
16
  def __init__(self, project):
18
17
  super().__init__(project, timeless=True)
19
18
 
20
19
  def filter(self, cfg, addr, func_addr, block, jumpkind):
21
- if not isinstance(self.project.simos, SimWindows):
22
- return False
23
- if jumpkind != "Ijk_Call":
20
+ if jumpkind not in {"Ijk_Call", "Ijk_Boring"}: # both call and jmp
24
21
  return False
25
22
 
26
23
  insns = self.project.factory.block(addr).capstone.insns