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
@@ -20,6 +20,7 @@ from angr.utils.graph import dominates, to_acyclic_graph, dfs_back_edges
20
20
  from angr.analyses.decompiler.sequence_walker import SequenceWalker
21
21
  from angr.analyses.decompiler.utils import (
22
22
  remove_last_statement,
23
+ remove_last_statements,
23
24
  extract_jump_targets,
24
25
  switch_extract_cmp_bounds,
25
26
  is_empty_or_label_only_node,
@@ -1191,8 +1192,10 @@ class PhoenixStructurer(StructurerBase):
1191
1192
  # update node_a
1192
1193
  node_a = next(iter(nn for nn in graph.nodes if nn.addr == target))
1193
1194
  if isinstance(node_a, IncompleteSwitchCaseNode):
1194
- self._unpack_incompleteswitchcasenode(graph, node_a)
1195
- self._unpack_incompleteswitchcasenode(full_graph, node_a)
1195
+ r = self._unpack_incompleteswitchcasenode(graph, node_a)
1196
+ if not r:
1197
+ return False
1198
+ self._unpack_incompleteswitchcasenode(full_graph, node_a) # this shall not fail
1196
1199
  # update node_a
1197
1200
  node_a = next(iter(nn for nn in graph.nodes if nn.addr == target))
1198
1201
 
@@ -1307,7 +1310,9 @@ class PhoenixStructurer(StructurerBase):
1307
1310
  ):
1308
1311
  out_nodes = set()
1309
1312
  for succ in successors:
1310
- out_nodes |= set(full_graph.successors(succ))
1313
+ out_nodes |= {
1314
+ succ for succ in full_graph.successors(succ) if succ is not node and succ not in successors
1315
+ }
1311
1316
  out_nodes = list(out_nodes)
1312
1317
  if len(out_nodes) <= 1:
1313
1318
  new_node = IncompleteSwitchCaseNode(node.addr, node, successors)
@@ -1507,7 +1512,10 @@ class PhoenixStructurer(StructurerBase):
1507
1512
  if node_default is not None:
1508
1513
  all_case_nodes.append(node_default)
1509
1514
  case_node: SequenceNode = next(nn for nn in all_case_nodes if nn.addr == out_src.addr)
1510
- case_node_last_stmt = self.cond_proc.get_last_statement(case_node)
1515
+ try:
1516
+ case_node_last_stmt = self.cond_proc.get_last_statement(case_node)
1517
+ except EmptyBlockNotice:
1518
+ case_node_last_stmt = None
1511
1519
  if not isinstance(case_node_last_stmt, Jump):
