angr 9.2.140__py3-none-manylinux2014_x86_64.whl → 9.2.141__py3-none-manylinux2014_x86_64.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 (40) hide show
  1. angr/__init__.py +1 -1
  2. angr/analyses/calling_convention/calling_convention.py +88 -32
  3. angr/analyses/calling_convention/fact_collector.py +44 -18
  4. angr/analyses/calling_convention/utils.py +3 -1
  5. angr/analyses/cfg/indirect_jump_resolvers/jumptable.py +9 -8
  6. angr/analyses/decompiler/ail_simplifier.py +48 -20
  7. angr/analyses/decompiler/callsite_maker.py +24 -11
  8. angr/analyses/decompiler/clinic.py +10 -0
  9. angr/analyses/decompiler/decompiler.py +1 -0
  10. angr/analyses/decompiler/optimization_passes/duplication_reverter/duplication_reverter.py +3 -1
  11. angr/analyses/decompiler/optimization_passes/flip_boolean_cmp.py +21 -2
  12. angr/analyses/decompiler/optimization_passes/lowered_switch_simplifier.py +84 -15
  13. angr/analyses/decompiler/optimization_passes/optimization_pass.py +76 -1
  14. angr/analyses/decompiler/optimization_passes/return_duplicator_base.py +51 -7
  15. angr/analyses/decompiler/peephole_optimizations/eager_eval.py +44 -7
  16. angr/analyses/decompiler/region_identifier.py +6 -4
  17. angr/analyses/decompiler/region_simplifiers/expr_folding.py +32 -18
  18. angr/analyses/decompiler/region_simplifiers/region_simplifier.py +4 -1
  19. angr/analyses/decompiler/ssailification/rewriting.py +23 -15
  20. angr/analyses/decompiler/ssailification/rewriting_engine.py +105 -24
  21. angr/analyses/decompiler/ssailification/ssailification.py +22 -14
  22. angr/analyses/decompiler/structured_codegen/c.py +73 -137
  23. angr/analyses/decompiler/structuring/dream.py +1 -1
  24. angr/analyses/decompiler/structuring/phoenix.py +6 -1
  25. angr/analyses/decompiler/structuring/structurer_base.py +2 -1
  26. angr/analyses/decompiler/utils.py +46 -20
  27. angr/analyses/s_reaching_definitions/s_rda_view.py +43 -25
  28. angr/analyses/variable_recovery/engine_ail.py +1 -1
  29. angr/analyses/variable_recovery/engine_vex.py +20 -4
  30. angr/calling_conventions.py +15 -10
  31. angr/factory.py +8 -3
  32. angr/knowledge_plugins/variables/variable_manager.py +7 -5
  33. angr/simos/simos.py +3 -1
  34. angr/utils/types.py +48 -0
  35. {angr-9.2.140.dist-info → angr-9.2.141.dist-info}/METADATA +6 -6
  36. {angr-9.2.140.dist-info → angr-9.2.141.dist-info}/RECORD +40 -39
  37. {angr-9.2.140.dist-info → angr-9.2.141.dist-info}/LICENSE +0 -0
  38. {angr-9.2.140.dist-info → angr-9.2.141.dist-info}/WHEEL +0 -0
  39. {angr-9.2.140.dist-info → angr-9.2.141.dist-info}/entry_points.txt +0 -0
  40. {angr-9.2.140.dist-info → angr-9.2.141.dist-info}/top_level.txt +0 -0
@@ -371,8 +371,8 @@ class StructurerBase(Analysis):
371
371
  jump_stmt = this_node.nodes[-1].statements[-1]
372
372
  this_node = this_node.nodes[-1]
373
373
 
374
- assert isinstance(this_node, ailment.Block)
375
374
  if isinstance(jump_stmt, ailment.Stmt.Jump):
375
+ assert isinstance(this_node, ailment.Block)
376
376
  next_node = node.nodes[i + 1]
