angr 9.2.130__py3-none-macosx_11_0_arm64.whl → 9.2.132__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 (128) 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 +361 -8
  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/__init__.py +0 -3
  16. angr/analyses/decompiler/optimization_passes/const_derefs.py +1 -0
  17. angr/analyses/decompiler/optimization_passes/div_simplifier.py +41 -16
  18. angr/analyses/decompiler/optimization_passes/engine_base.py +261 -83
  19. angr/analyses/decompiler/optimization_passes/inlined_string_transformation_simplifier.py +173 -35
  20. angr/analyses/decompiler/optimization_passes/mod_simplifier.py +5 -2
  21. angr/analyses/decompiler/optimization_passes/optimization_pass.py +39 -19
  22. angr/analyses/decompiler/peephole_optimizations/__init__.py +5 -1
  23. angr/analyses/decompiler/peephole_optimizations/a_mul_const_sub_a.py +34 -0
  24. angr/analyses/decompiler/peephole_optimizations/a_shl_const_sub_a.py +3 -1
  25. angr/analyses/decompiler/peephole_optimizations/bswap.py +10 -6
  26. angr/analyses/decompiler/peephole_optimizations/eager_eval.py +100 -19
  27. angr/analyses/decompiler/peephole_optimizations/remove_noop_conversions.py +17 -0
  28. angr/analyses/decompiler/peephole_optimizations/remove_redundant_conversions.py +42 -3
  29. angr/analyses/decompiler/peephole_optimizations/remove_redundant_shifts.py +4 -2
  30. angr/analyses/decompiler/peephole_optimizations/rol_ror.py +37 -10
  31. angr/analyses/decompiler/peephole_optimizations/shl_to_mul.py +25 -0
  32. angr/analyses/decompiler/peephole_optimizations/utils.py +18 -0
  33. angr/analyses/decompiler/presets/fast.py +0 -2
  34. angr/analyses/decompiler/presets/full.py +0 -2
  35. angr/analyses/decompiler/ssailification/rewriting.py +1 -2
  36. angr/analyses/decompiler/ssailification/rewriting_engine.py +140 -57
  37. angr/analyses/decompiler/ssailification/ssailification.py +2 -1
  38. angr/analyses/decompiler/ssailification/traversal.py +4 -6
  39. angr/analyses/decompiler/ssailification/traversal_engine.py +125 -42
  40. angr/analyses/decompiler/structured_codegen/c.py +79 -16
  41. angr/analyses/decompiler/structuring/phoenix.py +40 -14
  42. angr/analyses/decompiler/structuring/structurer_nodes.py +9 -0
  43. angr/analyses/deobfuscator/irsb_reg_collector.py +29 -60
  44. angr/analyses/deobfuscator/string_obf_finder.py +2 -2
  45. angr/analyses/init_finder.py +47 -22
  46. angr/analyses/propagator/engine_base.py +21 -14
  47. angr/analyses/propagator/engine_vex.py +149 -179
  48. angr/analyses/propagator/propagator.py +10 -28
  49. angr/analyses/propagator/top_checker_mixin.py +211 -5
  50. angr/analyses/propagator/vex_vars.py +1 -1
  51. angr/analyses/reaching_definitions/dep_graph.py +1 -1
  52. angr/analyses/reaching_definitions/engine_ail.py +304 -329
  53. angr/analyses/reaching_definitions/engine_vex.py +243 -229
  54. angr/analyses/reaching_definitions/function_handler.py +3 -3
  55. angr/analyses/reaching_definitions/rd_state.py +37 -32
  56. angr/analyses/s_propagator.py +38 -5
  57. angr/analyses/s_reaching_definitions/s_reaching_definitions.py +9 -5
  58. angr/analyses/typehoon/simple_solver.py +16 -7
  59. angr/analyses/typehoon/translator.py +8 -0
  60. angr/analyses/typehoon/typeconsts.py +10 -2
  61. angr/analyses/typehoon/typehoon.py +4 -1
  62. angr/analyses/typehoon/typevars.py +9 -7
  63. angr/analyses/variable_recovery/engine_ail.py +296 -256
  64. angr/analyses/variable_recovery/engine_base.py +137 -116
  65. angr/analyses/variable_recovery/engine_vex.py +175 -185
  66. angr/analyses/variable_recovery/irsb_scanner.py +49 -38
  67. angr/analyses/variable_recovery/variable_recovery.py +28 -5
  68. angr/analyses/variable_recovery/variable_recovery_base.py +32 -33
  69. angr/analyses/variable_recovery/variable_recovery_fast.py +2 -2
  70. angr/analyses/xrefs.py +46 -19
  71. angr/annocfg.py +19 -14
  72. angr/block.py +4 -9
  73. angr/calling_conventions.py +1 -1
  74. angr/engines/engine.py +30 -14
  75. angr/engines/light/__init__.py +11 -3
  76. angr/engines/light/engine.py +1003 -1185
  77. angr/engines/pcode/cc.py +2 -0
  78. angr/engines/successors.py +13 -9
  79. angr/engines/vex/claripy/datalayer.py +1 -1
  80. angr/engines/vex/claripy/irop.py +14 -3
  81. angr/engines/vex/light/slicing.py +2 -2
  82. angr/exploration_techniques/__init__.py +1 -124
  83. angr/exploration_techniques/base.py +126 -0
  84. angr/exploration_techniques/bucketizer.py +1 -1
  85. angr/exploration_techniques/dfs.py +3 -1
  86. angr/exploration_techniques/director.py +2 -3
  87. angr/exploration_techniques/driller_core.py +1 -1
  88. angr/exploration_techniques/explorer.py +4 -2
  89. angr/exploration_techniques/lengthlimiter.py +2 -1
  90. angr/exploration_techniques/local_loop_seer.py +2 -1
  91. angr/exploration_techniques/loop_seer.py +5 -5
  92. angr/exploration_techniques/manual_mergepoint.py +2 -1
  93. angr/exploration_techniques/memory_watcher.py +3 -1
  94. angr/exploration_techniques/oppologist.py +4 -5
  95. angr/exploration_techniques/slicecutor.py +4 -2
  96. angr/exploration_techniques/spiller.py +1 -1
  97. angr/exploration_techniques/stochastic.py +2 -1
  98. angr/exploration_techniques/stub_stasher.py +2 -1
  99. angr/exploration_techniques/suggestions.py +3 -1
  100. angr/exploration_techniques/symbion.py +3 -1
  101. angr/exploration_techniques/tech_builder.py +2 -1
  102. angr/exploration_techniques/threading.py +4 -7
  103. angr/exploration_techniques/timeout.py +4 -2
  104. angr/exploration_techniques/tracer.py +4 -3
  105. angr/exploration_techniques/unique.py +3 -2
  106. angr/exploration_techniques/veritesting.py +1 -1
  107. angr/knowledge_plugins/key_definitions/atoms.py +2 -2
  108. angr/knowledge_plugins/key_definitions/live_definitions.py +16 -13
  109. angr/knowledge_plugins/propagations/states.py +13 -8
  110. angr/knowledge_plugins/variables/variable_manager.py +23 -9
  111. angr/lib/angr_native.dylib +0 -0
  112. angr/sim_manager.py +1 -3
  113. angr/sim_state.py +39 -41
  114. angr/sim_type.py +5 -0
  115. angr/sim_variable.py +29 -28
  116. angr/utils/bits.py +17 -0
  117. angr/utils/formatting.py +4 -1
  118. angr/utils/orderedset.py +4 -1
  119. angr/utils/ssa/__init__.py +21 -3
  120. {angr-9.2.130.dist-info → angr-9.2.132.dist-info}/METADATA +6 -6
  121. {angr-9.2.130.dist-info → angr-9.2.132.dist-info}/RECORD +125 -124
  122. angr/analyses/decompiler/optimization_passes/multi_simplifier.py +0 -223
  123. angr/analyses/propagator/engine_ail.py +0 -1562
  124. angr/storage/memory_mixins/__init__.pyi +0 -48
  125. {angr-9.2.130.dist-info → angr-9.2.132.dist-info}/LICENSE +0 -0
  126. {angr-9.2.130.dist-info → angr-9.2.132.dist-info}/WHEEL +0 -0
  127. {angr-9.2.130.dist-info → angr-9.2.132.dist-info}/entry_points.txt +0 -0
  128. {angr-9.2.130.dist-info → angr-9.2.132.dist-info}/top_level.txt +0 -0
