angr 9.2.163__cp310-abi3-manylinux2014_x86_64.whl → 9.2.164__cp310-abi3-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 (33) hide show
  1. angr/__init__.py +1 -1
  2. angr/ailment/converter_vex.py +1 -1
  3. angr/ailment/expression.py +5 -1
  4. angr/analyses/cfg/cfg_base.py +16 -13
  5. angr/analyses/cfg/cfg_emulated.py +5 -1
  6. angr/analyses/cfg/cfg_fast.py +27 -4
  7. angr/analyses/cfg/indirect_jump_resolvers/arm_elf_fast.py +11 -1
  8. angr/analyses/cfg/indirect_jump_resolvers/const_resolver.py +194 -41
  9. angr/analyses/decompiler/ail_simplifier.py +19 -5
  10. angr/analyses/decompiler/callsite_maker.py +33 -17
  11. angr/analyses/decompiler/graph_region.py +19 -0
  12. angr/analyses/decompiler/optimization_passes/deadblock_remover.py +1 -1
  13. angr/analyses/decompiler/region_identifier.py +22 -1
  14. angr/analyses/decompiler/structuring/phoenix.py +72 -20
  15. angr/analyses/decompiler/structuring/recursive_structurer.py +3 -4
  16. angr/analyses/decompiler/structuring/structurer_nodes.py +3 -0
  17. angr/analyses/decompiler/utils.py +17 -5
  18. angr/analyses/s_reaching_definitions/s_rda_view.py +2 -1
  19. angr/analyses/typehoon/typeconsts.py +3 -1
  20. angr/blade.py +20 -15
  21. angr/engines/icicle.py +7 -2
  22. angr/knowledge_plugins/propagations/propagation_model.py +7 -0
  23. angr/rustylib.abi3.so +0 -0
  24. angr/sim_type.py +16 -1
  25. angr/utils/constants.py +1 -1
  26. angr/utils/graph.py +1 -1
  27. angr/utils/vex.py +11 -0
  28. {angr-9.2.163.dist-info → angr-9.2.164.dist-info}/METADATA +5 -5
  29. {angr-9.2.163.dist-info → angr-9.2.164.dist-info}/RECORD +33 -32
  30. {angr-9.2.163.dist-info → angr-9.2.164.dist-info}/WHEEL +0 -0
  31. {angr-9.2.163.dist-info → angr-9.2.164.dist-info}/entry_points.txt +0 -0
  32. {angr-9.2.163.dist-info → angr-9.2.164.dist-info}/licenses/LICENSE +0 -0
  33. {angr-9.2.163.dist-info → angr-9.2.164.dist-info}/top_level.txt +0 -0
@@ -99,7 +99,7 @@ class RegionIdentifier(Analysis):
99
99
 
100
100
  def _analyze(self):
101
101
  # make a copy of the graph
102
- graph = networkx.DiGraph(self._graph)
102
+ graph = self._pick_one_connected_component(self._graph, as_copy=True)
103
103
 
104
104
  # preprocess: make it a super graph
105
105
  self._make_supergraph(graph)
@@ -113,6 +113,27 @@ class RegionIdentifier(Analysis):
113
113
  # make regions into block address lists
114
114
  self.regions_by_block_addrs = self._make_regions_by_block_addrs()
115
115
 
116
+ def _pick_one_connected_component(self, digraph: networkx.DiGraph, as_copy: bool = False) -> networkx.DiGraph:
117
+ g = networkx.Graph(digraph)
118
+ components = list(networkx.connected_components(g))
119
+ if len(components) <= 1:
120
+ return networkx.DiGraph(digraph) if as_copy else digraph
121
+
122
+ the_component = None
123
+ largest_component = None
124
+ for component in components:
125
+ if largest_component is None or len(component) > len(largest_component):
126
+ largest_component = component
127
+ if any((block.addr, block.idx) == self.entry_node_addr for block in component):
128
+ the_component = component
129
+ break
130
+
131
+ if the_component is None:
132
+ the_component = largest_component
133
+
134
+ assert the_component is not None
135
+ return digraph.subgraph(the_component).to_directed()
136
+
116
137
  @staticmethod