377
377
  if (
378
378
  isinstance(jump_stmt.target, ailment.Expr.Const)
@@ -381,6 +381,7 @@ class StructurerBase(Analysis):
381
381
  # this goto is useless
382
382
  this_node.statements = this_node.statements[:-1]
383
383
  elif isinstance(jump_stmt, ailment.Stmt.ConditionalJump):
384
+ assert isinstance(this_node, ailment.Block)
384
385
  next_node = node.nodes[i + 1]
385
386
  if (
386
387
  isinstance(jump_stmt.true_target, ailment.Expr.Const)
@@ -625,6 +625,48 @@ def update_labels(graph: networkx.DiGraph):
625
625
  return add_labels(remove_labels(graph))
626
626
 
627
627
 
628
+ def _flatten_structured_node(packed_node: SequenceNode | MultiNode) -> list[ailment.Block]:
629
+ if not packed_node or not packed_node.nodes:
630
+ return []
631
+
632
+ blocks = []
633
+ if packed_node.nodes is not None:
634
+ for _node in packed_node.nodes:
635
+ if isinstance(_node, (SequenceNode, MultiNode)):
636
+ blocks += _flatten_structured_node(_node)
637
+ else:
638
+ blocks.append(_node)
639
+
640
+ return blocks
641
+
642
+
643
+ def _find_node_in_graph(node: ailment.Block, graph: networkx.DiGraph) -> ailment.Block | None:
644
+ for bb in graph:
645
+ if bb.addr == node.addr and bb.idx == node.idx:
646
+ return bb
647
+ return None
648
+
649
+
650
+ def structured_node_has_multi_predecessors(node: SequenceNode | MultiNode, graph: networkx.DiGraph) -> bool:
651
+ if graph is None:
652
+ return False
653
+
654
+ first_block = None
655
+ if isinstance(node, (SequenceNode, MultiNode)) and node.nodes:
656
+ flat_blocks = _flatten_structured_node(node)
657
+ node = flat_blocks[0]
658
+
659
+ if isinstance(node, ailment.Block):
660
+ first_block = node
661
+
662
+ if first_block is not None:
663
+ graph_node = _find_node_in_graph(first_block, graph)
664
+ if graph_node is not None:
665
+ return len(list(graph.predecessors(graph_node))) > 1
666
+
667
+ return False
668
+
669
+
628
670
  def structured_node_is_simple_return(
629
671
  node: SequenceNode | MultiNode, graph: networkx.DiGraph, use_packed_successors=False
630
672
  ) -> bool:
@@ -639,21 +681,6 @@ def structured_node_is_simple_return(
639
681
 
640
682
  Returns true on any block ending in linear statements and a return.
641
683
  """
642
-
643
- def _flatten_structured_node(packed_node: SequenceNode | MultiNode) -> list[ailment.Block]:
644
- if not packed_node or not packed_node.nodes:
645
- return []
646
-
647
- blocks = []
648
- if packed_node.nodes is not None:
649
- for _node in packed_node.nodes:
650
- if isinstance(_node, (SequenceNode, MultiNode)):
651
- blocks += _flatten_structured_node(_node)
652
- else:
653
- blocks.append(_node)
654
-
655
- return blocks
656
-
657
684
  # sanity check: we need a graph to understand returning blocks
658
685
  if graph is None:
659
686
  return False
@@ -676,11 +703,10 @@ def structured_node_is_simple_return(
676
703
  if valid_last_stmt:
677
704
  # note that the block may not be the same block in the AIL graph post dephication. we must find the block again
678
705
  # in the graph.
679
- for bb in graph:
680
- if bb.addr == last_block.addr and bb.idx == last_block.idx:
681
- # found it
682
- succs = list(graph.successors(bb))
683
- return not succs or succs == [bb]
706
+ last_graph_block = _find_node_in_graph(last_block, graph)
707
+ if last_graph_block is not None:
708
+ succs = list(graph.successors(last_graph_block))
709
+ return not succs or succs == [last_graph_block]
684
710
  return False
685
711
 
686
712
 
@@ -1,8 +1,10 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import logging
4
+ from collections.abc import Callable
4
5
  from collections import defaultdict
5
6
 
7
+ from ailment import Block
6
8
  from ailment.statement import Statement, Assignment, Call, Label
7
9
  from ailment.expression import VirtualVariable, VirtualVariableCategory, Expression
8
10
 
@@ -22,7 +24,7 @@ class RegVVarPredicate:
22
24
  Implements a predicate that is used in get_reg_vvar_by_stmt_idx and get_reg_vvar_by_insn.
23
25
  """
24
26
 
25
- def __init__(self, reg_offset: int, vvars: set[VirtualVariable], arch):
27
+ def __init__(self, reg_offset: int, vvars: list[VirtualVariable], arch):
26
28
  self.reg_offset = reg_offset
27
29
  self.vvars = vvars
28
30
  self.arch = arch
@@ -47,7 +49,8 @@ class RegVVarPredicate:
47
49
  and stmt.dst.was_reg
48
50
  and stmt.dst.reg_offset == self.reg_offset
49
51
  ):
50
- self.vvars.add(stmt.dst)
52
+ if stmt.dst not in self.vvars:
53
+ self.vvars.append(stmt.dst)
51
54
  return True
52
55
  if isinstance(stmt, Call):
53
56
  if (
@@ -55,7 +58,8 @@ class RegVVarPredicate:
55
58
  and stmt.ret_expr.was_reg
56
59
  and stmt.ret_expr.reg_offset == self.reg_offset
57
60
  ):
58
- self.vvars.add(stmt.ret_expr)
61
+ if stmt.ret_expr not in self.vvars:
62
+ self.vvars.append(stmt.ret_expr)
59
63
  return True
60
64
  # is it clobbered maybe?
61
65
  clobbered_regs = self._get_call_clobbered_regs(stmt)
@@ -69,7 +73,7 @@ class StackVVarPredicate:
69
73
  Implements a predicate that is used in get_stack_vvar_by_stmt_idx and get_stack_vvar_by_insn.
70
74
  """
71
75
 
72
- def __init__(self, stack_offset: int, size: int, vvars: set[VirtualVariable]):
76
+ def __init__(self, stack_offset: int, size: int, vvars: list[VirtualVariable]):
73
77
  self.stack_offset = stack_offset
74
78
  self.size = size
75
79
  self.vvars = vvars
@@ -82,7 +86,8 @@ class StackVVarPredicate:
82
86
  and stmt.dst.stack_offset <= self.stack_offset < stmt.dst.stack_offset + stmt.dst.size
83
87
  and stmt.dst.stack_offset <= self.stack_offset + self.size <= stmt.dst.stack_offset + stmt.dst.size
84
88
  ):
85
- self.vvars.add(stmt.dst)
89
+ if stmt.dst not in self.vvars:
90
+ self.vvars.append(stmt.dst)
86
91
  return True
87
92
  return False
88
93
 
@@ -96,7 +101,13 @@ class SRDAView:
96
101
  self.model = model
97
102
 
98
103
  def _get_vvar_by_stmt(
99
- self, block_addr: int, block_idx: int | None, stmt_idx: int, op_type: ObservationPointType, predicate
104
+ self,
105
+ block_addr: int,
106
+ block_idx: int | None,
107
+ stmt_idx: int,
108
+ op_type: ObservationPointType,
109
+ predicate: Callable,
110
+ consecutive: bool = False,
100
111
  ):
101
112
  # find the starting block
102
113
  for block in self.model.func_graph:
@@ -107,7 +118,10 @@ class SRDAView:
107
118
  return
108
119
 
109
120
  traversed = set()
110
- queue = [(the_block, stmt_idx if op_type == ObservationPointType.OP_BEFORE else stmt_idx + 1)]
121
+ queue: list[tuple[Block, int | None]] = [
122
+ (the_block, stmt_idx if op_type == ObservationPointType.OP_BEFORE else stmt_idx + 1)
123
+ ]
124
+ predicate_returned_true = False
111
125
  while queue:
112
126
  block, start_stmt_idx = queue.pop(0)
113
127
  traversed.add(block)
@@ -115,7 +129,8 @@ class SRDAView:
115
129
  stmts = block.statements[:start_stmt_idx] if start_stmt_idx is not None else block.statements
116
130
 
117
131
  for stmt in reversed(stmts):
118
- should_break = predicate(stmt)
132
+ r = predicate(stmt)
133
+ should_break = (predicate_returned_true and r is False) if consecutive else r
119
134
  if should_break:
120
135
  break
121
136
  else:
@@ -129,7 +144,7 @@ class SRDAView:
129
144
  self, reg_offset: int, block_addr: int, block_idx: int | None, stmt_idx: int, op_type: ObservationPointType
130
145
  ) -> VirtualVariable | None:
131
146
  reg_offset = get_reg_offset_base(reg_offset, self.model.arch)
132
- vvars = set()
147
+ vvars = []
133
148
  predicater = RegVVarPredicate(reg_offset, vvars, self.model.arch)
134
149
  self._get_vvar_by_stmt(block_addr, block_idx, stmt_idx, op_type, predicater.predicate)
135
150
 
@@ -137,14 +152,14 @@ class SRDAView:
137
152
  # not found - check function arguments
138
153
  for func_arg in self.model.func_args:
139
154
  if isinstance(func_arg, VirtualVariable):
140
- func_arg_category = func_arg.oident[0]
155
+ func_arg_category = func_arg.parameter_category
141
156
  if func_arg_category == VirtualVariableCategory.REGISTER:
142
- func_arg_regoff = func_arg.oident[1]
157
+ func_arg_regoff = func_arg.parameter_reg_offset
143
158
  if func_arg_regoff == reg_offset:
144
- vvars.add(func_arg)
159
+ vvars.append(func_arg)
145
160
 
146
161
  assert len(vvars) <= 1
147
- return next(iter(vvars), None)
162
+ return vvars[0] if vvars else None
148
163
 
149
164
  def get_stack_vvar_by_stmt( # pylint: disable=too-many-positional-arguments
150
165
  self,
@@ -155,21 +170,24 @@ class SRDAView:
155
170
  stmt_idx: int,
156
171
  op_type: ObservationPointType,
157
172
  ) -> VirtualVariable | None:
158
- vvars = set()
173
+ vvars = []
159
174
  predicater = StackVVarPredicate(stack_offset, size, vvars)
160
- self._get_vvar_by_stmt(block_addr, block_idx, stmt_idx, op_type, predicater.predicate)
175
+ self._get_vvar_by_stmt(block_addr, block_idx, stmt_idx, op_type, predicater.predicate, consecutive=True)
161
176
 
162
177
  if not vvars:
163
178
  # not found - check function arguments
164
179
  for func_arg in self.model.func_args:
165
180
  if isinstance(func_arg, VirtualVariable):
166
- func_arg_category = func_arg.oident[0]
181
+ func_arg_category = func_arg.parameter_category
167
182
  if func_arg_category == VirtualVariableCategory.STACK:
168
- func_arg_stackoff = func_arg.oident[1]
183
+ func_arg_stackoff = func_arg.oident[1] # type: ignore
169
184
  if func_arg_stackoff == stack_offset and func_arg.size == size:
170
- vvars.add(func_arg)
171
- assert len(vvars) <= 1
172
- return next(iter(vvars), None)
185
+ vvars.append(func_arg)
186
+ # there might be multiple vvars; we prioritize the one whose size fits the best
187
+ for v in vvars:
188
+ if v.stack_offset == stack_offset and v.size == size:
189
+ return v
190
+ return vvars[0] if vvars else None
173
191
 
174
192
  def _get_vvar_by_insn(self, addr: int, op_type: ObservationPointType, predicate, block_idx: int | None = None):
175
193
  # find the starting block
@@ -202,23 +220,23 @@ class SRDAView:
202
220
  self, reg_offset: int, addr: int, op_type: ObservationPointType, block_idx: int | None = None
203
221
  ) -> VirtualVariable | None:
204
222
  reg_offset = get_reg_offset_base(reg_offset, self.model.arch)
205
- vvars = set()
223
+ vvars = []
206
224
  predicater = RegVVarPredicate(reg_offset, vvars, self.model.arch)
207
225
 
208
226
  self._get_vvar_by_insn(addr, op_type, predicater.predicate, block_idx=block_idx)
209
227
 
210
228
  assert len(vvars) <= 1
211
- return next(iter(vvars), None)
229
+ return vvars[0] if vvars else None
212
230
 
213
231
  def get_stack_vvar_by_insn( # pylint: disable=too-many-positional-arguments
214
232
  self, stack_offset: int, size: int, addr: int, op_type: ObservationPointType, block_idx: int | None = None
215
233
  ) -> VirtualVariable | None:
216
- vvars = set()
234
+ vvars = []
217
235
  predicater = StackVVarPredicate(stack_offset, size, vvars)
218
236
  self._get_vvar_by_insn(addr, op_type, predicater.predicate, block_idx=block_idx)
219
237
 
220
238
  assert len(vvars) <= 1
221
- return next(iter(vvars), None)
239
+ return vvars[0] if vvars else None
222
240
 
223
241
  def get_vvar_value(self, vvar: VirtualVariable) -> Expression | None:
224
242
  if vvar not in self.model.all_vvar_definitions:
@@ -227,7 +245,7 @@ class SRDAView:
227
245
 
228
246
  for block in self.model.func_graph:
229
247
  if block.addr == codeloc.block_addr and block.idx == codeloc.block_idx:
230
- if codeloc.stmt_idx < len(block.statements):
248
+ if codeloc.stmt_idx is not None and codeloc.stmt_idx < len(block.statements):
231
249
  stmt = block.statements[codeloc.stmt_idx]
232
250
  if isinstance(stmt, Assignment) and stmt.dst.likes(vvar):
233
251
  return stmt.src
@@ -333,7 +333,7 @@ class SimEngineVRAIL(
333
333
  tvs = set()
334
334
  for _, vvar in expr.src_and_vvars:
335
335
  if vvar is not None:
336
- r = self._read_from_vvar(vvar, expr=expr, vvar_id=self._mapped_vvarid(vvar.varid))
336
+ r = self._read_from_vvar(vvar, expr=vvar, vvar_id=self._mapped_vvarid(vvar.varid))
337
337
  if r.typevar is not None:
338
338
  tvs.add(r.typevar)
339
339
 
@@ -8,12 +8,13 @@ from archinfo.arch_arm import is_arm_arch
8
8
 
9
9
  from angr.block import Block
10
10
  from angr.errors import SimMemoryMissingError
11
- from angr.calling_conventions import SimRegArg, SimStackArg, default_cc
11
+ from angr.calling_conventions import SimRegArg, SimStackArg, SimTypeFunction, default_cc
12
12
  from angr.engines.vex.claripy.datalayer import value as claripy_value
13
13
  from angr.engines.light import SimEngineNostmtVEX
14
14
  from angr.knowledge_plugins import Function
15
15
  from angr.storage.memory_mixins.paged_memory.pages.multi_values import MultiValues
16
16
  from angr.analyses.typehoon import typevars, typeconsts
17
+ from angr.sim_type import SimTypeBottom
17
18
  from .engine_base import SimEngineVRBase, RichR
18
19
  from .irsb_scanner import VEXIRSBScanner
19
20
 
@@ -222,24 +223,39 @@ class SimEngineVRVEX(
222
223
 
223
224
  def _process_block_end(self, stmt_result, whitelist):
224
225
  # handles block-end calls
226
+ has_call = False
225
227
  current_addr = self.state.block_addr
226
228
  for target_func in self.call_info.get(current_addr, []):
227
229
  self._handle_function_concrete(target_func)
230
+ has_call = True
228
231
 
229
- if self.block.vex.jumpkind == "Ijk_Call":
232
+ if has_call or self.block.vex.jumpkind == "Ijk_Call":
230
233
  # emulates return values from calls
231
234
  cc = None
235
+ proto: SimTypeFunction | None = None
232
236
  for target_func in self.call_info.get(self.state.block_addr, []):
233
237
  if target_func.calling_convention is not None:
234
238
  cc = target_func.calling_convention
239
+ proto = target_func.prototype
235
240
  break
236
241
  if cc is None:
237
242
  cc = default_cc(self.arch.name, platform=self.project.simos.name)(self.arch)
238
- if isinstance(cc.RETURN_VAL, SimRegArg):
239
- reg_offset, reg_size = self.arch.registers[cc.RETURN_VAL.reg_name]
243
+
244
+ if proto is not None and not isinstance(proto.returnty, SimTypeBottom):
245
+ ret_reg = cc.return_val(proto.returnty)
246
+ else:
247
+ ret_reg = cc.RETURN_VAL
248
+ if isinstance(ret_reg, SimRegArg):
249
+ reg_offset, reg_size = self.arch.registers[ret_reg.reg_name]
240
250
  data = self._top(reg_size * self.arch.byte_width)
241
251
  self._assign_to_register(reg_offset, data, reg_size, create_variable=False)
242
252
 
253
+ # handle tail-call optimizations
254
+ if self.block.vex.jumpkind == "Ijk_Boring":
255
+ self.state.ret_val_size = (
256
+ reg_size if self.state.ret_val_size is None else max(self.state.ret_val_size, reg_size)
257
+ )
258
+
243
259
  elif self.block.vex.jumpkind == "Ijk_Ret":
244
260
  # handles return statements
245
261
 
@@ -2,7 +2,8 @@
2
2
  from __future__ import annotations
3
3
  import logging
4
4
  from typing import cast
5
- from collections.abc import Iterable
5
+
6
+ from collections.abc import Iterable, Sequence
6
7
  from collections import defaultdict
7
8
  import contextlib
8
9
 
@@ -82,7 +83,8 @@ class AllocHelper:
82
83
 
83
84
  def size(self):
84
85
  val = self.translate(self.ptr, claripy.BVV(0, len(self.ptr)))
85
- assert val.op == "BVV"
86
+ assert isinstance(val, claripy.ast.Base) and val.op == "BVV"
87
+ assert isinstance(val.args[0], int)
86
88
  return abs(val.args[0])
87
89
 
88
90
  @classmethod
@@ -130,6 +132,7 @@ def refine_locs_with_struct_type(
130
132
  arg_type = SimTypeInt(label=arg_type.label).with_arch(arch)
131
133
 
132
134
  if isinstance(arg_type, (SimTypeReg, SimTypeNum, SimTypeFloat)):
135
+ assert arg_type.size is not None
133
136
  seen_bytes = 0
134
137
  pieces = []
135
138
  while seen_bytes < arg_type.size // arch.byte_width:
@@ -147,20 +150,21 @@ def refine_locs_with_struct_type(
147
150
  piece.is_fp = True
148
151
  return piece
149
152
  if isinstance(arg_type, SimTypeFixedSizeArray):
153
+ assert arg_type.elem_type.size is not None and arg_type.length is not None
150
154
  # TODO explicit stride
151
- locs = [
155
+ locs_list = [
152
156
  refine_locs_with_struct_type(
153
157
  arch, locs, arg_type.elem_type, offset=offset + i * arg_type.elem_type.size // arch.byte_width
154
158
  )
155
159
  for i in range(arg_type.length)
156
160
  ]
157
- return SimArrayArg(locs)
161
+ return SimArrayArg(locs_list)
158
162
  if isinstance(arg_type, SimStruct):
159
- locs = {
163
+ locs_dict = {
160
164
  field: refine_locs_with_struct_type(arch, locs, field_ty, offset=offset + arg_type.offsets[field])
161
165
  for field, field_ty in arg_type.fields.items()
162
166
  }
163
- return SimStructArg(arg_type, locs)
167
+ return SimStructArg(arg_type, locs_dict)
164
168
  if isinstance(arg_type, SimUnion):
165
169
  # Treat a SimUnion as functionality equivalent to its longest member
166
170
  for member in arg_type.members.values():
@@ -574,8 +578,8 @@ class SimCC:
574
578
  # (if applicable) and the arguments. Probably zero.
575
579
  STACKARG_SP_DIFF = 0 # The amount of stack space reserved for the return address
576
580
  CALLER_SAVED_REGS: list[str] = [] # Caller-saved registers
577
- RETURN_ADDR: SimFunctionArgument = None # The location where the return address is stored, as a SimFunctionArgument
578
- RETURN_VAL: SimFunctionArgument = None # The location where the return value is stored, as a SimFunctionArgument
581
+ RETURN_ADDR: SimFunctionArgument # The location where the return address is stored, as a SimFunctionArgument
582
+ RETURN_VAL: SimFunctionArgument # The location where the return value is stored, as a SimFunctionArgument
579
583
  OVERFLOW_RETURN_VAL: SimFunctionArgument | None = (
580
584
  None # The second half of the location where a double-length return value is stored
581
585
  )
@@ -728,6 +732,7 @@ class SimCC:
728
732
  l.warning("Function argument type cannot be BOT. Treating it as a 32-bit int.")
729
733
  arg_type = SimTypeInt().with_arch(self.arch)
730
734
  is_fp = isinstance(arg_type, SimTypeFloat)
735
+ assert arg_type.size is not None
731
736
  size = arg_type.size // self.arch.byte_width
732
737
  try:
733
738
  arg = next(session.fp_iter) if is_fp else next(session.int_iter)
@@ -760,7 +765,7 @@ class SimCC:
760
765
  def is_fp_value(val):
761
766
  return (
762
767
  isinstance(val, (float, claripy.ast.FP))
763
- or (isinstance(val, claripy.ast.Base) and val.op.startswith("fp"))
768
+ or (isinstance(val, claripy.ast.Base) and val.op.startswith("fp")) # type: ignore
764
769
  or (isinstance(val, claripy.ast.Base) and val.op == "Reverse" and val.args[0].op.startswith("fp"))
765
770
  )
766
771
 
@@ -1130,7 +1135,7 @@ class SimCC:
1130
1135
 
1131
1136
  @staticmethod
1132
1137
  def find_cc(
1133
- arch: archinfo.Arch, args: list[SimFunctionArgument], sp_delta: int, platform: str = "Linux"
1138
+ arch: archinfo.Arch, args: Sequence[SimFunctionArgument], sp_delta: int, platform: str = "Linux"
1134
1139
  ) -> SimCC | None:
1135
1140
  """
1136
1141
  Pinpoint the best-fit calling convention and return the corresponding SimCC instance, or None if no fit is
angr/factory.py CHANGED
@@ -7,6 +7,7 @@ from typing import overload, TYPE_CHECKING
7
7
  import archinfo
8
8
  from archinfo.arch_soot import ArchSoot, SootAddressDescriptor
9
9
 
10
+ from .knowledge_plugins.functions import Function
10
11
  from .sim_state import SimState
11
12
  from .calling_conventions import default_cc, SimRegArg, SimStackArg, PointerWrapper, SimCCUnknown
12
13
  from .callable import Callable
@@ -236,7 +237,7 @@ class AngrObjectFactory:
236
237
 
237
238
  def callable(
238
239
  self,
239
- addr,
240
+ addr: int | Function,
240
241
  prototype=None,
241
242
  concrete_only=False,
242
243
  perform_merge=True,
@@ -251,8 +252,9 @@ class AngrObjectFactory:
251
252
  A Callable is a representation of a function in the binary that can be interacted with like a native python
252
253
  function.
253
254
 
254
- :param addr: The address of the function to use
255
- :param prototype: The prototype of the call to use, as a string or a SimTypeFunction
255
+ :param addr: The address of the function to use. If you pass in the function object, we will take
256
+ its addr.
257
+ :param prototype: The prototype of the call to use, as a string or a SimTypeFunction
256
258
  :param concrete_only: Throw an exception if the execution splits into multiple states
257
259
  :param perform_merge: Merge all result states into one at the end (only relevant if concrete_only=False)
258
260
  :param base_state: The state from which to do these runs
@@ -263,6 +265,9 @@ class AngrObjectFactory:
263
265
  python function.
264
266
  :rtype: angr.callable.Callable
265
267
  """
268
+ if isinstance(addr, Function):
269
+ addr = addr.addr
270
+
266
271
  return Callable(
267
272
  self.project,
268
273
  addr=addr,
@@ -12,6 +12,7 @@ from cle.backends.elf.variable import Variable
12
12
 
13
13
  from angr.utils.orderedset import OrderedSet
14
14
  from angr.utils.ail import is_phi_assignment
15
+ from angr.utils.types import unpack_pointer, replace_pointer_pts_to
15
16
  from angr.protos import variables_pb2
16
17
  from angr.serializable import Serializable
17
18
  from angr.sim_variable import SimVariable, SimStackVariable, SimMemoryVariable, SimRegisterVariable
@@ -19,7 +20,6 @@ from angr.sim_type import (
19
20
  TypeRef,
20
21
  SimType,
21
22
  SimStruct,
22
- SimTypePointer,
23
23
  SimTypeBottom,
24
24
  SimTypeChar,
25
25
  SimTypeShort,
@@ -985,10 +985,12 @@ class VariableManagerInternal(Serializable):
985
985
  if name not in self.types:
986
986
  self.types[name] = TypeRef(name, ty).with_arch(self.manager._kb._project.arch)
987
987
  ty = self.types[name]
988
- elif isinstance(ty, SimTypePointer) and isinstance(ty.pts_to, SimStruct):
989
- typeref = self._register_struct_type(ty.pts_to)
990
- ty = ty.copy().with_arch(self.manager._kb._project.arch)
991
- ty.pts_to = typeref
988
+ elif (inner_ty := unpack_pointer(ty, iterative=True)) and isinstance(inner_ty, SimStruct):
989
+ typeref = self._register_struct_type(inner_ty)
990
+ # rebuild the multi-layer pointer type
991
+ replaced_ty = replace_pointer_pts_to(ty, inner_ty, typeref)
992
+ assert replaced_ty is not None
993
+ ty = replaced_ty.with_arch(self.manager._kb._project.arch)
992
994
  elif isinstance(ty, SimStruct):
993
995
  ty = self._register_struct_type(ty, name=name)
994
996
 
angr/simos/simos.py CHANGED
@@ -23,7 +23,9 @@ class SimOS:
23
23
  A class describing OS/arch-level configuration.
24
24
  """
25
25
 
26
- def __init__(self, project: angr.Project, name=None):
26
+ name: str | None
27
+
28
+ def __init__(self, project: angr.Project, name: str | None = None):
27
29
  self.arch = project.arch
28
30
  self.project = project
29
31
  self.name = name
angr/utils/types.py ADDED
@@ -0,0 +1,48 @@
1
+ from __future__ import annotations
2
+
3
+ from angr.sim_type import TypeRef, SimType, SimTypePointer, SimTypeArray, SimTypeFixedSizeArray
4
+
5
+
6
+ def unpack_typeref(ty):
7
+ if isinstance(ty, TypeRef):
8
+ return ty.type
9
+ return ty
10
+
11
+
12
+ def unpack_pointer(ty, iterative: bool = False) -> SimType | None:
13
+ if isinstance(ty, SimTypePointer):
14
+ if iterative:
15
+ inner = unpack_pointer(ty.pts_to, iterative=True)
16
+ return inner if inner is not None else ty.pts_to
17
+ return ty.pts_to
18
+ return None
19
+
20
+
21
+ def replace_pointer_pts_to(ty: SimType, old_pts_to: SimType, new_pts_to: SimType) -> SimTypePointer | None:
22
+ if isinstance(ty, SimTypePointer):
23
+ if ty.pts_to is old_pts_to:
24
+ inner = new_pts_to
25
+ elif isinstance(ty.pts_to, SimTypePointer):
26
+ # recursively replace pts_to inside
27
+ inner = replace_pointer_pts_to(ty.pts_to, old_pts_to, new_pts_to)
28
+ else:
29
+ return None
30
+ return SimTypePointer(inner, label=ty.label, offset=ty.offset)
31
+ return None
32
+
33
+
34
+ def unpack_array(ty) -> SimType | None:
35
+ if isinstance(ty, SimTypeArray):
36
+ return ty.elem_type
37
+ if isinstance(ty, SimTypeFixedSizeArray):
38
+ return ty.elem_type
39
+ return None
40
+
41
+
42
+ def squash_array_reference(ty):
43
+ pointed_to = unpack_pointer(ty)
44
+ if pointed_to:
45
+ array_of = unpack_array(pointed_to)
46
+ if array_of:
47
+ return SimTypePointer(array_of)
48
+ return ty
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: angr
3
- Version: 9.2.140
3
+ Version: 9.2.141
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.140
20
- Requires-Dist: archinfo==9.2.140
19
+ Requires-Dist: ailment==9.2.141
20
+ Requires-Dist: archinfo==9.2.141
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.140
25
- Requires-Dist: cle==9.2.140
24
+ Requires-Dist: claripy==9.2.141
25
+ Requires-Dist: cle==9.2.141
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.140
34
+ Requires-Dist: pyvex==9.2.141
35
35
  Requires-Dist: rich>=13.1.0
36
36
  Requires-Dist: sortedcontainers
37
37
  Requires-Dist: sympy