@@ -1,8 +1,10 @@
1
+ # pylint:disable=missing-class-docstring,no-self-use
1
2
  from __future__ import annotations
2
3
  from ailment.expression import BinaryOp, Const, Expression, Convert
3
4
  from ailment.statement import Call
4
5
 
5
6
  from .base import PeepholeOptimizationExprBase
7
+ from .utils import get_expr_shift_left_amount
6
8
 
7
9
 
8
10
  class Bswap(PeepholeOptimizationExprBase):
@@ -69,19 +71,21 @@ class Bswap(PeepholeOptimizationExprBase):
69
71
  cores = set()
70
72
  for piece in or_pieces:
71
73
  if isinstance(piece, BinaryOp):
72
- if piece.op == "Shl" and isinstance(piece.operands[1], Const):
74
+ if piece.op in {"Shl", "Mul"} and isinstance(piece.operands[1], Const):
73
75
  cores.add(piece.operands[0])
74
- shifts.add(("<<", piece.operands[1].value, 0xFFFFFFFF))
76
+ shift_amount = get_expr_shift_left_amount(piece)
77
+ shifts.add(("<<", shift_amount, 0xFFFFFFFF))
75
78
  elif piece.op == "And" and isinstance(piece.operands[1], Const):
76
79
  and_amount = piece.operands[1].value
