angr 9.2.138__py3-none-macosx_11_0_arm64.whl → 9.2.139__py3-none-macosx_11_0_arm64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of angr might be problematic. Click here for more details.

Files changed (59) hide show
  1. angr/__init__.py +1 -1
  2. angr/analyses/calling_convention/fact_collector.py +59 -12
  3. angr/analyses/calling_convention/utils.py +2 -2
  4. angr/analyses/cfg/cfg_fast.py +12 -4
  5. angr/analyses/decompiler/ail_simplifier.py +14 -3
  6. angr/analyses/decompiler/block_simplifier.py +0 -2
  7. angr/analyses/decompiler/callsite_maker.py +80 -14
  8. angr/analyses/decompiler/clinic.py +31 -37
  9. angr/analyses/decompiler/condition_processor.py +2 -2
  10. angr/analyses/decompiler/decompiler.py +2 -0
  11. angr/analyses/decompiler/dephication/rewriting_engine.py +16 -7
  12. angr/analyses/decompiler/optimization_passes/__init__.py +3 -0
  13. angr/analyses/decompiler/optimization_passes/condition_constprop.py +149 -0
  14. angr/analyses/decompiler/optimization_passes/deadblock_remover.py +12 -3
  15. angr/analyses/decompiler/optimization_passes/inlined_string_transformation_simplifier.py +1 -1
  16. angr/analyses/decompiler/optimization_passes/optimization_pass.py +5 -2
  17. angr/analyses/decompiler/optimization_passes/return_duplicator_base.py +15 -7
  18. angr/analyses/decompiler/optimization_passes/return_duplicator_high.py +7 -10
  19. angr/analyses/decompiler/peephole_optimizations/eager_eval.py +12 -1
  20. angr/analyses/decompiler/peephole_optimizations/remove_redundant_conversions.py +61 -25
  21. angr/analyses/decompiler/peephole_optimizations/remove_redundant_shifts.py +50 -1
  22. angr/analyses/decompiler/presets/fast.py +2 -0
  23. angr/analyses/decompiler/presets/full.py +2 -0
  24. angr/analyses/decompiler/region_simplifiers/region_simplifier.py +4 -0
  25. angr/analyses/decompiler/ssailification/rewriting_engine.py +20 -2
  26. angr/analyses/decompiler/ssailification/traversal_engine.py +4 -3
  27. angr/analyses/decompiler/structured_codegen/c.py +10 -3
  28. angr/analyses/decompiler/structuring/dream.py +7 -2
  29. angr/analyses/decompiler/structuring/phoenix.py +101 -49
  30. angr/analyses/decompiler/structuring/structurer_base.py +85 -36
  31. angr/analyses/decompiler/structuring/structurer_nodes.py +3 -1
  32. angr/analyses/deobfuscator/api_obf_finder.py +6 -1
  33. angr/analyses/deobfuscator/api_obf_type2_finder.py +158 -0
  34. angr/analyses/s_propagator.py +127 -50
  35. angr/analyses/s_reaching_definitions/s_rda_view.py +2 -2
  36. angr/analyses/s_reaching_definitions/s_reaching_definitions.py +3 -1
  37. angr/analyses/variable_recovery/engine_ail.py +1 -1
  38. angr/analyses/variable_recovery/engine_base.py +55 -62
  39. angr/analyses/variable_recovery/engine_vex.py +1 -1
  40. angr/analyses/variable_recovery/irsb_scanner.py +2 -2
  41. angr/calling_conventions.py +66 -9
  42. angr/engines/engine.py +2 -18
  43. angr/engines/light/engine.py +3 -8
  44. angr/engines/pcode/emulate.py +2 -2
  45. angr/engines/pcode/lifter.py +2 -2
  46. angr/engines/successors.py +1 -8
  47. angr/engines/vex/lifter.py +2 -2
  48. angr/engines/vex/light/light.py +2 -2
  49. angr/knowledge_plugins/cfg/cfg_model.py +3 -2
  50. angr/knowledge_plugins/labels.py +2 -2
  51. angr/knowledge_plugins/obfuscations.py +1 -0
  52. angr/knowledge_plugins/xrefs/xref_manager.py +4 -0
  53. angr/lib/angr_native.dylib +0 -0
  54. {angr-9.2.138.dist-info → angr-9.2.139.dist-info}/METADATA +6 -6
  55. {angr-9.2.138.dist-info → angr-9.2.139.dist-info}/RECORD +59 -57
  56. {angr-9.2.138.dist-info → angr-9.2.139.dist-info}/LICENSE +0 -0
  57. {angr-9.2.138.dist-info → angr-9.2.139.dist-info}/WHEEL +0 -0
  58. {angr-9.2.138.dist-info → angr-9.2.139.dist-info}/entry_points.txt +0 -0
  59. {angr-9.2.138.dist-info → angr-9.2.139.dist-info}/top_level.txt +0 -0
