angr 9.2.130__py3-none-manylinux2014_aarch64.whl → 9.2.131__py3-none-manylinux2014_aarch64.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.
- angr/__init__.py +1 -1
- angr/analyses/decompiler/clinic.py +283 -4
- angr/analyses/decompiler/optimization_passes/__init__.py +0 -3
- angr/analyses/decompiler/peephole_optimizations/__init__.py +5 -1
- angr/analyses/decompiler/peephole_optimizations/a_mul_const_sub_a.py +34 -0
- angr/analyses/decompiler/peephole_optimizations/a_shl_const_sub_a.py +3 -1
- angr/analyses/decompiler/peephole_optimizations/bswap.py +10 -6
- angr/analyses/decompiler/peephole_optimizations/eager_eval.py +100 -19
- angr/analyses/decompiler/peephole_optimizations/remove_noop_conversions.py +15 -0
- angr/analyses/decompiler/peephole_optimizations/remove_redundant_conversions.py +42 -3
- angr/analyses/decompiler/peephole_optimizations/remove_redundant_shifts.py +4 -2
- angr/analyses/decompiler/peephole_optimizations/rol_ror.py +37 -10
- angr/analyses/decompiler/peephole_optimizations/shl_to_mul.py +25 -0
- angr/analyses/decompiler/peephole_optimizations/utils.py +18 -0
- angr/analyses/decompiler/presets/fast.py +0 -2
- angr/analyses/decompiler/presets/full.py +0 -2
- angr/analyses/decompiler/ssailification/rewriting_engine.py +2 -2
- angr/analyses/decompiler/structured_codegen/c.py +74 -13
- angr/analyses/decompiler/structuring/phoenix.py +14 -5
- angr/analyses/s_propagator.py +20 -2
- angr/analyses/typehoon/simple_solver.py +9 -2
- angr/analyses/typehoon/typehoon.py +4 -1
- angr/analyses/variable_recovery/engine_ail.py +15 -15
- angr/analyses/variable_recovery/engine_base.py +3 -0
- angr/engines/light/engine.py +3 -3
- angr/engines/vex/claripy/irop.py +13 -2
- angr/utils/bits.py +5 -0
- angr/utils/formatting.py +4 -1
- {angr-9.2.130.dist-info → angr-9.2.131.dist-info}/METADATA +6 -6
- {angr-9.2.130.dist-info → angr-9.2.131.dist-info}/RECORD +34 -32
- angr/analyses/decompiler/optimization_passes/multi_simplifier.py +0 -223
- {angr-9.2.130.dist-info → angr-9.2.131.dist-info}/LICENSE +0 -0
- {angr-9.2.130.dist-info → angr-9.2.131.dist-info}/WHEEL +0 -0
- {angr-9.2.130.dist-info → angr-9.2.131.dist-info}/entry_points.txt +0 -0
- {angr-9.2.130.dist-info → angr-9.2.131.dist-info}/top_level.txt +0 -0
|
@@ -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
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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
|
-
|
|
231
|
-
|
|
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
|
|
@@ -26,5 +26,20 @@ class RemoveNoopConversions(PeepholeOptimizationExprBase):
|
|
|
26
26
|
# extension then truncation (e.g., 1->64->1) can be removed, but truncation then extension cannot be
|
|
27
27
|
# removed (e.g., the high 32 bits must be removed during 64->32->64)
|
|
28
28
|
return inner.operand
|
|
29
|
+
if (
|
|
30
|
+
expr.to_bits < expr.from_bits
|
|
31
|
+
and expr.from_bits == inner.to_bits
|
|
32
|
+
and inner.to_bits <= inner.from_bits
|
|
33
|
+
and expr.is_signed == inner.is_signed
|
|
34
|
+
):
|
|
35
|
+
# merging two truncations into one
|
|
36
|
+
return Convert(
|
|
37
|
+
expr.idx,
|
|
38
|
+
inner.from_bits,
|
|
39
|
+
expr.to_bits,
|
|
40
|
+
expr.is_signed,
|
|
41
|
+
inner.operand,
|
|
42
|
+
**expr.tags,
|
|
43
|
+
)
|
|
29
44
|
|
|
30
45
|
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
|
|
19
|
-
n1 = expr_a
|
|
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
|
|
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,
|
|
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
|
|
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
|
|
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
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
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
|
|
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,
|
|
@@ -169,14 +169,14 @@ class SimEngineSSARewriting(
|
|
|
169
169
|
new_addr = self._expr(stmt.addr)
|
|
170
170
|
new_guard = self._expr(stmt.guard) if stmt.guard is not None else None
|
|
171
171
|
|
|
172
|
-
if new_addr is not None or new_data is not None:
|
|
172
|
+
if new_addr is not None or new_data is not None or new_guard is not None:
|
|
173
173
|
return Store(
|
|
174
174
|
stmt.idx,
|
|
175
175
|
stmt.addr if new_addr is None else new_addr,
|
|
176
176
|
stmt.data if new_data is None else new_data,
|
|
177
177
|
stmt.size,
|
|
178
178
|
stmt.endness,
|
|
179
|
-
guard=new_guard,
|
|
179
|
+
guard=stmt.guard if new_guard is None else new_guard,
|
|
180
180
|
**stmt.tags,
|
|
181
181
|
)
|
|
182
182
|
|
|
@@ -52,6 +52,7 @@ from angr.analyses.decompiler.structuring.structurer_nodes import (
|
|
|
52
52
|
LoopNode,
|
|
53
53
|
BreakNode,
|
|
54
54
|
SwitchCaseNode,
|
|
55
|
+
IncompleteSwitchCaseNode,
|
|
55
56
|
ContinueNode,
|
|
56
57
|
CascadingConditionNode,
|
|
57
58
|
)
|
|
@@ -1136,6 +1137,53 @@ class CSwitchCase(CStatement):
|
|
|
1136
1137
|
yield "\n", None
|
|
1137
1138
|
|
|
1138
1139
|
|
|
1140
|
+
class CIncompleteSwitchCase(CStatement):
|
|
1141
|
+
"""
|
|
1142
|
+
Represents an incomplete switch-case construct; this only appear in the decompilation output when switch-case
|
|
1143
|
+
structuring fails (for whatever reason).
|
|
1144
|
+
"""
|
|
1145
|
+
|
|
1146
|
+
__slots__ = ("head", "cases", "tags")
|
|
1147
|
+
|
|
1148
|
+
def __init__(self, head, cases, tags=None, **kwargs):
|
|
1149
|
+
super().__init__(**kwargs)
|
|
1150
|
+
|
|
1151
|
+
self.head = head
|
|
1152
|
+
self.cases: list[tuple[int, CStatements]] = cases
|
|
1153
|
+
self.tags = tags
|
|
1154
|
+
|
|
1155
|
+
def c_repr_chunks(self, indent=0, asexpr=False):
|
|
1156
|
+
indent_str = self.indent_str(indent=indent)
|
|
1157
|
+
paren = CClosingObject("(")
|
|
1158
|
+
brace = CClosingObject("{")
|
|
1159
|
+
|
|
1160
|
+
yield from self.head.c_repr_chunks(indent=indent)
|
|
1161
|
+
yield "\n", None
|
|
1162
|
+
yield indent_str, None
|
|
1163
|
+
yield "switch ", self
|
|
1164
|
+
yield "(", paren
|
|
1165
|
+
yield "/* incomplete */", None
|
|
1166
|
+
yield ")", paren
|
|
1167
|
+
if self.codegen.braces_on_own_lines:
|
|
1168
|
+
yield "\n", None
|
|
1169
|
+
yield indent_str, None
|
|
1170
|
+
else:
|
|
1171
|
+
yield " ", None
|
|
1172
|
+
yield "{", brace
|
|
1173
|
+
yield "\n", None
|
|
1174
|
+
|
|
1175
|
+
# cases
|
|
1176
|
+
for case_addr, case in self.cases:
|
|
1177
|
+
yield indent_str, None
|
|
1178
|
+
yield f"case {case_addr:#x}", self
|
|
1179
|
+
yield ":\n", None
|
|
1180
|
+
yield from case.c_repr_chunks(indent=indent + INDENT_DELTA)
|
|
1181
|
+
|
|
1182
|
+
yield indent_str, None
|
|
1183
|
+
yield "}", brace
|
|
1184
|
+
yield "\n", None
|
|
1185
|
+
|
|
1186
|
+
|
|
1139
1187
|
class CAssignment(CStatement):
|
|
1140
1188
|
"""
|
|
1141
1189
|
a = b
|
|
@@ -1299,6 +1347,8 @@ class CFunctionCall(CStatement, CExpression):
|
|
|
1299
1347
|
if self.show_disambiguated_name and self._is_target_ambiguous(func_name):
|
|
1300
1348
|
func_name = self.callee_func.get_unambiguous_name(display_name=func_name)
|
|
1301
1349
|
yield func_name, self
|
|
1350
|
+
elif isinstance(self.callee_target, str):
|
|
1351
|
+
yield self.callee_target, self
|
|
1302
1352
|
else:
|
|
1303
1353
|
yield from CExpression._try_c_repr_chunks(self.callee_target)
|
|
1304
1354
|
|
|
@@ -2443,6 +2493,7 @@ class CStructuredCodeGenerator(BaseStructuredCodeGenerator, Analysis):
|
|
|
2443
2493
|
Block: self._handle_AILBlock,
|
|
2444
2494
|
BreakNode: self._handle_Break,
|
|
2445
2495
|
SwitchCaseNode: self._handle_SwitchCase,
|
|
2496
|
+
IncompleteSwitchCaseNode: self._handle_IncompleteSwitchCase,
|
|
2446
2497
|
ContinueNode: self._handle_Continue,
|
|
2447
2498
|
# AIL statements
|
|
2448
2499
|
Stmt.Store: self._handle_Stmt_Store,
|
|
@@ -2671,16 +2722,16 @@ class CStructuredCodeGenerator(BaseStructuredCodeGenerator, Analysis):
|
|
|
2671
2722
|
# Util methods
|
|
2672
2723
|
#
|
|
2673
2724
|
|
|
2674
|
-
def
|
|
2725
|
+
def default_simtype_from_bits(self, n: int, signed: bool = True) -> SimType:
|
|
2675
2726
|
_mapping = {
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2727
|
+
64: SimTypeLongLong,
|
|
2728
|
+
32: SimTypeInt,
|
|
2729
|
+
16: SimTypeShort,
|
|
2730
|
+
8: SimTypeChar,
|
|
2680
2731
|
}
|
|
2681
2732
|
if n in _mapping:
|
|
2682
2733
|
return _mapping.get(n)(signed=signed).with_arch(self.project.arch)
|
|
2683
|
-
return SimTypeNum(n
|
|
2734
|
+
return SimTypeNum(n, signed=signed).with_arch(self.project.arch)
|
|
2684
2735
|
|
|
2685
2736
|
def _variable(self, variable: SimVariable, fallback_type_size: int | None) -> CVariable:
|
|
2686
2737
|
# TODO: we need to fucking make sure that variable recovery and type inference actually generates a size
|
|
@@ -2690,7 +2741,9 @@ class CStructuredCodeGenerator(BaseStructuredCodeGenerator, Analysis):
|
|
|
2690
2741
|
variable, is_global=isinstance(variable, SimMemoryVariable) and not isinstance(variable, SimStackVariable)
|
|
2691
2742
|
)
|
|
2692
2743
|
if variable_type is None:
|
|
2693
|
-
variable_type = self.
|
|
2744
|
+
variable_type = self.default_simtype_from_bits(
|
|
2745
|
+
(fallback_type_size or self.project.arch.bytes) * self.project.arch.byte_width
|
|
2746
|
+
)
|
|
2694
2747
|
cvar = CVariable(variable, unified_variable=unified, variable_type=variable_type, codegen=self)
|
|
2695
2748
|
self._variables_in_use[variable] = cvar
|
|
2696
2749
|
return cvar
|
|
@@ -3172,6 +3225,12 @@ class CStructuredCodeGenerator(BaseStructuredCodeGenerator, Analysis):
|
|
|
3172
3225
|
tags = {"ins_addr": node.addr}
|
|
3173
3226
|
return CSwitchCase(switch_expr, cases, default=default, tags=tags, codegen=self)
|
|
3174
3227
|
|
|
3228
|
+
def _handle_IncompleteSwitchCase(self, node: IncompleteSwitchCaseNode, **kwargs):
|
|
3229
|
+
head = self._handle(node.head, is_expr=False)
|
|
3230
|
+
cases = [(case.addr, self._handle(case, is_expr=False)) for case in node.cases]
|
|
3231
|
+
tags = {"ins_addr": node.addr}
|
|
3232
|
+
return CIncompleteSwitchCase(head, cases, tags=tags, codegen=self)
|
|
3233
|
+
|
|
3175
3234
|
def _handle_Continue(self, node, **kwargs):
|
|
3176
3235
|
tags = {"ins_addr": node.addr}
|
|
3177
3236
|
|
|
@@ -3313,7 +3372,9 @@ class CStructuredCodeGenerator(BaseStructuredCodeGenerator, Analysis):
|
|
|
3313
3372
|
if result.is_expr and result.type.size != stmt.size * self.project.arch.byte_width:
|
|
3314
3373
|
result = CTypeCast(
|
|
3315
3374
|
result.type,
|
|
3316
|
-
self.
|
|
3375
|
+
self.default_simtype_from_bits(
|
|
3376
|
+
stmt.size * self.project.arch.byte_width, signed=getattr(result.type, "signed", False)
|
|
3377
|
+
),
|
|
3317
3378
|
result,
|
|
3318
3379
|
codegen=self,
|
|
3319
3380
|
)
|
|
@@ -3376,12 +3437,12 @@ class CStructuredCodeGenerator(BaseStructuredCodeGenerator, Analysis):
|
|
|
3376
3437
|
return cvar
|
|
3377
3438
|
offset = 0 if expr.variable_offset is None else expr.variable_offset
|
|
3378
3439
|
# FIXME: The type should be associated to the register expression itself
|
|
3379
|
-
type_ = self.
|
|
3440
|
+
type_ = self.default_simtype_from_bits(expr.bits, signed=False)
|
|
3380
3441
|
return self._access_constant_offset(self._get_variable_reference(cvar), offset, type_, lvalue, negotiate)
|
|
3381
3442
|
return CRegister(expr, tags=expr.tags, codegen=self)
|
|
3382
3443
|
|
|
3383
3444
|
def _handle_Expr_Load(self, expr: Expr.Load, **kwargs):
|
|
3384
|
-
ty = self.
|
|
3445
|
+
ty = self.default_simtype_from_bits(expr.bits)
|
|
3385
3446
|
|
|
3386
3447
|
def negotiate(old_ty: SimType, proposed_ty: SimType) -> SimType:
|
|
3387
3448
|
# we do not allow returning a struct for a primitive type
|
|
@@ -3406,7 +3467,7 @@ class CStructuredCodeGenerator(BaseStructuredCodeGenerator, Analysis):
|
|
|
3406
3467
|
l.warning("FIXME: Leftover Tmp expressions are found.")
|
|
3407
3468
|
return self._variable(SimTemporaryVariable(expr.tmp_idx), expr.size)
|
|
3408
3469
|
|
|
3409
|
-
def _handle_Expr_Const(self, expr, type_=None, reference_values=None, variable=None, **kwargs):
|
|
3470
|
+
def _handle_Expr_Const(self, expr: Expr.Const, type_=None, reference_values=None, variable=None, **kwargs):
|
|
3410
3471
|
inline_string = False
|
|
3411
3472
|
function_pointer = False
|
|
3412
3473
|
|
|
@@ -3483,7 +3544,7 @@ class CStructuredCodeGenerator(BaseStructuredCodeGenerator, Analysis):
|
|
|
3483
3544
|
|
|
3484
3545
|
if type_ is None:
|
|
3485
3546
|
# default to int
|
|
3486
|
-
type_ = self.
|
|
3547
|
+
type_ = self.default_simtype_from_bits(expr.bits)
|
|
3487
3548
|
|
|
3488
3549
|
if variable is None and hasattr(expr, "reference_variable") and expr.reference_variable is not None:
|
|
3489
3550
|
variable = expr.reference_variable
|
|
@@ -3561,7 +3622,7 @@ class CStructuredCodeGenerator(BaseStructuredCodeGenerator, Analysis):
|
|
|
3561
3622
|
# do we need an intermediate cast?
|
|
3562
3623
|
if orig_child_signed != expr.is_signed and expr.to_bits > expr.from_bits:
|
|
3563
3624
|
# this is a problem. sign-extension only happens when the SOURCE of the cast is signed
|
|
3564
|
-
child_ty = self.
|
|
3625
|
+
child_ty = self.default_simtype_from_bits(child.type.size, expr.is_signed)
|
|
3565
3626
|
child = CTypeCast(None, child_ty, child, codegen=self)
|
|
3566
3627
|
|
|
3567
3628
|
return CTypeCast(None, dst_type.with_arch(self.project.arch), child, tags=expr.tags, codegen=self)
|