angr 9.2.131__py3-none-macosx_10_9_x86_64.whl → 9.2.132__py3-none-macosx_10_9_x86_64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of angr might be problematic. Click here for more details.

Files changed (112) hide show
  1. angr/__init__.py +1 -1
  2. angr/analyses/analysis.py +6 -2
  3. angr/analyses/cfg/cfg_emulated.py +5 -5
  4. angr/analyses/cfg/cfg_fast.py +2 -2
  5. angr/analyses/cfg/indirect_jump_resolvers/jumptable.py +139 -94
  6. angr/analyses/cfg/indirect_jump_resolvers/x86_elf_pic_plt.py +1 -1
  7. angr/analyses/ddg.py +14 -11
  8. angr/analyses/decompiler/ail_simplifier.py +3 -2
  9. angr/analyses/decompiler/block_simplifier.py +10 -21
  10. angr/analyses/decompiler/clinic.py +108 -34
  11. angr/analyses/decompiler/condition_processor.py +12 -10
  12. angr/analyses/decompiler/dephication/graph_rewriting.py +1 -1
  13. angr/analyses/decompiler/dephication/rewriting_engine.py +169 -45
  14. angr/analyses/decompiler/dephication/seqnode_dephication.py +5 -4
  15. angr/analyses/decompiler/optimization_passes/const_derefs.py +1 -0
  16. angr/analyses/decompiler/optimization_passes/div_simplifier.py +41 -16
  17. angr/analyses/decompiler/optimization_passes/engine_base.py +261 -83
  18. angr/analyses/decompiler/optimization_passes/inlined_string_transformation_simplifier.py +173 -35
  19. angr/analyses/decompiler/optimization_passes/mod_simplifier.py +5 -2
  20. angr/analyses/decompiler/optimization_passes/optimization_pass.py +39 -19
  21. angr/analyses/decompiler/peephole_optimizations/remove_noop_conversions.py +2 -0
  22. angr/analyses/decompiler/ssailification/rewriting.py +1 -2
  23. angr/analyses/decompiler/ssailification/rewriting_engine.py +138 -55
  24. angr/analyses/decompiler/ssailification/ssailification.py +2 -1
  25. angr/analyses/decompiler/ssailification/traversal.py +4 -6
  26. angr/analyses/decompiler/ssailification/traversal_engine.py +125 -42
  27. angr/analyses/decompiler/structured_codegen/c.py +5 -3
  28. angr/analyses/decompiler/structuring/phoenix.py +26 -9
  29. angr/analyses/decompiler/structuring/structurer_nodes.py +9 -0
  30. angr/analyses/deobfuscator/irsb_reg_collector.py +29 -60
  31. angr/analyses/deobfuscator/string_obf_finder.py +2 -2
  32. angr/analyses/init_finder.py +47 -22
  33. angr/analyses/propagator/engine_base.py +21 -14
  34. angr/analyses/propagator/engine_vex.py +149 -179
  35. angr/analyses/propagator/propagator.py +10 -28
  36. angr/analyses/propagator/top_checker_mixin.py +211 -5
  37. angr/analyses/propagator/vex_vars.py +1 -1
  38. angr/analyses/reaching_definitions/dep_graph.py +1 -1
  39. angr/analyses/reaching_definitions/engine_ail.py +304 -329
  40. angr/analyses/reaching_definitions/engine_vex.py +243 -229
  41. angr/analyses/reaching_definitions/function_handler.py +3 -3
  42. angr/analyses/reaching_definitions/rd_state.py +37 -32
  43. angr/analyses/s_propagator.py +18 -3
  44. angr/analyses/s_reaching_definitions/s_reaching_definitions.py +9 -5
  45. angr/analyses/typehoon/simple_solver.py +7 -5
  46. angr/analyses/typehoon/translator.py +8 -0
  47. angr/analyses/typehoon/typeconsts.py +10 -2
  48. angr/analyses/typehoon/typevars.py +9 -7
  49. angr/analyses/variable_recovery/engine_ail.py +299 -259
  50. angr/analyses/variable_recovery/engine_base.py +135 -117
  51. angr/analyses/variable_recovery/engine_vex.py +175 -185
  52. angr/analyses/variable_recovery/irsb_scanner.py +49 -38
  53. angr/analyses/variable_recovery/variable_recovery.py +28 -5
  54. angr/analyses/variable_recovery/variable_recovery_base.py +32 -33
  55. angr/analyses/variable_recovery/variable_recovery_fast.py +2 -2
  56. angr/analyses/xrefs.py +46 -19
  57. angr/annocfg.py +19 -14
  58. angr/block.py +4 -9
  59. angr/calling_conventions.py +1 -1
  60. angr/engines/engine.py +30 -14
  61. angr/engines/light/__init__.py +11 -3
  62. angr/engines/light/engine.py +1003 -1185
  63. angr/engines/pcode/cc.py +2 -0
  64. angr/engines/successors.py +13 -9
  65. angr/engines/vex/claripy/datalayer.py +1 -1
  66. angr/engines/vex/claripy/irop.py +1 -1
  67. angr/engines/vex/light/slicing.py +2 -2
  68. angr/exploration_techniques/__init__.py +1 -124
  69. angr/exploration_techniques/base.py +126 -0
  70. angr/exploration_techniques/bucketizer.py +1 -1
  71. angr/exploration_techniques/dfs.py +3 -1
  72. angr/exploration_techniques/director.py +2 -3
  73. angr/exploration_techniques/driller_core.py +1 -1
  74. angr/exploration_techniques/explorer.py +4 -2
  75. angr/exploration_techniques/lengthlimiter.py +2 -1
  76. angr/exploration_techniques/local_loop_seer.py +2 -1
  77. angr/exploration_techniques/loop_seer.py +5 -5
  78. angr/exploration_techniques/manual_mergepoint.py +2 -1
  79. angr/exploration_techniques/memory_watcher.py +3 -1
  80. angr/exploration_techniques/oppologist.py +4 -5
  81. angr/exploration_techniques/slicecutor.py +4 -2
  82. angr/exploration_techniques/spiller.py +1 -1
  83. angr/exploration_techniques/stochastic.py +2 -1
  84. angr/exploration_techniques/stub_stasher.py +2 -1
  85. angr/exploration_techniques/suggestions.py +3 -1
  86. angr/exploration_techniques/symbion.py +3 -1
  87. angr/exploration_techniques/tech_builder.py +2 -1
  88. angr/exploration_techniques/threading.py +4 -7
  89. angr/exploration_techniques/timeout.py +4 -2
  90. angr/exploration_techniques/tracer.py +4 -3
  91. angr/exploration_techniques/unique.py +3 -2
  92. angr/exploration_techniques/veritesting.py +1 -1
  93. angr/knowledge_plugins/key_definitions/atoms.py +2 -2
  94. angr/knowledge_plugins/key_definitions/live_definitions.py +16 -13
  95. angr/knowledge_plugins/propagations/states.py +13 -8
  96. angr/knowledge_plugins/variables/variable_manager.py +23 -9
  97. angr/lib/angr_native.dylib +0 -0
  98. angr/sim_manager.py +1 -3
  99. angr/sim_state.py +39 -41
  100. angr/sim_type.py +5 -0
  101. angr/sim_variable.py +29 -28
  102. angr/utils/bits.py +12 -0
  103. angr/utils/orderedset.py +4 -1
  104. angr/utils/ssa/__init__.py +21 -3
  105. {angr-9.2.131.dist-info → angr-9.2.132.dist-info}/METADATA +6 -6
  106. {angr-9.2.131.dist-info → angr-9.2.132.dist-info}/RECORD +110 -111
  107. angr/analyses/propagator/engine_ail.py +0 -1562
  108. angr/storage/memory_mixins/__init__.pyi +0 -48
  109. {angr-9.2.131.dist-info → angr-9.2.132.dist-info}/LICENSE +0 -0
  110. {angr-9.2.131.dist-info → angr-9.2.132.dist-info}/WHEEL +0 -0
  111. {angr-9.2.131.dist-info → angr-9.2.132.dist-info}/entry_points.txt +0 -0
  112. {angr-9.2.131.dist-info → angr-9.2.132.dist-info}/top_level.txt +0 -0