@@ -241,6 +241,7 @@ class PhoenixStructurer(StructurerBase):
241
241
  def _match_cyclic_schemas(self, node, head, graph, full_graph) -> bool:
242
242
  matched, loop_node, successor_node = self._match_cyclic_while(node, head, graph, full_graph)
243
243
  if matched:
244
+ assert loop_node is not None and successor_node is not None
244
245
  # traverse this node and rewrite all conditional jumps that go outside the loop to breaks
245
246
  self._rewrite_conditional_jumps_to_breaks(loop_node.sequence_node, [successor_node.addr])
246
247
  # traverse this node and rewrite all jumps that go to the beginning of the loop to continue
@@ -249,6 +250,7 @@ class PhoenixStructurer(StructurerBase):
249
250
 
250
251
  matched, loop_node, successor_node = self._match_cyclic_dowhile(node, head, graph, full_graph)
251
252
  if matched:
253
+ assert loop_node is not None and successor_node is not None
252
254
  # traverse this node and rewrite all conditional jumps that go outside the loop to breaks
253
255
  self._rewrite_conditional_jumps_to_breaks(loop_node.sequence_node, [successor_node.addr])
254
256
  # traverse this node and rewrite all jumps that go to the beginning of the loop to continue
@@ -260,6 +262,7 @@ class PhoenixStructurer(StructurerBase):
260
262
  node, head, graph, full_graph
261
263
  )
262
264
  if matched:
265
+ assert loop_node is not None and successor_node is not None
263
266
  # traverse this node and rewrite all conditional jumps that go outside the loop to breaks
264
267
  self._rewrite_conditional_jumps_to_breaks(loop_node.sequence_node, [successor_node.addr])
265
268
  # traverse this node and rewrite all jumps that go to the beginning of the loop to continue
@@ -268,6 +271,7 @@ class PhoenixStructurer(StructurerBase):
268
271
 
269
272
  matched, loop_node = self._match_cyclic_natural_loop(node, head, graph, full_graph)
270
273
  if matched:
274
+ assert loop_node is not None
271
275
  if self._region.successors is not None and len(self._region.successors) == 1:
272
276
  # traverse this node and rewrite all conditional jumps that go outside the loop to breaks