117
138
  def _compute_node_order(graph: networkx.DiGraph) -> dict[Any, tuple[int, int]]:
118
139
  sorted_nodes = GraphUtils.quasi_topological_sort_nodes(graph)
@@ -13,7 +13,7 @@ from angr.ailment.block import Block
13
13
  from angr.ailment.statement import Statement, ConditionalJump, Jump, Label, Return
14
14
  from angr.ailment.expression import Const, UnaryOp, MultiStatementExpression, BinaryOp
15
15
 
16
- from angr.utils.graph import GraphUtils
16
+ from angr.utils.graph import GraphUtils, Dominators, compute_dominance_frontier
17
17
  from angr.utils.ail import is_phi_assignment, is_head_controlled_loop_block
18
18
  from angr.knowledge_plugins.cfg import IndirectJump, IndirectJumpType
19
19
  from angr.utils.constants import SWITCH_MISSING_DEFAULT_NODE_ADDR
@@ -669,7 +669,7 @@ class PhoenixStructurer(StructurerBase):
669
669
  continue_node = loop_head
670
670
 
671
671
  is_while, result_while = self._refine_cyclic_is_while_loop(graph, fullgraph, loop_head, head_succs)
672
- is_dowhile, result_dowhile = self._refine_cyclic_is_dowhile_loop(graph, fullgraph, loop_head, head_succs)
672
+ is_dowhile, result_dowhile = self._refine_cyclic_is_dowhile_loop(graph, fullgraph, loop_head)
673
673
 
674
674
  continue_edges: list[tuple[BaseNode, BaseNode]] = []
675
675
  outgoing_edges: list = []
@@ -702,22 +702,12 @@ class PhoenixStructurer(StructurerBase):
702
702
 
703
703
  if loop_type is None:
704
704
  # natural loop. select *any* exit edge to determine the successor
705
- # well actually, to maintain determinism, we select the successor with the highest address
706
- successor_candidates = set()
707
- for node in networkx.descendants(graph, loop_head):
708
- for succ in fullgraph.successors(node):
709
- if succ not in graph:
710
- successor_candidates.add(succ)
711
- if loop_head is succ:
712
- continue_edges.append((node, succ))
713
- if successor_candidates:
714
- successor_candidates = sorted(successor_candidates, key=lambda x: x.addr)
715
- successor = successor_candidates[0]
716
- # virtualize all other edges
717
- for succ in successor_candidates:
718
- for pred in fullgraph.predecessors(succ):
719
- if pred in graph:
720
- outgoing_edges.append((pred, succ))
705
+ is_natural, result_natural = self._refine_cyclic_make_natural_loop(graph, fullgraph, loop_head)
706
+ if not is_natural:
707
+ # cannot refine this loop
708
+ return False
709
+ assert result_natural is not None
710
+ continue_edges, outgoing_edges, successor = result_natural
721
711
 
722
712
  if outgoing_edges:
723
713
  # if there is a single successor, we convert all out-going edges into breaks;
@@ -963,8 +953,8 @@ class PhoenixStructurer(StructurerBase):
963
953
  return True, (continue_edges, outgoing_edges, loop_head, successor)
964
954
  return False, None
965
955
 
