angr 9.2.161__cp310-abi3-win_amd64.whl → 9.2.163__cp310-abi3-win_amd64.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 (51) hide show
  1. angr/__init__.py +1 -1
  2. angr/ailment/expression.py +12 -0
  3. angr/analyses/analysis.py +0 -1
  4. angr/analyses/cfg/cfg_base.py +6 -2
  5. angr/analyses/decompiler/ail_simplifier.py +20 -1
  6. angr/analyses/decompiler/block_simplifier.py +6 -3
  7. angr/analyses/decompiler/clinic.py +6 -6
  8. angr/analyses/decompiler/condition_processor.py +24 -0
  9. angr/analyses/decompiler/counters/call_counter.py +11 -1
  10. angr/analyses/decompiler/decompiler.py +3 -1
  11. angr/analyses/decompiler/graph_region.py +11 -2
  12. angr/analyses/decompiler/optimization_passes/const_prop_reverter.py +1 -1
  13. angr/analyses/decompiler/optimization_passes/lowered_switch_simplifier.py +1 -0
  14. angr/analyses/decompiler/optimization_passes/optimization_pass.py +31 -11
  15. angr/analyses/decompiler/optimization_passes/return_duplicator_low.py +2 -0
  16. angr/analyses/decompiler/region_simplifiers/goto.py +3 -3
  17. angr/analyses/decompiler/region_simplifiers/if_.py +2 -2
  18. angr/analyses/decompiler/region_simplifiers/loop.py +2 -2
  19. angr/analyses/decompiler/structured_codegen/c.py +3 -3
  20. angr/analyses/decompiler/structuring/dream.py +1 -1
  21. angr/analyses/decompiler/structuring/phoenix.py +119 -67
  22. angr/analyses/decompiler/structuring/recursive_structurer.py +3 -2
  23. angr/analyses/decompiler/structuring/sailr.py +51 -43
  24. angr/analyses/decompiler/structuring/structurer_base.py +2 -3
  25. angr/analyses/deobfuscator/string_obf_opt_passes.py +1 -1
  26. angr/analyses/disassembly.py +1 -1
  27. angr/analyses/fcp/fcp.py +11 -10
  28. angr/analyses/flirt/flirt_sig.py +5 -2
  29. angr/analyses/reaching_definitions/function_handler.py +2 -1
  30. angr/analyses/reaching_definitions/function_handler_library/stdio.py +7 -6
  31. angr/analyses/reaching_definitions/function_handler_library/stdlib.py +10 -4
  32. angr/analyses/reaching_definitions/function_handler_library/string.py +13 -2
  33. angr/analyses/reaching_definitions/function_handler_library/unistd.py +7 -0
  34. angr/analyses/s_propagator.py +2 -2
  35. angr/analyses/variable_recovery/engine_base.py +8 -4
  36. angr/knowledge_plugins/functions/function.py +1 -1
  37. angr/knowledge_plugins/functions/function_manager.py +1 -2
  38. angr/project.py +5 -2
  39. angr/rustylib.pyd +0 -0
  40. angr/sim_type.py +2 -2
  41. angr/simos/javavm.py +1 -1
  42. angr/unicornlib.dll +0 -0
  43. angr/utils/graph.py +28 -12
  44. angr/utils/library.py +13 -12
  45. angr/utils/ssa/__init__.py +54 -2
  46. {angr-9.2.161.dist-info → angr-9.2.163.dist-info}/METADATA +5 -5
  47. {angr-9.2.161.dist-info → angr-9.2.163.dist-info}/RECORD +51 -51
  48. {angr-9.2.161.dist-info → angr-9.2.163.dist-info}/WHEEL +0 -0
  49. {angr-9.2.161.dist-info → angr-9.2.163.dist-info}/entry_points.txt +0 -0
  50. {angr-9.2.161.dist-info → angr-9.2.163.dist-info}/licenses/LICENSE +0 -0
  51. {angr-9.2.161.dist-info → angr-9.2.163.dist-info}/top_level.txt +0 -0
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.161"
5
+ __version__ = "9.2.163"
6
6
 
7
7
  if bytes is str:
8
8
  raise Exception(
@@ -102,6 +102,18 @@ class Const(Atom):
102
102
  self.value = value
103
103
  self.bits = bits
104
104
 
105
+ @property
106
+ def value_int(self) -> int:
107
+ if isinstance(self.value, int):
108
+ return self.value
109
+ raise TypeError(f"Incorrect value type; expect int, got {type(self.value)}")
110
+
111
+ @property
112
+ def value_float(self) -> float:
113
+ if isinstance(self.value, float):
114
+ return self.value
115
+ raise TypeError(f"Incorrect value type; expect float, got {type(self.value)}")
116
+
105
117
  @property
106
118
  def size(self):
107
119
  return self.bits // 8
angr/analyses/analysis.py CHANGED
@@ -1,4 +1,3 @@
1
- # ruff: noqa: F401
2
1
  from __future__ import annotations
3
2
  import functools
4
3
  import sys
@@ -1868,7 +1868,7 @@ class CFGBase(Analysis):
1868
1868
  should_merge = True
1869
1869
  functions_to_merge = set()
1870
1870
  i = func_pos + 1
1871
- while i < len(all_func_addrs):
1871
+ while i < len(all_func_addrs) and all_func_addrs[i] < endpoint_addr:
1872
1872
  f_addr = all_func_addrs[i]
1873
1873
  i += 1
1874
1874
  f = functions[f_addr]
@@ -2566,7 +2566,11 @@ class CFGBase(Analysis):
2566
2566
  """
2567
2567
 
2568
2568
  if arch.name == "X86" or arch.name == "AMD64":
2569
- if set(block.bytes) == {0x90}:
2569
+ block_bytes_set = set(block.bytes)
2570
+ if block_bytes_set == {0x90}:
2571
+ return True
2572
+ if block_bytes_set == {0xCC}:
2573
+ # technically this is not a no-op, but for our purposes we can settle for now
2570
2574
  return True
2571
2575
  elif arch.name == "MIPS32":
2572
2576
  if arch.memory_endness == "Iend_BE":
@@ -10,7 +10,16 @@ import networkx
10
10
 
11
11
  from angr.ailment import AILBlockWalker
12
12
  from angr.ailment.block import Block
13
- from angr.ailment.statement import Statement, Assignment, Store, Call, ConditionalJump, DirtyStatement, WeakAssignment
13
+ from angr.ailment.statement import (
14
+ Statement,
15
+ Assignment,
16
+ Store,
17
+ Call,
18
+ ConditionalJump,
19
+ DirtyStatement,
20
+ WeakAssignment,
21
+ Return,
22
+ )
14
23
  from angr.ailment.expression import (
15
24
  Register,
16
25
  Convert,
@@ -1643,6 +1652,11 @@ class AILSimplifier(Analysis):
1643
1652
  stackarg_offsets = (
1644
1653
  {(tpl[1] & mask) for tpl in self._stack_arg_offsets} if self._stack_arg_offsets is not None else None
1645
1654
  )
1655
+ retpoints: set[tuple[int, int]] = {
1656
+ (node.addr, node.idx)
1657
+ for node in self.func_graph
1658
+ if node.statements and isinstance(node.statements[-1], Return) and self.func_graph.out_degree[node] == 0
1659
+ }
1646
1660
 
1647
1661
  while True:
1648
1662
  new_dead_vars_found = False
@@ -1676,6 +1690,11 @@ class AILSimplifier(Analysis):
1676
1690
  elif vvar_id in self._secondary_stackvars:
1677
1691
  # secondary stack variables are potentially removable
1678
1692
  pass
1693
+ elif (def_codeloc.block_addr, def_codeloc.block_idx) in retpoints:
1694
+ # slack variable assignments in endpoint blocks are potentially removable.
1695
+ # note that this is a hack! we should rely on more reliable stack variable
1696
+ # eliminatability detection.
1697
+ pass
1679
1698
  elif stackarg_offsets is not None:
1680
1699
  # we always remove definitions for stack arguments
1681
1700
  assert vvar.stack_offset is not None
@@ -5,15 +5,14 @@ from typing import TYPE_CHECKING
5
5
  from collections.abc import Iterable, Mapping
6
6
 
7
7
  from angr.ailment.statement import Statement, Assignment, Call, Store, Jump
8
- from angr.ailment.expression import Tmp, Load, Const, Register, Convert, Expression
8
+ from angr.ailment.expression import Tmp, Load, Const, Register, Convert, Expression, VirtualVariable
9
9
  from angr.ailment import AILBlockWalkerBase
10
-
11
10
  from angr.code_location import ExternalCodeLocation, CodeLocation
12
-
13
11
  from angr.knowledge_plugins.key_definitions import atoms
14
12
  from angr.analyses.s_propagator import SPropagatorAnalysis
15
13
  from angr.analyses.s_reaching_definitions import SReachingDefinitionsAnalysis, SRDAModel
16
14
  from angr.analyses import Analysis, register_analysis
15
+ from angr.utils.ssa import has_reference_to_vvar
17
16
  from .peephole_optimizations import (
18
17
  MULTI_STMT_OPTS,
19
18
  STMT_OPTS,
@@ -247,6 +246,10 @@ class BlockSimplifier(Analysis):
247
246
  # don't replace
248
247
  r = False
249
248
  new_stmt = None
249
+ elif isinstance(old, VirtualVariable) and has_reference_to_vvar(stmt, old.varid):
250
+ # never replace an l-value with an r-value
251
+ r = False
252
+ new_stmt = None
250
253
  elif isinstance(stmt, Call) and isinstance(new, Call) and old == stmt.ret_expr:
251
254
  # special case: do not replace the ret_expr of a call statement to another call statement
252
255
  r = False
@@ -1216,6 +1216,7 @@ class Clinic(Analysis):
1216
1216
  ):
1217
1217
  # found a single successor - replace the last statement
1218
1218
  new_last_stmt = last_stmt.copy()
1219
+ assert isinstance(successors[0].addr, int)
1219
1220
  new_last_stmt.target = ailment.Expr.Const(None, None, successors[0].addr, last_stmt.target.bits)
1220
1221
  block.statements[-1] = new_last_stmt
1221
1222
 
@@ -1844,8 +1845,6 @@ class Clinic(Analysis):
1844
1845
  if v.offset in vr.stack_offset_typevars:
1845
1846
  tv = vr.stack_offset_typevars[v.offset]
1846
1847
  tv_max_sizes[tv] = s
1847
- # clean up existing types for this function
1848
- var_manager.remove_types()
1849
1848
  # TODO: Type inference for global variables
1850
1849
  # run type inference
1851
1850
  if self._must_struct:
@@ -2158,9 +2157,9 @@ class Clinic(Analysis):
2158
2157
  }
2159
2158
  else:
2160
2159
  # global variable?
2161
- global_vars = global_variables.get_global_variables(expr.value)
2160
+ global_vars = global_variables.get_global_variables(expr.value_int)
2162
2161
  # detect if there is a related symbol
2163
- if not global_vars and self.project.loader.find_object_containing(expr.value):
2162
+ if not global_vars and self.project.loader.find_object_containing(expr.value_int):
2164
2163
  symbol = self.project.loader.find_symbol(expr.value)
2165
2164
  if symbol is not None:
2166
2165
  # Create a new global variable if there isn't one already
@@ -3041,12 +3040,12 @@ class Clinic(Analysis):
3041
3040
  op0, op1 = addr.operands
3042
3041
  if (
3043
3042
  isinstance(op0, ailment.Expr.Const)
3044
- and self.project.loader.find_object_containing(op0.value) is not None
3043
+ and self.project.loader.find_object_containing(op0.value_int) is not None
3045
3044
  ):
3046
3045
  return op0, op1
3047
3046
  if (
3048
3047
  isinstance(op1, ailment.Expr.Const)
3049
- and self.project.loader.find_object_containing(op1.value) is not None
3048
+ and self.project.loader.find_object_containing(op1.value_int) is not None
3050
3049
  ):
3051
3050
  return op1, op0
3052
3051
  return op0, op1 # best-effort guess
@@ -3279,6 +3278,7 @@ class Clinic(Analysis):
3279
3278
  )
3280
3279
  ):
3281
3280
  # found it!
3281
+ assert self.project.arch.sp_offset is not None
3282
3282
  alloca_node = node
3283
3283
  sp_equal_to = ailment.Expr.BinaryOp(
3284
3284
  None,
@@ -241,6 +241,30 @@ class ConditionProcessor:
241
241
  self.guarding_conditions = {}
242
242
  self._ast2annotations = {}
243
243
 
244
+ def have_opposite_edge_conditions(self, graph: networkx.DiGraph, src, dst0, dst1) -> bool:
245
+ """
246
+ Check if the edge conditions of two edges (src, dst0) and (src, dst1) are opposite to each other. Try to avoid
247
+ condition translation if possible.
248
+ """
249
+
250
+ if src in graph and graph.out_degree[src] == 2 and graph.has_edge(src, dst0) and graph.has_edge(src, dst1):
251
+ # sometimes the last statement is the conditional jump. sometimes it's the first statement of the block
252
+ if isinstance(src, ailment.Block) and src.statements and is_head_controlled_loop_block(src):
253
+ last_stmt = next(
254
+ iter(stmt for stmt in src.statements[:-1] if isinstance(stmt, ailment.Stmt.ConditionalJump)), None
255
+ )
256
+ assert last_stmt is not None
257
+ else:
258
+ last_stmt = self.get_last_statement(src)
259
+
260
+ if isinstance(last_stmt, ailment.Stmt.ConditionalJump):
261
+ return True
262
+
263
+ # fallback
264
+ edge_cond_left = self.recover_edge_condition(graph, src, dst0)
265
+ edge_cond_right = self.recover_edge_condition(graph, src, dst1)
266
+ return claripy.is_true(claripy.Not(edge_cond_left) == edge_cond_right) # type: ignore
267
+
244
268
  def recover_edge_condition(self, graph: networkx.DiGraph, src, dst):
245
269
  edge = src, dst
246
270
  edge_data = graph.get_edge_data(*edge)
@@ -2,7 +2,7 @@ from __future__ import annotations
2
2
  from typing import TYPE_CHECKING
3
3
 
4
4
  from angr.ailment import Block
5
- from angr.ailment.statement import Label
5
+ from angr.ailment.statement import Label, ConditionalJump
6
6
  from angr.ailment.block_walker import AILBlockWalkerBase
7
7
 
8
8
  from angr.analyses.decompiler.sequence_walker import SequenceWalker
@@ -18,6 +18,9 @@ class AILBlockCallCounter(AILBlockWalkerBase):
18
18
 
19
19
  calls = 0
20
20
 
21
+ def _handle_ConditionalJump(self, stmt_idx: int, stmt: ConditionalJump, block: Block | None):
22
+ return
23
+
21
24
  def _handle_CallExpr(self, expr_idx: int, expr: Call, stmt_idx: int, stmt, block: Block | None):
22
25
  self.calls += 1
23
26
  super()._handle_CallExpr(expr_idx, expr, stmt_idx, stmt, block)
@@ -40,6 +43,13 @@ class AILCallCounter(SequenceWalker):
40
43
  self.calls = 0
41
44
  self.non_label_stmts = 0
42
45
 
46
+ def _handle_Condition(self, node, **kwargs):
47
+ # do not count calls in conditions
48
+ if node.true_node is not None:
49
+ super()._handle(node.true_node, **kwargs)
50
+ if node.false_node is not None:
51
+ super()._handle(node.false_node, **kwargs)
52
+
43
53
  def _handle_Block(self, node: Block, **kwargs): # pylint:disable=unused-argument
44
54
  ctr = AILBlockCallCounter()
45
55
  ctr.walk(node)
@@ -618,7 +618,9 @@ class Decompiler(Analysis):
618
618
  new_type = var_manager.get_variable_type(var)
619
619
  if new_type is not None:
620
620
  self.func.prototype.args = (
621
- self.func.prototype.args[:i] + (new_type,) + self.func.prototype.args[i + 1 :]
621
+ *self.func.prototype.args[:i],
622
+ new_type,
623
+ *self.func.prototype.args[i + 1 :],
622
624
  )
623
625
  except Exception: # pylint:disable=broad-except
624
626
  if self._fail_fast:
@@ -324,6 +324,15 @@ class GraphRegion:
324
324
  out_edges = list(graph.out_edges(node))
325
325
 
326
326
  graph.remove_node(node)
327
+
328
+ # FIXME: this is a giant hack to work around the problem that the graph region might have been restructured
329
+ # but not updated in *all* other regions whose .graph_with_successors references this graph region (we only
330
+ # update the parent_region graph right now).
331
+ existing_graph_regions: dict[int, GraphRegion] = {r.addr: r for r in graph if isinstance(r, GraphRegion)}
332
+ for r in sub_graph:
333
+ if isinstance(r, GraphRegion) and r not in graph and r.addr in existing_graph_regions:
334
+ self._replaced_regions[r] = existing_graph_regions[r.addr]
335
+
327
336
  sub_graph_nodes = [self._replaced_regions.get(nn, nn) for nn in sub_graph.nodes]
328
337
  sub_graph_edges = [
329
338
  (self._replaced_regions.get(src, src), self._replaced_regions.get(dst, dst)) for src, dst in sub_graph.edges
@@ -376,11 +385,11 @@ class GraphRegion:
376
385
  else:
377
386
  if dst_in_subgraph in sub_graph:
378
387
  for src in sub_graph.predecessors(dst_in_subgraph):
379
- graph.add_edge(src, dst)
388
+ graph.add_edge(self._replaced_regions.get(src, src), dst)
380
389
  elif reference_full_graph is not None and dst_in_subgraph in reference_full_graph:
381
390
  for src in reference_full_graph.predecessors(dst_in_subgraph):
382
391
  if src in graph:
383
- graph.add_edge(src, dst)
392
+ graph.add_edge(self._replaced_regions.get(src, src), dst)
384
393
  else:
385
394
  # it may happen that the dst node no longer exists in sub_graph or its successors
386
395
  # this is because we have deemed that the dst node is no longer a valid successor for sub_graph
@@ -313,7 +313,7 @@ class ConstPropOptReverter(OptimizationPass):
313
313
 
314
314
  # construct new constant block
315
315
  new_const_block = const_block.copy()
316
- new_const_block.statements = new_const_block.statements[:-1] + [reg_assign] + [symb_return_stmt.copy()]
316
+ new_const_block.statements = [*new_const_block.statements[:-1], reg_assign, symb_return_stmt.copy()]
317
317
  self._update_block(const_block, new_const_block)
318
318
  self.resolution = True
319
319
  else:
@@ -159,6 +159,7 @@ class LoweredSwitchSimplifier(StructuringOptimizationPass):
159
159
  def __init__(self, func, min_distinct_cases=2, **kwargs):
160
160
  super().__init__(
161
161
  func,
162
+ require_structurable_graph=False,
162
163
  require_gotos=False,
163
164
  prevent_new_gotos=False,
164
165
  simplify_ail=False,
@@ -15,7 +15,7 @@ from angr.analyses.decompiler.ailgraph_walker import AILGraphWalker
15
15
  from angr.analyses.decompiler.condition_processor import ConditionProcessor
16
16
  from angr.analyses.decompiler.goto_manager import Goto, GotoManager
17
17
  from angr.analyses.decompiler.structuring import RecursiveStructurer, SAILRStructurer
18
- from angr.analyses.decompiler.utils import add_labels, remove_edges_in_ailgraph
18
+ from angr.analyses.decompiler.utils import add_labels, remove_edges_in_ailgraph, is_empty_node
19
19
  from angr.analyses.decompiler.counters import ControlFlowStructureCounter
20
20
  from angr.project import Project
21
21
 
@@ -432,12 +432,13 @@ class StructuringOptimizationPass(OptimizationPass):
432
432
  STAGE = OptimizationPassStage.DURING_REGION_IDENTIFICATION
433
433
 
434
434
  _initial_gotos: set[Goto]
435
- _goto_manager: GotoManager
435
+ _goto_manager: GotoManager | None
436
436
  _prev_graph: networkx.DiGraph
437
437
 
438
438
  def __init__(
439
439
  self,
440
440
  func,
441
+ require_structurable_graph: bool = True,
441
442
  prevent_new_gotos: bool = True,
442
443
  strictly_less_gotos: bool = False,
443
444
  recover_structure_fails: bool = True,
@@ -450,6 +451,7 @@ class StructuringOptimizationPass(OptimizationPass):
450
451
  **kwargs,
451
452
  ):
452
453
  super().__init__(func, **kwargs)
454
+ self._require_structurable_graph = require_structurable_graph
453
455
  self._prevent_new_gotos = prevent_new_gotos
454
456
  self._strictly_less_gotos = strictly_less_gotos
455
457
  self._recover_structure_fails = recover_structure_fails
@@ -459,6 +461,8 @@ class StructuringOptimizationPass(OptimizationPass):
459
461
  self._must_improve_rel_quality = must_improve_rel_quality
460
462
  self._readd_labels = readd_labels
461
463
  self._edges_to_remove = edges_to_remove or []
464
+ self._goto_manager = None
465
+ self._initial_gotos = set()
462
466
 
463
467
  # relative quality metrics (excludes gotos)
464
468
  self._initial_structure_counter = None
@@ -476,13 +480,20 @@ class StructuringOptimizationPass(OptimizationPass):
476
480
  if not ret:
477
481
  return
478
482
 
479
- if not self._graph_is_structurable(self._graph, initial=True):
480
- return
483
+ # only initialize self._goto_manager if this optimization requires a structurable graph or gotos
484
+ initial_structurable: bool | None = None
485
+ if self._require_structurable_graph or self._require_gotos or self._prevent_new_gotos:
486
+ initial_structurable = self._graph_is_structurable(self._graph, initial=True)
481
487
 
482
- self._initial_gotos = self._goto_manager.gotos.copy()
483
- if self._require_gotos and not self._initial_gotos:
488
+ if self._require_structurable_graph and initial_structurable is False:
484
489
  return
485
490
 
491
+ if self._require_gotos:
492
+ assert self._goto_manager is not None
493
+ self._initial_gotos = self._goto_manager.gotos.copy()
494
+ if not self._initial_gotos:
495
+ return
496
+
486
497
  # setup for the very first analysis
487
498
  self.out_graph = networkx.DiGraph(self._graph)
488
499
  if self._max_opt_iters > 1:
@@ -500,7 +511,13 @@ class StructuringOptimizationPass(OptimizationPass):
500
511
  if self._readd_labels:
501
512
  self.out_graph = add_labels(self.out_graph)
502
513
 
503
- if not self._graph_is_structurable(self.out_graph, readd_labels=False):
514
+ if (
515
+ self._require_structurable_graph
516
+ and self._max_opt_iters <= 1
517
+ and not self._graph_is_structurable(self.out_graph, readd_labels=False)
518
+ ):
519
+ # fixed-point analysis ensures that the output graph is always structurable, otherwise it clears the output
520
+ # graph. so we only check the structurability of the graph when fixed-point analysis did not run.
504
521
  self.out_graph = None
505
522
  return
506
523
 
@@ -523,13 +540,16 @@ class StructuringOptimizationPass(OptimizationPass):
523
540
  return
524
541
 
525
542
  def _get_new_gotos(self):
543
+ assert self._goto_manager is not None
526
544
  return self._goto_manager.gotos
527
545
 
528
546
  def _fixed_point_analyze(self, cache=None):
529
547
  had_any_changes = False
530
548
  for _ in range(self._max_opt_iters):
531
- if self._require_gotos and not self._goto_manager.gotos:
532
- break
549
+ if self._require_gotos:
550
+ assert self._goto_manager is not None
551
+ if not self._goto_manager.gotos:
552
+ break
533
553
 
534
554
  # backup the graph before the optimization
535
555
  if self._recover_structure_fails and self.out_graph is not None:
@@ -590,7 +610,7 @@ class StructuringOptimizationPass(OptimizationPass):
590
610
  _l.warning("Internal structuring failed for OptimizationPass on %s", self._func.name)
591
611
  rs = None
592
612
 
593
- if not rs or not rs.result or not rs.result.nodes or rs.result_incomplete:
613
+ if not rs or not rs.result or is_empty_node(rs.result) or rs.result_incomplete:
594
614
  return False
595
615
 
596
616
  rs = self.project.analyses.RegionSimplifier(self._func, rs.result, arg_vvars=self._arg_vvars, kb=self.kb)
@@ -648,7 +668,7 @@ class StructuringOptimizationPass(OptimizationPass):
648
668
  # Gotos play an important part in readability and control flow structure. We already count gotos in other parts
649
669
  # of the analysis, so we don't need to count them here. However, some gotos are worse than others. Much
650
670
  # like loops, trading gotos (keeping the same total, but getting worse types), is bad for decompilation.
651
- if len(self._initial_gotos) == len(self._goto_manager.gotos) != 0:
671
+ if self._goto_manager is not None and len(self._initial_gotos) == len(self._goto_manager.gotos) != 0:
652
672
  prev_labels = self._initial_structure_counter.goto_targets
653
673
  curr_labels = self._current_structure_counter.goto_targets
654
674
 
@@ -55,6 +55,7 @@ class ReturnDuplicatorLow(StructuringOptimizationPass, ReturnDuplicatorBase):
55
55
  region_identifier=None,
56
56
  vvar_id_start: int | None = None,
57
57
  scratch: dict[str, Any] | None = None,
58
+ max_func_blocks: int = 500,
58
59
  **kwargs,
59
60
  ):
60
61
  StructuringOptimizationPass.__init__(
@@ -76,6 +77,7 @@ class ReturnDuplicatorLow(StructuringOptimizationPass, ReturnDuplicatorBase):
76
77
  ri=region_identifier,
77
78
  vvar_id_start=vvar_id_start,
78
79
  scratch=scratch,
80
+ max_func_blocks=max_func_blocks,
79
81
  )
80
82
  self.analyze()
81
83
 
@@ -58,7 +58,7 @@ class GotoSimplifier(SequenceWalker):
58
58
  :return:
59
59
  """
60
60
 
61
- for n0, n1 in zip(node.nodes, node.nodes[1:] + [successor]):
61
+ for n0, n1 in zip(node.nodes, [*node.nodes[1:], successor]):
62
62
  self._handle(n0, successor=n1)
63
63
 
64
64
  def _handle_codenode(self, node, successor=None, **kwargs):
@@ -109,7 +109,7 @@ class GotoSimplifier(SequenceWalker):
109
109
  :return:
110
110
  """
111
111
 
112
- for n0, n1 in zip(node.nodes, node.nodes[1:] + [successor]):
112
+ for n0, n1 in zip(node.nodes, [*node.nodes[1:], successor]):
113
113
  self._handle(n0, successor=n1)
114
114
 
115
115
  def _handle_block(self, block, successor=None, **kwargs): # pylint:disable=no-self-use
@@ -170,7 +170,7 @@ class GotoSimplifier(SequenceWalker):
170
170
  dst_target = goto_stmt.true_target
171
171
  # false branch of a conditional jump
172
172
  else:
173
- dst_target = goto_stmt.true_target
173
+ dst_target = goto_stmt.false_target
174
174
 
175
175
  src_ins_addr = goto_stmt.ins_addr if "ins_addr" in goto_stmt.tags else block.addr
176
176
  goto = Goto(block.addr, dst_target.value, src_idx=block.idx, dst_idx=None, src_ins_addr=src_ins_addr)
@@ -44,7 +44,7 @@ class IfSimplifier(SequenceWalker):
44
44
  :return:
45
45
  """
46
46
 
47
- for n0, n1 in zip(node.nodes, node.nodes[1:] + [successor]):
47
+ for n0, n1 in zip(node.nodes, [*node.nodes[1:], successor]):
48
48
  self._handle(n0, successor=n1)
49
49
 
50
50
  def _handle_codenode(self, node, successor=None, **kwargs):
@@ -92,7 +92,7 @@ class IfSimplifier(SequenceWalker):
92
92
  :return:
93
93
  """
94
94
 
95
- for n0, n1 in zip(node.nodes, node.nodes[1:] + [successor]):
95
+ for n0, n1 in zip(node.nodes, [*node.nodes[1:], successor]):
96
96
  self._handle(n0, successor=n1)
97
97
 
98
98
  def _handle_block(self, block, successor=None, **kwargs): # pylint:disable=no-self-use
@@ -47,7 +47,7 @@ class LoopSimplifier(SequenceWalker):
47
47
  )
48
48
 
49
49
  def _handle_sequencenode(self, node, predecessor=None, successor=None, loop=None, loop_successor=None, **kwargs):
50
- for n0, n1, n2 in zip(node.nodes, node.nodes[1:] + [successor], [predecessor] + node.nodes[:-1]):
50
+ for n0, n1, n2 in zip(node.nodes, [*node.nodes[1:], successor], [predecessor, *node.nodes[:-1]]):
51
51
  self._handle(n0, predecessor=n2, successor=n1, loop=loop, loop_successor=loop_successor)
52
52
 
53
53
  def _handle_codenode(self, node, predecessor=None, successor=None, loop=None, loop_successor=None, **kwargs):
@@ -126,7 +126,7 @@ class LoopSimplifier(SequenceWalker):
126
126
  predecessor.statements = predecessor.statements[:-1]
127
127
 
128
128
  def _handle_multinode(self, node, predecessor=None, successor=None, loop=None, loop_successor=None, **kwargs):
129
- for n0, n1, n2 in zip(node.nodes, node.nodes[1:] + [successor], [predecessor] + node.nodes[:-1]):
129
+ for n0, n1, n2 in zip(node.nodes, [*node.nodes[1:], successor], [predecessor, *node.nodes[:-1]]):
130
130
  self._handle(n0, predecessor=n2, successor=n1, loop=loop, loop_successor=loop_successor)
131
131
 
132
132
  def _handle_block(
@@ -581,7 +581,7 @@ class CFunction(CConstruct): # pylint:disable=abstract-method
581
581
  yield " ", None
582
582
  # function name
583
583
  if self.demangled_name and self.show_demangled_name:
584
- normalized_name = get_cpp_function_name(self.demangled_name, specialized=False, qualified=True)
584
+ normalized_name = get_cpp_function_name(self.demangled_name)
585
585
  else:
586
586
  normalized_name = self.name
587
587
  yield normalized_name, self
@@ -1357,7 +1357,7 @@ class CFunctionCall(CStatement, CExpression):
1357
1357
 
1358
1358
  if self.callee_func is not None:
1359
1359
  if self.callee_func.demangled_name and self.show_demangled_name:
1360
- func_name = get_cpp_function_name(self.callee_func.demangled_name, specialized=False, qualified=True)
1360
+ func_name = get_cpp_function_name(self.callee_func.demangled_name)
1361
1361
  else:
1362
1362
  func_name = self.callee_func.name
1363
1363
  if (
@@ -2240,7 +2240,7 @@ class CConstant(CExpression):
2240
2240
  yield CConstant.str_to_c_str(v.content.decode("utf-8")), self
2241
2241
  return
2242
2242
  elif isinstance(v, Function):
2243
- yield get_cpp_function_name(v.demangled_name, specialized=False, qualified=True), self
2243
+ yield get_cpp_function_name(v.demangled_name), self
2244
2244
  return
2245
2245
  elif isinstance(v, str):
2246
2246
  yield CConstant.str_to_c_str(v), self
@@ -466,7 +466,7 @@ class DreamStructurer(StructurerBase):
466
466
  self._merge_nodes(node_0.node, node_1.node),
467
467
  node_0.reaching_condition,
468
468
  )
469
- seq.nodes = seq.nodes[:i] + [new_node] + seq.nodes[i + 2 :]
469
+ seq.nodes = [*seq.nodes[:i], new_node, *seq.nodes[i + 2 :]]
470
470
  continue
471
471
  i += 1
472
472