273
277
  self._rewrite_conditional_jumps_to_breaks(
@@ -318,7 +322,7 @@ class PhoenixStructurer(StructurerBase):
318
322
  # otherwise it's a do-while loop
319
323
  edge_cond_left = self.cond_proc.recover_edge_condition(full_graph, head_block, left)
320
324
  edge_cond_right = self.cond_proc.recover_edge_condition(full_graph, head_block, right)
321
- if claripy.is_true(claripy.Not(edge_cond_left) == edge_cond_right):
325
+ if claripy.is_true(claripy.Not(edge_cond_left) == edge_cond_right): # type: ignore
322
326
  # c = !c
323
327
  if head_block_idx == 0:
324
328
  self._remove_first_statement_if_jump(head_block)
@@ -346,7 +350,7 @@ class PhoenixStructurer(StructurerBase):
346
350
  if head_block is not None:
347
351
  edge_cond_left = self.cond_proc.recover_edge_condition(full_graph, head_block, left)
348
352
  edge_cond_right = self.cond_proc.recover_edge_condition(full_graph, head_block, right)
349
- if claripy.is_true(claripy.Not(edge_cond_left) == edge_cond_right):
353
+ if claripy.is_true(claripy.Not(edge_cond_left) == edge_cond_right): # type: ignore
350
354
  # c = !c
351
355
  if PhoenixStructurer._is_single_statement_block(node):
352
356
  # the single-statement-block check is to ensure we don't execute any code before the
@@ -366,6 +370,7 @@ class PhoenixStructurer(StructurerBase):
366
370
  return True, loop_node, right
367
371
  # we generate a while-true loop instead
368
372
  last_stmt = self._remove_last_statement_if_jump(head_block)
373
+ assert last_stmt is not None
369
374
  cond_jump = Jump(
370
375
  None,
371
376
  Const(None, None, right.addr, self.project.arch.bits),
@@ -394,7 +399,7 @@ class PhoenixStructurer(StructurerBase):
394
399
  if head_block is not None:
395
400
  edge_cond_left = self.cond_proc.recover_edge_condition(full_graph, head_block, left)
396
401
  edge_cond_right = self.cond_proc.recover_edge_condition(full_graph, head_block, right)
397
- if claripy.is_true(claripy.Not(edge_cond_left) == edge_cond_right):
402
+ if claripy.is_true(claripy.Not(edge_cond_left) == edge_cond_right): # type: ignore
398
403
  # c = !c
399
404
  self._remove_last_statement_if_jump(head_block)
400
405
  cond_break = ConditionalBreakNode(node.addr, edge_cond_right, right.addr)
@@ -426,6 +431,8 @@ class PhoenixStructurer(StructurerBase):
426
431
  return False, None, None
427
432
 
428
433
  loop_cond = None
434
+ successor_node = None
435
+ succ_node_is_true_node = None
429
436
  if (
430
437
  isinstance(node, SequenceNode)
431
438
  and node.nodes
@@ -436,21 +443,27 @@ class PhoenixStructurer(StructurerBase):
436
443
  and node.nodes[-1].true_node is not None
437
444
  and node.nodes[-1].false_node is not None
438
445
  ):
439
- successor_node = node.nodes[-1].true_node
440
- # test if the successor_node returns or not
441
- # FIXME: It might be too strict
442
- try:
443
- last_stmt = self.cond_proc.get_last_statement(successor_node)
444
- except EmptyBlockNotice:
445
- last_stmt = None
446
- if last_stmt is not None and isinstance(last_stmt, Return):
446
+ # try both true node and false node; pick the first node with only Returns as last statements as the
447
+ # successor.
448
+ if self._cyclic_while_with_single_successor_must_return(node.nodes[-1].true_node):
449
+ succ_node_is_true_node = True
450
+ successor_node = node.nodes[-1].true_node
447
451
  loop_cond = claripy.Not(node.nodes[-1].condition)
452
+ elif self._cyclic_while_with_single_successor_must_return(node.nodes[-1].false_node):
453
+ succ_node_is_true_node = False
454
+ successor_node = node.nodes[-1].false_node
455
+ loop_cond = node.nodes[-1].condition
456
+ else:
457
+ loop_cond = None
448
458
 
449
459
  if loop_cond is None:
450
460
  return False, None, None
451
461
 
452
462
  node_copy = node.copy()
453
- node_copy.nodes[-1] = node_copy.nodes[-1].false_node # replace the last node with the false node
463
+ # replace the last node with the intended successor node
464
+ node_copy.nodes[-1] = (
465
+ node_copy.nodes[-1].false_node if succ_node_is_true_node else node_copy.nodes[-1].true_node
466
+ )
454
467
  # check if there is a cycle that starts with node and ends with node
455
468
  next_node = node
456
469
  seq_node = SequenceNode(node.addr, nodes=[node_copy])
@@ -487,6 +500,15 @@ class PhoenixStructurer(StructurerBase):
487
500
 
488
501
  return True, loop_node, successor_node
489
502
 
503
+ def _cyclic_while_with_single_successor_must_return(self, successor_node: SequenceNode) -> bool:
504
+ try:
505
+ last_stmts = self.cond_proc.get_last_statements(successor_node)
506
+ except EmptyBlockNotice:
507
+ return False
508
+ if not last_stmts:
509
+ return False
510
+ return all(isinstance(stmt, Return) for stmt in last_stmts)
511
+
490
512
  def _match_cyclic_dowhile(self, node, head, graph, full_graph) -> tuple[bool, LoopNode | None, BaseNode | None]:
491
513
  preds = list(full_graph.predecessors(node))
492
514
  succs = list(full_graph.successors(node))
@@ -504,7 +526,7 @@ class PhoenixStructurer(StructurerBase):
504
526
  if succ_block is not None:
505
527
  edge_cond_succhead = self.cond_proc.recover_edge_condition(full_graph, succ_block, node)
506
528
  edge_cond_succout = self.cond_proc.recover_edge_condition(full_graph, succ_block, out_node)
507
- if claripy.is_true(claripy.Not(edge_cond_succhead) == edge_cond_succout):
529
+ if claripy.is_true(claripy.Not(edge_cond_succhead) == edge_cond_succout): # type: ignore
508
530
  # c = !c
509
531
  self._remove_last_statement_if_jump(succ)
510
532
  drop_succ = False
@@ -543,7 +565,7 @@ class PhoenixStructurer(StructurerBase):
543
565
  # possible candidate
544
566
  edge_cond_head = self.cond_proc.recover_edge_condition(full_graph, node, node)
545
567
  edge_cond_head_succ = self.cond_proc.recover_edge_condition(full_graph, node, succ)
546
- if claripy.is_true(claripy.Not(edge_cond_head) == edge_cond_head_succ):
568
+ if claripy.is_true(claripy.Not(edge_cond_head) == edge_cond_head_succ): # type: ignore
547
569
  # c = !c
548
570
  self._remove_last_statement_if_jump(node)
549
571
  seq_node = SequenceNode(node.addr, nodes=[node]) if not isinstance(node, SequenceNode) else node
@@ -617,9 +639,11 @@ class PhoenixStructurer(StructurerBase):
617
639
 
618
640
  def _refine_cyclic_core(self, loop_head) -> bool:
619
641
  graph: networkx.DiGraph = self._region.graph
620
- fullgraph: networkx.DiGraph = self._region.graph_with_successors
621
- if fullgraph is None:
622
- fullgraph = networkx.DiGraph(self._region.graph)
642
+ fullgraph: networkx.DiGraph = (
643
+ self._region.graph_with_successors
644
+ if self._region.graph_with_successors is not None
645
+ else networkx.DiGraph(self._region.graph)
646
+ )
623
647
 
624
648
  # check if there is an out-going edge from the loop head
625
649
  head_succs = list(fullgraph.successors(loop_head))
@@ -638,6 +662,7 @@ class PhoenixStructurer(StructurerBase):
638
662
  # for now, we handle the most common case: both successors exist in the graph of the parent region, and
639
663
  # one successor has a path to the other successor
640
664
  if is_while and is_dowhile and self._parent_region is not None:
665
+ assert result_while is not None and result_dowhile is not None
641
666
  succ_while = result_while[-1]
642
667
  succ_dowhile = result_dowhile[-1]
643
668
  if succ_while in self._parent_region.graph and succ_dowhile in self._parent_region.graph:
@@ -651,9 +676,11 @@ class PhoenixStructurer(StructurerBase):
651
676
  is_while = False
652
677
 
653
678
  if is_while:
679
+ assert result_while is not None
654
680
  loop_type = "while"
655
681
  continue_edges, outgoing_edges, continue_node, successor = result_while
656
682
  elif is_dowhile:
683
+ assert result_dowhile is not None
657
684
  loop_type = "do-while"
658
685
  continue_edges, outgoing_edges, continue_node, successor = result_dowhile
659
686
 
@@ -736,6 +763,7 @@ class PhoenixStructurer(StructurerBase):
736
763
  # unwanted results, e.g., inserting a break (that's intended to break out of the loop) inside a
737
764
  # switch-case that is nested within a loop.
738
765
  last_src_stmt = self.cond_proc.get_last_statement(src_block)
766
+ assert last_src_stmt is not None
739
767
  break_cond = self.cond_proc.recover_edge_condition(fullgraph, src_block, dst)
740
768
  if claripy.is_true(break_cond):
741
769
  break_stmt = Jump(
@@ -761,7 +789,9 @@ class PhoenixStructurer(StructurerBase):
761
789
  )
762
790
  new_node = SequenceNode(src_block.addr, nodes=[src_block, break_node])
763
791
  if has_continue:
764
- if self.is_a_jump_target(last_src_stmt, continue_node.addr):
792
+ if continue_node.addr is not None and self.is_a_jump_target(
793
+ last_src_stmt, continue_node.addr
794
+ ):
765
795
  # instead of a conditional break node, we should insert a condition node instead
766
796
  break_stmt = Jump(
767
797
  None,
@@ -817,7 +847,7 @@ class PhoenixStructurer(StructurerBase):
817
847
  if fullgraph.in_degree[dst] == 0:
818
848
  # drop this node
819
849
  fullgraph.remove_node(dst)
820
- if dst in self._region.successors:
850
+ if self._region.successors and dst in self._region.successors:
821
851
  self._region.successors.remove(dst)
822
852
 
823
853
  if len(continue_edges) > 1:
@@ -1074,7 +1104,7 @@ class PhoenixStructurer(StructurerBase):
1074
1104
 
1075
1105
  # make a fake jumptable
1076
1106
  node_default_addr = None
1077
- case_entries: dict[int, tuple[int, int | None]] = {}
1107
+ case_entries: dict[int, int | tuple[int, int | None]] = {}
1078
1108
  for _, case_value, case_target_addr, case_target_idx, _ in last_stmt.case_addrs:
1079
1109
  if isinstance(case_value, str):
1080
1110
  if case_value == "default":
@@ -1127,7 +1157,9 @@ class PhoenixStructurer(StructurerBase):
1127
1157
  if self._region.graph_with_successors is not None:
1128
1158
  self._region.graph_with_successors.remove_node(o)
1129
1159
 
1130
- self._switch_handle_gotos(cases, node_default, None)
1160
+ switch_end_addr = self._switch_find_switch_end_addr(cases, node_default, {nn.addr for nn in self._region.graph})
1161
+ if switch_end_addr is not None:
1162
+ self._switch_handle_gotos(cases, node_default, switch_end_addr)
1131
1163
  return True
1132
1164
 
1133
1165
  def _match_acyclic_switch_cases_address_loaded_from_memory(self, node, graph, full_graph, jump_tables) -> bool:
@@ -1136,6 +1168,9 @@ class PhoenixStructurer(StructurerBase):
1136
1168
  except EmptyBlockNotice:
1137
1169
  return False
1138
1170
 
1171
+ if last_stmt is None:
1172
+ return False
1173
+
1139
1174
  successor_addrs = extract_jump_targets(last_stmt)
1140
1175
  if len(successor_addrs) != 2:
1141
1176
  return False
@@ -1222,7 +1257,9 @@ class PhoenixStructurer(StructurerBase):
1222
1257
  switch_end_addr = node_b_addr
1223
1258
  else:
1224
1259
  # we don't know what the end address of this switch-case structure is. let's figure it out
1225
- switch_end_addr = None
1260
+ switch_end_addr = self._switch_find_switch_end_addr(
1261
+ cases, node_default, {nn.addr for nn in self._region.graph}
1262
+ )
1226
1263
  to_remove.add(node_default)
1227
1264
 
1228
1265
  to_remove.add(node_a) # add node_a
@@ -1243,7 +1280,8 @@ class PhoenixStructurer(StructurerBase):
1243
1280
 
1244
1281
  # fully structured into a switch-case. remove node from switch_case_known_heads
1245
1282
  self.switch_case_known_heads.remove(node)
1246
- self._switch_handle_gotos(cases, node_default, switch_end_addr)
1283
+ if switch_end_addr is not None:
1284
+ self._switch_handle_gotos(cases, node_default, switch_end_addr)
1247
1285
 
1248
1286
  return True
1249
1287
 
@@ -1311,7 +1349,7 @@ class PhoenixStructurer(StructurerBase):
1311
1349
  )
1312
1350
 
1313
1351
  assert node_default is None
1314
- switch_end_addr = None
1352
+ switch_end_addr = self._switch_find_switch_end_addr(cases, node_default, {nn.addr for nn in self._region.graph})
1315
1353
 
1316
1354
  r = self._make_switch_cases_core(
1317
1355
  node,
@@ -1330,7 +1368,8 @@ class PhoenixStructurer(StructurerBase):
1330
1368
 
1331
1369
  # fully structured into a switch-case. remove node from switch_case_known_heads
1332
1370
  self.switch_case_known_heads.remove(node)
1333
- self._switch_handle_gotos(cases, node_default, switch_end_addr)
1371
+ if switch_end_addr is not None:
1372
+ self._switch_handle_gotos(cases, node_default, switch_end_addr)
1334
1373
 
1335
1374
  return True
1336
1375
 
@@ -1431,7 +1470,7 @@ class PhoenixStructurer(StructurerBase):
1431
1470
  graph,
1432
1471
  full_graph,
1433
1472
  ) -> tuple[OrderedDict, Any, set[Any]]:
1434
- cases: OrderedDict[int | tuple[int], SequenceNode] = OrderedDict()
1473
+ cases: OrderedDict[int | tuple[int, ...], SequenceNode] = OrderedDict()
1435
1474
  to_remove = set()
1436
1475
 
1437
1476
  # it is possible that the default node gets duplicated by other analyses and creates a default node (addr.a)
@@ -1719,6 +1758,8 @@ class PhoenixStructurer(StructurerBase):
1719
1758
  succs = list(full_graph.successors(start_node))
1720
1759
  if len(succs) == 2:
1721
1760
  left, right = succs
1761
+ if left.addr > right.addr:
1762
+ left, right = right, left
1722
1763
  if self._is_switch_cases_address_loaded_from_memory_head_or_jumpnode(
1723
1764
  full_graph, left
1724
1765
  ) or self._is_switch_cases_address_loaded_from_memory_head_or_jumpnode(full_graph, right):
@@ -1747,7 +1788,7 @@ class PhoenixStructurer(StructurerBase):
1747
1788
  ):
1748
1789
  edge_cond_left = self.cond_proc.recover_edge_condition(full_graph, start_node, left)
1749
1790
  edge_cond_right = self.cond_proc.recover_edge_condition(full_graph, start_node, right)
1750
- if claripy.is_true(claripy.Not(edge_cond_left) == edge_cond_right):
1791
+ if claripy.is_true(claripy.Not(edge_cond_left) == edge_cond_right): # type: ignore
1751
1792
  # c = !c
1752
1793
  last_if_jump = self._remove_last_statement_if_jump(start_node)
1753
1794
  new_cond_node = ConditionNode(
@@ -1789,7 +1830,7 @@ class PhoenixStructurer(StructurerBase):
1789
1830
  ) and not self._is_node_unstructured_switch_case_head(right):
1790
1831
  edge_cond_left = self.cond_proc.recover_edge_condition(full_graph, start_node, left)
1791
1832
  edge_cond_right = self.cond_proc.recover_edge_condition(full_graph, start_node, right)
1792
- if claripy.is_true(claripy.Not(edge_cond_left) == edge_cond_right):
1833
+ if claripy.is_true(claripy.Not(edge_cond_left) == edge_cond_right): # type: ignore
1793
1834
  # c = !c
1794
1835
  last_if_jump = self._remove_last_statement_if_jump(start_node)
1795
1836
  new_cond_node = ConditionNode(
@@ -1822,7 +1863,7 @@ class PhoenixStructurer(StructurerBase):
1822
1863
  ):
1823
1864
  edge_cond_left = self.cond_proc.recover_edge_condition(full_graph, start_node, left)
1824
1865
  edge_cond_right = self.cond_proc.recover_edge_condition(full_graph, start_node, right)
1825
- if claripy.is_true(claripy.Not(edge_cond_left) == edge_cond_right):
1866
+ if claripy.is_true(claripy.Not(edge_cond_left) == edge_cond_right): # type: ignore
1826
1867
  # c = !c
1827
1868
  last_if_jump = self._remove_last_statement_if_jump(start_node)
1828
1869
  new_cond_node = ConditionNode(
@@ -1857,7 +1898,7 @@ class PhoenixStructurer(StructurerBase):
1857
1898
  ):
1858
1899
  edge_cond_left = self.cond_proc.recover_edge_condition(full_graph, start_node, left)
1859
1900
  edge_cond_right = self.cond_proc.recover_edge_condition(full_graph, start_node, right)
1860
- if claripy.is_true(claripy.Not(edge_cond_left) == edge_cond_right):
1901
+ if claripy.is_true(claripy.Not(edge_cond_left) == edge_cond_right): # type: ignore
1861
1902
  # c = !c
1862
1903
  try:
1863
1904
  last_stmt = self.cond_proc.get_last_statement(start_node)
@@ -1876,7 +1917,7 @@ class PhoenixStructurer(StructurerBase):
1876
1917
  self._remove_last_statement_if_jump(start_node)
1877
1918
  # add a goto node at the end
1878
1919
  new_jump_node = Block(
1879
- new_cond_node.addr,
1920
+ new_cond_node.addr if new_cond_node.addr is not None else 0x7EFF_FFFF,
1880
1921
  0,
1881
1922
  statements=[
1882
1923
  Jump(
@@ -2162,7 +2203,7 @@ class PhoenixStructurer(StructurerBase):
2162
2203
  ):
2163
2204
  edge_cond_left = self.cond_proc.recover_edge_condition(full_graph, start_node, left)
2164
2205
  edge_cond_right = self.cond_proc.recover_edge_condition(full_graph, start_node, right)
2165
- if claripy.is_true(claripy.Not(edge_cond_left) == edge_cond_right):
2206
+ if claripy.is_true(claripy.Not(edge_cond_left) == edge_cond_right): # type: ignore
2166
2207
  # c0 = !c0
2167
2208
  left_succs = list(full_graph.successors(left))
2168
2209
  if len(left_succs) == 2 and right in left_succs:
@@ -2171,7 +2212,9 @@ class PhoenixStructurer(StructurerBase):
2171
2212
  # there must be an edge between right and other_succ
2172
2213
  edge_cond_left_right = self.cond_proc.recover_edge_condition(full_graph, left, right)
2173
2214
  edge_cond_left_other = self.cond_proc.recover_edge_condition(full_graph, left, other_succ)
2174
- if claripy.is_true(claripy.Not(edge_cond_left_right) == edge_cond_left_other):
2215
+ if claripy.is_true(
2216
+ claripy.Not(edge_cond_left_right) == edge_cond_left_other # type: ignore
2217
+ ):
2175
2218
  # c1 = !c1
2176
2219
  return left, edge_cond_left, right, edge_cond_left_right, other_succ
2177
2220
  return None
@@ -2211,7 +2254,7 @@ class PhoenixStructurer(StructurerBase):
2211
2254
  ):
2212
2255
  edge_cond_left = self.cond_proc.recover_edge_condition(full_graph, start_node, left)
2213
2256
  edge_cond_right = self.cond_proc.recover_edge_condition(full_graph, start_node, right)
2214
- if claripy.is_true(claripy.Not(edge_cond_left) == edge_cond_right):
2257
+ if claripy.is_true(claripy.Not(edge_cond_left) == edge_cond_right): # type: ignore
2215
2258
  # c0 = !c0
2216
2259
  right_succs = list(full_graph.successors(right))
2217
2260
  left_succs = list(full_graph.successors(left))
@@ -2220,7 +2263,9 @@ class PhoenixStructurer(StructurerBase):
2220
2263
  if len([succ for succ in left_succs if succ is not else_node]) == 1:
2221
2264
  edge_cond_right_left = self.cond_proc.recover_edge_condition(full_graph, right, left)
2222
2265
  edge_cond_right_else = self.cond_proc.recover_edge_condition(full_graph, right, else_node)
2223
- if claripy.is_true(claripy.Not(edge_cond_right_left) == edge_cond_right_else):
2266
+ if claripy.is_true(
2267
+ claripy.Not(edge_cond_right_left) == edge_cond_right_else # type: ignore
2268
+ ):
2224
2269
  # c1 = !c1
2225
2270
  return left, edge_cond_left, right, edge_cond_right_left, else_node
2226
2271
  return None
@@ -2316,7 +2361,7 @@ class PhoenixStructurer(StructurerBase):
2316
2361
  return left, edge_cond_left, right, edge_cond_left_right, else_node
2317
2362
  return None
2318
2363
 
2319
- def _last_resort_refinement(self, head, graph: networkx.DiGraph, full_graph: networkx.DiGraph | None) -> bool:
2364
+ def _last_resort_refinement(self, head, graph: networkx.DiGraph, full_graph: networkx.DiGraph) -> bool:
2320
2365
  if self._improve_algorithm:
2321
2366
  while self._edge_virtualization_hints:
2322
2367
  src, dst = self._edge_virtualization_hints.pop(0)
@@ -2461,7 +2506,7 @@ class PhoenixStructurer(StructurerBase):
2461
2506
 
2462
2507
  @staticmethod
2463
2508
  def _find_node_going_to_dst(
2464
- node: SequenceNode,
2509
+ node: BaseNode,
2465
2510
  dst: Block | BaseNode,
2466
2511
  last=True,
2467
2512
  condjump_only=False,
@@ -2477,6 +2522,14 @@ class PhoenixStructurer(StructurerBase):
2477
2522
  dst_addr = dst.addr
2478
2523
  dst_idx = dst.idx if isinstance(dst, Block) else ...
2479
2524
 
2525
+ class _Holder:
2526
+ """
2527
+ Holds parent_and_block and is accessible from within the handlers.
2528
+ """
2529
+
2530
+ parent_and_block: list[tuple[int, Any, Block | MultiNode | BreakNode]] = []
2531
+ block_id: int = -1
2532
+
2480
2533
  def _check(last_stmt):
2481
2534
  return (
2482
2535
  (
@@ -2512,34 +2565,34 @@ class PhoenixStructurer(StructurerBase):
2512
2565
  first_stmt = first_nonlabel_nonphi_statement(block)
2513
2566
  if first_stmt is not None:
2514
2567
  # this block has content. increment the block ID counter
2515
- walker.block_id += 1
2568
+ _Holder.block_id += 1
2516
2569
 
2517
2570
  if _check(first_stmt):
2518
- walker.parent_and_block.append((walker.block_id, parent, block))
2571
+ _Holder.parent_and_block.append((_Holder.block_id, parent, block))
2519
2572
  elif len(block.statements) > 1:
2520
2573
  last_stmt = block.statements[-1]
2521
2574
  if _check(last_stmt) or (
2522
2575
  not isinstance(last_stmt, (Jump, ConditionalJump))
2523
2576
  and block.addr + block.original_size == dst_addr
2524
2577
  ):
2525
- walker.parent_and_block.append((walker.block_id, parent, block))
2578
+ _Holder.parent_and_block.append((_Holder.block_id, parent, block))
2526
2579
 
2527
2580
  def _handle_MultiNode(block: MultiNode, parent=None, **kwargs): # pylint:disable=unused-argument
2528
2581
  if block.nodes and isinstance(block.nodes[-1], Block) and block.nodes[-1].statements:
2529
2582
  first_stmt = first_nonlabel_nonphi_statement(block)
2530
2583
  if first_stmt is not None:
2531
2584
  # this block has content. increment the block ID counter
2532
- walker.block_id += 1
2585
+ _Holder.block_id += 1
2533
2586
  if _check(block.nodes[-1].statements[-1]):
2534
- walker.parent_and_block.append((walker.block_id, parent, block))
2587
+ _Holder.parent_and_block.append((_Holder.block_id, parent, block))
2535
2588
 
2536
2589
  def _handle_BreakNode(break_node: BreakNode, parent=None, **kwargs): # pylint:disable=unused-argument
2537
- walker.block_id += 1
2590
+ _Holder.block_id += 1
2538
2591
  if break_node.target == dst_addr or (
2539
2592
  isinstance(break_node.target, Const) and break_node.target.value == dst_addr
2540
2593
  ):
2541
2594
  # FIXME: idx is ignored
2542
- walker.parent_and_block.append((walker.block_id, parent, break_node))
2595
+ _Holder.parent_and_block.append((_Holder.block_id, parent, break_node))
2543
2596
 
2544
2597
  walker = SequenceWalker(
2545
2598
  handlers={
@@ -2550,14 +2603,13 @@ class PhoenixStructurer(StructurerBase):
2550
2603
  update_seqnode_in_place=False,
2551
2604
  force_forward_scan=True,
2552
2605
  )
2553
- walker.parent_and_block: list[tuple[int, Any, Block | MultiNode]] = []
2554
- walker.block_id = -1
2606
+ _Holder.block_id = -1
2555
2607
  walker.walk(node)
2556
- if not walker.parent_and_block:
2608
+ if not _Holder.parent_and_block:
2557
2609
  return None, None, None
2558
2610
  if last:
2559
- return walker.parent_and_block[-1]
2560
- return walker.parent_and_block[0]
2611
+ return _Holder.parent_and_block[-1]
2612
+ return _Holder.parent_and_block[0]
2561
2613
 
2562
2614
  @staticmethod
2563
2615
  def _unpack_sequencenode_head(graph: networkx.DiGraph, seq: SequenceNode, new_seq=None):