77
80
  and_core = piece.operands[0]
78
81
  if (
79
82
  isinstance(and_core, BinaryOp)
80
- and and_core.op == "Shl"
83
+ and and_core.op in {"Shl", "Mul"}
81
84
  and isinstance(and_core.operands[1], Const)
82
85
  ):
83
86
  cores.add(and_core.operands[0])
84
- shifts.add(("<<", and_core.operands[1].value, and_amount))
87
+ shift_amount = get_expr_shift_left_amount(and_core)
88
+ shifts.add(("<<", shift_amount, and_amount))
85
89
  elif (
86
90
  isinstance(and_core, BinaryOp)
87
91
  and and_core.op == "Shr"
@@ -112,9 +116,9 @@ class Bswap(PeepholeOptimizationExprBase):
112
116
  if (
113
117
  (
114
118
  isinstance(inner_first, BinaryOp)
115
- and inner_first.op == "Shl"
119
+ and inner_first.op in {"Shl", "Mul"}
116
120
  and isinstance(inner_first.operands[1], Const)
117
- and inner_first.operands[1].value == 8
121
+ and get_expr_shift_left_amount(inner_first) == 8
118
122
  )
119
123
  and (
120
124
  isinstance(inner_second, BinaryOp)
@@ -3,6 +3,7 @@ from math import gcd
3
3
 
4
4
  from ailment.expression import BinaryOp, UnaryOp, Const, Convert, StackBaseOffset
5
5
 
6
+ from angr.utils.bits import sign_extend
6
7
  from .base import PeepholeOptimizationExprBase
7
8
 
8
9
 
@@ -59,22 +60,50 @@ class EagerEvaluation(PeepholeOptimizationExprBase):
59
60
  expr.signed,
60
61
  **expr.tags,
61
62
  )
62
- if (
63
- isinstance(expr.operands[0], BinaryOp)
64
- and expr.operands[0].op == "Mul"
65
- and isinstance(expr.operands[0].operands[1], Const)
66
- and expr.operands[0].operands[0].likes(expr.operands[1])
67
- ):
68
- # A * x + x => (A + 1) * x
69
- coeff_expr = expr.operands[0].operands[1]
70
- new_coeff = coeff_expr.value + 1
71
- return BinaryOp(
72
- expr.idx,
73
- "Mul",
74
- [Const(coeff_expr.idx, None, new_coeff, coeff_expr.bits), expr.operands[1]],
75
- expr.signed,
76
- **expr.tags,
77
- )
63
+ op0, op1 = expr.operands
64
+ if op0.likes(op1):
65
+ # x + x => 2 * x
66
+ count = Const(expr.idx, None, 2, op0.bits, **expr.tags)
67
+ return BinaryOp(expr.idx, "Mul", [op0, count], expr.signed, **expr.tags)
68
+
69
+ op0_is_mulconst = (
70
+ isinstance(op0, BinaryOp)
71
+ and op0.op == "Mul"
72
+ and (isinstance(op0.operands[0], Const) or isinstance(op0.operands[1], Const))
73
+ )
74
+ op1_is_mulconst = (
75
+ isinstance(op1, BinaryOp)
76
+ and op1.op == "Mul"
77
+ and (isinstance(op1.operands[0], Const) or isinstance(op1.operands[1], Const))
78
+ )
79
+ const0, x0 = None, None
80
+ const1, x1 = None, None
81
+ if op0_is_mulconst:
82
+ if isinstance(op0.operands[0], Const):
83
+ const0, x0 = op0.operands
84
+ elif isinstance(op0.operands[1], Const):
85
+ x0, const0 = op0.operands
86
+ if op1_is_mulconst:
87
+ if isinstance(op1.operands[0], Const):
88
+ const1, x1 = op1.operands
89
+ elif isinstance(op1.operands[1], Const):
90
+ x1, const1 = op1.operands
91
+
92
+ if op0_is_mulconst ^ op1_is_mulconst:
93
+ if x0 is not None and const0 is not None and x0.likes(op1):
94
+ # x * A + x => (A + 1) * x
95
+ new_const = Const(const0.idx, None, const0.value + 1, const0.bits, **const0.tags)
96
+ return BinaryOp(expr.idx, "Mul", [x0, new_const], expr.signed, **expr.tags)
97
+ if x1 is not None and const1 is not None and x1.likes(op0):
98
+ # x + x * A => (A + 1) * x
99
+ new_const = Const(const1.idx, None, const1.value + 1, const1.bits, **const1.tags)
100
+ return BinaryOp(expr.idx, "Mul", [x1, new_const], expr.signed, **expr.tags)
101
+ elif op0_is_mulconst and op1_is_mulconst:
102
+ if x0.likes(x1):
103
+ # x * A + x * B => (A + B) * x
104
+ new_const = Const(const0.idx, None, const0.value + const1.value, const0.bits, **const0.tags)
105
+ return BinaryOp(expr.idx, "Mul", [x0, new_const], expr.signed, **expr.tags)
106
+
78
107
  elif expr.op == "Sub":
79
108
  if isinstance(expr.operands[0], Const) and isinstance(expr.operands[1], Const):
80
109
  mask = (1 << expr.bits) - 1
@@ -119,12 +148,25 @@ class EagerEvaluation(PeepholeOptimizationExprBase):
119
148
 
120
149
  elif expr.op == "Mul":
121
150
  if isinstance(expr.operands[1], Const) and expr.operands[1].value == 1:
151
+ # x * 1 => x
122
152
  return expr.operands[0]
123
153
  if isinstance(expr.operands[0], Const) and isinstance(expr.operands[1], Const):
154
+ # constant multiplication
124
155
  mask = (1 << expr.bits) - 1
125
156
  return Const(
126
157
  expr.idx, None, (expr.operands[0].value * expr.operands[1].value) & mask, expr.bits, **expr.tags
127
158
  )
159
+ if {type(expr.operands[0]), type(expr.operands[1])} == {BinaryOp, Const}:
160
+ op0, op1 = expr.operands
161
+ const_, x0 = (op0, op1) if isinstance(op0, Const) else (op1, op0)
162
+ if x0.op == "Mul" and (isinstance(x0.operands[0], Const) or isinstance(x0.operands[1], Const)):
163
+ # (A * x) * C => (A * C) * x
164
+ if isinstance(x0.operands[0], Const):
165
+ const_x0, x = x0.operands[0], x0.operands[1]
166
+ else:
167
+ const_x0, x = x0.operands[1], x0.operands[0]
168
+ new_const = Const(const_.idx, None, const_.value * const_x0.value, const_.bits, **const_x0.tags)
169
+ return BinaryOp(expr.idx, "Mul", [x, new_const], expr.signed, bits=expr.bits, **expr.tags)
128
170
 
129
171
  elif (
130
172
  expr.op == "Div"
@@ -197,6 +239,42 @@ class EagerEvaluation(PeepholeOptimizationExprBase):
197
239
  if expr.operands[0].likes(expr.operands[1]):
198
240
  return expr.operands[0]
199
241
 
242
+ elif expr.op in {"CmpEQ", "CmpLE", "CmpGE"}:
243
+ if expr.operands[0].likes(expr.operands[1]):
244
+ # x == x => 1
245
+ return Const(expr.idx, None, 1, 1, **expr.tags)
246
+ if isinstance(expr.operands[0], Const) and isinstance(expr.operands[1], Const):
247
+ if expr.op == "CmpEQ":
248
+ return Const(
249
+ expr.idx, None, 1 if expr.operands[0].value == expr.operands[1].value else 0, 1, **expr.tags
250
+ )
251
+ if expr.op == "CmpLE":
252
+ return Const(
253
+ expr.idx, None, 1 if expr.operands[0].value <= expr.operands[1].value else 0, 1, **expr.tags
254
+ )
255
+ if expr.op == "CmpGE":
256
+ return Const(
257
+ expr.idx, None, 1 if expr.operands[0].value >= expr.operands[1].value else 0, 1, **expr.tags
258
+ )
259
+
260
+ elif expr.op in {"CmpNE", "CmpLT", "CmpGT"}:
261
+ if expr.operands[0].likes(expr.operands[1]):
262
+ # x != x => 0
263
+ return Const(expr.idx, None, 0, 1, **expr.tags)
264
+ if isinstance(expr.operands[0], Const) and isinstance(expr.operands[1], Const):
265
+ if expr.op == "CmpNE":
266
+ return Const(
267
+ expr.idx, None, 1 if expr.operands[0].value != expr.operands[1].value else 0, 1, **expr.tags
268
+ )
269
+ if expr.op == "CmpLT":
270
+ return Const(
271
+ expr.idx, None, 1 if expr.operands[0].value < expr.operands[1].value else 0, 1, **expr.tags
272
+ )
273
+ if expr.op == "CmpGT":
274
+ return Const(
275
+ expr.idx, None, 1 if expr.operands[0].value > expr.operands[1].value else 0, 1, **expr.tags
276
+ )
277
+
200
278
  return None
201
279
 
202
280
  @staticmethod
@@ -225,8 +303,11 @@ class EagerEvaluation(PeepholeOptimizationExprBase):
225
303
  and expr.from_type == Convert.TYPE_INT
226
304
  and expr.to_type == Convert.TYPE_INT
227
305
  and expr.from_bits <= expr.to_bits
228
- and expr.is_signed is False
229
306
  ):
230
- # unsigned extension
231
- return Const(expr.idx, expr.operand.variable, expr.operand.value, expr.to_bits, **expr.operand.tags)
307
+ if expr.is_signed is False:
308
+ # unsigned extension
309
+ return Const(expr.idx, expr.operand.variable, expr.operand.value, expr.to_bits, **expr.operand.tags)
310
+ # signed extension
311
+ v = sign_extend(expr.operand.value, expr.to_bits)
312
+ return Const(expr.idx, expr.operand.variable, v, expr.to_bits, **expr.operand.tags)
232
313
  return None
@@ -22,9 +22,26 @@ class RemoveNoopConversions(PeepholeOptimizationExprBase):
22
22
  and expr.to_bits < expr.from_bits
23
23
  and expr.from_bits == inner.to_bits
24
24
  and expr.is_signed == inner.is_signed
25
+ and expr.from_type == expr.to_type == inner.from_type == inner.to_type == Convert.TYPE_INT
25
26
  ):
26
27
  # extension then truncation (e.g., 1->64->1) can be removed, but truncation then extension cannot be
27
28
  # removed (e.g., the high 32 bits must be removed during 64->32->64)
28
29
  return inner.operand
30
+ if (
31
+ expr.to_bits < expr.from_bits
32
+ and expr.from_bits == inner.to_bits
33
+ and inner.to_bits <= inner.from_bits
34
+ and expr.is_signed == inner.is_signed
35
+ and expr.from_type == expr.to_type == inner.from_type == inner.to_type == Convert.TYPE_INT
36
+ ):
37
+ # merging two truncations into one
38
+ return Convert(
39
+ expr.idx,
40
+ inner.from_bits,
41
+ expr.to_bits,
42
+ expr.is_signed,
43
+ inner.operand,
44
+ **expr.tags,
45
+ )
29
46
 
30
47
  return None
@@ -1,4 +1,4 @@
1
- # pylint: disable=missing-class-docstring
1
+ # pylint: disable=missing-class-docstring,too-many-boolean-expressions
2
2
  from __future__ import annotations
3
3
  from ailment.expression import BinaryOp, Convert, Const
4
4
 
@@ -9,9 +9,18 @@ class RemoveRedundantConversions(PeepholeOptimizationExprBase):
9
9
  __slots__ = ()
10
10
 
11
11
  NAME = "Remove redundant conversions around binary operators"
12
- expr_classes = (BinaryOp,)
12
+ expr_classes = (BinaryOp, Convert)
13
13
 
14
- def optimize(self, expr: BinaryOp, **kwargs):
14
+ def optimize(self, expr: BinaryOp | Convert, **kwargs):
15
+
16
+ if isinstance(expr, BinaryOp):
17
+ return self._optimize_BinaryOp(expr)
18
+ if isinstance(expr, Convert):
19
+ return self._optimize_Convert(expr)
20
+ return None
21
+
22
+ @staticmethod
23
+ def _optimize_BinaryOp(expr: BinaryOp):
15
24
  # TODO make this lhs/rhs agnostic
16
25
  if isinstance(expr.operands[0], Convert):
17
26
  # check: is the lhs convert an up-cast and is rhs a const?
@@ -156,3 +165,33 @@ class RemoveRedundantConversions(PeepholeOptimizationExprBase):
156
165
  )