966
- def _refine_cyclic_is_dowhile_loop( # pylint:disable=unused-argument
967
- self, graph, fullgraph, loop_head, head_succs
956
+ def _refine_cyclic_is_dowhile_loop(
957
+ self, graph, fullgraph, loop_head
968
958
  ) -> tuple[bool, tuple[list, list, BaseNode, BaseNode] | None]:
969
959
  # check if there is an out-going edge from the loop tail
970
960
  head_preds = list(fullgraph.predecessors(loop_head))
@@ -996,6 +986,64 @@ class PhoenixStructurer(StructurerBase):
996
986
  return True, (continue_edges, outgoing_edges, continue_node, successor)
997
987
  return False, None
998
988
 
989
+ def _refine_cyclic_make_natural_loop(
990
+ self, graph, fullgraph, loop_head
991
+ ) -> tuple[bool, tuple[list, list, Any] | None]:
992
+ continue_edges = []
993
+ outgoing_edges = []
994
+
995
+ # find dominance frontier
996
+ doms = Dominators(fullgraph, self._region.head)
997
+ dom_frontiers = compute_dominance_frontier(fullgraph, doms.dom)
998
+
999
+ if loop_head not in dom_frontiers:
1000
+ return False, None
1001
+ dom_frontier = dom_frontiers[loop_head]
1002
+
1003
+ # now this is a little complex
1004
+ dom_frontier = {node for node in dom_frontier if node is not loop_head}
1005
+ if len(dom_frontier) == 0:
1006
+ # the dominance frontier is empty (the loop head dominates all nodes in the full graph). however, this does
1007
+ # not mean that the loop head must dominate all the nodes, because we only have a limited view of the full
1008
+ # graph (e.g., some predecessors of the successor may not be in this full graph). as such, successors are
1009
+ # the ones that are in the fullgraph but not in the graph.
1010
+ successor_candidates = set()
1011
+ for node in networkx.descendants(graph, loop_head):
1012
+ for succ in fullgraph.successors(node):
1013
+ if succ not in graph:
1014
+ successor_candidates.add(succ)
1015
+ if loop_head is succ:
1016
+ continue_edges.append((node, succ))
1017
+
1018
+ else:
1019
+ # this loop has a single successor
1020
+ successor_candidates = dom_frontier
1021
+ # traverse the loop body to find all continue edges
1022
+ tmp_graph = networkx.DiGraph(graph)
1023
+ tmp_graph.remove_nodes_from(successor_candidates)
1024
+ for node in networkx.descendants(tmp_graph, loop_head):
1025
+ if tmp_graph.has_edge(node, loop_head):
1026
+ continue_edges.append((node, loop_head))
1027
+
1028
+ if len(successor_candidates) == 0:
1029
+ successor = None
1030
+ else:
1031
+ # one or multiple successors; try to pick a successor in graph, and prioritize the one with the lowest
1032
+ # address
1033
+ successor_candidates_in_graph = {nn for nn in successor_candidates if nn in graph}
1034
+ if successor_candidates_in_graph:
1035
+ # pick the one with the lowest address
1036
+ successor = next(iter(sorted(successor_candidates_in_graph, key=lambda x: x.addr)))
1037
+ else:
1038
+ successor = next(iter(sorted(successor_candidates, key=lambda x: x.addr)))
1039
+ # mark all edges as outgoing edges so they will be virtualized if they don't lead to the successor
1040
+ for node in successor_candidates:
1041
+ for pred in fullgraph.predecessors(node):
1042
+ if networkx.has_path(doms.dom, loop_head, pred):
1043
+ outgoing_edges.append((pred, node))
1044
+
1045
+ return True, (continue_edges, outgoing_edges, successor)
1046
+
999
1047
  def _analyze_acyclic(self) -> bool:
1000
1048
  # match against known schemas
1001
1049
  l.debug("Matching acyclic schemas for region %r.", self._region)
@@ -1219,6 +1267,10 @@ class PhoenixStructurer(StructurerBase):
1219
1267
  node_a = next(iter(nn for nn in graph.nodes if nn.addr == target), None)
1220
1268
  if node_a is None:
1221
1269
  return False
1270
+ if node_a is self._region.head:
1271
+ # avoid structuring if node_a is the region head; this means the current node is a duplicated switch-case
1272
+ # head (instead of the original one), which is not something we want to structure
1273
+ return False
1222
1274
 
1223
1275
  # the default case
1224
1276
  node_b_addr = next(iter(t for t in successor_addrs if t != target), None)
@@ -3,8 +3,6 @@ import itertools
3
3
  from typing import TYPE_CHECKING
4
4
  import logging
5
5
 
6
- import networkx
7
-
8
6
  from angr.analyses import Analysis, register_analysis
9
7
  from angr.analyses.decompiler.condition_processor import ConditionProcessor
10
8
  from angr.analyses.decompiler.graph_region import GraphRegion
@@ -12,6 +10,7 @@ from angr.analyses.decompiler.jumptable_entry_condition_rewriter import JumpTabl
12
10
  from angr.analyses.decompiler.empty_node_remover import EmptyNodeRemover
13
11
  from angr.analyses.decompiler.jump_target_collector import JumpTargetCollector
14
12
  from angr.analyses.decompiler.redundant_label_remover import RedundantLabelRemover
13
+ from angr.utils.graph import GraphUtils
15
14
  from .structurer_nodes import BaseNode
16
15
  from .structurer_base import StructurerBase
17
16
  from .dream import DreamStructurer
@@ -61,7 +60,7 @@ class RecursiveStructurer(Analysis):
61
60
  current_region = stack[-1]
62
61
 
63
62
  has_region = False
64
- for node in networkx.dfs_postorder_nodes(current_region.graph, current_region.head):
63
+ for node in GraphUtils.dfs_postorder_nodes_deterministic(current_region.graph, current_region.head):
65
64
  subnodes = []
66
65
  if type(node) is GraphRegion:
67
66
  if node.cyclic:
@@ -177,7 +176,7 @@ class RecursiveStructurer(Analysis):
177
176
  for node in region.graph.nodes:
178
177
  if not isinstance(node, BaseNode):
179
178
  continue
180
- if node.addr == self.function.addr:
179
+ if self.function is not None and node.addr == self.function.addr:
181
180
  return node
182
181
  if min_node is None or (min_node.addr is not None and node.addr is not None and min_node.addr < node.addr):
183
182
  min_node = node
@@ -392,6 +392,9 @@ class IncompleteSwitchCaseNode(BaseNode):
392
392
  self.head = head
393
393
  self.cases: list = cases
394
394
 
395
+ def __repr__(self):
396
+ return f"<IncompleteSwitchCase {self.addr:#x} with {len(self.cases)} cases>"
397
+
395
398
 
396
399
  #
397
400
  # The following classes are custom AIL statements (not nodes, unfortunately)
@@ -158,10 +158,14 @@ def switch_extract_cmp_bounds(
158
158
  return None
159
159
 
160
160
  # TODO: Add more operations
161
- if isinstance(last_stmt.condition, ailment.Expr.BinaryOp) and last_stmt.condition.op == "CmpLE":
161
+ if isinstance(last_stmt.condition, ailment.Expr.BinaryOp) and last_stmt.condition.op in {"CmpLE", "CmpLT"}:
162
162
  if not isinstance(last_stmt.condition.operands[1], ailment.Expr.Const):
163
163
  return None
164
- cmp_ub = last_stmt.condition.operands[1].value
164
+ cmp_ub = (
165
+ last_stmt.condition.operands[1].value
166
+ if last_stmt.condition.op == "CmpLE"
167
+ else last_stmt.condition.operands[1].value - 1
168
+ )
165
169
  cmp_lb = 0
166
170
  cmp = last_stmt.condition.operands[0]
167
171
  if (
@@ -250,6 +254,10 @@ def switch_extract_bitwiseand_jumptable_info(last_stmt: ailment.Stmt.Jump) -> tu
250
254
  size=4, endness=Iend_LE) + 0x4530e4<32>))