angr/__init__.py CHANGED
@@ -2,7 +2,7 @@
2
2
  # pylint: disable=wrong-import-position
3
3
  from __future__ import annotations
4
4
 
5
- __version__ = "9.2.131"
5
+ __version__ = "9.2.132"
6
6
 
7
7
  if bytes is str:
8
8
  raise Exception(
angr/analyses/analysis.py CHANGED
@@ -20,6 +20,7 @@ from rich import progress
20
20
  from angr.misc.plugins import PluginVendor, VendorPreset
21
21
  from angr.misc.ux import deprecated
22
22
  from angr.misc import telemetry
23
+ from angr.misc.testing import is_testing
23
24
 
24
25
  if TYPE_CHECKING:
25
26
  from angr.knowledge_base import KnowledgeBase
@@ -190,11 +191,14 @@ class AnalysisFactory(Generic[A]):
190
191
 
191
192
  def prep(
192
193
  self,
193
- fail_fast=False,
194
+ fail_fast: bool | None = None,
194
195
  kb: KnowledgeBase | None = None,
195
196
  progress_callback: Callable | None = None,
196
197
  show_progressbar: bool = False,
197
198
  ) -> type[A]:
199
+ if fail_fast is None:
200
+ fail_fast = is_testing
201
+
198
202
  @functools.wraps(self._analysis_cls.__init__)
199
203
  @t.start_as_current_span(self._analysis_cls.__name__)
200
204
  def wrapper(*args, **kwargs):
@@ -253,7 +257,7 @@ class AnalysisFactory(Generic[A]):
253
257
  return wrapper # type: ignore
254
258
 
255
259
  def __call__(self, *args, **kwargs) -> A:
256
- fail_fast = kwargs.pop("fail_fast", False)
260
+ fail_fast = kwargs.pop("fail_fast", is_testing)
257
261
  kb = kwargs.pop("kb", self._project.kb)
258
262
  progress_callback = kwargs.pop("progress_callback", None)
259
263
  show_progressbar = kwargs.pop("show_progressbar", False)
@@ -2972,7 +2972,7 @@ class CFGEmulated(ForwardAnalysis, CFGBase): # pylint: disable=abstract-method
2972
2972
  # Skip this IRSB
2973
2973
  l.debug("Caught a SimIRSBError %s. Don't panic, this is usually expected.", ex)
2974
2974
  inst = SIM_PROCEDURES["stubs"]["PathTerminator"]()
2975
- sim_successors = ProcedureEngine().process(state, procedure=inst)
2975
+ sim_successors = ProcedureEngine(self.project).process(state, procedure=inst)
2976
2976
 
2977
2977
  except SimIRSBError:
2978
2978
  exception_info = sys.exc_info()
@@ -2980,28 +2980,28 @@ class CFGEmulated(ForwardAnalysis, CFGBase): # pylint: disable=abstract-method
2980
2980
  # does not support. I'll create a terminating stub there
2981
2981
  l.debug("Caught a SimIRSBError during CFG recovery. Creating a PathTerminator.", exc_info=True)
2982
2982
  inst = SIM_PROCEDURES["stubs"]["PathTerminator"]()
2983
- sim_successors = ProcedureEngine().process(state, procedure=inst)
2983
+ sim_successors = ProcedureEngine(self.project).process(state, procedure=inst)
2984
2984
 
2985
2985
  except claripy.ClaripyError:
2986
2986
  exception_info = sys.exc_info()
2987
2987
  l.debug("Caught a ClaripyError during CFG recovery. Don't panic, this is usually expected.", exc_info=True)
2988
2988
  # Generate a PathTerminator to terminate the current path
2989
2989
  inst = SIM_PROCEDURES["stubs"]["PathTerminator"]()
2990
- sim_successors = ProcedureEngine().process(state, procedure=inst)
2990
+ sim_successors = ProcedureEngine(self.project).process(state, procedure=inst)
2991
2991
 
2992
2992
  except SimError:
2993
2993
  exception_info = sys.exc_info()
2994
2994
  l.debug("Caught a SimError during CFG recovery. Don't panic, this is usually expected.", exc_info=True)
2995
2995
  # Generate a PathTerminator to terminate the current path
2996
2996
  inst = SIM_PROCEDURES["stubs"]["PathTerminator"]()
2997
- sim_successors = ProcedureEngine().process(state, procedure=inst)
2997
+ sim_successors = ProcedureEngine(self.project).process(state, procedure=inst)
2998
2998
 
2999
2999
  except AngrExitError:
3000
3000
  exception_info = sys.exc_info()
3001
3001
  l.debug("Caught a AngrExitError during CFG recovery. Don't panic, this is usually expected.", exc_info=True)
3002
3002
  # Generate a PathTerminator to terminate the current path
3003
3003
  inst = SIM_PROCEDURES["stubs"]["PathTerminator"]()
3004
- sim_successors = ProcedureEngine().process(state, procedure=inst)
3004
+ sim_successors = ProcedureEngine(self.project).process(state, procedure=inst)
3005
3005
 
3006
3006
  except AngrError:
3007
3007
  exception_info = sys.exc_info()
@@ -3068,10 +3068,10 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
3068
3068
  irsb = self.project.factory.block(irsb.addr, size=irsb.size, opt_level=1, cross_insn_opt=False).vex
3069
3069
 
3070
3070
  # try to resolve the jump target
3071
- simsucc = self.project.factory.default_engine.process(self._initial_state, irsb, force_addr=addr)
3071
+ simsucc = self.project.factory.default_engine.process(self._initial_state, block=irsb, force_addr=addr)
3072
3072
  if len(simsucc.successors) == 1:
3073
3073
  ip = simsucc.successors[0].ip
3074
- if isinstance(ip, claripy.ast.Base):
3074
+ if isinstance(ip, claripy.ast.BV):
3075
3075
  target_addr = ip.concrete_value
3076
3076
  obj = self.project.loader.find_object_containing(target_addr, membership_check=False)
3077
3077
  if (obj is not None and obj is not self.project.loader.main_object) or self.project.is_hooked(
@@ -1,11 +1,11 @@
1
1
  # pylint:disable=wrong-import-position,wrong-import-order
2
2
  from __future__ import annotations
3
3
  import enum
4
- from typing import TYPE_CHECKING
4
+ from typing import TYPE_CHECKING, Any, Literal, cast
5
5
  from collections.abc import Sequence
6
+ from collections import defaultdict, OrderedDict
6
7
  import logging
7
8
  import functools
8
- from collections import defaultdict, OrderedDict
9
9
 
10
10
  import pyvex
11
11
  import claripy
@@ -18,15 +18,18 @@ from angr.code_location import CodeLocation
18
18
  from angr.concretization_strategies import SimConcretizationStrategyAny
19
19
  from angr.knowledge_plugins.cfg import IndirectJump, IndirectJumpType
20
20
  from angr.engines.vex.claripy import ccall
21
- from angr.engines.light import SimEngineLightVEXMixin, SimEngineLight, SpOffset, RegisterOffset
21
+ from angr.engines.light import SimEngineNostmtVEX, SpOffset, RegisterOffset
22
22
  from angr.errors import AngrError, SimError
23
23
  from angr.blade import Blade
24
24
  from angr.annocfg import AnnotatedCFG
25
25
  from angr.exploration_techniques.slicecutor import Slicecutor
26
26
  from angr.exploration_techniques.local_loop_seer import LocalLoopSeer
27
27
  from angr.exploration_techniques.explorer import Explorer
28
+ from angr.project import Project
28
29
  from angr.utils.constants import DEFAULT_STATEMENT
29
30
  from angr.analyses.propagator.vex_vars import VEXReg
31
+ from angr.analyses.propagator.top_checker_mixin import ClaripyDataVEXEngineMixin
32
+ from angr.engines.vex.claripy.datalayer import value
30
33
  from .resolver import IndirectJumpResolver
31
34
  from .propagator_utils import PropagatorLoadCallback
32
35
 
@@ -113,12 +116,14 @@ class JumpTargetBaseAddr:
113
116
  Model for jump targets and their data origin.
114
117
  """
115
118
 
116
- def __init__(self, stmt_loc, stmt, tmp, base_addr=None, tmp_1=None):
119
+ def __init__(
120
+ self, stmt_loc, stmt: pyvex.stmt.IRStmt, tmp: int, base_addr: int | None = None, tmp_1: int | None = None
121
+ ):
117
122
  self.stmt_loc = stmt_loc
118
123
  self.stmt = stmt
119
- self.tmp: int = tmp
124
+ self.tmp = tmp
120
125
  self.tmp_1 = tmp_1
121
- self.base_addr: int = base_addr
126
+ self.base_addr = base_addr
122
127
 
123
128
  assert base_addr is not None or tmp_1 is not None
124
129
 
@@ -144,16 +149,17 @@ class ConstantValueManager:
144
149
  "mapping",
145
150
  )
146
151
 
147
- def __init__(self, project, kb, func: Function):
152
+ def __init__(self, project: Project, kb, func: Function):
148
153
  self.project = project
149
154
  self.kb = kb
150
155
  self.func = func
151
156
 
152
- self.mapping = None
157
+ self.mapping: dict[Any, dict[Any, claripy.ast.Base]] | None = None
153
158
 
154
159
  def reg_read_callback(self, state: SimState):
155
160
  if not self.mapping:
156
161
  self._build_mapping()
162
+ assert self.mapping is not None
157
163
 
158
164
  codeloc = CodeLocation(state.scratch.bbl_addr, state.scratch.stmt_idx, ins_addr=state.scratch.ins_addr)
159
165
  if codeloc in self.mapping:
@@ -250,7 +256,7 @@ class JumpTableProcessorState:
250
256
  self._stack = {}
251
257
  self._tmpvar_source = {} # a mapping from temporary variables to their origins
252
258
 
253
- self.is_jumptable = None # is the current slice representing a jump table?
259
+ self.is_jumptable: bool | None = None # is the current slice representing a jump table?
254
260
  self.stmts_to_instrument = [] # Store/Put statements that we should instrument
255
261
  self.regs_to_initialize = [] # registers that we should initialize
256
262
 
@@ -274,9 +280,12 @@ class RegOffsetAnnotation(claripy.Annotation):
274
280
  return False
275
281
 
276
282
 
283
+ binop_handler = SimEngineNostmtVEX[JumpTableProcessorState, claripy.ast.BV, JumpTableProcessorState].binop_handler
284
+
285
+
277
286
  class JumpTableProcessor(
278
- SimEngineLightVEXMixin,
279
- SimEngineLight,
287
+ SimEngineNostmtVEX[JumpTableProcessorState, claripy.ast.BV, JumpTableProcessorState],
288
+ ClaripyDataVEXEngineMixin[JumpTableProcessorState, claripy.ast.BV, JumpTableProcessorState, None],
280
289
  ): # pylint:disable=abstract-method
281
290
  """
282
291
  Implements a simple and stupid data dependency tracking for stack and register variables.
@@ -298,20 +307,18 @@ class JumpTableProcessor(
298
307
  """
299
308
 
300
309
  def __init__(self, project, indirect_jump_node_pred_addrs: set[int], bp_sp_diff=0x100):
301
- super().__init__()
302
- self.project = project
310
+ super().__init__(project)
303
311
  self._bp_sp_diff = bp_sp_diff # bp - sp
304
- self._tsrc = set() # a scratch variable to store source information for values
312
+ self._tsrc: set[Literal["const"] | tuple[int, int]] = (
313
+ set()
314
+ ) # a scratch variable to store source information for values
305
315
  self._indirect_jump_node_pred_addrs = indirect_jump_node_pred_addrs
306
316
 
307
317
  self._SPOFFSET_BASE = claripy.BVS("SpOffset", self.project.arch.bits, explicit_name=True)
308
318
  self._REGOFFSET_BASE: dict[int, claripy.ast.BV] = {}
309
319
 
310
- def _top(self, size: int):
311
- return None
312
-
313
- def _is_top(self, expr) -> bool:
314
- return expr is None
320
+ def _process_block_end(self, stmt_result, whitelist):
321
+ return self.state
315
322
 
316
323
  @staticmethod
317
324
  def _is_spoffset(expr) -> bool:
@@ -321,23 +328,23 @@ class JumpTableProcessor(
321
328
  return self._SPOFFSET_BASE.annotate(RegOffsetAnnotation(sp_offset))
322
329
 
323
330
  @staticmethod
324
- def _extract_spoffset_from_expr(expr: claripy.ast.Base) -> SpOffset | None:
331
+ def _extract_spoffset_from_expr(expr: claripy.ast.Base) -> RegisterOffset | None:
325
332
  if expr.op == "BVS":
326
333
  for anno in expr.annotations:
327
334
  if isinstance(anno, RegOffsetAnnotation):
328
335
  return anno.reg_offset
329
336
  elif expr.op == "__add__":
330
337
  if len(expr.args) == 1:
331
- return JumpTableProcessor._extract_spoffset_from_expr(expr.args[0])
332
- if len(expr.args) == 2 and expr.args[1].op == "BVV":
333
- sp_offset = JumpTableProcessor._extract_spoffset_from_expr(expr.args[0])
338
+ return JumpTableProcessor._extract_spoffset_from_expr(cast(claripy.ast.BV, expr.args[0]))
339
+ if len(expr.args) == 2 and cast(claripy.ast.BV, expr.args[1]).op == "BVV":
340
+ sp_offset = JumpTableProcessor._extract_spoffset_from_expr(cast(claripy.ast.BV, expr.args[0]))
334
341
  if sp_offset is not None:
335
- delta = expr.args[1].concrete_value
342
+ delta = cast(claripy.ast.BV, expr.args[1]).concrete_value
336
343
  sp_offset += delta
337
344
  return sp_offset
338
- elif expr.op == "__and__" and len(expr.args) == 2 and expr.args[1].op == "BVV":
345
+ elif expr.op == "__and__" and len(expr.args) == 2 and cast(claripy.ast.BV, expr.args[1]).op == "BVV":
339
346
  # ignore all masking on SpOffsets
340
- return JumpTableProcessor._extract_spoffset_from_expr(expr.args[0])
347
+ return JumpTableProcessor._extract_spoffset_from_expr(cast(claripy.ast.BV, expr.args[0]))
341
348
  return None
342
349
 
343
350
  @staticmethod
@@ -357,33 +364,33 @@ class JumpTableProcessor(
357
364
  return anno.reg_offset
358
365
  elif expr.op == "__add__":
359
366
  if len(expr.args) == 1:
360
- return JumpTableProcessor._extract_regoffset_from_expr(expr.args[0])
361
- if len(expr.args) == 2 and expr.args[1].op == "BVV":
362
- reg_offset = JumpTableProcessor._extract_regoffset_from_expr(expr.args[0])
367
+ return JumpTableProcessor._extract_regoffset_from_expr(cast(claripy.ast.BV, expr.args[0]))
368
+ if len(expr.args) == 2 and cast(claripy.ast.BV, expr.args[1]).op == "BVV":
369
+ reg_offset = JumpTableProcessor._extract_regoffset_from_expr(cast(claripy.ast.BV, expr.args[0]))
363
370
  if reg_offset is not None:
364
- delta = expr.args[1].concrete_value
371
+ delta = cast(claripy.ast.BV, expr.args[1]).concrete_value
365
372
  reg_offset += delta
366
373
  return reg_offset
367
- elif expr.op == "__and__" and len(expr.args) == 2 and expr.args[1].op == "BVV":
374
+ elif expr.op == "__and__" and len(expr.args) == 2 and cast(claripy.ast.BV, expr.args[1]).op == "BVV":
368
375
  # ignore all masking on SpOffsets
369
- return JumpTableProcessor._extract_spoffset_from_expr(expr.args[0])
376
+ return JumpTableProcessor._extract_spoffset_from_expr(cast(claripy.ast.BV, expr.args[0]))
370
377
  return None
371
378
 
372
- def _handle_WrTmp(self, stmt):
379
+ def _handle_stmt_WrTmp(self, stmt):
373
380
  self._tsrc = set()
374
- super()._handle_WrTmp(stmt)
375
381
 
382
+ self.tmps[stmt.tmp] = self._expr(stmt.data)
376
383
  if self._tsrc:
377
384
  self.state._tmpvar_source[stmt.tmp] = self._tsrc
378
385
 
379
- def _handle_Put(self, stmt):
386
+ def _handle_stmt_Put(self, stmt):
380
387
  self._tsrc = set()
381
388
  offset = stmt.offset
382
389
  data = self._expr(stmt.data)
383
390
  r = (self._tsrc, data) if self._tsrc is not None else ((self.block.addr, self.stmt_idx), data)
384
391
  self.state._registers[offset] = r
385
392
 
386
- def _handle_Store(self, stmt):
393
+ def _handle_stmt_Store(self, stmt):
387
394
  self._tsrc = set()
388
395
  addr = self._expr(stmt.addr)
389
396
  data = self._expr(stmt.data)
@@ -394,13 +401,16 @@ class JumpTableProcessor(
394
401
  if isinstance(addr, SpOffset):
395
402
  self.state._stack[addr.offset] = ((self.block.addr, self.stmt_idx), data)
396
403
 
397
- def _handle_RdTmp(self, expr):
398
- v = super()._handle_RdTmp(expr)
404
+ def _handle_expr_RdTmp(self, expr):
405
+ try:
406
+ v = self.tmps[expr.tmp]
407
+ except KeyError:
408
+ v = self._top(expr.result_size(self.tyenv))
399
409
  if expr.tmp in self.state._tmpvar_source:
400
410
  self._tsrc |= set(self.state._tmpvar_source[expr.tmp])
401
411
  return v
402
412
 
403
- def _handle_Get(self, expr):
413
+ def _handle_expr_Get(self, expr):
404
414
  if expr.offset == self.arch.bp_offset:
405
415
  v = self._get_spoffset_expr(SpOffset(self.arch.bits, self._bp_sp_diff))
406
416
  elif expr.offset == self.arch.sp_offset:
@@ -433,55 +443,77 @@ class JumpTableProcessor(
433
443
  def _handle_function(self, expr): # pylint:disable=unused-argument,no-self-use
434
444
  return None # This analysis is not interprocedural
435
445
 
436
- def _handle_Load(self, expr):
446
+ def _handle_expr_Load(self, expr):
437
447
  addr = self._expr(expr.addr)
438
448
  size = expr.result_size(self.tyenv) // 8
439
449
  return self._do_load(addr, size)
440
450
 
441
- def _handle_LoadG(self, stmt):
442
- guard = self._expr(stmt.guard)
443
- if guard is True:
444
- return self._do_load(stmt.addr, stmt.addr.result_size(self.tyenv) // 8)
445
- if guard is False:
446
- return self._do_load(stmt.alt, stmt.alt.result_size(self.tyenv) // 8)
447
- return None
451
+ def _handle_stmt_LoadG(self, stmt: pyvex.stmt.LoadG):
452
+ self._tsrc = set()
448
453
 
449
- def _handle_Const(self, expr):
450
- v = super()._handle_Const(expr)
454
+ guard = self._expr(stmt.guard) != 0
455
+ iftrue = self._do_load(self._expr(stmt.addr), stmt.addr.result_size(self.tyenv) // 8)
456
+ iffalse = self._expr(stmt.alt)
457
+ result = claripy.If(guard, iftrue, iffalse)
458
+ self.tmps[stmt.dst] = result
459
+ if self._tsrc:
460
+ self.state._tmpvar_source[stmt.dst] = self._tsrc
461
+
462
+ def _handle_expr_Const(self, expr):
463
+ v = value(expr.con.type, expr.con.value)
451
464
  self._tsrc.add("const")
465
+ if not isinstance(v, claripy.ast.BV):
466
+ return self._top(expr.result_size(self.tyenv))
452
467
  return v
453
468
 
454
- def _handle_CmpLE(self, expr):
455
- self._handle_Comparison(*expr.args)
469
+ @binop_handler
470
+ def _handle_binop_CmpLE(self, expr):
471
+ return self._handle_Comparison(*expr.args)
456
472
 
457
- def _handle_CmpGE(self, expr):
458
- self._handle_Comparison(*expr.args)
473
+ @binop_handler
474
+ def _handle_binop_CmpGE(self, expr):
475
+ return self._handle_Comparison(*expr.args)
459
476
 
460
- def _handle_CmpLT(self, expr):
461
- self._handle_Comparison(*expr.args)
477
+ @binop_handler
478
+ def _handle_binop_CmpLT(self, expr):
479
+ return self._handle_Comparison(*expr.args)
462
480
 
463
- def _handle_CmpGT(self, expr):
464
- self._handle_Comparison(*expr.args)
481
+ @binop_handler
482
+ def _handle_binop_CmpGT(self, expr):
483
+ return self._handle_Comparison(*expr.args)
465
484
 
466
- def _handle_CCall(self, expr):
467
- if not isinstance(expr.args[0], pyvex.IRExpr.Const):
468
- return
469
- cond_type_enum = expr.args[0].con.value
485
+ def _handle_expr_CCall(self, expr):
486
+ if isinstance(expr.args[0], pyvex.IRExpr.Const):
487
+ cond_type_enum = expr.args[0].con.value
470
488
 
471
- if self.arch.name in {"X86", "AMD64", "AARCH64"}:
472
- if cond_type_enum in EXPECTED_COND_TYPES[self.arch.name]:
473
- self._handle_Comparison(expr.args[2], expr.args[3])
474
- elif is_arm_arch(self.arch):
475
- if cond_type_enum in EXPECTED_COND_TYPES["ARM"]:
489
+ if self.arch.name in {"X86", "AMD64", "AARCH64"}:
490
+ if cond_type_enum in EXPECTED_COND_TYPES[self.arch.name]:
491
+ self._handle_Comparison(expr.args[2], expr.args[3])
492
+ elif is_arm_arch(self.arch):
493
+ if cond_type_enum in EXPECTED_COND_TYPES["ARM"]:
494
+ self._handle_Comparison(expr.args[2], expr.args[3])
495
+ else:
496
+ # other architectures
497
+ l.warning("Please fill in EXPECTED_COND_TYPES for %s.", self.arch.name)
476
498
  self._handle_Comparison(expr.args[2], expr.args[3])
477
- else:
478
- # other architectures
479
- l.warning("Please fill in EXPECTED_COND_TYPES for %s.", self.arch.name)
480
- self._handle_Comparison(expr.args[2], expr.args[3])
481
499
 
482
- def _handle_Comparison(self, arg0, arg1):
500
+ return self._top(expr.result_size(self.tyenv))
501
+
502
+ def _handle_expr_VECRET(self, expr):
503
+ return self._top(expr.result_size(self.tyenv))
504
+
505
+ def _handle_expr_GSPTR(self, expr):
506
+ return self._top(expr.result_size(self.tyenv))
507
+
508
+ def _handle_expr_GetI(self, expr):
509
+ return self._top(expr.result_size(self.tyenv))
510
+
511
+ def _handle_expr_ITE(self, expr):
512
+ return self._top(expr.result_size(self.tyenv))
513
+
514
+ def _handle_Comparison(self, arg0: pyvex.expr.IRExpr, arg1: pyvex.expr.IRExpr) -> claripy.ast.BV:
483
515
  if self.block.addr not in self._indirect_jump_node_pred_addrs:
484
- return
516
+ return self._top(1)
485
517
 
486
518
  # found the comparison
487
519
  arg0_src, arg1_src = None, None
@@ -502,10 +534,10 @@ class JumpTableProcessor(
502
534
  if arg0_src == "const" and arg1_src == "const":
503
535
  # comparison of two consts... there is nothing we can do
504
536
  self.state.is_jumptable = True
505
- return
537
+ return self._top(1)
506
538
  if arg0_src not in {"const", None} and arg1_src not in {"const", None}:
507
539
  # this is probably not a jump table
508
- return
540
+ return self._top(1)
509
541
  if arg1_src == "const":
510
542
  # make sure arg0_src is const
511
543
  arg0_src, arg1_src = arg1_src, arg0_src
@@ -515,7 +547,7 @@ class JumpTableProcessor(
515
547
  if arg0_src != "const":
516
548
  # we failed during dependency tracking so arg0_src couldn't be determined
517
549
  # but we will still try to resolve it as a jump table as a fall back
518
- return
550
+ return self._top(1)
519
551
 
520
552
  if isinstance(arg1_src, tuple):
521
553
  arg1_src_stmt = self.project.factory.block(arg1_src[0], cross_insn_opt=True).vex.statements[arg1_src[1]]
@@ -563,23 +595,17 @@ class JumpTableProcessor(
563
595
  #
564
596
  self.state.stmts_to_instrument.append(("reg_write", *arg1_src))
565
597
 
566
- def _do_load(self, addr, size):
598
+ return self._top(1)
599
+
600
+ def _do_load(self, addr: claripy.ast.BV, size: int) -> claripy.ast.BV:
567
601
  src = (self.block.addr, self.stmt_idx)
568
602
  self._tsrc = {src}
569
- if addr is None:
570
- return None
571
603
 
572
604
  if self._is_spoffset(addr):
573
605
  spoffset = self._extract_spoffset_from_expr(addr)
574
606
  if spoffset is not None and spoffset.offset in self.state._stack:
575
607
  self._tsrc = {self.state._stack[spoffset.offset][0]}
576
608
  return self.state._stack[spoffset.offset][1]
577
- elif isinstance(addr, int):
578
- # Load data from memory if it is mapped
579
- try:
580
- return self.project.loader.memory.unpack_word(addr, size=size)
581
- except KeyError:
582
- return None
583
609
  elif self._is_registeroffset(addr):
584
610
  # Load data from a register, but this register hasn't been initialized at this point
585
611
  # We will need to initialize this register during slice execution later
@@ -598,9 +624,7 @@ class JumpTableProcessor(
598
624
  # function call sub_375c04. Since we do not analyze sub_375c04, we treat r0@11e918 as a constant 0.
599
625
  pass
600
626
 
601
- return None
602
-
603
- return None
627
+ return self._top(size)
604
628
 
605
629
 
606
630
  #
@@ -658,10 +682,10 @@ class RegisterInitializerHook:
658
682
  Hook for register init.
659
683
  """
660
684
 
661
- def __init__(self, reg_offset, reg_bits, value):
685
+ def __init__(self, reg_offset, reg_bits, initial_value):
662
686
  self.reg_offset = reg_offset
663
687
  self.reg_bits = reg_bits
664
- self.value = value
688
+ self.value = initial_value
665
689
 
666
690
  def hook(self, state):
667
691
  state.registers.store(self.reg_offset, claripy.BVV(self.value, self.reg_bits))
@@ -786,7 +810,7 @@ class JumpTableResolver(IndirectJumpResolver):
786
810
 
787
811
  self._bss_regions = None
788
812
  # the maximum number of resolved targets. Will be initialized from CFG.
789
- self._max_targets = None
813
+ self._max_targets = 0
790
814
 
791
815
  # cached memory read addresses that are used to initialize uninitialized registers
792
816
  # should be cleared before every symbolic execution run on the slice
@@ -1044,8 +1068,10 @@ class JumpTableResolver(IndirectJumpResolver):
1044
1068
  simgr.use_technique(slicecutor)
1045
1069
  simgr.use_technique(LocalLoopSeer(bound=1))
1046
1070
  if load_stmt is not None:
1071
+ assert load_stmt_loc is not None
1047
1072
  explorer = Explorer(find=load_stmt_loc[0])
1048
1073
  elif ite_stmt is not None:
1074
+ assert ite_stmt_loc is not None
1049
1075
  explorer = Explorer(find=ite_stmt_loc[0])
1050
1076
  else:
1051
1077
  raise TypeError("Unsupported type of jump table.")
@@ -1144,7 +1170,14 @@ class JumpTableResolver(IndirectJumpResolver):
1144
1170
  l.info("Could not resolve indirect jump %#x in function %#x.", addr, func_addr)
1145
1171
  return False, None
1146
1172
 
1147
- def _find_load_statement(self, b, stmt_loc):
1173
+ def _find_load_statement(self, b, stmt_loc: tuple[int, int]) -> tuple[
1174
+ tuple[int, int] | None,
1175
+ pyvex.stmt.IRStmt | None,
1176
+ int | None,
1177
+ list[tuple[int, int]],
1178
+ list[JumpTargetBaseAddr],
1179
+ OrderedDict[tuple[int, int], AddressTransformation],
1180
+ ]:
1148
1181
  """
1149
1182
  Find the location of the final Load statement that loads indirect jump targets from the jump table.
1150
1183
  """
@@ -1306,6 +1339,7 @@ class JumpTableResolver(IndirectJumpResolver):
1306
1339
  if isinstance(stmt.data.args[0], pyvex.IRExpr.Const) and isinstance(
1307
1340
  stmt.data.args[1], pyvex.IRExpr.RdTmp
1308
1341
  ):
1342
+ assert isinstance(stmt, pyvex.stmt.WrTmp)
1309
1343
  transformations[(stmt_loc[0], stmt.tmp)] = AddressTransformation(
1310
1344
  AddressTransformationTypes.Add, [stmt.data.args[0].con.value, AddressSingleton]
1311
1345
  )
@@ -1315,6 +1349,7 @@ class JumpTableResolver(IndirectJumpResolver):
1315
1349
  elif isinstance(stmt.data.args[0], pyvex.IRExpr.RdTmp) and isinstance(
1316
1350
  stmt.data.args[1], pyvex.IRExpr.Const
1317
1351
  ):
1352
+ assert isinstance(stmt, pyvex.stmt.WrTmp)
1318
1353
  transformations[(stmt_loc[0], stmt.tmp)] = AddressTransformation(
1319
1354
  AddressTransformationTypes.Add, [AddressSingleton, stmt.data.args[1].con.value]
1320
1355
  )
@@ -1363,6 +1398,7 @@ class JumpTableResolver(IndirectJumpResolver):
1363
1398
  and isinstance(stmt.data.args[1], pyvex.IRExpr.Const)
1364
1399
  and stmt.data.args[1].con.value == 1
1365
1400
  ):
1401
+ assert isinstance(stmt, pyvex.stmt.WrTmp)
1366
1402
  # great. here it is
1367
1403
  stmts_to_remove.append(stmt_loc)
1368
1404
  transformations[(stmt_loc[0], stmt.tmp)] = AddressTransformation(
@@ -1387,6 +1423,7 @@ class JumpTableResolver(IndirectJumpResolver):
1387
1423
  if isinstance(stmt.data.args[0], pyvex.IRExpr.RdTmp) and isinstance(
1388
1424
  stmt.data.args[1], pyvex.IRExpr.Const
1389
1425
  ):
1426
+ assert isinstance(stmt, pyvex.stmt.WrTmp)
1390
1427
  # found it
1391
1428
  stmts_to_remove.append(stmt_loc)
1392
1429
  transformations[(stmt_loc[0], stmt.tmp)] = AddressTransformation(
@@ -1424,6 +1461,7 @@ class JumpTableResolver(IndirectJumpResolver):
1424
1461
  and isinstance(stmt.data.args[0], pyvex.IRExpr.RdTmp)
1425
1462
  and isinstance(stmt.data.args[1], pyvex.IRExpr.Const)
1426
1463
  ):
1464
+ assert isinstance(stmt, pyvex.stmt.WrTmp)
1427
1465
  # found it
1428
1466
  stmts_to_remove.append(stmt_loc)
1429
1467
  transformations[(stmt_loc[0], stmt.tmp)] = AddressTransformation(
@@ -1431,6 +1469,7 @@ class JumpTableResolver(IndirectJumpResolver):
1431
1469
  )
1432
1470
  continue
1433
1471
  elif isinstance(stmt.data, pyvex.IRExpr.Load):
1472
+ assert isinstance(stmt, pyvex.stmt.WrTmp)
1434
1473
  # Got it!
1435
1474
  load_stmt, load_stmt_loc, load_size = (
1436
1475
  stmt,
@@ -1558,9 +1597,10 @@ class JumpTableResolver(IndirectJumpResolver):
1558
1597
 
1559
1598
  block = self.project.factory.block(block_addr, cross_insn_opt=True, backup_state=self.base_state)
1560
1599
  stmt_whitelist = annotatedcfg.get_whitelisted_statements(block_addr)
1600
+ assert isinstance(stmt_whitelist, list)
1561
1601
  try:
1562
1602
  engine.process(state, block=block, whitelist=stmt_whitelist)
1563
- except (claripy.errors.ClaripyError, SimError, AngrError):
1603
+ except (claripy.ClaripyError, SimError, AngrError):
1564
1604
  # anything can happen
1565
1605
  break
1566
1606
 
@@ -1598,6 +1638,7 @@ class JumpTableResolver(IndirectJumpResolver):
1598
1638
 
1599
1639
  # If we're just reading a constant, don't bother with the rest of this mess!
1600
1640
  if isinstance(load_stmt, pyvex.IRStmt.WrTmp):
1641
+ assert isinstance(load_stmt.data, pyvex.IRExpr.Load)
1601
1642
  if type(load_stmt.data.addr) is pyvex.IRExpr.Const:
1602
1643
  # It's directly loading from a constant address
1603
1644
  # e.g.,
@@ -1717,7 +1758,7 @@ class JumpTableResolver(IndirectJumpResolver):
1717
1758
  # full-function data propagation before performing jump table recovery.
1718
1759
  l.debug("Multiple statements adding bases, not supported yet") # FIXME: Just check the addresses?
1719
1760
 
1720
- if jumptable_addr.has_annotation_type(claripy.annotation.RegionAnnotation):
1761
+ if jumptable_addr.has_annotation_type(claripy.RegionAnnotation):
1721
1762
  return None
1722
1763
 
1723
1764
  all_targets = []
@@ -1730,7 +1771,7 @@ class JumpTableResolver(IndirectJumpResolver):
1730
1771
  jumptable_si = claripy.backends.vsa.simplify(jumptable_addr)
1731
1772
  si_annotation = jumptable_si.get_annotation(claripy.annotation.StridedIntervalAnnotation)
1732
1773
  stride = si_annotation.stride if si_annotation is not None else 0
1733
- except claripy.errors.BackendError:
1774
+ except claripy.ClaripyError:
1734
1775
  return None
1735
1776
 
1736
1777
  # we may resolve a vtable (in C, e.g., the IO_JUMPS_FUNC in libc), but the stride of this load is usually 1
@@ -1745,6 +1786,7 @@ class JumpTableResolver(IndirectJumpResolver):
1745
1786
  total_cases = jumptable_addr.cardinality
1746
1787
  sort = "jumptable"
1747
1788
 
1789
+ assert self._max_targets is not None
1748
1790
  if total_cases > self._max_targets:
1749
1791
  if (
1750
1792
  potential_call_table
@@ -1755,6 +1797,7 @@ class JumpTableResolver(IndirectJumpResolver):
1755
1797
  # Undetermined table size. Take a guess based on target plausibility.
1756
1798
  table_base_addr = None
1757
1799
  for arg in jumptable_addr.args:
1800
+ assert isinstance(arg, (claripy.ast.BV, claripy.ast.FP, claripy.ast.Bool))
1758
1801
  if arg.concrete:
1759
1802
  table_base_addr = state.solver.eval(arg)
1760
1803
  break
@@ -2223,6 +2266,7 @@ class JumpTableResolver(IndirectJumpResolver):
2223
2266
  load_addr_tmp = None
2224
2267
 
2225
2268
  if isinstance(load_stmt, pyvex.IRStmt.WrTmp):
2269
+ assert isinstance(load_stmt.data, pyvex.IRExpr.Load)
2226
2270
  if type(load_stmt.data.addr) is pyvex.IRExpr.RdTmp:
2227
2271
  load_addr_tmp = load_stmt.data.addr.tmp
2228
2272
  elif type(load_stmt.data.addr) is pyvex.IRExpr.Const:
@@ -2260,6 +2304,7 @@ class JumpTableResolver(IndirectJumpResolver):
2260
2304
 
2261
2305
  if isinstance(load_stmt, pyvex.IRStmt.LoadG) and not isinstance(load_stmt.guard, pyvex.IRExpr.Const):
2262
2306
  # LoadG comes with a guard. We should apply this guard to the load expression
2307
+ assert isinstance(load_stmt.guard, pyvex.expr.RdTmp)
2263
2308
  guard_tmp = load_stmt.guard.tmp
2264
2309
  guard = state.scratch.temps[guard_tmp] != 0
2265
2310
  try:
@@ -78,7 +78,7 @@ class X86ElfPicPltResolver(IndirectJumpResolver):
78
78
  state = cfg._initial_state.copy() if cfg._initial_state is not None else self.project.factory.blank_state()
79
79
  state.regs.ebx = got_addr
80
80
 
81
- successors = self.project.factory.default_engine.process(state, block, force_addr=addr)
81
+ successors = self.project.factory.default_engine.process(state, block=block, force_addr=addr)
82
82
 
83
83
  if len(successors.flat_successors) != 1:
84
84
  return False, []