angr 9.2.127__py3-none-macosx_11_0_arm64.whl → 9.2.129__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.

angr/__init__.py CHANGED
@@ -2,7 +2,7 @@
2
2
  # pylint: disable=wrong-import-position
3
3
  from __future__ import annotations
4
4
 
5
- __version__ = "9.2.127"
5
+ __version__ = "9.2.129"
6
6
 
7
7
  if bytes is str:
8
8
  raise Exception(
@@ -113,6 +113,8 @@ class Clinic(Analysis):
113
113
  vvar_id_start: int = 0,
114
114
  optimization_scratch: dict[str, Any] | None = None,
115
115
  desired_variables: set[str] | None = None,
116
+ force_loop_single_exit: bool = True,
117
+ complete_successors: bool = False,
116
118
  ):
117
119
  if not func.normalized and mode == ClinicMode.DECOMPILE:
118
120
  raise ValueError("Decompilation must work on normalized function graphs.")
@@ -157,6 +159,8 @@ class Clinic(Analysis):
157
159
  self._inlined_counts = {} if inlined_counts is None else inlined_counts
158
160
  self._inlining_parents = inlining_parents or ()
159
161
  self._desired_variables = desired_variables
162
+ self._force_loop_single_exit = force_loop_single_exit
163
+ self._complete_successors = complete_successors
160
164
 
161
165
  self._register_save_areas_removed: bool = False
162
166
 
@@ -333,6 +337,7 @@ class Clinic(Analysis):
333
337
  optimization_passes=[StackCanarySimplifier],
334
338
  sp_shift=self._max_stack_depth,
335
339
  vvar_id_start=self.vvar_id_start,
340
+ fail_fast=self._fail_fast,
336
341
  )
337
342
  self.vvar_id_start = callee_clinic.vvar_id_start + 1
338
343
  self._max_stack_depth = callee_clinic._max_stack_depth
@@ -787,7 +792,7 @@ class Clinic(Analysis):
787
792
 
788
793
  # case 2: the callee is a SimProcedure
789
794
  if target_func.is_simprocedure:
790
- cc = self.project.analyses.CallingConvention(target_func)
795
+ cc = self.project.analyses.CallingConvention(target_func, fail_fast=self._fail_fast)
791
796
  if cc.cc is not None and cc.prototype is not None:
792
797
  target_func.calling_convention = cc.cc
793
798
  target_func.prototype = cc.prototype
@@ -795,7 +800,7 @@ class Clinic(Analysis):
795
800
 
796
801
  # case 3: the callee is a PLT function
797
802
  if target_func.is_plt:
798
- cc = self.project.analyses.CallingConvention(target_func)
803
+ cc = self.project.analyses.CallingConvention(target_func, fail_fast=self._fail_fast)
799
804
  if cc.cc is not None and cc.prototype is not None:
800
805
  target_func.calling_convention = cc.cc
801
806
  target_func.prototype = cc.prototype
@@ -834,6 +839,7 @@ class Clinic(Analysis):
834
839
  callsite_block_addr=callsite.addr,
835
840
  callsite_insn_addr=callsite_ins_addr,
836
841
  func_graph=func_graph,
842
+ fail_fast=self._fail_fast,
837
843
  )
838
844
 
839
845
  if cc.cc is not None and cc.prototype is not None:
@@ -864,6 +870,7 @@ class Clinic(Analysis):
864
870
  # finally, recover the calling convention of the current function
865
871
  if self.function.prototype is None or self.function.calling_convention is None:
866
872
  self.project.analyses.CompleteCallingConventions(
873
+ fail_fast=self._fail_fast,
867
874
  recover_variables=True,
868
875
  prioritize_func_addrs=[self.function.addr],
869
876
  skip_other_funcs=True,
@@ -896,6 +903,7 @@ class Clinic(Analysis):
896
903
  spt = self.project.analyses.StackPointerTracker(
897
904
  self.function,
898
905
  regs,
906
+ fail_fast=self._fail_fast,
899
907
  track_memory=self._sp_tracker_track_memory,
900
908
  cross_insn_opt=False,
901
909
  initial_reg_values=initial_reg_values,
@@ -1130,6 +1138,7 @@ class Clinic(Analysis):
1130
1138
  simp = self.project.analyses.AILBlockSimplifier(
1131
1139
  ail_block,
1132
1140
  self.function.addr,
1141
+ fail_fast=self._fail_fast,
1133
1142
  remove_dead_memdefs=remove_dead_memdefs,
1134
1143
  stack_pointer_tracker=stack_pointer_tracker,
1135
1144
  peephole_optimizations=self.peephole_optimizations,
@@ -1201,6 +1210,7 @@ class Clinic(Analysis):
1201
1210
 
1202
1211
  simp = self.project.analyses.AILSimplifier(
1203
1212
  self.function,
1213
+ fail_fast=self._fail_fast,
1204
1214
  func_graph=ail_graph,
1205
1215
  remove_dead_memdefs=remove_dead_memdefs,
1206
1216
  unify_variables=unify_variables,
@@ -1259,6 +1269,8 @@ class Clinic(Analysis):
1259
1269
  vvar_id_start=self.vvar_id_start,
1260
1270
  entry_node_addr=self.entry_node_addr,
1261
1271
  scratch=self.optimization_scratch,
1272
+ force_loop_single_exit=self._force_loop_single_exit,
1273
+ complete_successors=self._complete_successors,
1262
1274
  **kwargs,
1263
1275
  )
1264
1276
  if a.out_graph:
@@ -1342,6 +1354,7 @@ class Clinic(Analysis):
1342
1354
  ssailification = self.project.analyses.Ssailification(
1343
1355
  self.function,
1344
1356
  ail_graph,
1357
+ fail_fast=self._fail_fast,
1345
1358
  entry=next(iter(bb for bb in ail_graph if (bb.addr, bb.idx) == self.entry_node_addr)),
1346
1359
  ail_manager=self._ail_manager,
1347
1360
  ssa_stackvars=False,
@@ -1355,6 +1368,7 @@ class Clinic(Analysis):
1355
1368
  ssailification = self.project.analyses.Ssailification(
1356
1369
  self.function,
1357
1370
  ail_graph,
1371
+ fail_fast=self._fail_fast,
1358
1372
  entry=next(iter(bb for bb in ail_graph if (bb.addr, bb.idx) == self.entry_node_addr)),
1359
1373
  ail_manager=self._ail_manager,
1360
1374
  ssa_tmps=True,
@@ -1369,6 +1383,7 @@ class Clinic(Analysis):
1369
1383
  dephication = self.project.analyses.GraphDephicationVVarMapping(
1370
1384
  self.function,
1371
1385
  ail_graph,
1386
+ fail_fast=self._fail_fast,
1372
1387
  entry=next(iter(bb for bb in ail_graph if (bb.addr, bb.idx) == self.entry_node_addr)),
1373
1388
  vvar_id_start=self.vvar_id_start,
1374
1389
  )
@@ -1421,6 +1436,7 @@ class Clinic(Analysis):
1421
1436
  rd = self.project.analyses.SReachingDefinitions(
1422
1437
  subject=self.function,
1423
1438
  func_graph=ail_graph,
1439
+ fail_fast=self._fail_fast,
1424
1440
  # use_callee_saved_regs_at_return=not self._register_save_areas_removed, FIXME
1425
1441
  )
1426
1442
 
@@ -1431,6 +1447,7 @@ class Clinic(Analysis):
1431
1447
  def _handler(block):
1432
1448
  csm = self.project.analyses.AILCallSiteMaker(
1433
1449
  block,
1450
+ fail_fast=self._fail_fast,
1434
1451
  reaching_definitions=rd,
1435
1452
  stack_pointer_tracker=stack_pointer_tracker,
1436
1453
  ail_manager=self._ail_manager,
@@ -1444,6 +1461,7 @@ class Clinic(Analysis):
1444
1461
  simp = self.project.analyses.AILBlockSimplifier(
1445
1462
  ail_block,
1446
1463
  self.function.addr,
1464
+ fail_fast=self._fail_fast,
1447
1465
  stack_pointer_tracker=stack_pointer_tracker,
1448
1466
  peephole_optimizations=self.peephole_optimizations,
1449
1467
  )
@@ -1527,6 +1545,7 @@ class Clinic(Analysis):
1527
1545
  tmp_kb.functions = self.kb.functions
1528
1546
  vr = self.project.analyses.VariableRecoveryFast(
1529
1547
  self.function, # pylint:disable=unused-variable
1548
+ fail_fast=self._fail_fast,
1530
1549
  func_graph=ail_graph,
1531
1550
  kb=tmp_kb,
1532
1551
  track_sp=False,
@@ -1559,6 +1578,7 @@ class Clinic(Analysis):
1559
1578
  vr.type_constraints,
1560
1579
  vr.func_typevar,
1561
1580
  kb=tmp_kb,
1581
+ fail_fast=self._fail_fast,
1562
1582
  var_mapping=vr.var_to_typevars,
1563
1583
  must_struct=must_struct,
1564
1584
  ground_truth=groundtruth,
@@ -235,6 +235,7 @@ class Decompiler(Analysis):
235
235
  clinic = self.project.analyses.Clinic(
236
236
  self.func,
237
237
  kb=self.kb,
238
+ fail_fast=self._fail_fast,
238
239
  variable_kb=variable_kb,
239
240
  reset_variable_names=reset_variable_names,
240
241
  optimization_passes=self._optimization_passes,
@@ -248,6 +249,8 @@ class Decompiler(Analysis):
248
249
  inline_functions=self._inline_functions,
249
250
  desired_variables=self._desired_variables,
250
251
  optimization_scratch=self._optimization_scratch,
252
+ force_loop_single_exit=self._force_loop_single_exit,
253
+ complete_successors=self._complete_successors,
251
254
  **self.options_to_params(self.options_by_class["clinic"]),
252
255
  )
253
256
  else:
@@ -308,7 +311,7 @@ class Decompiler(Analysis):
308
311
  self._update_progress(75.0, text="Structuring code")
309
312
 
310
313
  # structure it
311
- rs = self.project.analyses[RecursiveStructurer].prep(kb=self.kb)(
314
+ rs = self.project.analyses[RecursiveStructurer].prep(kb=self.kb, fail_fast=self._fail_fast)(
312
315
  ri.region,
313
316
  cond_proc=cond_proc,
314
317
  func=self.func,
@@ -321,6 +324,7 @@ class Decompiler(Analysis):
321
324
  self.func,
322
325
  rs.result,
323
326
  kb=self.kb,
327
+ fail_fast=self._fail_fast,
324
328
  variable_kb=clinic.variable_kb,
325
329
  **self.options_to_params(self.options_by_class["region_simplifier"]),
326
330
  )
@@ -345,6 +349,7 @@ class Decompiler(Analysis):
345
349
  flavor=self._flavor,
346
350
  func_args=clinic.arg_list,
347
351
  kb=self.kb,
352
+ fail_fast=self._fail_fast,
348
353
  variable_kb=clinic.variable_kb,
349
354
  expr_comments=old_codegen.expr_comments if old_codegen is not None else None,
350
355
  stmt_comments=old_codegen.stmt_comments if old_codegen is not None else None,
@@ -365,7 +370,7 @@ class Decompiler(Analysis):
365
370
  self.kb.decompilations[(self.func.addr, self._flavor)] = self.cache
366
371
 
367
372
  def _recover_regions(self, graph: networkx.DiGraph, condition_processor, update_graph: bool = True):
368
- return self.project.analyses[RegionIdentifier].prep(kb=self.kb)(
373
+ return self.project.analyses[RegionIdentifier].prep(kb=self.kb, fail_fast=self._fail_fast)(
369
374
  self.func,
370
375
  graph=graph,
371
376
  cond_proc=condition_processor,
@@ -418,6 +423,8 @@ class Decompiler(Analysis):
418
423
  reaching_definitions=reaching_definitions,
419
424
  entry_node_addr=self.clinic.entry_node_addr,
420
425
  scratch=self._optimization_scratch,
426
+ force_loop_single_exit=self._force_loop_single_exit,
427
+ complete_successors=self._complete_successors,
421
428
  **kwargs,
422
429
  )
423
430
 
@@ -478,6 +485,8 @@ class Decompiler(Analysis):
478
485
  vvar_id_start=self.vvar_id_start,
479
486
  entry_node_addr=self.clinic.entry_node_addr,
480
487
  scratch=self._optimization_scratch,
488
+ force_loop_single_exit=self._force_loop_single_exit,
489
+ complete_successors=self._complete_successors,
481
490
  **kwargs,
482
491
  )
483
492
 
@@ -561,6 +570,7 @@ class Decompiler(Analysis):
561
570
  type_constraints,
562
571
  func_typevar,
563
572
  kb=var_kb,
573
+ fail_fast=self._fail_fast,
564
574
  var_mapping=var_to_typevar,
565
575
  must_struct=must_struct,
566
576
  ground_truth=groundtruth,
@@ -628,11 +638,15 @@ class Decompiler(Analysis):
628
638
  )
629
639
 
630
640
  def _transform_graph_from_ssa(self, ail_graph: networkx.DiGraph) -> networkx.DiGraph:
631
- dephication = self.project.analyses.GraphDephication(self.func, ail_graph, rewrite=True)
641
+ dephication = self.project.analyses.GraphDephication(
642
+ self.func, ail_graph, rewrite=True, kb=self.kb, fail_fast=self._fail_fast
643
+ )
632
644
  return dephication.output
633
645
 
634
646
  def _transform_seqnode_from_ssa(self, seq_node: SequenceNode) -> SequenceNode:
635
- dephication = self.project.analyses.SeqNodeDephication(self.func, seq_node, rewrite=True)
647
+ dephication = self.project.analyses.SeqNodeDephication(
648
+ self.func, seq_node, rewrite=True, kb=self.kb, fail_fast=self._fail_fast
649
+ )
636
650
  return dephication.output
637
651
 
638
652
  @staticmethod
@@ -382,12 +382,9 @@ class GraphRegion:
382
382
  if src in graph:
383
383
  graph.add_edge(src, dst)
384
384
  else:
385
- # it may happen that the dst node does not exist in sub_graph
386
- # fallback
387
- l.info("Node dst is not found in sub_graph. Enter the fall back logic.")
388
- for src in sub_graph.nodes:
389
- if sub_graph.out_degree[src] == 0:
390
- graph.add_edge(src, dst)
385
+ # it may happen that the dst node no longer exists in sub_graph or its successors
386
+ # this is because we have deemed that the dst node is no longer a valid successor for sub_graph
387
+ pass
391
388
 
392
389
  graph.add_nodes_from(sub_graph_nodes)
393
390
  graph.add_edges_from(sub_graph_edges)
@@ -0,0 +1,32 @@
1
+ # pylint:disable=unused-argument
2
+ from __future__ import annotations
3
+ from collections import defaultdict
4
+
5
+ import ailment
6
+
7
+ from .sequence_walker import SequenceWalker
8
+
9
+
10
+ class LabelCollector:
11
+ """
12
+ Collect all labels.
13
+ """
14
+
15
+ def __init__(self, node):
16
+ self.root = node
17
+ self.labels: defaultdict[str, list[tuple[int, int | None]]] = defaultdict(list)
18
+
19
+ handlers = {
20
+ ailment.Block: self._handle_Block,
21
+ }
22
+ self._walker = SequenceWalker(handlers=handlers)
23
+ self._walker.walk(self.root)
24
+
25
+ #
26
+ # Handlers
27
+ #
28
+
29
+ def _handle_Block(self, block: ailment.Block, **kwargs):
30
+ for stmt in block.statements:
31
+ if isinstance(stmt, ailment.Stmt.Label):
32
+ self.labels[stmt.name].append((block.addr, block.idx))
@@ -32,6 +32,7 @@ from .inlined_string_transformation_simplifier import InlinedStringTransformatio
32
32
  from .const_prop_reverter import ConstPropOptReverter
33
33
  from .call_stmt_rewriter import CallStatementRewriter
34
34
  from .duplication_reverter import DuplicationReverter
35
+ from .switch_reused_entry_rewriter import SwitchReusedEntryRewriter
35
36
 
36
37
  if TYPE_CHECKING:
37
38
  from angr.analyses.decompiler.presets import DecompilationPreset
@@ -55,6 +56,7 @@ ALL_OPTIMIZATION_PASSES = [
55
56
  ReturnDuplicatorHigh,
56
57
  DeadblockRemover,
57
58
  SwitchDefaultCaseDuplicator,
59
+ SwitchReusedEntryRewriter,
58
60
  ConstPropOptReverter,
59
61
  DuplicationReverter,
60
62
  LoweredSwitchSimplifier,
@@ -129,6 +131,7 @@ __all__ = (
129
131
  "CrossJumpReverter",
130
132
  "CodeMotionOptimization",
131
133
  "SwitchDefaultCaseDuplicator",
134
+ "SwitchReusedEntryRewriter",
132
135
  "DeadblockRemover",
133
136
  "InlinedStringTransformationSimplifier",
134
137
  "ConstPropOptReverter",
@@ -118,6 +118,8 @@ class OptimizationPass(BaseOptimizationPass):
118
118
  vvar_id_start=None,
119
119
  entry_node_addr=None,
120
120
  scratch: dict[str, Any] | None = None,
121
+ force_loop_single_exit: bool = True,
122
+ complete_successors: bool = False,
121
123
  **kwargs,
122
124
  ):
123
125
  super().__init__(func)
@@ -134,6 +136,8 @@ class OptimizationPass(BaseOptimizationPass):
134
136
  self.entry_node_addr: tuple[int, int | None] = (
135
137
  entry_node_addr if entry_node_addr is not None else (func.addr, None)
136
138
  )
139
+ self._force_loop_single_exit = force_loop_single_exit
140
+ self._complete_successors = complete_successors
137
141
 
138
142
  # output
139
143
  self.out_graph: networkx.DiGraph | None = None
@@ -255,9 +259,8 @@ class OptimizationPass(BaseOptimizationPass):
255
259
  graph=graph,
256
260
  cond_proc=condition_processor or ConditionProcessor(self.project.arch),
257
261
  update_graph=update_graph,
258
- # TODO: find a way to pass Phoenix/DREAM options here (see decompiler.py for correct use)
259
- force_loop_single_exit=True,
260
- complete_successors=False,
262
+ force_loop_single_exit=self._force_loop_single_exit,
263
+ complete_successors=self._complete_successors,
261
264
  entry_node_addr=self.entry_node_addr,
262
265
  )
263
266
 
@@ -1,10 +1,15 @@
1
1
  # pylint:disable=too-many-boolean-expressions
2
2
  from __future__ import annotations
3
3
  from itertools import count
4
+ from collections import defaultdict
4
5
  import logging
5
6
 
6
7
  import networkx
7
8
 
9
+ from ailment.block import Block
10
+ from ailment.statement import Jump
11
+ from ailment.expression import Const
12
+
8
13
  from angr.knowledge_plugins.cfg import IndirectJumpType
9
14
  from .optimization_pass import OptimizationPass, OptimizationPassStage
10
15
 
@@ -29,17 +34,19 @@ class SwitchDefaultCaseDuplicator(OptimizationPass):
29
34
 
30
35
  ARCHES = None
31
36
  PLATFORMS = None
32
- STAGE = OptimizationPassStage.BEFORE_REGION_IDENTIFICATION
37
+ STAGE = OptimizationPassStage.AFTER_AIL_GRAPH_CREATION
33
38
  NAME = "Duplicate default-case nodes to undo default-case node reuse caused by compiler code deduplication"
34
39
  DESCRIPTION = __doc__.strip()
35
40
 
36
41
  def __init__(self, func, **kwargs):
37
42
  super().__init__(func, **kwargs)
38
43
 
39
- self.node_idx = count(start=0)
44
+ self.node_idx = count(start=self._scratch.get("node_idx", 0))
40
45
 
41
46
  self.analyze()
42
47
 
48
+ self._scratch["node_idx"] = next(self.node_idx)
49
+
43
50
  def _check(self):
44
51
  jumptables = self.kb.cfgs.get_most_accurate().jump_tables
45
52
  switch_jump_block_addrs = {
@@ -77,8 +84,39 @@ class SwitchDefaultCaseDuplicator(OptimizationPass):
77
84
  out_graph = None
78
85
  duplicated_default_addrs: set[int] = set()
79
86
 
87
+ default_addr_count = defaultdict(int)
88
+ goto_rewritten_default_addrs = set()
89
+ for _, _, default_addr in default_case_node_addrs:
90
+ default_addr_count[default_addr] += 1
91
+ for default_addr, cnt in default_addr_count.items():
92
+ if cnt > 1:
93
+ # rewrite all of them into gotos
94
+ default_node = self._get_block(default_addr)
95
+ for switch_head_addr in sorted((sa for sa, _, da in default_case_node_addrs if da == default_addr)):
96
+ switch_head_node = self._get_block(switch_head_addr)
97
+ goto_stmt = Jump(
98
+ None,
99
+ Const(None, None, default_addr, self.project.arch.bits, ins_addr=default_addr),
100
+ target_idx=None, # I'm assuming the ID of the default node is None here
101
+ ins_addr=default_addr,
102
+ )
103
+ goto_node = Block(
104
+ default_addr,
105
+ 0,
106
+ statements=[goto_stmt],
107
+ idx=next(self.node_idx),
108
+ )
109
+
110
+ if out_graph is None:
111
+ out_graph = self._graph
112
+ out_graph.remove_edge(switch_head_node, default_node)
113
+ out_graph.add_edge(switch_head_node, goto_node)
114
+ out_graph.add_edge(goto_node, default_node)
115
+
116
+ goto_rewritten_default_addrs.add(default_addr)
117
+
80
118
  for switch_head_addr, jump_node_addr, default_addr in default_case_node_addrs:
81
- if default_addr in duplicated_default_addrs:
119
+ if default_addr in duplicated_default_addrs or default_addr in goto_rewritten_default_addrs:
82
120
  continue
83
121
 
84
122
  default_case_node = self._func.get_node(default_addr)
@@ -0,0 +1,102 @@
1
+ # pylint:disable=too-many-boolean-expressions
2
+ from __future__ import annotations
3
+ from itertools import count
4
+ import logging
5
+
6
+ from ailment.block import Block
7
+ from ailment.statement import Jump
8
+ from ailment.expression import Const
9
+
10
+ from angr.knowledge_plugins.cfg import IndirectJumpType
11
+
12
+ from .optimization_pass import OptimizationPass, OptimizationPassStage
13
+
14
+
15
+ _l = logging.getLogger(name=__name__)
16
+
17
+
18
+ class SwitchReusedEntryRewriter(OptimizationPass):
19
+ """
20
+ For each switch-case construct (identified by jump tables), rewrite the entry into a goto block when we detect
21
+ situations where an entry node is reused by edges in switch-case constructs that are not the current one. This code
22
+ reuse is usually caused by compiler code deduplication.
23
+ """
24
+
25
+ ARCHES = None
26
+ PLATFORMS = None
27
+ STAGE = OptimizationPassStage.AFTER_AIL_GRAPH_CREATION
28
+ NAME = "Rewrite switch-case entry nodes with multiple predecessors into goto statements."
29
+ DESCRIPTION = __doc__.strip()
30
+
31
+ def __init__(self, func, **kwargs):
32
+ super().__init__(func, **kwargs)
33
+
34
+ self.node_idx = count(start=self._scratch.get("node_idx", 0))
35
+
36
+ self.analyze()
37
+
38
+ self._scratch["node_idx"] = next(self.node_idx)
39
+
40
+ def _check(self):
41
+ jumptables = self.kb.cfgs.get_most_accurate().jump_tables
42
+ switch_jump_block_addrs = {
43
+ jumptable.addr
44
+ for jumptable in jumptables.values()
45
+ if jumptable.type
46
+ in {IndirectJumpType.Jumptable_AddressComputed, IndirectJumpType.Jumptable_AddressLoadedFromMemory}
47
+ }
48
+ jump_node_addrs = self._func.block_addrs_set.intersection(switch_jump_block_addrs)
49
+ if not jump_node_addrs:
50
+ return False, None
51
+
52
+ # ensure each jump table entry node has only one predecessor
53
+ reused_entries: dict[Block, set[Block]] = {}
54
+ for jumptable in jumptables.values():
55
+ for entry_addr in sorted(set(jumptable.jumptable_entries)):
56
+ entry_nodes = self._get_blocks(entry_addr)
57
+ for entry_node in entry_nodes:
58
+ preds = list(self._graph.predecessors(entry_node))
59
+ if len(preds) > 1:
60
+ non_current_jumptable_preds = [pred for pred in preds if pred.addr != jumptable.addr]
61
+ if any(p.addr in switch_jump_block_addrs for p in non_current_jumptable_preds):
62
+ reused_entries[entry_node] = {
63
+ pred for pred in preds if pred.addr in switch_jump_block_addrs
64
+ }
65
+
66
+ if not reused_entries:
67
+ return False, None
68
+ cache = {"reused_entries": reused_entries}
69
+ return True, cache
70
+
71
+ def _analyze(self, cache=None):
72
+
73
+ reused_entries: dict[Block, set[Block]] = cache["reused_entries"]
74
+ out_graph = None
75
+
76
+ for entry_node, pred_nodes in reused_entries.items():
77
+ # we assign the entry node to the predecessor with the lowest address
78
+ sorted_pred_nodes = sorted(pred_nodes, key=lambda x: (x.addr, x.idx))
79
+
80
+ for head_node in sorted_pred_nodes[1:]:
81
+
82
+ # create the new goto node
83
+ goto_stmt = Jump(
84
+ None,
85
+ Const(None, None, entry_node.addr, self.project.arch.bits, ins_addr=entry_node.addr),
86
+ target_idx=entry_node.idx,
87
+ ins_addr=entry_node.addr,
88
+ )
89
+ goto_node = Block(
90
+ entry_node.addr,
91
+ 0,
92
+ statements=[goto_stmt],
93
+ idx=next(self.node_idx),
94
+ )
95
+
96
+ if out_graph is None:
97
+ out_graph = self._graph
98
+ out_graph.remove_edge(head_node, entry_node)
99
+ out_graph.add_edge(head_node, goto_node)
100
+ # we are virtualizing these edges, so we don't need to add the edge from goto_node to the entry_node
101
+
102
+ self.out_graph = out_graph
@@ -9,6 +9,7 @@ from angr.analyses.decompiler.optimization_passes import (
9
9
  RetAddrSaveSimplifier,
10
10
  X86GccGetPcSimplifier,
11
11
  CallStatementRewriter,
12
+ SwitchReusedEntryRewriter,
12
13
  )
13
14
 
14
15
 
@@ -23,6 +24,7 @@ preset_basic = DecompilationPreset(
23
24
  RetAddrSaveSimplifier,
24
25
  X86GccGetPcSimplifier,
25
26
  CallStatementRewriter,
27
+ SwitchReusedEntryRewriter,
26
28
  ],
27
29
  )
28
30
 
@@ -21,6 +21,7 @@ from angr.analyses.decompiler.optimization_passes import (
21
21
  CallStatementRewriter,
22
22
  MultiSimplifier,
23
23
  DeadblockRemover,
24
+ SwitchReusedEntryRewriter,
24
25
  )
25
26
 
26
27
 
@@ -41,6 +42,7 @@ preset_fast = DecompilationPreset(
41
42
  ReturnDuplicatorHigh,
42
43
  DeadblockRemover,
43
44
  SwitchDefaultCaseDuplicator,
45
+ SwitchReusedEntryRewriter,
44
46
  LoweredSwitchSimplifier,
45
47
  ReturnDuplicatorLow,
46
48
  ReturnDeduplicator,
@@ -26,6 +26,7 @@ from angr.analyses.decompiler.optimization_passes import (
26
26
  FlipBooleanCmp,
27
27
  InlinedStringTransformationSimplifier,
28
28
  CallStatementRewriter,
29
+ SwitchReusedEntryRewriter,
29
30
  )
30
31
 
31
32
 
@@ -57,6 +58,7 @@ preset_full = DecompilationPreset(
57
58
  FlipBooleanCmp,
58
59
  InlinedStringTransformationSimplifier,
59
60
  CallStatementRewriter,
61
+ SwitchReusedEntryRewriter,
60
62
  ],
61
63
  )
62
64
 
@@ -532,7 +532,7 @@ class RegionIdentifier(Analysis):
532
532
  )
533
533
  if len(region.successors) > 1 and self._force_loop_single_exit:
534
534
  # multi-successor region. refinement is required
535
- self._refine_loop_successors(region, graph)
535
+ self._refine_loop_successors_to_guarded_successors(region, graph)
536
536
 
537
537
  # if the head node is in the graph and it's not the head of the graph, we will need to update the head node
538
538
  # address.
@@ -543,10 +543,10 @@ class RegionIdentifier(Analysis):
543
543
 
544
544
  return region
545
545
 
546
- def _refine_loop_successors(self, region, graph: networkx.DiGraph):
546
+ def _refine_loop_successors_to_guarded_successors(self, region, graph: networkx.DiGraph):
547
547
  """
548
- If there are multiple successors of a loop, convert them into conditional gotos. Eventually there should be
549
- only one loop successor.
548
+ If there are multiple successors of a loop, convert them into guarded successors. Eventually there should be
549
+ only one loop successor. This is used in the DREAM structuring algorithm.
550
550
 
551
551
  :param GraphRegion region: The cyclic region to refine.
552
552
  :param networkx.DiGraph graph: The current graph that is being structured.
@@ -565,11 +565,11 @@ class RegionIdentifier(Analysis):
565
565
  cond = ConditionNode(
566
566
  condnode_addr,
567
567
  None,
568
- self.cond_proc.reaching_conditions[successors[0]],
569
- successors[0],
570
- false_node=None,
568
+ self.cond_proc.reaching_conditions[successors[1]],
569
+ successors[1],
570
+ false_node=successors[0],
571
571
  )
572
- for succ in successors[1:]:
572
+ for succ in successors[2:]:
573
573
  cond = ConditionNode(
574
574
  condnode_addr,
575
575
  None,
@@ -127,6 +127,8 @@ class PhoenixStructurer(StructurerBase):
127
127
  @staticmethod
128
128
  def _assert_graph_ok(g, msg: str) -> None:
129
129
  if _DEBUG:
130
+ if g is None:
131
+ return
130
132
  assert (
131
133
  len(list(networkx.connected_components(networkx.Graph(g)))) <= 1
132
134
  ), f"{msg}: More than one connected component. Please report this."
@@ -229,7 +231,8 @@ class PhoenixStructurer(StructurerBase):
229
231
  )
230
232
  l.debug("... matching cyclic schemas: %s at %r", matched, node)
231
233
  any_matches |= matched
232
- self._assert_graph_ok(self._region.graph, "Removed incorrect edges")
234
+ if matched:
235
+ self._assert_graph_ok(self._region.graph, "Removed incorrect edges")
233
236
  return any_matches
234
237
 
235
238
  def _match_cyclic_schemas(self, node, head, graph, full_graph) -> bool:
@@ -559,9 +562,12 @@ class PhoenixStructurer(StructurerBase):
559
562
  seq_node = SequenceNode(node.addr, nodes=[node])
560
563
  seen_nodes = set()
561
564
  while True:
562
- succs = list(full_graph.successors(next_node))
565
+ succs = list(graph.successors(next_node))
563
566
  if len(succs) != 1:
564
567
  return False, None
568
+ if full_graph.out_degree[next_node] > 1:
569
+ # all successors in the full graph should have been refined away at this point
570
+ return False, None
565
571
  next_node = succs[0]
566
572
 
567
573
  if next_node is node:
@@ -600,6 +606,8 @@ class PhoenixStructurer(StructurerBase):
600
606
  refined = self._refine_cyclic_core(head)
601
607
  l.debug("... refined: %s", refined)
602
608
  if refined:
609
+ self._assert_graph_ok(self._region.graph, "Refinement went wrong")
610
+ # cyclic refinement may create dangling nodes in the full graph
603
611
  return True
604
612
  return False
605
613
 
@@ -1020,7 +1028,9 @@ class PhoenixStructurer(StructurerBase):
1020
1028
  any_matches |= matched
1021
1029
  if matched:
1022
1030
  break
1023
- self._assert_graph_ok(self._region.graph, "Removed incorrect edges")
1031
+
1032
+ self._assert_graph_ok(self._region.graph, "Removed incorrect edges")
1033
+
1024
1034
  return any_matches
1025
1035
 
1026
1036
  # switch cases
@@ -1094,7 +1104,7 @@ class PhoenixStructurer(StructurerBase):
1094
1104
  to_remove,
1095
1105
  graph,
1096
1106
  full_graph,
1097
- can_bail=True,
1107
+ bail_on_nonhead_outedges=True,
1098
1108
  )
1099
1109
  if not r:
1100
1110
  return False
@@ -1161,18 +1171,18 @@ class PhoenixStructurer(StructurerBase):
1161
1171
  node_pred = next(iter(graph.predecessors(node)))
1162
1172
 
1163
1173
  case_nodes = list(graph.successors(node_a))
1164
- case_node_successors = set()
1165
- for case_node in case_nodes:
1166
- if case_node is node_pred:
1167
- continue
1168
- if case_node.addr in jump_table.jumptable_entries:
1169
- succs = set(graph.successors(case_node))
1170
- case_node_successors |= {succ for succ in succs if succ.addr not in jump_table.jumptable_entries}
1171
- if len(case_node_successors) > 1:
1172
- return False
1173
1174
 
1174
- # we will definitely be able to structure this into a full switch-case. remove node from switch_case_known_heads
1175
- self.switch_case_known_heads.remove(node)
1175
+ # case 1: the common successor happens to be directly reachable from node_a (usually as a result of compiler
1176
+ # optimization)
1177
+ # example: touch_touch_no_switch.o:main
1178
+ r = self.switch_case_entry_node_has_common_successor_case_1(graph, jump_table, case_nodes, node_pred)
1179
+
1180
+ # case 2: the common successor is not directly reachable from node_a. this is a more common case.
1181
+ if not r:
1182
+ r |= self.switch_case_entry_node_has_common_successor_case_2(graph, jump_table, case_nodes, node_pred)
1183
+
1184
+ if not r:
1185
+ return False
1176
1186
 
1177
1187
  # un-structure IncompleteSwitchCaseNode
1178
1188
  if isinstance(node_a, SequenceNode) and node_a.nodes and isinstance(node_a.nodes[0], IncompleteSwitchCaseNode):
@@ -1186,8 +1196,10 @@ class PhoenixStructurer(StructurerBase):
1186
1196
  # update node_a
1187
1197
  node_a = next(iter(nn for nn in graph.nodes if nn.addr == target))
1188
1198
 
1199
+ case_and_entry_addrs = self._find_case_and_entry_addrs(node_a, graph, cmp_lb, jump_table)
1200
+
1189
1201
  cases, node_default, to_remove = self._switch_build_cases(
1190
- {cmp_lb + i: entry_addr for (i, entry_addr) in enumerate(jump_table.jumptable_entries)},
1202
+ case_and_entry_addrs,
1191
1203
  node,
1192
1204
  node_a,
1193
1205
  node_b_addr,
@@ -1203,7 +1215,7 @@ class PhoenixStructurer(StructurerBase):
1203
1215
  to_remove.add(node_default)
1204
1216
 
1205
1217
  to_remove.add(node_a) # add node_a
1206
- self._make_switch_cases_core(
1218
+ r = self._make_switch_cases_core(
1207
1219
  node,
1208
1220
  cmp_expr,
1209
1221
  cases,
@@ -1215,7 +1227,11 @@ class PhoenixStructurer(StructurerBase):
1215
1227
  full_graph,
1216
1228
  node_a=node_a,
1217
1229
  )
1230
+ if not r:
1231
+ return False
1218
1232
 
1233
+ # fully structured into a switch-case. remove node from switch_case_known_heads
1234
+ self.switch_case_known_heads.remove(node)
1219
1235
  self._switch_handle_gotos(cases, node_default, switch_end_addr)
1220
1236
 
1221
1237
  return True
@@ -1253,8 +1269,10 @@ class PhoenixStructurer(StructurerBase):
1253
1269
  else:
1254
1270
  return False
1255
1271
 
1272
+ case_and_entry_addrs = self._find_case_and_entry_addrs(node, graph, cmp_lb, jump_table)
1273
+
1256
1274
  cases, node_default, to_remove = self._switch_build_cases(
1257
- {cmp_lb + i: entry_addr for (i, entry_addr) in enumerate(jump_table.jumptable_entries)},
1275
+ case_and_entry_addrs,
1258
1276
  node,
1259
1277
  node,
1260
1278
  default_addr,
@@ -1265,12 +1283,10 @@ class PhoenixStructurer(StructurerBase):
1265
1283
  # there must be a default case
1266
1284
  return False
1267
1285
 
1268
- self._make_switch_cases_core(
1286
+ return self._make_switch_cases_core(
1269
1287
  node, cmp_expr, cases, default_addr, node_default, node.addr, to_remove, graph, full_graph
1270
1288
  )
1271
1289
 
1272
- return True
1273
-
1274
1290
  def _match_acyclic_incomplete_switch_cases(
1275
1291
  self, node, graph: networkx.DiGraph, full_graph: networkx.DiGraph, jump_tables: dict
1276
1292
  ) -> bool:
@@ -1322,14 +1338,9 @@ class PhoenixStructurer(StructurerBase):
1322
1338
  # and a case node (addr.b). The addr.a node is a successor to the head node while the addr.b node is a
1323
1339
  # successor to node_a
1324
1340
  default_node_candidates = [nn for nn in graph.nodes if nn.addr == node_b_addr]
1325
- if len(default_node_candidates) == 0:
1326
- node_default: BaseNode | None = None
1327
- elif len(default_node_candidates) == 1:
1328
- node_default: BaseNode | None = default_node_candidates[0]
1329
- else:
1330
- node_default: BaseNode | None = next(
1331
- iter(nn for nn in default_node_candidates if graph.has_edge(head_node, nn)), None
1332
- )
1341
+ node_default: BaseNode | None = next(
1342
+ iter(nn for nn in default_node_candidates if graph.has_edge(head_node, nn)), None
1343
+ )
1333
1344
 
1334
1345
  if node_default is not None and not isinstance(node_default, SequenceNode):
1335
1346
  # make the default node a SequenceNode so that we can insert Break and Continue nodes into it later
@@ -1432,7 +1443,7 @@ class PhoenixStructurer(StructurerBase):
1432
1443
  graph: networkx.DiGraph,
1433
1444
  full_graph: networkx.DiGraph,
1434
1445
  node_a=None,
1435
- can_bail=False,
1446
+ bail_on_nonhead_outedges: bool = False,
1436
1447
  ) -> bool:
1437
1448
  scnode = SwitchCaseNode(cmp_expr, cases, node_default, addr=addr)
1438
1449
 
@@ -1454,14 +1465,24 @@ class PhoenixStructurer(StructurerBase):
1454
1465
  if dst not in to_remove:
1455
1466
  out_edges.append((nn, dst))
1456
1467
 
1457
- if can_bail:
1468
+ if bail_on_nonhead_outedges:
1458
1469
  nonhead_out_nodes = {edge[1] for edge in out_edges if edge[1] is not head}
1459
1470
  if len(nonhead_out_nodes) > 1:
1460
1471
  # not ready to be structured yet - do it later
1461
1472
  return False
1462
1473
 
1474
+ # check if structuring will create any dangling nodes
1475
+ for case_node in to_remove:
1476
+ if case_node is not node_default and case_node is not node_a and case_node is not head:
1477
+ for succ in graph.successors(case_node):
1478
+ if succ is not case_node and succ is not head and graph.in_degree[succ] == 1:
1479
+ # succ will be dangling - not ready to be structured yet - do it later
1480
+ return False
1481
+
1463
1482
  if node_default is not None:
1464
1483
  # the head no longer goes to the default case
1484
+ if graph.has_edge(head, node_default):
1485
+ pass
1465
1486
  graph.remove_edge(head, node_default)
1466
1487
  full_graph.remove_edge(head, node_default)
1467
1488
  else:
@@ -1505,6 +1526,13 @@ class PhoenixStructurer(StructurerBase):
1505
1526
  if full_graph.has_edge(head, out_dst):
1506
1527
  full_graph.remove_edge(head, out_dst)
1507
1528
 
1529
+ # fix full_graph if needed: remove successors that are no longer needed
1530
+ for out_src, out_dst in out_edges[1:]:
1531
+ if out_dst in full_graph and out_dst not in graph and full_graph.in_degree[out_dst] == 0:
1532
+ full_graph.remove_node(out_dst)
1533
+ if out_dst in self._region.successors:
1534
+ self._region.successors.remove(out_dst)
1535
+
1508
1536
  # remove the last statement (conditional jump) in the head node
1509
1537
  remove_last_statement(head)
1510
1538
 
@@ -1514,6 +1542,25 @@ class PhoenixStructurer(StructurerBase):
1514
1542
 
1515
1543
  return True
1516
1544
 
1545
+ @staticmethod
1546
+ def _find_case_and_entry_addrs(
1547
+ jump_head, graph, cmp_lb: int, jump_table
1548
+ ) -> dict[int, int | tuple[int, int | None]]:
1549
+ case_and_entry_addrs = {}
1550
+
1551
+ addr_to_entry_nodes = defaultdict(list)
1552
+ for succ in graph.successors(jump_head):
1553
+ addr_to_entry_nodes[succ.addr].append(succ)
1554
+
1555
+ for i, entry_addr in enumerate(jump_table.jumptable_entries):
1556
+ case_no = cmp_lb + i
1557
+ if entry_addr in addr_to_entry_nodes and isinstance(addr_to_entry_nodes[entry_addr][0], (MultiNode, Block)):
1558
+ case_and_entry_addrs[case_no] = entry_addr, addr_to_entry_nodes[entry_addr][0].idx
1559
+ else:
1560
+ case_and_entry_addrs[case_no] = entry_addr
1561
+
1562
+ return case_and_entry_addrs
1563
+
1517
1564
  # other acyclic schemas
1518
1565
 
1519
1566
  def _match_acyclic_sequence(self, graph, full_graph, start_node) -> bool:
@@ -1982,6 +2029,11 @@ class PhoenixStructurer(StructurerBase):
1982
2029
 
1983
2030
  if full_graph.in_degree[left] > 1 and full_graph.in_degree[right] == 1:
1984
2031
  left, right = right, left
2032
+
2033
+ # ensure left and right nodes are not the head of a switch-case construct
2034
+ if left in self.switch_case_known_heads or right in self.switch_case_known_heads:
2035
+ return None
2036
+
1985
2037
  if (
1986
2038
  self._is_sequential_statement_block(left)
1987
2039
  and full_graph.in_degree[left] == 1
@@ -2024,6 +2076,11 @@ class PhoenixStructurer(StructurerBase):
2024
2076
 
2025
2077
  if full_graph.in_degree[left] == 1 and full_graph.in_degree[right] == 2:
2026
2078
  left, right = right, left
2079
+
2080
+ # ensure left and right nodes are not the head of a switch-case construct
2081
+ if left in self.switch_case_known_heads or right in self.switch_case_known_heads:
2082
+ return None
2083
+
2027
2084
  if (
2028
2085
  self._is_sequential_statement_block(right)
2029
2086
  and full_graph.in_degree[left] == 2
@@ -2060,6 +2117,11 @@ class PhoenixStructurer(StructurerBase):
2060
2117
 
2061
2118
  if full_graph.in_degree[left] > 1 and full_graph.in_degree[successor] == 1:
2062
2119
  left, successor = successor, left
2120
+
2121
+ # ensure left and successor nodes are not the head of a switch-case construct
2122
+ if left in self.switch_case_known_heads or successor in self.switch_case_known_heads:
2123
+ return None
2124
+
2063
2125
  if (
2064
2126
  self._is_sequential_statement_block(left)
2065
2127
  and full_graph.in_degree[left] == 1
@@ -2103,6 +2165,11 @@ class PhoenixStructurer(StructurerBase):
2103
2165
 
2104
2166
  if full_graph.in_degree[left] > 1 and full_graph.in_degree[else_node] == 1:
2105
2167
  left, else_node = else_node, left
2168
+
2169
+ # ensure left and else nodes are not the head of a switch-case construct
2170
+ if left in self.switch_case_known_heads or else_node in self.switch_case_known_heads:
2171
+ return None
2172
+
2106
2173
  if (
2107
2174
  self._is_sequential_statement_block(left)
2108
2175
  and full_graph.in_degree[left] == 1
@@ -2563,3 +2630,36 @@ class PhoenixStructurer(StructurerBase):
2563
2630
  graph_with_str.add_edge(f'"{src!r}"', f'"{dst!r}"')
2564
2631
 
2565
2632
  networkx.drawing.nx_pydot.write_dot(graph_with_str, path)
2633
+
2634
+ @staticmethod
2635
+ def switch_case_entry_node_has_common_successor_case_1(graph, jump_table, case_nodes, node_pred) -> bool:
2636
+ all_succs = set()
2637
+ for case_node in case_nodes:
2638
+ if case_node is node_pred:
2639
+ continue
2640
+ if case_node.addr in jump_table.jumptable_entries:
2641
+ all_succs |= set(graph.successors(case_node))
2642
+
2643
+ case_node_successors = set()
2644
+ for case_node in case_nodes:
2645
+ if case_node is node_pred:
2646
+ continue
2647
+ if case_node in all_succs:
2648
+ continue
2649
+ if case_node.addr in jump_table.jumptable_entries:
2650
+ succs = set(graph.successors(case_node))
2651
+ case_node_successors |= {succ for succ in succs if succ.addr not in jump_table.jumptable_entries}
2652
+
2653
+ return len(case_node_successors) <= 1
2654
+
2655
+ @staticmethod
2656
+ def switch_case_entry_node_has_common_successor_case_2(graph, jump_table, case_nodes, node_pred) -> bool:
2657
+ case_node_successors = set()
2658
+ for case_node in case_nodes:
2659
+ if case_node is node_pred:
2660
+ continue
2661
+ if case_node.addr in jump_table.jumptable_entries:
2662
+ succs = set(graph.successors(case_node))
2663
+ case_node_successors |= {succ for succ in succs if succ.addr not in jump_table.jumptable_entries}
2664
+
2665
+ return len(case_node_successors) <= 1
@@ -83,7 +83,9 @@ class RecursiveStructurer(Analysis):
83
83
  # Get the parent region
84
84
  parent_region = parent_map.get(current_region)
85
85
  # structure this region
86
- st: StructurerBase = self.project.analyses[self.structurer_cls].prep()(
86
+ st: StructurerBase = self.project.analyses[self.structurer_cls].prep(
87
+ kb=self.kb, fail_fast=self._fail_fast
88
+ )(
87
89
  current_region.copy(),
88
90
  parent_map=parent_map,
89
91
  condition_processor=self.cond_proc,
@@ -18,6 +18,7 @@ from angr.analyses.decompiler.utils import (
18
18
  remove_last_statement,
19
19
  has_nonlabel_nonphi_statements,
20
20
  )
21
+ from angr.analyses.decompiler.label_collector import LabelCollector
21
22
  from .structurer_nodes import (
22
23
  MultiNode,
23
24
  SequenceNode,
@@ -800,9 +801,17 @@ class StructurerBase(Analysis):
800
801
  starting_case_ids.append(idx)
801
802
  continue
802
803
 
804
+ # we can't just collect addresses and block IDs of switch-case entry nodes because SequenceNode does not keep
805
+ # track of block IDs.
806
+ case_label_addrs = set()
807
+ for case_node in cases.values():
808
+ lc = LabelCollector(case_node)
809
+ for lst in lc.labels.values():
810
+ case_label_addrs |= set(lst)
811
+
803
812
  for idx in starting_case_ids:
804
813
  new_cases[idx] = cases[idx]
805
- self._remove_last_statement_if_jump(new_cases[idx])
814
+ self._remove_last_statement_if_jump_to_addr(new_cases[idx], case_label_addrs)
806
815
  succs = networkx.dfs_successors(graph, idx)
807
816
  idx_ = idx
808
817
  while idx_ in succs:
@@ -813,6 +822,29 @@ class StructurerBase(Analysis):
813
822
 
814
823
  return new_cases
815
824
 
825
+ @staticmethod
826
+ def _remove_last_statement_if_jump_to_addr(
827
+ node: BaseNode | ailment.Block, addr_and_ids: set[tuple[int, int | None]]
828
+ ) -> ailment.Stmt.Jump | ailment.Stmt.ConditionalJump | None:
829
+ try:
830
+ last_stmts = ConditionProcessor.get_last_statements(node)
831
+ except EmptyBlockNotice:
832
+ return None
833
+
834
+ if len(last_stmts) == 1 and isinstance(last_stmts[0], (ailment.Stmt.Jump, ailment.Stmt.ConditionalJump)):
835
+ last_stmt = last_stmts[0]
836
+ jump_targets = []
837
+ if isinstance(last_stmt, ailment.Stmt.Jump) and isinstance(last_stmt.target, ailment.Expr.Const):
838
+ jump_targets = [(last_stmt.target.value, last_stmt.target_idx)]
839
+ elif isinstance(last_stmt, ailment.Stmt.ConditionalJump):
840
+ if isinstance(last_stmt.true_target, ailment.Expr.Const):
841
+ jump_targets.append((last_stmt.true_target.value, last_stmt.true_target_idx))
842
+ if isinstance(last_stmt.false_target, ailment.Expr.Const):
843
+ jump_targets.append((last_stmt.false_target.value, last_stmt.false_target_idx))
844
+ if any(tpl in addr_and_ids for tpl in jump_targets):
845
+ return remove_last_statement(node)
846
+ return None
847
+
816
848
  @staticmethod
817
849
  def _remove_last_statement_if_jump(
818
850
  node: BaseNode | ailment.Block,
Binary file
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: angr
3
- Version: 9.2.127
3
+ Version: 9.2.129
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.127
20
- Requires-Dist: archinfo==9.2.127
19
+ Requires-Dist: ailment==9.2.129
20
+ Requires-Dist: archinfo==9.2.129
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.127
25
- Requires-Dist: cle==9.2.127
24
+ Requires-Dist: claripy==9.2.129
25
+ Requires-Dist: cle==9.2.129
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.127
34
+ Requires-Dist: pyvex==9.2.129
35
35
  Requires-Dist: rich>=13.1.0
36
36
  Requires-Dist: sortedcontainers
37
37
  Requires-Dist: sympy
@@ -1,4 +1,4 @@
1
- angr/__init__.py,sha256=RVxXUHB8F4gQKi1KltS9-39I-4HywT29QiDLx2F1bMY,9153
1
+ angr/__init__.py,sha256=H7vjKb7cGcs7fXtPwftz3xSELPNZUzWL7xFWRC4tvvg,9153
2
2
  angr/__main__.py,sha256=XeawhF6Cco9eWcfMTDWzYYggLB3qjnQ87IIeFOplaHM,2873
3
3
  angr/annocfg.py,sha256=5fiS9TPt5r1_8g_qSfD2XkETlBdm5MTClBIQKqhm040,10624
4
4
  angr/blade.py,sha256=GpbEumxMsb_6qD7TbtfZuW2CMzV7W1iwqYzQWYlXnxM,15394
@@ -102,19 +102,20 @@ angr/analyses/decompiler/block_io_finder.py,sha256=xMwG8Bi69OGNYVs0U0F4yxM8kEsny
102
102
  angr/analyses/decompiler/block_similarity.py,sha256=ISMoOm-TGJ_1wD2i_4m8IYTletgnP66gReQESJnfvS0,6873
103
103
  angr/analyses/decompiler/block_simplifier.py,sha256=_WYyfFW8bJ_-RkrudJIlDdHh9fc6_aHkuwzW9gylY-k,13922
104
104
  angr/analyses/decompiler/callsite_maker.py,sha256=Gs_FmlmIs5jM-XccL9OMCaj_-L83NlYzkzxsy2HmcfQ,18749
105
- angr/analyses/decompiler/clinic.py,sha256=pSM3UVv-HMolsBeNZwLLKxpYof_fVH5m9G95gD4-OfE,106033
105
+ angr/analyses/decompiler/clinic.py,sha256=Psy7ljBDFOqYx_Al8xvDPe3naJi8YsdMryXo8wVlrhM,106999
106
106
  angr/analyses/decompiler/condition_processor.py,sha256=MbpbSk6zXHmMLWjLAHxpYjcLCVL1TuL08KmTBTNpm_4,52839
107
107
  angr/analyses/decompiler/decompilation_cache.py,sha256=ELz1DDVYvrs6IeUX4_L0OZDZICUifSBdJkdJXWrFZAY,1375
108
108
  angr/analyses/decompiler/decompilation_options.py,sha256=QWUGnfQ0FUekGs_I6X-ZKvAvL39VX2hFRcZrlXd72fY,7957
109
- angr/analyses/decompiler/decompiler.py,sha256=h6f8fsy3BzFm_-WuGdtXCb_u9xTL494lUEXI5j-PreU,27928
109
+ angr/analyses/decompiler/decompiler.py,sha256=BF859tVg7IWwEnarW30dXoQYHXco3lngTh0vx7Q5Je8,28672
110
110
  angr/analyses/decompiler/empty_node_remover.py,sha256=_RAGjqDyRmannEGPcMmWkL7em990-_sKgl5CYreb-yI,7403
111
111
  angr/analyses/decompiler/expression_narrower.py,sha256=TvkqtgNI9aDsquqyNFH5kXLW04rP_J940GFhrGopxP4,10343
112
112
  angr/analyses/decompiler/goto_manager.py,sha256=GUWt3Y_NCpmreIt4plxX5Y3UO2V8IVGZuRtF2GqI-cw,4006
113
- angr/analyses/decompiler/graph_region.py,sha256=PqXOqxOtk8haGNB7zlPzvXgkE0JdeGCIpLIUSeKswo8,16661
113
+ angr/analyses/decompiler/graph_region.py,sha256=KFJ_8qCn0bbjPO1l0D2-eVqRzkgiLNpxiynhWHhtxdA,16538
114
114
  angr/analyses/decompiler/jump_target_collector.py,sha256=qR11VsIp6H1N-19xCdXMRs1LGX31o3_Cz1Z5wRyMIl8,1173
115
115
  angr/analyses/decompiler/jumptable_entry_condition_rewriter.py,sha256=f_JyNiSZfoudElfl2kIzONoYCiosR4xYFOe8Q5SkvLg,2176
116
+ angr/analyses/decompiler/label_collector.py,sha256=JLaGuSZu-DdJMBTYOPt4QpWJ6UigOpsC5bgNANrSao4,798
116
117
  angr/analyses/decompiler/redundant_label_remover.py,sha256=J9hpP3C_P08v84FjVU0q5Rmj5M1N9q3HKWSWsA2u7Yg,5879
117
- angr/analyses/decompiler/region_identifier.py,sha256=FUynH4k09y5NiTdor8PLiPFviDcdWpzwz0xa9fRocJs,47293
118
+ angr/analyses/decompiler/region_identifier.py,sha256=DGxaIc3cy-NYxKeZgMO_BuybQvI86XXxNX88Ya6elAw,47396
118
119
  angr/analyses/decompiler/region_walker.py,sha256=u0hR0bEX1hSwkv-vejIM1gS-hcX2F2DLsDqpKhQ5_pQ,752
119
120
  angr/analyses/decompiler/return_maker.py,sha256=pKn9_y5VXqTeJnD5uzLLd9sH_Dp_9wkPcWPiJPTV-7A,2550
120
121
  angr/analyses/decompiler/seq_to_blocks.py,sha256=bB-1m8oBO59AlAp6izAROks3BBxFW8zigLlrIMt6Yfs,564
@@ -135,7 +136,7 @@ angr/analyses/decompiler/dephication/graph_rewriting.py,sha256=R0rlwYL0Cnt1UPjdE
135
136
  angr/analyses/decompiler/dephication/graph_vvar_mapping.py,sha256=nsMwppwMXrGC8_RF-neehpaz-7kmEORVhpAbjOdVFWM,13703
136
137
  angr/analyses/decompiler/dephication/rewriting_engine.py,sha256=3KUIxwUmfTZj2Jm1mB98J5vMkHqy4LLPYTrLzZfLbBA,10442
137
138
  angr/analyses/decompiler/dephication/seqnode_dephication.py,sha256=q29kS8lOs_-mxgJMtQvoZdw6l3q2lUDeXcTcGienIrY,4343
138
- angr/analyses/decompiler/optimization_passes/__init__.py,sha256=pCYamez51inHric94E2tD_Hy9gHl8sGHJbfG2dcpGBQ,4833
139
+ angr/analyses/decompiler/optimization_passes/__init__.py,sha256=7CvrHdbjvebdxzXYIvLqzKNv41bc19tecWZJBnEwEyo,4965
139
140
  angr/analyses/decompiler/optimization_passes/base_ptr_save_simplifier.py,sha256=uUzQWVkeKL2C9Lq8NZ7UkkZBAXydxOd0F1jxr0Zi__Q,5514
140
141
  angr/analyses/decompiler/optimization_passes/call_stmt_rewriter.py,sha256=G1CEWo62dAMrm-3V4DfEDxT6kwXxuks10bcTtW9C_tA,1320
141
142
  angr/analyses/decompiler/optimization_passes/code_motion.py,sha256=7o6lf-qahXv5H8jLqEASVXHaz-_PGo3r6l7qH5PbDtU,15343
@@ -153,7 +154,7 @@ angr/analyses/decompiler/optimization_passes/ite_region_converter.py,sha256=zTpl
153
154
  angr/analyses/decompiler/optimization_passes/lowered_switch_simplifier.py,sha256=1Yto_EBmmB5FkwZzaAO7S0MEvbQNEknFbbq-nUU0Eao,38818
154
155
  angr/analyses/decompiler/optimization_passes/mod_simplifier.py,sha256=papR480h-t_wEWMEdu6UTmc33lPSy_MOmiMgidPGnxc,3115
155
156
  angr/analyses/decompiler/optimization_passes/multi_simplifier.py,sha256=sIp2YISvafpyFzn8sgGMfohJsARiS3JFX_Y3IUXD_vo,10119
156
- angr/analyses/decompiler/optimization_passes/optimization_pass.py,sha256=Ot9iYGqHK_5TZM8cU8IUaUazDH8LJjf1EcG4Av0Udv8,21382
157
+ angr/analyses/decompiler/optimization_passes/optimization_pass.py,sha256=ZCGlsTK_3pF2uKdUkLoI2zkQTVvR3w1zt0ZLhy0_BcA,21530
157
158
  angr/analyses/decompiler/optimization_passes/register_save_area_simplifier.py,sha256=tc4FruEl0sFpm1DUu9g8gWlueRm0t9rhfHsdUrVplBo,7932
158
159
  angr/analyses/decompiler/optimization_passes/ret_addr_save_simplifier.py,sha256=GIFiYM_C_ChHrD2D1JGRFlE--PU3FOxTqzOX-lXmJLY,6404
159
160
  angr/analyses/decompiler/optimization_passes/ret_deduplicator.py,sha256=STMnRyZWiqdoGPa3c7Q4KCHv-JHUdJ2t4oLEltEMpII,7995
@@ -161,7 +162,8 @@ angr/analyses/decompiler/optimization_passes/return_duplicator_base.py,sha256=7Q
161
162
  angr/analyses/decompiler/optimization_passes/return_duplicator_high.py,sha256=ICDYwQJt5E2OVasdqn42jzbjwUXhSj6Plh3Y1iUHpAQ,2178
162
163
  angr/analyses/decompiler/optimization_passes/return_duplicator_low.py,sha256=-mBEVfwGz986lDDEGwBG8wvGQTrFZHE7TLV-7rWt-H0,10076
163
164
  angr/analyses/decompiler/optimization_passes/stack_canary_simplifier.py,sha256=cEbZ5jyYbRiBJSzVJbnqssUY5MzirXXgzvzpxllY_Zk,14343
164
- angr/analyses/decompiler/optimization_passes/switch_default_case_duplicator.py,sha256=pxb0jyDQ5BXkjzzo4JiHEA1NZeKVmp0G5Pd-T5_UIa8,4758
165
+ angr/analyses/decompiler/optimization_passes/switch_default_case_duplicator.py,sha256=nMI9geZiLG5diaI3YciNKvJ0PAZXtUBLgAfknCf48QE,6539
166
+ angr/analyses/decompiler/optimization_passes/switch_reused_entry_rewriter.py,sha256=m7ZMkqE2qbl4rl4M_Fi8xS6I1vUaTzUBIzsE6qpbwkM,4020
165
167
  angr/analyses/decompiler/optimization_passes/tag_slicer.py,sha256=8_gmoeYgDD1Hb8Rpqcb-01_B4897peDF-J6KA5PjQT8,1176
166
168
  angr/analyses/decompiler/optimization_passes/win_stack_canary_simplifier.py,sha256=cpbsP6_ilZDu2M_jX8TEnwVrsQXljHEjSMw25HyK6PM,12806
167
169
  angr/analyses/decompiler/optimization_passes/x86_gcc_getpc_simplifier.py,sha256=6NxaX2oT6BMkevb8xt9vlS3Jl-CmSK59F0FVab68B48,3088
@@ -219,9 +221,9 @@ angr/analyses/decompiler/peephole_optimizations/single_bit_cond_to_boolexpr.py,s
219
221
  angr/analyses/decompiler/peephole_optimizations/single_bit_xor.py,sha256=tnjWYeKWL63rvLVcicVSD1NbVQJfHtLT85E_PNeYt6s,979
220
222
  angr/analyses/decompiler/peephole_optimizations/tidy_stack_addr.py,sha256=RRzuc2iGzQPvYaZAmpLKim0pJ_yR3-muGnJQxvpcN8w,4786
221
223
  angr/analyses/decompiler/presets/__init__.py,sha256=FfnTqgY3oINacW7JFPIxx3r9dwpgI0Pu8yygBCN9d68,375
222
- angr/analyses/decompiler/presets/basic.py,sha256=xL7UjuuRnZFoG2j8C-qB1F8xwC3tA189PmVdQO_1tCU,737
223
- angr/analyses/decompiler/presets/fast.py,sha256=vaHnlv39xQwilXVtdkAEFUKKDQIwQQOTw6Synvse6rI,1469
224
- angr/analyses/decompiler/presets/full.py,sha256=sFFP4ZUqPlrEnSfwDJ5Owh-wlqnuW459qY2AAXoof2s,1642
224
+ angr/analyses/decompiler/presets/basic.py,sha256=KDHlMq_XWonN2-JIYYVIhbI6FbfzXttmkgXFrwi5MiA,803
225
+ angr/analyses/decompiler/presets/fast.py,sha256=7C6-7Enha_ZRP-z_1tD3y3tG-FFqFPdp97rCTro0I6o,1535
226
+ angr/analyses/decompiler/presets/full.py,sha256=OJU5vmih0QPzoHWfy4Umwek7RHh6TV7fuUTi5VolDTo,1708
225
227
  angr/analyses/decompiler/presets/preset.py,sha256=sTK5fJfx_Cdx0Gjn7y4bOrDp-2eFPeg1e1d5Eyc9uXk,1256
226
228
  angr/analyses/decompiler/region_simplifiers/__init__.py,sha256=BSD9osrReTEdapOMmyI1kFiN7AmE1EeJGLBV7i0u-Uc,117
227
229
  angr/analyses/decompiler/region_simplifiers/cascading_cond_transformer.py,sha256=qLs1LxEYHdPrh5c33IdkHJqtjBU7z4Sz6fxOK4Fn0Oc,3816
@@ -250,10 +252,10 @@ angr/analyses/decompiler/structured_codegen/dummy.py,sha256=JZLeovXE-8C-unp2hbej
250
252
  angr/analyses/decompiler/structured_codegen/dwarf_import.py,sha256=J6V40RuIyKXN7r6ESftIYfoREgmgFavnUL5m3lyTzlM,7072
251
253
  angr/analyses/decompiler/structuring/__init__.py,sha256=u2SGBezMdqQF_2ixo8wr66vCMedAMY-cSjQyq2m-nR8,711
252
254
  angr/analyses/decompiler/structuring/dream.py,sha256=mPNNsNvNb-LoDcoU_HjUejRytIFY_ZyCAbK4tNq_5lM,48254
253
- angr/analyses/decompiler/structuring/phoenix.py,sha256=uJ6V7DMoc7DOH2b_NfnuRPvyKvB31eUIUOmuWmC7Sz4,120632
254
- angr/analyses/decompiler/structuring/recursive_structurer.py,sha256=HRUpZiD8xlpJjHWL8WHORakuBw_ip7h8K9ichyLX1j8,7075
255
+ angr/analyses/decompiler/structuring/phoenix.py,sha256=-DkejyxETWXqGGdDw2-HBqlMgNUkVpApyyMpPforhSA,125114
256
+ angr/analyses/decompiler/structuring/recursive_structurer.py,sha256=wxMiixVpmao1Rpuo3wN-gxkPh2YrgTTW4usgtdjdY9E,7150
255
257
  angr/analyses/decompiler/structuring/sailr.py,sha256=6lM9cK3iU1kQ_eki7v1Z2VxTiX5OwQzIRF_BbEsw67Q,5721
256
- angr/analyses/decompiler/structuring/structurer_base.py,sha256=ql8HoTn9SG6snNmgEx1xQVeIHAlvdkASQpDwNR04YKw,41547
258
+ angr/analyses/decompiler/structuring/structurer_base.py,sha256=b2fGaDOYy_XdgbOHsKIalJWVTXEt4zDK7w3IO-ZgIjI,43276
257
259
  angr/analyses/decompiler/structuring/structurer_nodes.py,sha256=a916imPog4YCCtWtzcnHIQMPLEC73C5t-zSvx9mnvGk,12081
258
260
  angr/analyses/deobfuscator/__init__.py,sha256=dkmq-mm3V6kiuchwUZCXr3bDRAEB1-zsPHeEt54tlUE,648
259
261
  angr/analyses/deobfuscator/api_obf_finder.py,sha256=WWl55WESeAwcJMrJPX0LqGKN0druzWz--PsG79IliQA,13241
@@ -544,7 +546,7 @@ angr/knowledge_plugins/xrefs/__init__.py,sha256=5PhqVOtTZ27lCjJ9wp7akUeJydqILbyC
544
546
  angr/knowledge_plugins/xrefs/xref.py,sha256=ROo_kAEKwB51whVYcGtTV0QRtYmQZV8nEoEtbQWyC9U,4883
545
547
  angr/knowledge_plugins/xrefs/xref_manager.py,sha256=Yb88z3L8Y26TfGeRHdsGWQlT9f6oWntjEg6s-kcVtUQ,4040
546
548
  angr/knowledge_plugins/xrefs/xref_types.py,sha256=LcQ9pD4E4XlC51Us49xiqAoGAFGpnCrpYO4mOzILiKI,308
547
- angr/lib/angr_native.dylib,sha256=OdJVVXraXbWldaBhIh1eXkak4aIuD5ExVasukiW-KfI,16187984
549
+ angr/lib/angr_native.dylib,sha256=ZCMdrML_FWvg-qcuIc52tMZ0o4tZ-QGmv68I0QEPpwk,16187984
548
550
  angr/misc/__init__.py,sha256=ZPHXbrIdsfe_qdmq8CnXuS_bBWOy4MDT2NjwUnPgHZc,355
549
551
  angr/misc/ansi.py,sha256=nPJHC0SKfqasMQZ0LxdmmIYojjmk4nr5jI6FrzoTwS0,811
550
552
  angr/misc/autoimport.py,sha256=iZagpuPwZWczUTYIqs-JkDMQjftMqc_cchcm7OBFiEg,3450
@@ -1351,9 +1353,9 @@ angr/utils/timing.py,sha256=ELuRPzdRSHzOATgtAzTFByMlVr021ypMrsvtpopreLg,1481
1351
1353
  angr/utils/ssa/__init__.py,sha256=Sz9zQVnvtmCbJJYeTG_k2JxcZtgxvIxap-KqzvRnIFQ,8015
1352
1354
  angr/utils/ssa/tmp_uses_collector.py,sha256=rSpvMxBHzg-tmvhsfjn3iLyPEKzaZN-xpQrdslMq3J4,793
1353
1355
  angr/utils/ssa/vvar_uses_collector.py,sha256=8gfAWdRMz73Deh-ZshDM3GPAot9Lf-rHzCiaCil0hlE,1342
1354
- angr-9.2.127.dist-info/LICENSE,sha256=cgL_ho5B1NH8UxwtBuqThRWdjear8b7hktycaS1sz6g,1327
1355
- angr-9.2.127.dist-info/METADATA,sha256=ShqPxcNeKaZBhxu__-FOBkH4XxlaufXprp4jtO5F_yg,4762
1356
- angr-9.2.127.dist-info/WHEEL,sha256=obDBrsNvkbO7xvVLrBUaB5Z67T0LixWUZ_N8DpT93rI,105
1357
- angr-9.2.127.dist-info/entry_points.txt,sha256=Vjh1C8PMyr5dZFMnik5WkEP01Uwr2T73I3a6N32sgQU,44
1358
- angr-9.2.127.dist-info/top_level.txt,sha256=dKw0KWTbwLXytFvv15oAAG4sUs3ey47tt6DorJG9-hw,5
1359
- angr-9.2.127.dist-info/RECORD,,
1356
+ angr-9.2.129.dist-info/LICENSE,sha256=cgL_ho5B1NH8UxwtBuqThRWdjear8b7hktycaS1sz6g,1327
1357
+ angr-9.2.129.dist-info/METADATA,sha256=WOdMRFoJxdkUHH8TILuVOxyo2fJZ2amFBMlZdEZpkbg,4762
1358
+ angr-9.2.129.dist-info/WHEEL,sha256=PE3TTq9DEejtN2L2P-N0EQfD4k3aiKkcWs6tm9oiho0,105
1359
+ angr-9.2.129.dist-info/entry_points.txt,sha256=Vjh1C8PMyr5dZFMnik5WkEP01Uwr2T73I3a6N32sgQU,44
1360
+ angr-9.2.129.dist-info/top_level.txt,sha256=dKw0KWTbwLXytFvv15oAAG4sUs3ey47tt6DorJG9-hw,5
1361
+ angr-9.2.129.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.3.0)
2
+ Generator: setuptools (75.5.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-macosx_11_0_arm64
5
5