angr 9.2.147__py3-none-win_amd64.whl → 9.2.149__py3-none-win_amd64.whl

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

Potentially problematic release.


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

Files changed (91) hide show
  1. angr/__init__.py +1 -1
  2. angr/analyses/analysis.py +3 -11
  3. angr/analyses/calling_convention/calling_convention.py +42 -2
  4. angr/analyses/calling_convention/fact_collector.py +5 -4
  5. angr/analyses/calling_convention/utils.py +1 -0
  6. angr/analyses/cfg/cfg_base.py +3 -59
  7. angr/analyses/cfg/cfg_emulated.py +17 -14
  8. angr/analyses/cfg/cfg_fast.py +68 -63
  9. angr/analyses/cfg/cfg_fast_soot.py +3 -3
  10. angr/analyses/decompiler/ail_simplifier.py +65 -32
  11. angr/analyses/decompiler/block_simplifier.py +20 -6
  12. angr/analyses/decompiler/callsite_maker.py +28 -18
  13. angr/analyses/decompiler/clinic.py +84 -17
  14. angr/analyses/decompiler/condition_processor.py +0 -21
  15. angr/analyses/decompiler/counters/call_counter.py +3 -0
  16. angr/analyses/decompiler/dephication/rewriting_engine.py +24 -2
  17. angr/analyses/decompiler/optimization_passes/__init__.py +5 -0
  18. angr/analyses/decompiler/optimization_passes/base_ptr_save_simplifier.py +15 -13
  19. angr/analyses/decompiler/optimization_passes/const_prop_reverter.py +1 -1
  20. angr/analyses/decompiler/optimization_passes/determine_load_sizes.py +64 -0
  21. angr/analyses/decompiler/optimization_passes/eager_std_string_concatenation.py +165 -0
  22. angr/analyses/decompiler/optimization_passes/engine_base.py +11 -2
  23. angr/analyses/decompiler/optimization_passes/inlined_string_transformation_simplifier.py +17 -2
  24. angr/analyses/decompiler/optimization_passes/optimization_pass.py +10 -6
  25. angr/analyses/decompiler/optimization_passes/win_stack_canary_simplifier.py +99 -30
  26. angr/analyses/decompiler/peephole_optimizations/__init__.py +6 -0
  27. angr/analyses/decompiler/peephole_optimizations/base.py +43 -3
  28. angr/analyses/decompiler/peephole_optimizations/constant_derefs.py +1 -1
  29. angr/analyses/decompiler/peephole_optimizations/inlined_strcpy.py +3 -0
  30. angr/analyses/decompiler/peephole_optimizations/inlined_strcpy_consolidation.py +4 -1
  31. angr/analyses/decompiler/peephole_optimizations/remove_cxx_destructor_calls.py +32 -0
  32. angr/analyses/decompiler/peephole_optimizations/remove_redundant_bitmasks.py +69 -2
  33. angr/analyses/decompiler/peephole_optimizations/remove_redundant_conversions.py +14 -0
  34. angr/analyses/decompiler/peephole_optimizations/rewrite_conv_mul.py +40 -0
  35. angr/analyses/decompiler/peephole_optimizations/rewrite_cxx_operator_calls.py +90 -0
  36. angr/analyses/decompiler/presets/fast.py +2 -0
  37. angr/analyses/decompiler/presets/full.py +2 -0
  38. angr/analyses/decompiler/ssailification/rewriting_engine.py +51 -4
  39. angr/analyses/decompiler/ssailification/ssailification.py +23 -3
  40. angr/analyses/decompiler/ssailification/traversal_engine.py +15 -1
  41. angr/analyses/decompiler/structured_codegen/c.py +146 -15
  42. angr/analyses/decompiler/structuring/phoenix.py +11 -3
  43. angr/analyses/decompiler/utils.py +6 -1
  44. angr/analyses/deobfuscator/api_obf_finder.py +5 -1
  45. angr/analyses/deobfuscator/api_obf_peephole_optimizer.py +1 -1
  46. angr/analyses/forward_analysis/visitors/graph.py +0 -8
  47. angr/analyses/identifier/runner.py +1 -1
  48. angr/analyses/reaching_definitions/function_handler.py +4 -4
  49. angr/analyses/reassembler.py +1 -1
  50. angr/analyses/s_reaching_definitions/s_rda_view.py +1 -0
  51. angr/analyses/stack_pointer_tracker.py +1 -1
  52. angr/analyses/static_hooker.py +11 -9
  53. angr/analyses/typehoon/lifter.py +20 -0
  54. angr/analyses/typehoon/simple_solver.py +42 -9
  55. angr/analyses/typehoon/translator.py +4 -1
  56. angr/analyses/typehoon/typeconsts.py +17 -6
  57. angr/analyses/typehoon/typehoon.py +21 -5
  58. angr/analyses/variable_recovery/engine_ail.py +52 -13
  59. angr/analyses/variable_recovery/engine_base.py +37 -12
  60. angr/analyses/variable_recovery/variable_recovery_fast.py +33 -2
  61. angr/calling_conventions.py +96 -27
  62. angr/engines/light/engine.py +7 -0
  63. angr/exploration_techniques/director.py +1 -1
  64. angr/knowledge_plugins/functions/function.py +109 -38
  65. angr/knowledge_plugins/functions/function_manager.py +9 -0
  66. angr/knowledge_plugins/functions/function_parser.py +9 -1
  67. angr/knowledge_plugins/functions/soot_function.py +1 -1
  68. angr/knowledge_plugins/key_definitions/key_definition_manager.py +1 -1
  69. angr/knowledge_plugins/propagations/states.py +5 -2
  70. angr/knowledge_plugins/variables/variable_manager.py +3 -3
  71. angr/lib/angr_native.dll +0 -0
  72. angr/procedures/definitions/__init__.py +15 -12
  73. angr/procedures/definitions/types_stl.py +22 -0
  74. angr/procedures/stubs/format_parser.py +1 -1
  75. angr/project.py +23 -29
  76. angr/protos/cfg_pb2.py +14 -25
  77. angr/protos/function_pb2.py +11 -22
  78. angr/protos/primitives_pb2.py +36 -47
  79. angr/protos/variables_pb2.py +28 -39
  80. angr/protos/xrefs_pb2.py +8 -19
  81. angr/sim_type.py +251 -146
  82. angr/simos/cgc.py +1 -1
  83. angr/simos/linux.py +5 -5
  84. angr/simos/windows.py +5 -5
  85. angr/storage/memory_mixins/paged_memory/paged_memory_mixin.py +1 -1
  86. {angr-9.2.147.dist-info → angr-9.2.149.dist-info}/METADATA +9 -8
  87. {angr-9.2.147.dist-info → angr-9.2.149.dist-info}/RECORD +91 -85
  88. {angr-9.2.147.dist-info → angr-9.2.149.dist-info}/WHEEL +1 -1
  89. {angr-9.2.147.dist-info → angr-9.2.149.dist-info/licenses}/LICENSE +3 -0
  90. {angr-9.2.147.dist-info → angr-9.2.149.dist-info}/entry_points.txt +0 -0
  91. {angr-9.2.147.dist-info → angr-9.2.149.dist-info}/top_level.txt +0 -0