157
166
 
158
167
  return None
168
+
169
+ @staticmethod
170
+ def _optimize_Convert(expr: Convert):
171
+ operand_expr = expr.operand
172
+ if isinstance(operand_expr, BinaryOp) and operand_expr.op in {
173
+ "Mul",
174
+ "Shl",
175
+ "Div",
176
+ "DivMod",
177
+ "Mod",
178
+ "Add",
179
+ "Sub",
180
+ }:
181
+ op0, op1 = operand_expr.operands
182
+ if (
183
+ isinstance(op0, Convert)
184
+ and isinstance(op1, Convert)
185
+ and op0.from_bits == op1.from_bits
186
+ and op0.to_bits == op1.to_bits
187
+ and expr.from_bits == op0.to_bits
188
+ and expr.to_bits == op1.from_bits
189
+ ):
190
+ return BinaryOp(
191
+ operand_expr.idx,
192
+ operand_expr.op,
193
+ [op0.operand, op1.operand],
194
+ expr.is_signed,
195
+ **operand_expr.tags,
196
+ )
197
+ return None
@@ -1,7 +1,9 @@
1
+ # pylint:disable=no-self-use,missing-class-docstring
1
2
  from __future__ import annotations
2
3
  from ailment.expression import BinaryOp, Const, Convert
