angr 9.2.129__py3-none-macosx_11_0_arm64.whl → 9.2.131__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 (41) hide show
  1. angr/__init__.py +1 -1
  2. angr/analyses/decompiler/ail_simplifier.py +1 -1
  3. angr/analyses/decompiler/clinic.py +283 -4
  4. angr/analyses/decompiler/optimization_passes/__init__.py +0 -3
  5. angr/analyses/decompiler/peephole_optimizations/__init__.py +5 -1
  6. angr/analyses/decompiler/peephole_optimizations/a_mul_const_sub_a.py +34 -0
  7. angr/analyses/decompiler/peephole_optimizations/a_shl_const_sub_a.py +3 -1
  8. angr/analyses/decompiler/peephole_optimizations/bswap.py +10 -6
  9. angr/analyses/decompiler/peephole_optimizations/eager_eval.py +100 -19
  10. angr/analyses/decompiler/peephole_optimizations/remove_noop_conversions.py +15 -0
  11. angr/analyses/decompiler/peephole_optimizations/remove_redundant_conversions.py +42 -3
  12. angr/analyses/decompiler/peephole_optimizations/remove_redundant_shifts.py +4 -2
  13. angr/analyses/decompiler/peephole_optimizations/rol_ror.py +37 -10
  14. angr/analyses/decompiler/peephole_optimizations/shl_to_mul.py +25 -0
  15. angr/analyses/decompiler/peephole_optimizations/utils.py +18 -0
  16. angr/analyses/decompiler/presets/fast.py +0 -2
  17. angr/analyses/decompiler/presets/full.py +0 -2
  18. angr/analyses/decompiler/region_identifier.py +6 -1
  19. angr/analyses/decompiler/ssailification/rewriting_engine.py +7 -5
  20. angr/analyses/decompiler/ssailification/traversal_engine.py +2 -0
  21. angr/analyses/decompiler/structured_codegen/c.py +74 -13
  22. angr/analyses/decompiler/structuring/phoenix.py +17 -7
  23. angr/analyses/decompiler/utils.py +27 -0
  24. angr/analyses/s_propagator.py +20 -2
  25. angr/analyses/typehoon/simple_solver.py +9 -2
  26. angr/analyses/typehoon/typehoon.py +4 -1
  27. angr/analyses/variable_recovery/engine_ail.py +15 -15
  28. angr/analyses/variable_recovery/engine_base.py +3 -0
  29. angr/analyses/variable_recovery/engine_vex.py +17 -2
  30. angr/engines/light/engine.py +15 -13
  31. angr/engines/vex/claripy/irop.py +13 -2
  32. angr/lib/angr_native.dylib +0 -0
  33. angr/utils/bits.py +5 -0
  34. angr/utils/formatting.py +4 -1
  35. {angr-9.2.129.dist-info → angr-9.2.131.dist-info}/METADATA +6 -6
  36. {angr-9.2.129.dist-info → angr-9.2.131.dist-info}/RECORD +40 -38
  37. {angr-9.2.129.dist-info → angr-9.2.131.dist-info}/WHEEL +1 -1
  38. angr/analyses/decompiler/optimization_passes/multi_simplifier.py +0 -223
  39. {angr-9.2.129.dist-info → angr-9.2.131.dist-info}/LICENSE +0 -0
  40. {angr-9.2.129.dist-info → angr-9.2.131.dist-info}/entry_points.txt +0 -0
  41. {angr-9.2.129.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
- 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
@@ -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 == "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,
@@ -220,7 +220,10 @@ class RegionIdentifier(Analysis):
220
220
  type_ = data.get("type", None)
221
221
  if type_ == "fake_return":
222
222
  if len(list(graph.successors(src))) == 1 and len(list(graph.predecessors(dst))) == 1:
223
- self._merge_nodes(graph, src, dst, force_multinode=True)
223
+ merged_node = self._merge_nodes(graph, src, dst, force_multinode=True)
224
+ # update the entry_node if necessary
225
+ if entry_node is not None and entry_node is src:
226
+ entry_node = merged_node
224
227
  break
225
228
  elif type_ == "call":
226
229
  graph.remove_node(dst)
@@ -1059,6 +1062,8 @@ class RegionIdentifier(Analysis):
1059
1062
  assert node_a not in graph
1060
1063
  assert node_b not in graph
1061
1064
 
1065
+ return new_node
1066
+
1062
1067
  def _absorb_node(
1063
1068
  self, graph: networkx.DiGraph, node_mommy, node_kiddie, force_multinode=False
1064
1069
  ): # pylint:disable=no-self-use
@@ -160,21 +160,23 @@ class SimEngineSSARewriting(
160
160
 
161
161
  def _handle_Store(self, stmt: Store) -> Store | Assignment | None:
162
162
  new_data = self._expr(stmt.data)
163
- vvar = self._replace_def_store(self.block.addr, self.block.idx, self.stmt_idx, stmt)
164
- if vvar is not None:
165
- return Assignment(stmt.idx, vvar, stmt.data if new_data is None else new_data, **stmt.tags)
163
+ if stmt.guard is None:
164
+ vvar = self._replace_def_store(self.block.addr, self.block.idx, self.stmt_idx, stmt)
165
+ if vvar is not None:
166
+ return Assignment(stmt.idx, vvar, stmt.data if new_data is None else new_data, **stmt.tags)
166
167
 
167
168
  # fall back to Store
168
169
  new_addr = self._expr(stmt.addr)
170
+ new_guard = self._expr(stmt.guard) if stmt.guard is not None else None
169
171
 
170
- 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:
171
173
  return Store(
172
174
  stmt.idx,
173
175
  stmt.addr if new_addr is None else new_addr,
174
176
  stmt.data if new_data is None else new_data,
175
177
  stmt.size,
176
178
  stmt.endness,
177
- guard=stmt.guard,
179
+ guard=stmt.guard if new_guard is None else new_guard,
178
180
  **stmt.tags,
179
181
  )
180
182
 
@@ -60,6 +60,8 @@ class SimEngineSSATraversal(
60
60
  def _handle_Store(self, stmt: Store):
61
61
  self._expr(stmt.addr)
62
62
  self._expr(stmt.data)
63
+ if stmt.guard is not None:
64
+ self._expr(stmt.guard)
63
65
 
64
66
  if self.stackvars and isinstance(stmt.addr, StackBaseOffset) and isinstance(stmt.addr.offset, int):
65
67
  codeloc = self._codeloc()
@@ -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 default_simtype_from_size(self, n: int, signed: bool = True) -> SimType:
2725
+ def default_simtype_from_bits(self, n: int, signed: bool = True) -> SimType:
2675
2726
  _mapping = {
2676
- 8: SimTypeLongLong,
2677
- 4: SimTypeInt,
2678
- 2: SimTypeShort,
2679
- 1: SimTypeChar,
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 * self.project.arch.byte_width, signed=signed).with_arch(self.project.arch)
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.default_simtype_from_size(fallback_type_size or self.project.arch.bytes)
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.default_simtype_from_size(stmt.size, signed=getattr(result.type, "signed", False)),
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.default_simtype_from_size(expr.size, signed=False)
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.default_simtype_from_size(expr.size)
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.default_simtype_from_size(expr.size)
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.default_simtype_from_size(child.type.size // self.project.arch.byte_width, expr.is_signed)
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)