@@ -2,6 +2,7 @@ from __future__ import annotations
2
2
  from typing import TYPE_CHECKING
3
3
 
4
4
  from ailment import Block
5
+ from ailment.statement import Label
5
6
  from ailment.block_walker import AILBlockWalkerBase
6
7
 
7
8
  from angr.analyses.decompiler.sequence_walker import SequenceWalker
@@ -37,8 +38,10 @@ class AILCallCounter(SequenceWalker):
37
38
  }
38
39
  super().__init__(handlers)
39
40
  self.calls = 0
41
+ self.non_label_stmts = 0
40
42
 
41
43
  def _handle_Block(self, node: Block, **kwargs): # pylint:disable=unused-argument
42
44
  ctr = AILBlockCallCounter()
43
45
  ctr.walk(node)
44
46
  self.calls += ctr.calls
47
+ self.non_label_stmts += sum(1 for stmt in node.statements if not isinstance(stmt, Label))
@@ -3,7 +3,16 @@ from __future__ import annotations
3
3
  import logging
4
4
 
5
5
  from ailment.block import Block
6
- from ailment.statement import Statement, Assignment, Store, Call, Return, ConditionalJump, DirtyStatement
6
+ from ailment.statement import (
7
+ Statement,
8
+ Assignment,
9
+ Store,
10
+ Call,
11
+ Return,
12
+ ConditionalJump,
13
+ DirtyStatement,
14
+ WeakAssignment,
15
+ )
7
16
  from ailment.expression import (
8
17
  Expression,
9
18
  VirtualVariable,
@@ -99,6 +108,19 @@ class SimEngineDephiRewriting(SimEngineNostmtAIL[None, Expression | None, Statem
99
108
  return Assignment(stmt.idx, dst, src, **stmt.tags)
100
109
  return None
101
110
 
111
+ def _handle_stmt_WeakAssignment(self, stmt) -> WeakAssignment | None:
112
+ new_src = self._expr(stmt.src)
113
+ new_dst = self._expr(stmt.dst)
114
+
115
+ if new_dst is not None or new_src is not None:
116
+ return WeakAssignment(
117
+ stmt.idx,
118
+ stmt.dst if new_dst is None else new_dst, # type: ignore
119
+ stmt.src if new_src is None else new_src,
120
+ **stmt.tags,
121
+ )
122
+ return None
123
+
102
124
  def _handle_stmt_Store(self, stmt):
103
125
  new_addr = self._expr(stmt.addr)
104
126
  new_data = self._expr(stmt.data)
@@ -299,7 +321,7 @@ class SimEngineDephiRewriting(SimEngineNostmtAIL[None, Expression | None, Statem
299
321
  return VEXCCallExpression(
300
322
  expr.idx,
301
323
  expr.callee,
302
- new_operands,
324
+ tuple(new_operands),
303
325
  bits=expr.bits,
304
326
  **expr.tags,
305
327
  )
@@ -33,6 +33,8 @@ from .call_stmt_rewriter import CallStatementRewriter
33
33
  from .duplication_reverter import DuplicationReverter
34
34
  from .switch_reused_entry_rewriter import SwitchReusedEntryRewriter
35
35
  from .condition_constprop import ConditionConstantPropagation
36
+ from .determine_load_sizes import DetermineLoadSizes
37
+ from .eager_std_string_concatenation import EagerStdStringConcatenationPass
36
38
 
37
39
  if TYPE_CHECKING:
38
40
  from angr.analyses.decompiler.presets import DecompilationPreset
@@ -68,6 +70,8 @@ ALL_OPTIMIZATION_PASSES = [
68
70
  CallStatementRewriter,
69
71
  TagSlicer,
70
72
  ConditionConstantPropagation,
73
+ DetermineLoadSizes,
74
+ EagerStdStringConcatenationPass,
71
75
  ]
72
76
 
73
77
  # these passes may duplicate code to remove gotos or improve the structure of the graph
@@ -122,6 +126,7 @@ __all__ = (
122
126
  "DeadblockRemover",
123
127
  "DivSimplifier",
124
128
  "DuplicationReverter",
129
+ "EagerStdStringConcatenationPass",
125
130
  "ExprOpSwapper",
126
131
  "FlipBooleanCmp",
127
132
  "ITEExprConverter",
@@ -98,20 +98,22 @@ class BasePointerSaveSimplifier(OptimizationPass):
98
98
  and isinstance(stmt.dst, ailment.Expr.VirtualVariable)
99
99
  and stmt.dst.was_stack
100
100
  and stmt.dst.stack_offset < 0
101
- and isinstance(stmt.src, ailment.Expr.VirtualVariable)
102
- and stmt.src.was_reg
103
- and stmt.src.reg_offset == self.project.arch.bp_offset
104
101
  ):
105
- return first_block, idx, stmt.dst
106
- if (
107
- isinstance(stmt, ailment.Stmt.Assignment)
108
- and isinstance(stmt.dst, ailment.Expr.VirtualVariable)
109
- and stmt.dst.was_stack
110
- and stmt.dst.stack_offset < 0
111
- and isinstance(stmt.src, ailment.Expr.StackBaseOffset)
112
- and stmt.src.offset == 0
113
- ):
114
- return first_block, idx, stmt.dst
102
+ if (
103
+ isinstance(stmt.src, ailment.Expr.VirtualVariable)
104
+ and stmt.src.was_reg
105
+ and stmt.src.reg_offset == self.project.arch.bp_offset
106
+ ):
107
+ return first_block, idx, stmt.dst
108
+ if isinstance(stmt.src, ailment.Expr.StackBaseOffset) and stmt.src.offset == 0:
109
+ return first_block, idx, stmt.dst
110
+ if (
111
+ isinstance(stmt.src, ailment.Expr.UnaryOp)
112
+ and isinstance(stmt.src.operand, ailment.Expr.VirtualVariable)
113
+ and stmt.src.operand.was_stack
114
+ and stmt.src.operand.stack_offset == 0
115
+ ):
116
+ return first_block, idx, stmt.dst
115
117
 
116
118
  # Not found
117
119
  return None
@@ -66,7 +66,7 @@ class PairAILBlockWalker:
66
66
  def _handle_call_expr(expr_idx: int, expr: Call, stmt_idx: int, stmt: Statement, block_):
67
67
  walked_objs[Call].add(expr)
68
68
 
69
- _stmt_handlers = {typ: _handle_ail_obj for typ in walked_objs}
69
+ _stmt_handlers = dict.fromkeys(walked_objs, _handle_ail_obj)
70
70
  walker.stmt_handlers = _stmt_handlers
71
71
  walker.expr_handlers[Call] = _handle_call_expr
72
72
 
@@ -0,0 +1,64 @@
1
+ from __future__ import annotations
2
+ import logging
3
+
4
+ from ailment.constant import UNDETERMINED_SIZE
5
+ from ailment.expression import BinaryOp, Load, Const
6
+ from ailment.statement import Assignment, WeakAssignment
7
+
8
+ from .optimization_pass import OptimizationPass, OptimizationPassStage
9
+
10
+
11
+ _l = logging.getLogger(name=__name__)
12
+
13
+
14
+ class DetermineLoadSizes(OptimizationPass):
15
+ """
16
+ Determine the sizes of Load expressions whose sizes are undetermined.
17
+ """
18
+
19
+ ARCHES = None
20
+ PLATFORMS = None
21
+ STAGE = OptimizationPassStage.AFTER_GLOBAL_SIMPLIFICATION
22
+ NAME = "Determine sizes of loads whose sizes are undetermined"
23
+ DESCRIPTION = __doc__.strip() # type: ignore
24
+
25
+ def __init__(self, func, **kwargs):
26
+ super().__init__(func, **kwargs)
27
+
28
+ self.analyze()
29
+
30
+ def _check(self):
31
+ return True, None
32
+
33
+ def _analyze(self, cache=None):
34
+
35
+ changed = False
36
+
37
+ for block in self._graph.nodes:
38
+ for idx in range(len(block.statements)): # pylint:disable=consider-using-enumerate
39
+ stmt = block.statements[idx]
40
+ if isinstance(stmt, (Assignment, WeakAssignment)):
41
+ if isinstance(stmt.src, BinaryOp) and stmt.src.op == "Add" and stmt.src.operands:
42
+ operands = stmt.src.operands
43
+ elif isinstance(stmt.src, Load):
44
+ operands = [stmt.src]
45
+ else:
46
+ continue
47
+
48
+ for operand in operands:
49
+ if (
50
+ isinstance(operand, Load)
51
+ and isinstance(operand.addr, Const)
52
+ and operand.size == UNDETERMINED_SIZE
53
+ ):
54
+ # probably a string!
55
+ bs = self.project.loader.memory.load_null_terminated_bytes(
56
+ operand.addr.value, max_size=4096
57
+ )
58
+ if bs is not None:
59
+ operand.size = len(bs)
60
+ operand.bits = len(bs) * 8
61
+ changed = True
62
+
63
+ if changed:
64
+ self.out_graph = self._graph
@@ -0,0 +1,165 @@
1
+ # pylint:disable=too-many-boolean-expressions,unused-argument
2
+ from __future__ import annotations
3
+ from typing import TYPE_CHECKING
4
+ import logging
5
+ import re
6
+
7
+ from archinfo import Endness
8
+
9
+ from ailment.constant import UNDETERMINED_SIZE
10
+ from ailment.statement import Assignment, WeakAssignment
11
+ from ailment.expression import VirtualVariable, BinaryOp, Const, Load
12
+
13
+ from .optimization_pass import OptimizationPass, OptimizationPassStage
14
+
15
+ if TYPE_CHECKING:
16
+ from angr.analyses.s_reaching_definitions import SRDAModel
17
+
18
+
19
+ _l = logging.getLogger(name=__name__)
20
+
21
+
22
+ class EagerStdStringConcatenationPass(OptimizationPass):
23
+ """
24
+ TODO: Unfinished
25
+ """
26
+
27
+ ARCHES = None
28
+ PLATFORMS = None
29
+ STAGE = OptimizationPassStage.BEFORE_VARIABLE_RECOVERY
30
+ NAME = "Condense multiple constant std::string creation calls into one when possible"
31
+ DESCRIPTION = __doc__.strip() # type: ignore
32
+
33
+ def __init__(self, func, **kwargs):
34
+ super().__init__(func, **kwargs)
35
+ self.analyze()
36
+
37
+ def _check(self):
38
+ # TODO: ensure func calls std::string::operator+ and std::string::operator=
39
+ return False, {}
40
+
41
+ def _analyze(self, cache=None):
42
+ rd = self.project.analyses.SReachingDefinitions(subject=self._func, func_graph=self._graph).model
43
+ cfg = self.kb.cfgs.get_most_accurate()
44
+ assert cfg is not None
45
+
46
+ # update each block
47
+ for key in list(self.blocks_by_addr_and_idx):
48
+ block = self.blocks_by_addr_and_idx[key]
49
+ new_block = None
50
+ for idx, stmt in enumerate(block.statements):
51
+ if (
52
+ isinstance(stmt, Assignment)
53
+ and hasattr(stmt, "type")
54
+ and "dst" in stmt.type
55
+ and "src" in stmt.type
56
+ and isinstance(stmt.dst, VirtualVariable)
57
+ and isinstance(stmt.src, BinaryOp)
58
+ and stmt.src.op == "Add"
59
+ ):
60
+ dst_ty, src_ty = stmt.type["dst"], stmt.type["src"]
61
+ if self._is_std_string_type(dst_ty.c_repr()) and self._is_std_string_type(src_ty.c_repr()):
62
+ op0, op1 = stmt.src.operands
63
+ if isinstance(op1, VirtualVariable) and isinstance(op0, Load):
64
+ op0, op1 = op1, op0
65
+ if (
66
+ isinstance(op0, VirtualVariable)
67
+ and isinstance(op1, Load)
68
+ and isinstance(op1.addr, Const)
69
+ and isinstance(op1.addr.value, int)
70
+ # is op1 a constant string?
71
+ and op1.addr.value in cfg.memory_data
72
+ and cfg.memory_data[op1.addr.value].sort == "string"
73
+ ):
74
+ op1_str = cfg.memory_data[op1.addr.value].content
75
+ # is op0 also an std::string?
76
+ op0_str = self._get_vvar_def_string(op0.varid, rd, cfg, block.addr, block.idx)
77
+ if op0_str is not None and op1_str is not None:
78
+ # let's create a new string
79
+ final_str = op0_str + op1_str
80
+ str_id = self.kb.custom_strings.allocate(final_str)
81
+ # replace the assignment with a new assignment
82
+ new_stmt = WeakAssignment(
83
+ stmt.idx,
84
+ stmt.dst,
85
+ Load(
86
+ None,
87
+ Const(None, None, str_id, self.project.arch.bits, custom_string=True),
88
+ UNDETERMINED_SIZE,
89
+ Endness.BE,
90
+ ),
91
+ **stmt.tags,
92
+ )
93
+ new_block = block.copy() if new_block is None else new_block
94
+ new_block.statements[idx] = new_stmt
95
+ if new_block is not None:
96
+ self._update_block(block, new_block)
97
+
98
+ def _get_vvar_def_string(self, vvar_id: int, rd: SRDAModel, cfg, block_addr, block_idx) -> bytes | None:
99
+ # search for the closest weak definition of the specified variable
100
+ # TODO: Optimize this logic in the future
101
+
102
+ starting_block = self.blocks_by_addr_and_idx[(block_addr, block_idx)]
103
+ queue = [starting_block]
104
+ visited = set()
105
+ while queue:
106
+ block = queue.pop(0)
107
+ if block in visited:
108
+ continue
109
+ visited.add(block)
110
+
111
+ if not (block.addr == block_addr and block.idx == block_idx):
112
+ for stmt in block.statements:
113
+ if (
114
+ isinstance(stmt, WeakAssignment)
115
+ and isinstance(stmt.dst, VirtualVariable)
116
+ and stmt.dst.varid == vvar_id
117
+ ):
118
+ if (
119
+ isinstance(stmt.src, Load)
120
+ and isinstance(stmt.src.addr, Const)
121
+ and stmt.src.addr.value in cfg.memory_data
122
+ ):
123
+ if cfg.memory_data[stmt.src.addr.value].sort == "string":
124
+ return cfg.memory_data[stmt.src.addr.value].content
125
+ elif (
126
+ isinstance(stmt.src, Const)
127
+ and hasattr(stmt.src, "custom_string")
128
+ and stmt.src.custom_string
129
+ ):
130
+ return self.kb.custom_strings.get(stmt.src.value)
131
+
132
+ preds = list(self._graph.predecessors(block))
133
+ if len(preds) == 1:
134
+ queue.append(preds[0])
135
+
136
+ return None
137
+
138
+ @staticmethod
139
+ def _is_std_string_type(type_str: str) -> bool:
140
+ type_str = type_str.removeprefix("const ")
141
+ return (
142
+ re.match(
143
+ r"class std::basic_string<char a\d+, struct std::char_traits<char> a\d+, class std::allocator<char>>",
144
+ type_str,
145
+ )
146
+ is not None
147
+ )
148
+
149
+ # pcreg_offset = self.project.arch.registers[getpc_reg][0]
150
+
151
+
152
+ #
153
+ # old_block = self.blocks_by_addr_and_idx[block_key]
154
+ # block = old_block.copy()
155
+ # old_stmt = block.statements[stmt_idx]
156
+ # block.statements[stmt_idx] = ailment.Stmt.Assignment(
157
+ # old_stmt.idx,
158
+ # ailment.Expr.Register(None, None, pcreg_offset, 32, reg_name=getpc_reg),
159
+ # ailment.Expr.Const(None, None, getpc_reg_value, 32),
160
+ # **old_stmt.tags,
161
+ # )
162
+ # # remove the statement that pushes return address onto the stack
163
+ # if stmt_idx > 0 and isinstance(block.statements[stmt_idx - 1], ailment.Stmt.Store):
164
+ # block.statements = block.statements[: stmt_idx - 1] + block.statements[stmt_idx:]
165
+ # self._update_block(old_block, block)
@@ -73,7 +73,16 @@ class SimplifierAILEngine(
73
73
  self.state.store_variable(dst, src)
74
74
 
75
75
  if (src, dst) != (stmt.src, stmt.dst):
76
- return ailment.statement.Assignment(stmt.idx, dst, src, **stmt.tags)
76
+ return ailment.statement.Assignment(stmt.idx, dst, src, **stmt.tags) # type:ignore
77
+
78
+ return stmt
79
+
80
+ def _handle_stmt_WeakAssignment(self, stmt: ailment.statement.WeakAssignment):
81
+ src = self._expr(stmt.src)
82
+ dst = self._expr(stmt.dst)
83
+
84
+ if (src, dst) != (stmt.src, stmt.dst):
85
+ return ailment.statement.WeakAssignment(stmt.idx, dst, src, **stmt.tags) # type:ignore
77
86
 
78
87
  return stmt
79
88
 
@@ -150,7 +159,7 @@ class SimplifierAILEngine(
150
159
  def _handle_stmt_DirtyStatement(self, stmt):
151
160
  expr = self._expr(stmt.dirty)
152
161
  if expr != stmt.dirty:
153
- return ailment.statement.DirtyStatement(stmt.idx, expr, **stmt.tags)
162
+ return ailment.statement.DirtyStatement(stmt.idx, expr, **stmt.tags) # type:ignore
154
163
  return stmt
155
164
 
156
165
  def _handle_stmt_Label(self, stmt):
@@ -161,13 +161,28 @@ class InlinedStringTransformationAILEngine(
161
161
  return addr.value, "mem"
162
162
  if isinstance(addr, StackBaseOffset):
163
163
  return (addr.offset + self.STACK_BASE) & self.MASK, "stack"
164
- if isinstance(addr, BinaryOp) and isinstance(addr.operands[0], StackBaseOffset):
164
+ if (
165
+ isinstance(addr, UnaryOp)
166
+ and addr.op == "Reference"
167
+ and isinstance(addr.operand, VirtualVariable)
168
+ and addr.operand.was_stack
169
+ ):
170
+ return (addr.operand.stack_offset + self.STACK_BASE) & self.MASK, "stack"
171
+ if (
172
+ isinstance(addr, BinaryOp)
173
+ and addr.op in {"Add", "Sub"}
174
+ and isinstance(addr.operands[0], (StackBaseOffset, UnaryOp, Const))
175
+ ):
165
176
  v0_and_type = self._process_address(addr.operands[0])
166
177
  if v0_and_type is not None:
167
178
  v0 = v0_and_type[0]
168
179
  v1 = self._expr(addr.operands[1])
169
180
  if isinstance(v1, claripy.ast.Bits) and v1.concrete:
170
- return (v0 + v1.concrete_value) & self.MASK, "stack"
181
+ if addr.op == "Add":
182
+ return (v0 + v1.concrete_value) & self.MASK, "stack"
183
+ if addr.op == "Sub":
184
+ return (v0 - v1.concrete_value) & self.MASK, "stack"
185
+ raise NotImplementedError("Unreachable")
171
186
  return None
172
187
 
173
188
  def _handle_stmt_Assignment(self, stmt):
@@ -53,12 +53,14 @@ class OptimizationPassStage(Enum):
53
53
  AFTER_AIL_GRAPH_CREATION = 0
54
54
  BEFORE_SSA_LEVEL0_TRANSFORMATION = 1
55
55
  AFTER_SINGLE_BLOCK_SIMPLIFICATION = 2
56
- AFTER_MAKING_CALLSITES = 3
57
- AFTER_GLOBAL_SIMPLIFICATION = 4
58
- AFTER_VARIABLE_RECOVERY = 5
59
- BEFORE_REGION_IDENTIFICATION = 6
60
- DURING_REGION_IDENTIFICATION = 7
61
- AFTER_STRUCTURING = 8
56
+ BEFORE_SSA_LEVEL1_TRANSFORMATION = 3
57
+ AFTER_MAKING_CALLSITES = 4
58
+ AFTER_GLOBAL_SIMPLIFICATION = 5
59
+ BEFORE_VARIABLE_RECOVERY = 6
60
+ AFTER_VARIABLE_RECOVERY = 7
61
+ BEFORE_REGION_IDENTIFICATION = 8
62
+ DURING_REGION_IDENTIFICATION = 9
63
+ AFTER_STRUCTURING = 10
62
64
 
63
65
 
64
66
  class BaseOptimizationPass:
@@ -137,6 +139,7 @@ class OptimizationPass(BaseOptimizationPass):
137
139
  avoid_vvar_ids: set[int] | None = None,
138
140
  arg_vvars: set[int] | None = None,
139
141
  peephole_optimizations=None,
142
+ stack_pointer_tracker=None,
140
143
  **kwargs,
141
144
  ):
142
145
  super().__init__(func)
@@ -158,6 +161,7 @@ class OptimizationPass(BaseOptimizationPass):
158
161
  self._complete_successors = complete_successors
159
162
  self._avoid_vvar_ids = avoid_vvar_ids or set()
160
163
  self._peephole_optimizations = peephole_optimizations
164
+ self._stack_pointer_tracker = stack_pointer_tracker
161
165
 
162
166
  # output
163
167
  self.out_graph: networkx.DiGraph | None = None