3
4
 
4
5
  from .base import PeepholeOptimizationExprBase
6
+ from .utils import get_expr_shift_left_amount
5
7
 
6
8
 
7
9
  class RemoveRedundantShifts(PeepholeOptimizationExprBase):
@@ -15,8 +17,8 @@ class RemoveRedundantShifts(PeepholeOptimizationExprBase):
15
17
  if expr.op in ("Shr", "Sar") and isinstance(expr.operands[1], Const):
16
18
  expr_a = expr.operands[0]
17
19
  n0 = expr.operands[1].value
18
- if isinstance(expr_a, BinaryOp) and expr_a.op == "Shl" and isinstance(expr_a.operands[1], Const):
19
- n1 = expr_a.operands[1].value
20
+ if isinstance(expr_a, BinaryOp) and expr_a.op in {"Shl", "Mul"} and isinstance(expr_a.operands[1], Const):
21
+ n1 = get_expr_shift_left_amount(expr_a)
20
22
  if n0 == n1:
21
23
  inner_expr = expr_a.operands[0]
22
24
  conv_inner_expr = Convert(
@@ -3,6 +3,7 @@ from ailment.statement import Assignment
3
3
  from ailment.expression import BinaryOp, Const, Tmp
4
4
 
5
5
  from .base import PeepholeOptimizationStmtBase
6
+ from .utils import get_expr_shift_left_amount
6
7
 
7
8
 
8
9
  class RolRorRewriter(PeepholeOptimizationStmtBase):
@@ -61,38 +62,64 @@ class RolRorRewriter(PeepholeOptimizationStmtBase):
61
62
  if not (isinstance(stmt1_op1, Const) and isinstance(stmt2_op1, Const)):
62
63
  return None
63
64
 
64
- if stmt_1.src.op == "Shl" and stmt_2.src.op == "Shr" and stmt1_op1.value + stmt2_op1.value == stmt.dst.bits:
65
+ if (
66
+ stmt_1.src.op in {"Shl", "Mul"}
67
+ and stmt_2.src.op == "Shr"
68
+ and (shiftleft_amount := get_expr_shift_left_amount(stmt_1.src)) is not None
69
+ and shiftleft_amount + stmt2_op1.value == stmt.dst.bits
70
+ ):
71
+ rol_amount = Const(None, None, shiftleft_amount, 8, **stmt1_op1.tags)
65
72
  return Assignment(
66
73
  stmt.idx,
67
74
  stmt.dst,
68
- BinaryOp(None, "Rol", [stmt1_op0, stmt1_op1], False, bits=stmt.dst.bits, **stmt_1.src.tags),
75
+ BinaryOp(None, "Rol", [stmt1_op0, rol_amount], False, bits=stmt.dst.bits, **stmt_1.src.tags),
69
76
  **stmt.tags,
70
77
  )
71
- if stmt_1.src.op == "Shr" and stmt_2.src.op == "Shl" and stmt1_op1.value + stmt2_op1.value == stmt.dst.bits:
78
+ if (
79
+ stmt_1.src.op == "Shr"
80
+ and stmt_2.src.op in {"Shl", "Mul"}
81
+ and (shiftleft_amount := get_expr_shift_left_amount(stmt_2.src)) is not None
82
+ and stmt1_op1.value + shiftleft_amount == stmt.dst.bits
83
+ ):
72
84
  return Assignment(
73
85
  stmt.idx,
74
86
  stmt.dst,
75
87
  BinaryOp(None, "Ror", [stmt1_op0, stmt1_op1], False, bits=stmt.dst.bits, **stmt_1.src.tags),
76
88
  **stmt.tags,
77
89
  )
78
- elif isinstance(op0, BinaryOp) and isinstance(op1, BinaryOp) and {op0.op, op1.op} == {"Shl", "Shr"}:
90
+ elif (
91
+ isinstance(op0, BinaryOp)
92
+ and isinstance(op1, BinaryOp)
93
+ and {op0.op, op1.op} in [{"Shl", "Shr"}, {"Mul", "Shr"}]
94
+ ):
79
95
  if not op0.operands[0].likes(op1.operands[0]):
80
96
  return None
81
97
 
82
98
  if not isinstance(op0.operands[1], Const) or not isinstance(op1.operands[1], Const):
83
99
  return None
84
- shiftamount = op0.operands[1]
85
- op0_shiftamount = op0.operands[1].value
86
- op1_shiftamount = op1.operands[1].value
87
-
88
- if op0.op == "Shl" and op1.op == "Shr" and op0_shiftamount + op1_shiftamount == stmt.dst.bits:
100
+ op0_v = op0.operands[1].value
101
+ op1_v = op1.operands[1].value
102
+
103
+ if (
104
+ op0.op in {"Shl", "Mul"}
105
+ and op1.op == "Shr"
106
+ and (op0_shiftamount := get_expr_shift_left_amount(op0)) is not None
107
+ and op0_shiftamount + op1_v == stmt.dst.bits
108
+ ):
109
+ shiftamount = Const(None, None, op0_shiftamount, 8, **op0.operands[1].tags)
89
110
  return Assignment(
90
111
  stmt.idx,
91
112
  stmt.dst,
92
113
  BinaryOp(None, "Rol", [op0.operands[0], shiftamount], False, bits=stmt.dst.bits, **op0.tags),
93
114
  **stmt.tags,
94
115
  )
95
- if op0.op == "Shr" and op1.op == "Shl" and op0_shiftamount + op1_shiftamount == stmt.dst.bits:
116
+ if (
117
+ op0.op == "Shr"
118
+ and op1.op in {"Shl", "Mul"}
119
+ and (op1_shiftamount := get_expr_shift_left_amount(op1)) is not None
120
+ and op0_v + op1_shiftamount == stmt.dst.bits
121
+ ):
122
+ shiftamount = op0.operands[1]
96
123
  return Assignment(
97
124
  stmt.idx,
98
125
  stmt.dst,
@@ -0,0 +1,25 @@
1
+ # pylint:disable=missing-class-docstring,no-self-use
2
+ from __future__ import annotations
3
+ from ailment.expression import BinaryOp, Const
4
+
5
+ from .base import PeepholeOptimizationExprBase
6
+
7
+
8
+ class ShlToMul(PeepholeOptimizationExprBase):
9
+ __slots__ = ()
10
+
11
+ NAME = "a << A => a * (2 ** A)"
12
+ expr_classes = (BinaryOp,) # all expressions are allowed
13
+
14
+ def optimize(self, expr: BinaryOp, **kwargs):
15
+ if expr.op == "Shl" and isinstance(expr.operands[1], Const):
16
+ mul_amount = Const(None, None, 2 ** expr.operands[1].value, expr.operands[0].bits)
17
+ return BinaryOp(
18
+ expr.idx,
19
+ "Mul",
20
+ [expr.operands[0], mul_amount],
21
+ expr.signed,
22
+ **expr.tags,
23
+ )
24
+
25
+ return None
@@ -0,0 +1,18 @@
1
+ from __future__ import annotations
2
+ from ailment.expression import BinaryOp, Const
3
+
4
+
5
+ def get_expr_shift_left_amount(expr: BinaryOp) -> int | None:
6
+ """
7
+ Get the shift amount of a shift-left or multiplication operation if the shift amount is a constant.
8
+
9
+ :param expr: The shift-left or multiplication expression (must be a BinaryOp).
10
+ :return: The shift amount if it is a constant, or None if it is not.
11
+ """
12
+ if expr.op == "Shl" and isinstance(expr.operands[1], Const):
13
+ return expr.operands[1].value
14
+ if expr.op == "Mul" and isinstance(expr.operands[1], Const):
15
+ v = expr.operands[1].value
16
+ if v & (v - 1) == 0:
17
+ return v.bit_length() - 1
18
+ return None
@@ -19,7 +19,6 @@ from angr.analyses.decompiler.optimization_passes import (
19
19
  FlipBooleanCmp,
20
20
  InlinedStringTransformationSimplifier,
21
21
  CallStatementRewriter,
22
- MultiSimplifier,
23
22
  DeadblockRemover,
24
23
  SwitchReusedEntryRewriter,
25
24
  )
@@ -32,7 +31,6 @@ preset_fast = DecompilationPreset(
32
31
  StackCanarySimplifier,
33
32
  WinStackCanarySimplifier,
34
33
  BasePointerSaveSimplifier,
35
- MultiSimplifier, # TODO: MultiSimplifier should be replaced by a peephole optimization
36
34
  ConstantDereferencesSimplifier,
37
35
  RetAddrSaveSimplifier,
38
36
  X86GccGetPcSimplifier,
@@ -6,7 +6,6 @@ from angr.analyses.decompiler.optimization_passes import (
6
6
  WinStackCanarySimplifier,
7
7
  BasePointerSaveSimplifier,
8
8
  DivSimplifier,
9
- MultiSimplifier,
10
9
  ModSimplifier,
11
10
  ConstantDereferencesSimplifier,
12
11
  RetAddrSaveSimplifier,
@@ -38,7 +37,6 @@ preset_full = DecompilationPreset(
38
37
  WinStackCanarySimplifier,
39
38
  BasePointerSaveSimplifier,
40
39
  DivSimplifier,
41
- MultiSimplifier,
42
40
  ModSimplifier,
43
41
  ConstantDereferencesSimplifier,
44
42
  RetAddrSaveSimplifier,
@@ -56,8 +56,7 @@ class RewritingAnalysis(ForwardAnalysis[RewritingState, NodeType, object, object
56
56
  self._rewrite_tmps = rewrite_tmps
57
57
  self._ail_manager = ail_manager
58
58
  self._engine_ail = SimEngineSSARewriting(
59
- self.project.arch,
60
- project=self.project,
59
+ self.project,
61
60
  sp_tracker=sp_tracker,
62
61
  bp_as_gpr=bp_as_gpr,
63
62
  udef_to_phiid=self._udef_to_phiid,