251
255
  )
252
256
 
257
+ Another example:
258
+
259
+ Load(addr=(((vvar_9{reg 36} & 0x3<32>) * 0x4<32>) + 0x42cd28<32>), size=4, endness=Iend_LE)
260
+
253
261
  :param last_stmt: The last statement of the switch-case header node.
254
262
  :return: A tuple of (index expression, lower bound, upper bound), or None
255
263
  """
@@ -269,16 +277,20 @@ def switch_extract_bitwiseand_jumptable_info(last_stmt: ailment.Stmt.Jump) -> tu
269
277
  continue
270
278
  if isinstance(target, ailment.Expr.BinaryOp) and target.op == "Add":
271
279
  if isinstance(target.operands[0], ailment.Expr.Const) and isinstance(target.operands[1], ailment.Expr.Load):
272
- jump_addr_offset = target.operands[0]
280
+ jump_addr_offset = target.operands[0].value
273
281
  jumptable_load_addr = target.operands[1].addr
274
282
  break
275
283
  if isinstance(target.operands[1], ailment.Expr.Const) and isinstance(target.operands[0], ailment.Expr.Load):
276
- jump_addr_offset = target.operands[1]
284
+ jump_addr_offset = target.operands[1].value
277
285
  jumptable_load_addr = target.operands[0].addr
278
286
  break
279
287
  return None
280
288
  if isinstance(target, ailment.Expr.Const):
281
289
  return None
290
+ if isinstance(target, ailment.Expr.Load):
291
+ jumptable_load_addr = target.addr
292
+ jump_addr_offset = 0
293
+ break
282
294
  break
283
295
 
284
296
  if jump_addr_offset is None or jumptable_load_addr is None:
@@ -655,7 +667,7 @@ def _flatten_structured_node(packed_node: SequenceNode | MultiNode) -> list[ailm
655
667
 
656
668
  def _find_node_in_graph(node: ailment.Block, graph: networkx.DiGraph) -> ailment.Block | None:
657
669
  for bb in graph:
658
- if bb.addr == node.addr and bb.idx == node.idx:
670
+ if isinstance(bb, ailment.Block) and bb.addr == node.addr and bb.idx == node.idx:
659
671
  return bb
660
672
  return None
661
673
 
@@ -37,7 +37,8 @@ class RegVVarPredicate:
37
37
  if cc is not None:
38
38
  reg_list = cc.CALLER_SAVED_REGS
39
39
  if isinstance(cc.RETURN_VAL, SimRegArg):
40
- reg_list.append(cc.RETURN_VAL.reg_name)
40
+ # do not update reg_list directly, otherwise you may update cc.CALLER_SAVED_REGS!
41
+ reg_list = [*reg_list, cc.RETURN_VAL.reg_name]
41
42
  return {self.arch.registers[reg_name][0] for reg_name in reg_list}
42
43
  log.warning("Cannot determine registers that are clobbered by call statement %r.", stmt)
43
44
  return set()
@@ -245,7 +245,9 @@ class Struct(TypeConstant):
245
245
  if not self.fields:
246
246
  return 0
247
247
  max_field_off = max(self.fields.keys())
248
- return max_field_off + self.fields[max_field_off].size
248
+ return max_field_off + (
249
+ self.fields[max_field_off].size if not isinstance(self.fields[max_field_off], BottomType) else 1
250
+ )
249
251
 
250
252
  @memoize
251
253
  def __repr__(self, memo=None):
angr/blade.py CHANGED
@@ -40,6 +40,7 @@ class Blade:
40
40
  cross_insn_opt=False,
41
41
  max_predecessors: int = 10,
42
42
  include_imarks: bool = True,
43
+ control_dependence: bool = True,
43
44
  ):
44
45
  """