1512
1520
  jump_stmt = Jump(
1513
1521
  None, Const(None, None, head.addr, self.project.arch.bits), None, ins_addr=out_src.addr
@@ -1538,7 +1546,7 @@ class PhoenixStructurer(StructurerBase):
1538
1546
 
1539
1547
  if node_a is not None:
1540
1548
  # remove the last statement in node_a
1541
- remove_last_statement(node_a)
1549
+ remove_last_statements(node_a)
1542
1550
 
1543
1551
  return True
1544
1552
 
@@ -2308,7 +2316,7 @@ class PhoenixStructurer(StructurerBase):
2308
2316
  if new_src is not None:
2309
2317
  self.replace_nodes(full_graph, src, new_src)
2310
2318
  if remove_src_last_stmt:
2311
- remove_last_statement(src)
2319
+ remove_last_statements(src)
2312
2320
 
2313
2321
  def _should_use_multistmtexprs(self, node: Block | BaseNode) -> bool:
2314
2322
  """
@@ -2459,7 +2467,7 @@ class PhoenixStructurer(StructurerBase):
2459
2467
  return True, new_seq
2460
2468
 
2461
2469
  @staticmethod
2462
- def _unpack_incompleteswitchcasenode(graph: networkx.DiGraph, incscnode: IncompleteSwitchCaseNode):
2470
+ def _unpack_incompleteswitchcasenode(graph: networkx.DiGraph, incscnode: IncompleteSwitchCaseNode) -> bool:
2463
2471
  preds = list(graph.predecessors(incscnode))
2464
2472
  succs = list(graph.successors(incscnode))
2465
2473
  if len(succs) <= 1:
@@ -2470,6 +2478,8 @@ class PhoenixStructurer(StructurerBase):
2470
2478
  graph.add_edge(incscnode.head, case_node)
2471
2479
  if succs:
2472
2480
  graph.add_edge(case_node, succs[0])
2481
+ return True
2482
+ return False
2473
2483
 
2474
2484
  @staticmethod
2475
2485
  def _count_statements(node: BaseNode | Block) -> int:
@@ -45,6 +45,33 @@ def remove_last_statement(node):
45
45
  return stmt
46
46
 
47
47
 
48
+ def remove_last_statements(node) -> bool:
49
+ if type(node) is CodeNode:
50
+ return remove_last_statements(node.node)
51
+ if type(node) is ailment.Block:
52
+ if not node.statements:
53
+ return False
54
+ node.statements = node.statements[:-1]
55
+ return True
56
+ if type(node) is MultiNode or type(node) is SequenceNode:
57
+ if node.nodes:
58
+ remove_last_statements(node.nodes[-1])
59
+ if BaseNode.test_empty_node(node.nodes[-1]):
60
+ node.nodes = node.nodes[:-1]
61
+ return True
62
+ return False
63
+ if type(node) is ConditionNode:
64
+ r = False
65
+ if node.true_node is None and node.false_node is not None:
66
+ r |= remove_last_statements(node.false_node)
67
+ if node.true_node is not None and node.false_node is None:
68
+ r |= remove_last_statements(node.true_node)
69
+ return r
70
+ if type(node) is LoopNode:
71
+ return remove_last_statements(node.sequence_node)
72
+ raise NotImplementedError
73
+
74
+
48
75
  def append_statement(node, stmt):
49
76
  if type(node) is CodeNode:
50
77
  append_statement(node.node, stmt)
@@ -214,16 +214,34 @@ class SPropagatorAnalysis(Analysis):
214
214
 
215
215
  if self._sp_tracker is not None and vvar.category == VirtualVariableCategory.REGISTER:
216
216
  if vvar.oident == self.project.arch.sp_offset:
217
+ sp_bits = (
218
+ (self.project.arch.registers["sp"][1] * self.project.arch.byte_width)
219
+ if "sp" in self.project.arch.registers
220
+ else None
221
+ )
217
222
  for vvar_at_use, useloc in vvar_uselocs[vvar.varid]:
218
223
  sb_offset = self._sp_tracker.offset_before(useloc.ins_addr, self.project.arch.sp_offset)
219
224
  if sb_offset is not None:
220
- replacements[useloc][vvar_at_use] = StackBaseOffset(None, self.project.arch.bits, sb_offset)
225
+ v = StackBaseOffset(None, self.project.arch.bits, sb_offset)
226
+ if sp_bits is not None and vvar.bits < sp_bits:
227
+ # truncation needed
228
+ v = Convert(None, sp_bits, vvar.bits, False, v)
229
+ replacements[useloc][vvar_at_use] = v
221
230
  continue
222
231
  if not self._bp_as_gpr and vvar.oident == self.project.arch.bp_offset:
232
+ bp_bits = (
233
+ (self.project.arch.registers["bp"][1] * self.project.arch.byte_width)
234
+ if "bp" in self.project.arch.registers
235
+ else None
236
+ )
223
237
  for vvar_at_use, useloc in vvar_uselocs[vvar.varid]:
224
238
  sb_offset = self._sp_tracker.offset_before(useloc.ins_addr, self.project.arch.bp_offset)
225
239
  if sb_offset is not None:
226
- replacements[useloc][vvar_at_use] = StackBaseOffset(None, self.project.arch.bits, sb_offset)
240
+ v = StackBaseOffset(None, self.project.arch.bits, sb_offset)
241
+ if bp_bits is not None and vvar.bits < bp_bits:
242
+ # truncation needed
243
+ v = Convert(None, bp_bits, vvar.bits, False, v)
244
+ replacements[useloc][vvar_at_use] = v
227
245
  continue
228
246
 
229
247
  # find all tmp definitions
@@ -190,7 +190,11 @@ class Sketch:
190
190
  for _, dst, data in self.graph.out_edges(node, data=True):
191
191
  if "label" in data and data["label"] == label:
192
192
  succs.append(dst)
193
- assert len(succs) <= 1
193
+ if len(succs) > 1:
194
+ _l.warning(
195
+ "Multiple successors found for node %s with label %s. Picking the first one.", node, label
196
+ )
197
+ succs = succs[:1]
194
198
  if not succs:
195
199
  return None
196
200
  node = succs[0]
@@ -1166,7 +1170,10 @@ class SimpleSolver:
1166
1170
  for labels, succ in path_and_successors:
1167
1171
  last_label = labels[-1] if labels else None
1168
1172
  if isinstance(last_label, HasField):
1169
- candidate_bases[last_label.offset].add(last_label.bits // 8)
1173
+ # TODO: Really determine the maximum possible size of the field when MAX_POINTSTO_BITS is in use
1174
+ candidate_bases[last_label.offset].add(
1175
+ 1 if last_label.bits == MAX_POINTSTO_BITS else (last_label.bits // 8)
1176
+ )
1170
1177
 
1171
1178
  node_to_base = {}
1172
1179
 
@@ -103,8 +103,11 @@ class Typehoon(Analysis):
103
103
  print(f"### {sum(map(len, self._constraints.values()))} constraints")
104
104
  for func_var in self._constraints:
105
105
  print(f"{func_var}:")
106
+ lst = []
106
107
  for constraint in self._constraints[func_var]:
107
- print(" " + constraint.pp_str(typevar_to_var))
108
+ lst.append(" " + constraint.pp_str(typevar_to_var))
109
+ lst = sorted(lst)
110
+ print("\n".join(lst))
108
111
  print("### end of constraints ###")
109
112
 
110
113
  def pp_solution(self) -> None:
@@ -384,7 +384,7 @@ class SimEngineVRAIL(
384
384
  def _ail_handle_Cmp(self, expr): # pylint:disable=useless-return
385
385
  self._expr(expr.operands[0])
386
386
  self._expr(expr.operands[1])
387
- return RichR(self.state.top(1))
387
+ return RichR(self.state.top(expr.bits))
388
388
 
389
389
  _ail_handle_CmpF = _ail_handle_Cmp
390
390
  _ail_handle_CmpEQ = _ail_handle_Cmp
@@ -459,16 +459,17 @@ class SimEngineVRAIL(
459
459
  r0 = self._expr(arg0)
460
460
  r1 = self._expr(arg1)
461
461
 
462
+ result_size = arg0.bits
462
463
  if r0.data.concrete and r1.data.concrete:
463
- # constants
464
- result_size = arg0.bits
465
- return RichR(r0.data * r1.data, typevar=typeconsts.int_type(result_size), type_constraints=None)
466
-
467
- r = self.state.top(expr.bits)
468
- return RichR(
469
- r,
470
- typevar=r0.typevar,
471
- )
464
+ v = r0.data * r1.data
465
+ tv = r0.typevar
466
+ elif r1.data.concrete:
467
+ v = r0.data * r1.data
468
+ tv = typeconsts.int_type(result_size)
469
+ else:
470
+ v = self.state.top(expr.bits)
471
+ tv = typeconsts.int_type(result_size)
472
+ return RichR(v, typevar=tv, type_constraints=None)
472
473
 
473
474
  def _ail_handle_Mull(self, expr):
474
475
  arg0, arg1 = expr.operands
@@ -608,7 +609,6 @@ class SimEngineVRAIL(
608
609
  )
609
610
 
610
611
  shiftamount = r1.data.concrete_value
611
-
612
612
  return RichR(r0.data << shiftamount, typevar=typeconsts.int_type(result_size), type_constraints=None)
613
613
 
614
614
  def _ail_handle_Shr(self, expr):
@@ -676,8 +676,8 @@ class SimEngineVRAIL(
676
676
  r0 = self._expr(arg0)
677
677
  r1 = self._expr(arg1)
678
678
 
679
+ result_size = arg0.bits
679
680
  if r0.data.concrete and r1.data.concrete:
680
- result_size = arg0.bits
681
681
  return RichR(
682
682
  r0.data & r1.data,
683
683
  typevar=typeconsts.int_type(result_size),
@@ -685,7 +685,7 @@ class SimEngineVRAIL(
685
685
  )
686
686
 
687
687
  r = self.state.top(expr.bits)
688
- return RichR(r, typevar=r0.typevar)
688
+ return RichR(r, typevar=typeconsts.int_type(result_size))
689
689
 
690
690
  def _ail_handle_Or(self, expr):
691
691
  arg0, arg1 = expr.operands
@@ -693,8 +693,8 @@ class SimEngineVRAIL(
693
693
  r0 = self._expr(arg0)
694
694
  r1 = self._expr(arg1)
695
695
 
696
+ result_size = arg0.bits
696
697
  if r0.data.concrete and r1.data.concrete:
697
- result_size = arg0.bits
698
698
  return RichR(
699
699
  r0.data | r1.data,
700
700
  typevar=typeconsts.int_type(result_size),
@@ -702,7 +702,7 @@ class SimEngineVRAIL(
702
702
  )
703
703
 
704
704
  r = self.state.top(expr.bits)
705
- return RichR(r, typevar=r0.typevar)
705
+ return RichR(r, typevar=typeconsts.int_type(result_size))
706
706
 
707
707
  def _ail_handle_LogicalAnd(self, expr):
708
708
  arg0, arg1 = expr.operands
@@ -133,6 +133,9 @@ class SimEngineVRBase(SimEngineLight):
133
133
  if abs_offset.op == "__lshift__" and abs_offset.args[1].concrete:
134
134
  offset = abs_offset.args[0]
135
135
  elem_size = 2 ** abs_offset.args[1].concrete_value
136
+ elif abs_offset.op == "__mul__" and abs_offset.args[1].concrete:
137
+ offset = abs_offset.args[0]
138
+ elem_size = abs_offset.args[1].concrete_value
136
139
 
137
140
  if base_addr is not None and offset is not None and elem_size is not None:
138
141
  return base_addr, offset, elem_size
@@ -214,8 +214,23 @@ class SimEngineVRVEX(
214
214
  for target_func in self.call_info.get(current_addr, []):
215
215
  self._handle_function_concrete(target_func)
216
216
 
217
- # handles return statements
218
- if self.block.vex.jumpkind == "Ijk_Ret":
217
+ if self.block.vex.jumpkind == "Ijk_Call":
218
+ # emulates return values from calls
219
+ cc = None
220
+ for target_func in self.call_info.get(self.state.block_addr, []):
221
+ if target_func.calling_convention is not None:
222
+ cc = target_func.calling_convention
223
+ break
224
+ if cc is None:
225
+ cc = default_cc(self.arch.name, platform=self.project.simos.name)(self.arch)
226
+ if isinstance(cc.RETURN_VAL, SimRegArg):
227
+ reg_offset, reg_size = self.arch.registers[cc.RETURN_VAL.reg_name]
228
+ data = self._top(reg_size * self.arch.byte_width)
229
+ self._assign_to_register(reg_offset, data, reg_size, create_variable=False)
230
+
231
+ elif self.block.vex.jumpkind == "Ijk_Ret":
232
+ # handles return statements
233
+
219
234
  # determine the size of the return register
220
235
  # TODO: Handle multiple return registers
221
236
  cc = self.state.function.calling_convention
@@ -1272,19 +1272,21 @@ class SimEngineLightAILMixin(SimEngineLightMixin):
1272
1272
  if expr_1 is None:
1273
1273
  expr_1 = arg1
1274
1274
 
1275
- try:
1276
- return expr_0 * expr_1
1277
- except TypeError:
1278
- return ailment.Expr.BinaryOp(
1279
- expr.idx,
1280
- "Mull",
1281
- [expr_0, expr_1],
1282
- expr.signed,
1283
- bits=expr.bits,
1284
- floating_point=expr.floating_point,
1285
- rounding_mode=expr.rounding_mode,
1286
- **expr.tags,
1287
- )
1275
+ if isinstance(expr_0, claripy.ast.Bits) and isinstance(expr_1, claripy.ast.Bits):
1276
+ expr0_ext = claripy.ZeroExt(expr.bits - expr_0.size(), expr_0) if expr.bits > expr_0.size() else expr_0
1277
+ expr1_ext = claripy.ZeroExt(expr.bits - expr_1.size(), expr_1) if expr.bits > expr_1.size() else expr_1
1278
+ return expr0_ext * expr1_ext
1279
+
1280
+ return ailment.Expr.BinaryOp(
1281
+ expr.idx,
1282
+ "Mull",
1283
+ [expr_0, expr_1],
1284
+ expr.signed,
1285
+ bits=expr.bits,
1286
+ floating_point=expr.floating_point,
1287
+ rounding_mode=expr.rounding_mode,
1288
+ **expr.tags,
1289
+ )
1288
1290
 
1289
1291
  def _ail_handle_And(self, expr):
1290
1292
  arg0, arg1 = expr.operands
@@ -17,6 +17,7 @@ import pyvex
17
17
  import claripy
18
18
 
19
19
  from angr.errors import UnsupportedIROpError, SimOperationError, SimValueError, SimZeroDivisionException
20
+ from angr.state_plugins.sim_action_object import SimActionObject
20
21
 
21
22
 
22
23
  l = logging.getLogger(name=__name__)
@@ -425,8 +426,18 @@ class SimIROp:
425
426
  print(f"... {k}: {v}")
426
427
 
427
428
  def calculate(self, *args):
428
- if not all(isinstance(a, claripy.ast.Base) for a in args):
429
- raise SimOperationError("IROp needs all args as claripy expressions")
429
+ # calculate may recieve SimActionObjects (if AST_DEPS is enabled) or
430
+ # claripy expressions, so we need to unpack the SAOs before passing them
431
+ # to claripy.
432
+ unpacked_args = []
433
+ for arg in args:
434
+ if isinstance(arg, SimActionObject):
435
+ unpacked_args.append(arg.to_claripy())
436
+ elif isinstance(arg, claripy.ast.Base):
437
+ unpacked_args.append(arg)
438
+ else:
439
+ raise SimOperationError(f"Unsupported argument type {type(arg)}")
440
+ args = unpacked_args
430
441
 
431
442
  if not self._float:
432
443
  args = tuple(arg.raw_to_bv() for arg in args)
Binary file
angr/utils/bits.py CHANGED
@@ -14,3 +14,8 @@ def truncate_bits(value: int, nbits: int) -> int:
14
14
 
15
15
  def ffs(x: int) -> int:
16
16
  return (x & -x).bit_length() - 1
17
+
18
+
19
+ def sign_extend(value: int, bits: int) -> int:
20
+ sign_bit = 1 << (bits - 1)
21
+ return (value & (sign_bit - 1)) - (value & sign_bit)
angr/utils/formatting.py CHANGED
@@ -1,4 +1,5 @@
1
1
  from __future__ import annotations
2
+ import os
2
3
  import sys
3
4
  from collections.abc import Sequence, Callable
4
5
 
@@ -23,7 +24,9 @@ def setup_terminal():
23
24
  colorama.init()
24
25
 
25
26
  global ansi_color_enabled # pylint:disable=global-statement
26
- ansi_color_enabled = isatty
27
+ # https://no-color.org/
28
+ no_color = os.environ.get("NO_COLOR", "")
29
+ ansi_color_enabled = isatty and not no_color
27
30
 
28
31
 
29
32
  def ansi_color(s: str, color: str | None) -> str:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: angr
3
- Version: 9.2.129
3
+ Version: 9.2.131
4
4
  Summary: A multi-architecture binary analysis toolkit, with the ability to perform dynamic symbolic execution and various static analyses on binaries
5
5
  Home-page: https://github.com/angr/angr
6
6
  License: BSD-2-Clause
@@ -16,13 +16,13 @@ Description-Content-Type: text/markdown
16
16
  License-File: LICENSE
17
17
  Requires-Dist: CppHeaderParser
18
18
  Requires-Dist: GitPython
19
- Requires-Dist: ailment==9.2.129
20
- Requires-Dist: archinfo==9.2.129
19
+ Requires-Dist: ailment==9.2.131
20
+ Requires-Dist: archinfo==9.2.131
21
21
  Requires-Dist: cachetools
22
22
  Requires-Dist: capstone==5.0.3
23
23
  Requires-Dist: cffi>=1.14.0
24
- Requires-Dist: claripy==9.2.129
25
- Requires-Dist: cle==9.2.129
24
+ Requires-Dist: claripy==9.2.131
25
+ Requires-Dist: cle==9.2.131
26
26
  Requires-Dist: itanium-demangler
27
27
  Requires-Dist: mulpyplexer
28
28
  Requires-Dist: nampa
@@ -31,7 +31,7 @@ Requires-Dist: protobuf>=5.28.2
31
31
  Requires-Dist: psutil
32
32
  Requires-Dist: pycparser>=2.18
33
33
  Requires-Dist: pyformlang
34
- Requires-Dist: pyvex==9.2.129
34
+ Requires-Dist: pyvex==9.2.131
35
35
  Requires-Dist: rich>=13.1.0
36
36
  Requires-Dist: sortedcontainers
37
37
  Requires-Dist: sympy