angr 9.2.161__cp310-abi3-macosx_11_0_arm64.whl → 9.2.163__cp310-abi3-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 +1 -1
- angr/ailment/expression.py +12 -0
- angr/analyses/analysis.py +0 -1
- angr/analyses/cfg/cfg_base.py +6 -2
- angr/analyses/decompiler/ail_simplifier.py +20 -1
- angr/analyses/decompiler/block_simplifier.py +6 -3
- angr/analyses/decompiler/clinic.py +6 -6
- angr/analyses/decompiler/condition_processor.py +24 -0
- angr/analyses/decompiler/counters/call_counter.py +11 -1
- angr/analyses/decompiler/decompiler.py +3 -1
- angr/analyses/decompiler/graph_region.py +11 -2
- angr/analyses/decompiler/optimization_passes/const_prop_reverter.py +1 -1
- angr/analyses/decompiler/optimization_passes/lowered_switch_simplifier.py +1 -0
- angr/analyses/decompiler/optimization_passes/optimization_pass.py +31 -11
- angr/analyses/decompiler/optimization_passes/return_duplicator_low.py +2 -0
- angr/analyses/decompiler/region_simplifiers/goto.py +3 -3
- angr/analyses/decompiler/region_simplifiers/if_.py +2 -2
- angr/analyses/decompiler/region_simplifiers/loop.py +2 -2
- angr/analyses/decompiler/structured_codegen/c.py +3 -3
- angr/analyses/decompiler/structuring/dream.py +1 -1
- angr/analyses/decompiler/structuring/phoenix.py +119 -67
- angr/analyses/decompiler/structuring/recursive_structurer.py +3 -2
- angr/analyses/decompiler/structuring/sailr.py +51 -43
- angr/analyses/decompiler/structuring/structurer_base.py +2 -3
- angr/analyses/deobfuscator/string_obf_opt_passes.py +1 -1
- angr/analyses/disassembly.py +1 -1
- angr/analyses/fcp/fcp.py +11 -10
- angr/analyses/flirt/flirt_sig.py +5 -2
- angr/analyses/reaching_definitions/function_handler.py +2 -1
- angr/analyses/reaching_definitions/function_handler_library/stdio.py +7 -6
- angr/analyses/reaching_definitions/function_handler_library/stdlib.py +10 -4
- angr/analyses/reaching_definitions/function_handler_library/string.py +13 -2
- angr/analyses/reaching_definitions/function_handler_library/unistd.py +7 -0
- angr/analyses/s_propagator.py +2 -2
- angr/analyses/variable_recovery/engine_base.py +8 -4
- angr/knowledge_plugins/functions/function.py +1 -1
- angr/knowledge_plugins/functions/function_manager.py +1 -2
- angr/project.py +5 -2
- angr/rustylib.abi3.so +0 -0
- angr/sim_type.py +2 -2
- angr/simos/javavm.py +1 -1
- angr/unicornlib.dylib +0 -0
- angr/utils/graph.py +28 -12
- angr/utils/library.py +13 -12
- angr/utils/ssa/__init__.py +54 -2
- {angr-9.2.161.dist-info → angr-9.2.163.dist-info}/METADATA +5 -5
- {angr-9.2.161.dist-info → angr-9.2.163.dist-info}/RECORD +51 -51
- {angr-9.2.161.dist-info → angr-9.2.163.dist-info}/WHEEL +0 -0
- {angr-9.2.161.dist-info → angr-9.2.163.dist-info}/entry_points.txt +0 -0
- {angr-9.2.161.dist-info → angr-9.2.163.dist-info}/licenses/LICENSE +0 -0
- {angr-9.2.161.dist-info → angr-9.2.163.dist-info}/top_level.txt +0 -0
angr/__init__.py
CHANGED
angr/ailment/expression.py
CHANGED
|
@@ -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
angr/analyses/cfg/cfg_base.py
CHANGED
|
@@ -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
|
-
|
|
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
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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]
|
|
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]
|
|
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
|
-
|
|
480
|
-
|
|
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.
|
|
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
|
|
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
|
|
532
|
-
|
|
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
|
|
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:]
|
|
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:]
|
|
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.
|
|
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:]
|
|
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:]
|
|
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:]
|
|
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:]
|
|
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
|
|
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
|
|
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
|
|
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]
|
|
469
|
+
seq.nodes = [*seq.nodes[:i], new_node, *seq.nodes[i + 2 :]]
|
|
470
470
|
continue
|
|
471
471
|
i += 1
|
|
472
472
|
|