45
46
  :param graph: A graph representing the control flow graph. Note that it does not take
@@ -56,6 +57,8 @@ class Blade:
56
57
  :param stop_at_calls: Limit slicing within a single function. Do not proceed when encounters a call
57
58
  edge.
58
59
  :param include_imarks: Should IMarks (instruction boundaries) be included in the slice.
60
+ :param control_dependence: Whether to consider control dependencies. If True, the temps controlling
61
+ conditional exits will be added to the tainting set.
59
62
  :return: None
60
63
  """
61
64
 
@@ -70,6 +73,7 @@ class Blade:
70
73
  self._cross_insn_opt = cross_insn_opt
71
74
  self._max_predecessors = max_predecessors
72
75
  self._include_imarks = include_imarks
76
+ self._control_dependence = control_dependence
73
77
 
74
78
  self._slice = networkx.DiGraph()
75
79
 
@@ -347,7 +351,7 @@ class Blade:
347
351
  except (SimTranslationError, BadJumpkindNotification):
348
352
  return
349
353
 
350
- if exit_stmt_idx is None or exit_stmt_idx == DEFAULT_STATEMENT:
354
+ if self._control_dependence and (exit_stmt_idx is None or exit_stmt_idx == DEFAULT_STATEMENT):
351
355
  # Initialize the temps set with whatever in the `next` attribute of this irsb
352
356
  next_expr = self._get_irsb(run).next
353
357
  if type(next_expr) is pyvex.IRExpr.RdTmp:
@@ -357,20 +361,21 @@ class Blade:
357
361
  self._inslice_callback(DEFAULT_STATEMENT, None, {"irsb_addr": irsb_addr, "prev": prev})
358
362
  prev = irsb_addr, DEFAULT_STATEMENT
359
363
 
360
- # if there are conditional exits, we *always* add them into the slice (so if they should not be taken, we do not
361
- # lose the condition)
362
- for stmt_idx_, s_ in enumerate(self._get_irsb(run).statements):
363
- if type(s_) is not pyvex.IRStmt.Exit:
364
- continue
365
- if s_.jumpkind != "Ijk_Boring":
366
- continue
367
-
368
- if type(s_.guard) is pyvex.IRExpr.RdTmp:
369
- temps.add(s_.guard.tmp)
370
-
371
- # Put it in our slice
372
- self._inslice_callback(stmt_idx_, s_, {"irsb_addr": irsb_addr, "prev": prev})
373
- prev = (irsb_addr, stmt_idx_)
364
+ if self._control_dependence:
365
+ # if there are conditional exits, we *always* add them into the slice (so if they should not be taken, we
366
+ # do not lose the condition)
367
+ for stmt_idx_, s_ in enumerate(self._get_irsb(run).statements):
368
+ if type(s_) is not pyvex.IRStmt.Exit:
369
+ continue
370
+ if s_.jumpkind != "Ijk_Boring":
371
+ continue
372
+
373
+ if type(s_.guard) is pyvex.IRExpr.RdTmp:
374
+ temps.add(s_.guard.tmp)
375
+
376
+ # Put it in our slice
377
+ self._inslice_callback(stmt_idx_, s_, {"irsb_addr": irsb_addr, "prev": prev})
378
+ prev = (irsb_addr, stmt_idx_)
374
379
 
375
380
  infodict = {"irsb_addr": irsb_addr, "prev": prev, "has_statement": False}
376
381
 
angr/engines/icicle.py CHANGED
@@ -123,7 +123,7 @@ class IcicleEngine(ConcreteEngine):
123
123
  if proj is None:
124
124
  raise ValueError("IcicleEngine requires a project to be set")
125
125
 
126
- emu = Icicle(icicle_arch, PROCESSORS_DIR)
126
+ emu = Icicle(icicle_arch, PROCESSORS_DIR, True)
127
127
 
128
128
  copied_registers = set()
129
129
 
@@ -194,7 +194,8 @@ class IcicleEngine(ConcreteEngine):
194
194
  addr = page_num * state.memory.page_size
195
195
  state.memory.store(addr, emu.mem_read(addr, state.memory.page_size))
196
196
 
197
- # 3. Set history.jumpkind
197
+ # 3. Set history
198
+ # 3.1 history.jumpkind
198
199
  exc = emu.exception_code
199
200
  if status == VmExit.UnhandledException:
200
201
  if exc in (
@@ -216,6 +217,10 @@ class IcicleEngine(ConcreteEngine):
216
217
  else:
217
218
  state.history.jumpkind = "Ijk_Boring"
218
219
 
220
+ # 3.2 history.recent_bbl_addrs
221
+ # Skip the last block, because it will be added by Successors
222
+ state.history.recent_bbl_addrs.extend([b[0] for b in emu.recent_blocks][:-1])
223
+
219
224
  # 4. Set history.recent_instruction_count
220
225
  state.history.recent_instruction_count = emu.cpu_icount - translation_data.initial_cpu_icount
221
226
 
@@ -19,6 +19,7 @@ class PropagationModel(Serializable):
19
19
  "_initial_state",
20
20
  "block_initial_reg_values",
21
21
  "equivalence",
22
+ "function_block_count",
22
23
  "graph_visitor",
23
24
  "input_states",
24
25
  "key",
@@ -37,6 +38,7 @@ class PropagationModel(Serializable):
37
38
  equivalence: set | None = None,
38
39
  function: Function | None = None,
39
40
  input_states: dict | None = None,
41
+ function_block_count: int | None = None,
40
42
  ):
41
43
  self.key = prop_key
42
44
  self.node_iterations = node_iterations if node_iterations is not None else defaultdict(int)
@@ -49,6 +51,11 @@ class PropagationModel(Serializable):
49
51
  self.graph_visitor = None
50
52
  self._initial_state = None
51
53
  self._function = function
54
+ self.function_block_count = (
55
+ function_block_count
56
+ if function_block_count is not None
57
+ else len(function.block_addrs_set) if function is not None else None
58
+ )
52
59
 
53
60
  def downsize(self):
54
61
  self.node_iterations = None
angr/rustylib.abi3.so CHANGED
Binary file
angr/sim_type.py CHANGED
@@ -1789,6 +1789,7 @@ class SimCppClass(SimStruct):
1789
1789
  vtable_ptrs=None,
1790
1790
  pack: bool = False,
1791
1791
  align=None,
1792
+ size: int | None = None,
1792
1793
  ):
1793
1794
  super().__init__(members or {}, name=name, pack=pack, align=align)
1794
1795
  self.unique_name = unique_name
@@ -1797,6 +1798,10 @@ class SimCppClass(SimStruct):
1797
1798
  # this should also be added to the fields once we know the offsets of the members of this object
1798
1799
  self.vtable_ptrs = [] if vtable_ptrs is None else vtable_ptrs
1799
1800
 
1801
+ # we can force the size (in bits) of a class because sometimes the class can be opaque and we don't know its
1802
+ # layout
1803
+ self._size = size
1804
+
1800
1805
  @property
1801
1806
  def members(self):
1802
1807
  return self.fields
@@ -1805,6 +1810,12 @@ class SimCppClass(SimStruct):
1805
1810
  def members(self, value):
1806
1811
  self.fields = value
1807
1812
 
1813
+ @property
1814
+ def size(self):
1815
+ if self._size is not None:
1816
+ return self._size
1817
+ return super().size
1818
+
1808
1819
  def __repr__(self):
1809
1820
  return f"class {self.name}" if not self.name.startswith("class") else self.name
1810
1821
 
@@ -1848,6 +1859,7 @@ class SimCppClass(SimStruct):
1848
1859
  vtable_ptrs=self.vtable_ptrs,
1849
1860
  pack=self._pack,
1850
1861
  align=self._align,
1862
+ size=self._size,
1851
1863
  )
1852
1864
  out._arch = arch
1853
1865
  self._arch_memo[arch.name] = out
@@ -1877,6 +1889,7 @@ class SimCppClass(SimStruct):
1877
1889
  align=self._align,
1878
1890
  function_members=self.function_members,
1879
1891
  vtable_ptrs=self.vtable_ptrs,
1892
+ size=self._size,
1880
1893
  )
1881
1894
 
1882
1895
 
@@ -2029,6 +2042,8 @@ BASIC_TYPES: dict[str, SimType] = {
2029
2042
  "long long int": SimTypeLongLong(True),
2030
2043
  "signed long long int": SimTypeLongLong(True),
2031
2044
  "unsigned long long int": SimTypeLongLong(False),
2045
+ "__int32": SimTypeInt(True),
2046
+ "__int64": SimTypeLongLong(True),
2032
2047
  "__int128": SimTypeNum(128, True),
2033
2048
  "unsigned __int128": SimTypeNum(128, False),
2034
2049
  "__int256": SimTypeNum(256, True),
@@ -3563,7 +3578,7 @@ def _cpp_decl_to_type(
3563
3578
  t = ALL_TYPES[lbl]
3564
3579
  elif opaque_classes is True:
3565
3580
  # create a class without knowing the internal members
3566
- t = SimCppClass(unique_name=lbl, name=lbl, members={})
3581
+ t = SimCppClass(unique_name=lbl, name=lbl, members={}, size=32)
3567
3582
  else:
3568
3583
  raise TypeError(f'Unknown type "{lbl}"')
3569
3584
 
angr/utils/constants.py CHANGED
@@ -6,4 +6,4 @@ MAX_POINTSTO_BITS = -1330 * 8
6
6
 
7
7
 
8
8
  def is_alignment_mask(n):
9
- return n in {0xFFFFFFFFFFFFFFE0, 0xFFFFFFFFFFFFFFF0, 0xFFFFFFF0, 0xFFFFFFFC, 0xFFFFFFF8}
9
+ return n in {0xFFFFFFFFFFFFFFE0, 0xFFFFFFFFFFFFFFF0, 0xFFFFFFE0, 0xFFFFFFF0, 0xFFFFFFFC, 0xFFFFFFF8}
angr/utils/graph.py CHANGED
@@ -76,7 +76,7 @@ def to_acyclic_graph(
76
76
  for src, dst in graph.edges():
77
77
  src_order = node_order[src]
78
78
  dst_order = node_order[dst]
79
- if src_order > dst_order:
79
+ if src_order >= dst_order:
80
80
  # this is a back edge, we need to remove it
81
81
  edges_to_remove.append((src, dst))
82
82
 
angr/utils/vex.py ADDED
@@ -0,0 +1,11 @@
1
+ from __future__ import annotations
2
+
3
+ from pyvex import IRSB
4
+ from pyvex.stmt import WrTmp
5
+
6
+
7
+ def get_tmp_def_stmt(vex_block: IRSB, tmp_idx: int) -> int | None:
8
+ for i, stmt in enumerate(vex_block.statements):
9
+ if isinstance(stmt, WrTmp) and stmt.tmp == tmp_idx:
10
+ return i
11
+ return None
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: angr
3
- Version: 9.2.163
3
+ Version: 9.2.164
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
  License: BSD-2-Clause
6
6
  Project-URL: Homepage, https://angr.io/
@@ -16,12 +16,12 @@ Description-Content-Type: text/markdown
16
16
  License-File: LICENSE
17
17
  Requires-Dist: cxxheaderparser
18
18
  Requires-Dist: GitPython
19
- Requires-Dist: archinfo==9.2.163
19
+ Requires-Dist: archinfo==9.2.164
20
20
  Requires-Dist: cachetools
21
21
  Requires-Dist: capstone==5.0.3
22
22
  Requires-Dist: cffi>=1.14.0
23
- Requires-Dist: claripy==9.2.163
24
- Requires-Dist: cle==9.2.163
23
+ Requires-Dist: claripy==9.2.164
24
+ Requires-Dist: cle==9.2.164
25
25
  Requires-Dist: mulpyplexer
26
26
  Requires-Dist: networkx!=2.8.1,>=2.0
27
27
  Requires-Dist: protobuf>=5.28.2
@@ -30,7 +30,7 @@ Requires-Dist: pycparser>=2.18
30
30
  Requires-Dist: pydemumble
31
31
  Requires-Dist: pyformlang
32
32
  Requires-Dist: pypcode<4.0,>=3.2.1
33
- Requires-Dist: pyvex==9.2.163
33
+ Requires-Dist: pyvex==9.2.164
34
34
  Requires-Dist: rich>=13.1.0
35
35
  Requires-Dist: sortedcontainers
36
36
  Requires-Dist: sympy