angr 9.2.139__py3-none-manylinux2014_x86_64.whl → 9.2.140__py3-none-manylinux2014_x86_64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of angr might be problematic. Click here for more details.
- angr/__init__.py +1 -1
- angr/analyses/calling_convention/calling_convention.py +48 -21
- angr/analyses/cfg/cfg_base.py +13 -0
- angr/analyses/cfg/cfg_fast.py +11 -0
- angr/analyses/decompiler/ail_simplifier.py +67 -52
- angr/analyses/decompiler/clinic.py +68 -43
- angr/analyses/decompiler/decompiler.py +17 -7
- angr/analyses/decompiler/expression_narrower.py +1 -1
- angr/analyses/decompiler/optimization_passes/const_prop_reverter.py +8 -7
- angr/analyses/decompiler/optimization_passes/ite_region_converter.py +21 -13
- angr/analyses/decompiler/optimization_passes/optimization_pass.py +16 -10
- angr/analyses/decompiler/optimization_passes/return_duplicator_base.py +2 -2
- angr/analyses/decompiler/region_simplifiers/expr_folding.py +259 -108
- angr/analyses/decompiler/region_simplifiers/region_simplifier.py +27 -12
- angr/analyses/decompiler/structuring/dream.py +21 -17
- angr/analyses/decompiler/structuring/phoenix.py +152 -40
- angr/analyses/decompiler/structuring/recursive_structurer.py +1 -0
- angr/analyses/decompiler/structuring/structurer_base.py +36 -10
- angr/analyses/decompiler/structuring/structurer_nodes.py +4 -1
- angr/analyses/decompiler/utils.py +60 -1
- angr/analyses/deobfuscator/api_obf_finder.py +8 -5
- angr/analyses/deobfuscator/api_obf_type2_finder.py +18 -10
- angr/analyses/deobfuscator/string_obf_finder.py +105 -18
- angr/analyses/forward_analysis/forward_analysis.py +1 -1
- angr/analyses/propagator/top_checker_mixin.py +6 -6
- angr/analyses/reaching_definitions/__init__.py +2 -1
- angr/analyses/reaching_definitions/dep_graph.py +1 -12
- angr/analyses/reaching_definitions/engine_vex.py +36 -31
- angr/analyses/reaching_definitions/function_handler.py +15 -2
- angr/analyses/reaching_definitions/rd_state.py +1 -37
- angr/analyses/reaching_definitions/reaching_definitions.py +13 -24
- angr/analyses/s_propagator.py +6 -41
- angr/analyses/s_reaching_definitions/s_rda_model.py +7 -1
- angr/analyses/stack_pointer_tracker.py +36 -22
- angr/analyses/typehoon/simple_solver.py +45 -7
- angr/analyses/typehoon/typeconsts.py +18 -5
- angr/analyses/variable_recovery/engine_base.py +7 -5
- angr/block.py +69 -107
- angr/callable.py +14 -7
- angr/calling_conventions.py +15 -1
- angr/distributed/__init__.py +1 -1
- angr/engines/__init__.py +7 -8
- angr/engines/engine.py +1 -120
- angr/engines/failure.py +2 -2
- angr/engines/hook.py +2 -2
- angr/engines/light/engine.py +2 -2
- angr/engines/pcode/engine.py +2 -14
- angr/engines/procedure.py +2 -2
- angr/engines/soot/engine.py +2 -2
- angr/engines/soot/statements/switch.py +1 -1
- angr/engines/successors.py +124 -11
- angr/engines/syscall.py +2 -2
- angr/engines/unicorn.py +3 -3
- angr/engines/vex/heavy/heavy.py +3 -15
- angr/factory.py +4 -19
- angr/knowledge_plugins/key_definitions/atoms.py +8 -4
- angr/knowledge_plugins/key_definitions/live_definitions.py +41 -103
- angr/sim_type.py +19 -17
- angr/state_plugins/plugin.py +19 -4
- angr/storage/memory_mixins/memory_mixin.py +1 -1
- angr/storage/memory_mixins/paged_memory/pages/multi_values.py +10 -5
- angr/utils/ssa/__init__.py +119 -4
- {angr-9.2.139.dist-info → angr-9.2.140.dist-info}/METADATA +6 -6
- {angr-9.2.139.dist-info → angr-9.2.140.dist-info}/RECORD +68 -68
- {angr-9.2.139.dist-info → angr-9.2.140.dist-info}/LICENSE +0 -0
- {angr-9.2.139.dist-info → angr-9.2.140.dist-info}/WHEEL +0 -0
- {angr-9.2.139.dist-info → angr-9.2.140.dist-info}/entry_points.txt +0 -0
- {angr-9.2.139.dist-info → angr-9.2.140.dist-info}/top_level.txt +0 -0
|
@@ -89,6 +89,8 @@ class Clinic(Analysis):
|
|
|
89
89
|
A Clinic deals with AILments.
|
|
90
90
|
"""
|
|
91
91
|
|
|
92
|
+
_ail_manager: ailment.Manager
|
|
93
|
+
|
|
92
94
|
def __init__(
|
|
93
95
|
self,
|
|
94
96
|
func,
|
|
@@ -117,7 +119,6 @@ class Clinic(Analysis):
|
|
|
117
119
|
desired_variables: set[str] | None = None,
|
|
118
120
|
force_loop_single_exit: bool = True,
|
|
119
121
|
complete_successors: bool = False,
|
|
120
|
-
unsound_fix_abnormal_switches: bool = True,
|
|
121
122
|
):
|
|
122
123
|
if not func.normalized and mode == ClinicMode.DECOMPILE:
|
|
123
124
|
raise ValueError("Decompilation must work on normalized function graphs.")
|
|
@@ -135,7 +136,6 @@ class Clinic(Analysis):
|
|
|
135
136
|
self.optimization_scratch = optimization_scratch if optimization_scratch is not None else {}
|
|
136
137
|
|
|
137
138
|
self._func_graph: networkx.DiGraph | None = None
|
|
138
|
-
self._ail_manager = None
|
|
139
139
|
self._blocks_by_addr_and_size = {}
|
|
140
140
|
self.entry_node_addr: tuple[int, int | None] = self.function.addr, None
|
|
141
141
|
|
|
@@ -149,7 +149,6 @@ class Clinic(Analysis):
|
|
|
149
149
|
self._must_struct = must_struct
|
|
150
150
|
self._reset_variable_names = reset_variable_names
|
|
151
151
|
self._rewrite_ites_to_diamonds = rewrite_ites_to_diamonds
|
|
152
|
-
self._unsound_fix_abnormal_switches = unsound_fix_abnormal_switches
|
|
153
152
|
self.reaching_definitions: ReachingDefinitionsAnalysis | None = None
|
|
154
153
|
self._cache = cache
|
|
155
154
|
self._mode = mode
|
|
@@ -167,6 +166,7 @@ class Clinic(Analysis):
|
|
|
167
166
|
self._complete_successors = complete_successors
|
|
168
167
|
|
|
169
168
|
self._register_save_areas_removed: bool = False
|
|
169
|
+
self.edges_to_remove: list[tuple[tuple[int, int | None], tuple[int, int | None]]] = []
|
|
170
170
|
|
|
171
171
|
self._new_block_addrs = set()
|
|
172
172
|
|
|
@@ -209,6 +209,7 @@ class Clinic(Analysis):
|
|
|
209
209
|
|
|
210
210
|
:return:
|
|
211
211
|
"""
|
|
212
|
+
assert self.graph is not None
|
|
212
213
|
|
|
213
214
|
s = ""
|
|
214
215
|
|
|
@@ -297,6 +298,8 @@ class Clinic(Analysis):
|
|
|
297
298
|
return ail_graph
|
|
298
299
|
|
|
299
300
|
def _slice_variables(self, ail_graph):
|
|
301
|
+
assert self.variable_kb is not None
|
|
302
|
+
|
|
300
303
|
nodes_index = {(n.addr, n.idx): n for n in ail_graph.nodes()}
|
|
301
304
|
|
|
302
305
|
vfm = self.variable_kb.variables.function_managers[self.function.addr]
|
|
@@ -344,7 +347,7 @@ class Clinic(Analysis):
|
|
|
344
347
|
optimization_passes=[StackCanarySimplifier],
|
|
345
348
|
sp_shift=self._max_stack_depth,
|
|
346
349
|
vvar_id_start=self.vvar_id_start,
|
|
347
|
-
fail_fast=self._fail_fast,
|
|
350
|
+
fail_fast=self._fail_fast, # type: ignore
|
|
348
351
|
)
|
|
349
352
|
self.vvar_id_start = callee_clinic.vvar_id_start + 1
|
|
350
353
|
self._max_stack_depth = callee_clinic._max_stack_depth
|
|
@@ -618,6 +621,7 @@ class Clinic(Analysis):
|
|
|
618
621
|
|
|
619
622
|
# remove empty nodes from the graph
|
|
620
623
|
ail_graph = self.remove_empty_nodes(ail_graph)
|
|
624
|
+
# note that there are still edges to remove before we can structure this graph!
|
|
621
625
|
|
|
622
626
|
self.arg_list = arg_list
|
|
623
627
|
self.arg_vvars = arg_vvars
|
|
@@ -800,7 +804,7 @@ class Clinic(Analysis):
|
|
|
800
804
|
|
|
801
805
|
# case 2: the callee is a SimProcedure
|
|
802
806
|
if target_func.is_simprocedure:
|
|
803
|
-
cc = self.project.analyses.CallingConvention(target_func, fail_fast=self._fail_fast)
|
|
807
|
+
cc = self.project.analyses.CallingConvention(target_func, fail_fast=self._fail_fast) # type: ignore
|
|
804
808
|
if cc.cc is not None and cc.prototype is not None:
|
|
805
809
|
target_func.calling_convention = cc.cc
|
|
806
810
|
target_func.prototype = cc.prototype
|
|
@@ -808,7 +812,7 @@ class Clinic(Analysis):
|
|
|
808
812
|
|
|
809
813
|
# case 3: the callee is a PLT function
|
|
810
814
|
if target_func.is_plt:
|
|
811
|
-
cc = self.project.analyses.CallingConvention(target_func, fail_fast=self._fail_fast)
|
|
815
|
+
cc = self.project.analyses.CallingConvention(target_func, fail_fast=self._fail_fast) # type: ignore
|
|
812
816
|
if cc.cc is not None and cc.prototype is not None:
|
|
813
817
|
target_func.calling_convention = cc.cc
|
|
814
818
|
target_func.prototype = cc.prototype
|
|
@@ -878,7 +882,7 @@ class Clinic(Analysis):
|
|
|
878
882
|
# finally, recover the calling convention of the current function
|
|
879
883
|
if self.function.prototype is None or self.function.calling_convention is None:
|
|
880
884
|
self.project.analyses.CompleteCallingConventions(
|
|
881
|
-
fail_fast=self._fail_fast,
|
|
885
|
+
fail_fast=self._fail_fast, # type: ignore
|
|
882
886
|
recover_variables=True,
|
|
883
887
|
prioritize_func_addrs=[self.function.addr],
|
|
884
888
|
skip_other_funcs=True,
|
|
@@ -2109,49 +2113,60 @@ class Clinic(Analysis):
|
|
|
2109
2113
|
# the overlapped instructions and add an unconditional jump so that it jumps to 0x41da9d.
|
|
2110
2114
|
# this is the most common case created by jump threading optimization in compilers. it's easy to handle.
|
|
2111
2115
|
|
|
2112
|
-
# Case 2: the intended head and the other heads do not share the same suffix of instructions. in this
|
|
2113
|
-
#
|
|
2114
|
-
#
|
|
2115
|
-
#
|
|
2116
|
+
# Case 2 & 3: the intended head and the other heads do not share the same suffix of instructions. in this
|
|
2117
|
+
# case, we have two choices:
|
|
2118
|
+
# Case 2: The intended head has two successors, but at least one unintended head has only one successor.
|
|
2119
|
+
# we cannot reliably convert the blocks into a properly structured switch-case construct. we will
|
|
2120
|
+
# last instruction of all other heads to jump to the cmp instruction in the intended head, but do
|
|
2121
|
+
# not remove any other instructions in these other heads. this is unsound, but is the best we can
|
|
2122
|
+
# do in this case.
|
|
2123
|
+
# Case 3: The intended head has only one successor (which is the indirect jump node). during structuring,
|
|
2124
|
+
# we expect it will be structured as a no-default-node switch-case construct. in this case, we
|
|
2125
|
+
# can simply remove the edges from all other heads to the jump node and only leave the edge from
|
|
2126
|
+
# the intended head to the jump node. we will see goto statements in the output, but this will
|
|
2127
|
+
# lead to correct structuring result.
|
|
2116
2128
|
|
|
2117
2129
|
overlaps = [self._get_overlapping_suffix_instructions(intended_head, head) for head in other_heads]
|
|
2118
2130
|
if overlaps and (overlap := min(overlaps)) > 0:
|
|
2119
2131
|
# Case 1
|
|
2120
2132
|
self._fix_abnormal_switch_case_heads_case1(ail_graph, candidate, intended_head, other_heads, overlap)
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
None,
|
|
2137
|
-
)
|
|
2138
|
-
intended_head_block = self.project.factory.block(
|
|
2139
|
-
intended_head.addr, size=intended_head.original_size
|
|
2133
|
+
elif ail_graph.out_degree[intended_head] == 2:
|
|
2134
|
+
# Case 2
|
|
2135
|
+
l.warning("Switch-case at %#x has multiple head nodes but cannot be fixed soundly.", candidate.addr)
|
|
2136
|
+
# find the comparison instruction in the intended head
|
|
2137
|
+
comparison_stmt = None
|
|
2138
|
+
if "cc_op" in self.project.arch.registers:
|
|
2139
|
+
comparison_stmt = next(
|
|
2140
|
+
iter(
|
|
2141
|
+
stmt
|
|
2142
|
+
for stmt in intended_head.statements
|
|
2143
|
+
if isinstance(stmt, ailment.Stmt.Assignment)
|
|
2144
|
+
and isinstance(stmt.dst, ailment.Expr.Register)
|
|
2145
|
+
and stmt.dst.reg_offset == self.project.arch.registers["cc_op"][0]
|
|
2146
|
+
),
|
|
2147
|
+
None,
|
|
2140
2148
|
)
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
else:
|
|
2146
|
-
cmp_rpos = min(len(intended_head_block.instruction_addrs), 2)
|
|
2147
|
-
self._fix_abnormal_switch_case_heads_case2(
|
|
2148
|
-
ail_graph,
|
|
2149
|
-
candidate,
|
|
2150
|
-
intended_head,
|
|
2151
|
-
other_heads,
|
|
2152
|
-
intended_head_split_insns=cmp_rpos,
|
|
2153
|
-
other_head_split_insns=0,
|
|
2149
|
+
intended_head_block = self.project.factory.block(intended_head.addr, size=intended_head.original_size)
|
|
2150
|
+
if comparison_stmt is not None:
|
|
2151
|
+
cmp_rpos = len(intended_head_block.instruction_addrs) - intended_head_block.instruction_addrs.index(
|
|
2152
|
+
comparison_stmt.ins_addr
|
|
2154
2153
|
)
|
|
2154
|
+
else:
|
|
2155
|
+
cmp_rpos = min(len(intended_head_block.instruction_addrs), 2)
|
|
2156
|
+
self._fix_abnormal_switch_case_heads_case2(
|
|
2157
|
+
ail_graph,
|
|
2158
|
+
candidate,
|
|
2159
|
+
intended_head,
|
|
2160
|
+
other_heads,
|
|
2161
|
+
intended_head_split_insns=cmp_rpos,
|
|
2162
|
+
other_head_split_insns=0,
|
|
2163
|
+
)
|
|
2164
|
+
else:
|
|
2165
|
+
# Case 3
|
|
2166
|
+
self._fix_abnormal_switch_case_heads_case3(
|
|
2167
|
+
candidate,
|
|
2168
|
+
other_heads,
|
|
2169
|
+
)
|
|
2155
2170
|
|
|
2156
2171
|
def _get_overlapping_suffix_instructions(self, ailblock_0: ailment.Block, ailblock_1: ailment.Block) -> int:
|
|
2157
2172
|
# we first compare their ending conditional jumps
|
|
@@ -2360,6 +2375,16 @@ class Clinic(Analysis):
|
|
|
2360
2375
|
# it should be going to the default node. ignore it
|
|
2361
2376
|
pass
|
|
2362
2377
|
|
|
2378
|
+
def _fix_abnormal_switch_case_heads_case3(
|
|
2379
|
+
self, indirect_jump_node: ailment.Block, other_heads: list[ailment.Block]
|
|
2380
|
+
) -> None:
|
|
2381
|
+
# remove all edges from other_heads to the indirect jump node
|
|
2382
|
+
for other_head in other_heads:
|
|
2383
|
+
# delay the edge removal so that we don't mess up the SSA analysis
|
|
2384
|
+
self.edges_to_remove.append(
|
|
2385
|
+
((other_head.addr, other_head.idx), (indirect_jump_node.addr, indirect_jump_node.idx))
|
|
2386
|
+
)
|
|
2387
|
+
|
|
2363
2388
|
@staticmethod
|
|
2364
2389
|
def _remove_redundant_jump_blocks(ail_graph):
|
|
2365
2390
|
def first_conditional_jump(block: ailment.Block) -> ailment.Stmt.ConditionalJump | None:
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
from __future__ import annotations
|
|
3
3
|
import logging
|
|
4
4
|
from collections import defaultdict
|
|
5
|
-
from typing import Optional, Union, Any, TYPE_CHECKING
|
|
6
5
|
from collections.abc import Iterable
|
|
6
|
+
from typing import Optional, Union, Any, TYPE_CHECKING
|
|
7
7
|
|
|
8
8
|
import networkx
|
|
9
9
|
from cle import SymbolType
|
|
@@ -15,6 +15,7 @@ from angr.knowledge_base import KnowledgeBase
|
|
|
15
15
|
from angr.sim_variable import SimMemoryVariable, SimRegisterVariable, SimStackVariable
|
|
16
16
|
from angr.utils import timethis
|
|
17
17
|
from angr.analyses import Analysis, AnalysesHub
|
|
18
|
+
from .structured_codegen.c import CStructuredCodeGenerator
|
|
18
19
|
from .structuring import RecursiveStructurer, PhoenixStructurer, DEFAULT_STRUCTURER
|
|
19
20
|
from .region_identifier import RegionIdentifier
|
|
20
21
|
from .optimization_passes.optimization_pass import OptimizationPassStage
|
|
@@ -22,7 +23,7 @@ from .ailgraph_walker import AILGraphWalker
|
|
|
22
23
|
from .condition_processor import ConditionProcessor
|
|
23
24
|
from .decompilation_options import DecompilationOption
|
|
24
25
|
from .decompilation_cache import DecompilationCache
|
|
25
|
-
from .utils import remove_labels
|
|
26
|
+
from .utils import remove_labels, remove_edges_in_ailgraph
|
|
26
27
|
from .sequence_walker import SequenceWalker
|
|
27
28
|
from .structuring.structurer_nodes import SequenceNode
|
|
28
29
|
from .presets import DECOMPILATION_PRESETS, DecompilationPreset
|
|
@@ -30,7 +31,6 @@ from .presets import DECOMPILATION_PRESETS, DecompilationPreset
|
|
|
30
31
|
if TYPE_CHECKING:
|
|
31
32
|
from angr.knowledge_plugins.cfg.cfg_model import CFGModel
|
|
32
33
|
from .peephole_optimizations import PeepholeOptimizationExprBase, PeepholeOptimizationStmtBase
|
|
33
|
-
from .structured_codegen.c import CStructuredCodeGenerator
|
|
34
34
|
|
|
35
35
|
l = logging.getLogger(name=__name__)
|
|
36
36
|
|
|
@@ -157,6 +157,8 @@ class Decompiler(Analysis):
|
|
|
157
157
|
self.kb.decompilations[(self.func.addr, self._flavor)].errors.append(error.format())
|
|
158
158
|
|
|
159
159
|
def _can_use_decompilation_cache(self, cache: DecompilationCache) -> bool:
|
|
160
|
+
if self._cache_parameters is None or cache.parameters is None:
|
|
161
|
+
return False
|
|
160
162
|
a, b = self._cache_parameters, cache.parameters
|
|
161
163
|
id_checks = {"cfg", "variable_kb"}
|
|
162
164
|
return all(a[k] is b[k] if k in id_checks else a[k] == b[k] for k in self._cache_parameters)
|
|
@@ -201,7 +203,7 @@ class Decompiler(Analysis):
|
|
|
201
203
|
|
|
202
204
|
variable_kb = self._variable_kb
|
|
203
205
|
# fall back to old codegen
|
|
204
|
-
if variable_kb is None and old_codegen is not None:
|
|
206
|
+
if variable_kb is None and old_codegen is not None and isinstance(old_codegen, CStructuredCodeGenerator):
|
|
205
207
|
variable_kb = old_codegen._variable_kb
|
|
206
208
|
|
|
207
209
|
if variable_kb is None:
|
|
@@ -223,7 +225,8 @@ class Decompiler(Analysis):
|
|
|
223
225
|
fold_callexprs_into_conditions = True
|
|
224
226
|
|
|
225
227
|
cache = DecompilationCache(self.func.addr)
|
|
226
|
-
|
|
228
|
+
if self._cache_parameters is not None:
|
|
229
|
+
cache.parameters = self._cache_parameters
|
|
227
230
|
cache.ite_exprs = ite_exprs
|
|
228
231
|
cache.binop_operators = binop_operators
|
|
229
232
|
|
|
@@ -296,8 +299,14 @@ class Decompiler(Analysis):
|
|
|
296
299
|
ri,
|
|
297
300
|
clinic.reaching_definitions,
|
|
298
301
|
ite_exprs=ite_exprs,
|
|
302
|
+
arg_vvars=set(clinic.arg_vvars),
|
|
303
|
+
edges_to_remove=clinic.edges_to_remove,
|
|
299
304
|
)
|
|
300
305
|
|
|
306
|
+
# finally (no more graph-based simplifications will run in the future), we can remove the edges that should be
|
|
307
|
+
# removed!
|
|
308
|
+
remove_edges_in_ailgraph(clinic.graph, clinic.edges_to_remove)
|
|
309
|
+
|
|
301
310
|
# Rewrite the graph to remove phi expressions
|
|
302
311
|
# this is probably optional if we do not pretty-print clinic.graph
|
|
303
312
|
clinic.graph = self._transform_graph_from_ssa(clinic.graph)
|
|
@@ -325,9 +334,9 @@ class Decompiler(Analysis):
|
|
|
325
334
|
s = self.project.analyses.RegionSimplifier(
|
|
326
335
|
self.func,
|
|
327
336
|
rs.result,
|
|
337
|
+
arg_vvars=set(self.clinic.arg_vvars),
|
|
328
338
|
kb=self.kb,
|
|
329
339
|
fail_fast=self._fail_fast,
|
|
330
|
-
variable_kb=clinic.variable_kb,
|
|
331
340
|
**self.options_to_params(self.options_by_class["region_simplifier"]),
|
|
332
341
|
)
|
|
333
342
|
seq_node = s.result
|
|
@@ -439,7 +448,7 @@ class Decompiler(Analysis):
|
|
|
439
448
|
return ail_graph
|
|
440
449
|
|
|
441
450
|
@timethis
|
|
442
|
-
def _run_region_simplification_passes(self, ail_graph, ri, reaching_definitions, **kwargs):
|
|
451
|
+
def _run_region_simplification_passes(self, ail_graph, ri, reaching_definitions, arg_vvars: set[int], **kwargs):
|
|
443
452
|
"""
|
|
444
453
|
Runs optimizations that should be executed after a single region identification. This function will return
|
|
445
454
|
two items: the new RegionIdentifier object and the new AIL Graph, which should probably be written
|
|
@@ -483,6 +492,7 @@ class Decompiler(Analysis):
|
|
|
483
492
|
blocks_by_addr_and_idx=addr_and_idx_to_blocks,
|
|
484
493
|
graph=ail_graph,
|
|
485
494
|
variable_kb=self._variable_kb,
|
|
495
|
+
arg_vvars=arg_vvars,
|
|
486
496
|
region_identifier=ri,
|
|
487
497
|
reaching_definitions=reaching_definitions,
|
|
488
498
|
vvar_id_start=self.vvar_id_start,
|
|
@@ -38,7 +38,7 @@ class ExprNarrowingInfo:
|
|
|
38
38
|
narrowable: bool,
|
|
39
39
|
to_size: int | None = None,
|
|
40
40
|
use_exprs: list[tuple[atoms.VirtualVariable, CodeLocation, tuple[str, tuple[Expression, ...]]]] | None = None,
|
|
41
|
-
phi_vars: set[
|
|
41
|
+
phi_vars: set[VirtualVariable] | None = None,
|
|
42
42
|
):
|
|
43
43
|
self.narrowable = narrowable
|
|
44
44
|
self.to_size = to_size
|
|
@@ -8,7 +8,7 @@ import claripy
|
|
|
8
8
|
from ailment import Const
|
|
9
9
|
from ailment.block_walker import AILBlockWalkerBase
|
|
10
10
|
from ailment.statement import Call, Statement, ConditionalJump, Assignment, Store, Return
|
|
11
|
-
from ailment.expression import Convert, Register, Expression
|
|
11
|
+
from ailment.expression import Convert, Register, Expression, Load
|
|
12
12
|
|
|
13
13
|
from .optimization_pass import OptimizationPass, OptimizationPassStage
|
|
14
14
|
from angr.analyses.decompiler.structuring import SAILRStructurer, DreamStructurer
|
|
@@ -207,16 +207,17 @@ class ConstPropOptReverter(OptimizationPass):
|
|
|
207
207
|
continue
|
|
208
208
|
|
|
209
209
|
unwrapped_sym_arg = sym_arg.operands[0] if isinstance(sym_arg, Convert) else sym_arg
|
|
210
|
-
|
|
210
|
+
if (
|
|
211
|
+
isinstance(unwrapped_sym_arg, Load)
|
|
212
|
+
and isinstance(unwrapped_sym_arg.addr, Const)
|
|
213
|
+
and isinstance(unwrapped_sym_arg.addr.value, int)
|
|
214
|
+
):
|
|
211
215
|
# TODO: make this support more than just Loads
|
|
212
216
|
# target must be a Load of a memory location
|
|
213
217
|
target_atom = MemoryLocation(unwrapped_sym_arg.addr.value, unwrapped_sym_arg.size, "Iend_LE")
|
|
214
218
|
const_state = self.rd.get_reaching_definitions_by_node(blks[calls[const_arg]].addr, OP_BEFORE)
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
except AttributeError:
|
|
218
|
-
continue
|
|
219
|
-
except KeyError:
|
|
219
|
+
state_load_vals = const_state.get_values(target_atom)
|
|
220
|
+
else:
|
|
220
221
|
continue
|
|
221
222
|
|
|
222
223
|
if not state_load_vals:
|
|
@@ -287,19 +287,27 @@ class ITERegionConverter(OptimizationPass):
|
|
|
287
287
|
((region_head.addr, region_head.idx), original_vvars[0] if original_vvars else None)
|
|
288
288
|
)
|
|
289
289
|
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
290
|
+
if len(new_src_and_vvars) == 1:
|
|
291
|
+
new_assignment = Assignment(
|
|
292
|
+
stmt.idx,
|
|
293
|
+
stmt.dst,
|
|
294
|
+
new_src_and_vvars[0][1],
|
|
295
|
+
**stmt.tags,
|
|
296
|
+
)
|
|
297
|
+
else:
|
|
298
|
+
new_phi = Phi(
|
|
299
|
+
stmt.src.idx,
|
|
300
|
+
stmt.src.bits,
|
|
301
|
+
new_src_and_vvars,
|
|
302
|
+
**stmt.src.tags,
|
|
303
|
+
)
|
|
304
|
+
new_assignment = Assignment(
|
|
305
|
+
stmt.idx,
|
|
306
|
+
stmt.dst,
|
|
307
|
+
new_phi,
|
|
308
|
+
**stmt.tags,
|
|
309
|
+
)
|
|
310
|
+
stmts.append(new_assignment)
|
|
303
311
|
new_region_tail = Block(region_tail.addr, region_tail.original_size, statements=stmts, idx=region_tail.idx)
|
|
304
312
|
|
|
305
313
|
#
|
|
@@ -13,7 +13,7 @@ from angr.analyses.decompiler import RegionIdentifier
|
|
|
13
13
|
from angr.analyses.decompiler.condition_processor import ConditionProcessor
|
|
14
14
|
from angr.analyses.decompiler.goto_manager import Goto, GotoManager
|
|
15
15
|
from angr.analyses.decompiler.structuring import RecursiveStructurer, SAILRStructurer
|
|
16
|
-
from angr.analyses.decompiler.utils import add_labels
|
|
16
|
+
from angr.analyses.decompiler.utils import add_labels, remove_edges_in_ailgraph
|
|
17
17
|
from angr.analyses.decompiler.counters import ControlFlowStructureCounter
|
|
18
18
|
from angr.project import Project
|
|
19
19
|
|
|
@@ -129,6 +129,7 @@ class OptimizationPass(BaseOptimizationPass):
|
|
|
129
129
|
force_loop_single_exit: bool = True,
|
|
130
130
|
complete_successors: bool = False,
|
|
131
131
|
avoid_vvar_ids: set[int] | None = None,
|
|
132
|
+
arg_vvars: set[int] | None = None,
|
|
132
133
|
**kwargs,
|
|
133
134
|
):
|
|
134
135
|
super().__init__(func)
|
|
@@ -141,6 +142,7 @@ class OptimizationPass(BaseOptimizationPass):
|
|
|
141
142
|
self._rd = reaching_definitions
|
|
142
143
|
self._scratch = scratch if scratch is not None else {}
|
|
143
144
|
self._new_block_addrs = set()
|
|
145
|
+
self._arg_vvars = arg_vvars
|
|
144
146
|
self.vvar_id_start = vvar_id_start
|
|
145
147
|
self.entry_node_addr: tuple[int, int | None] = (
|
|
146
148
|
entry_node_addr if entry_node_addr is not None else (func.addr, None)
|
|
@@ -331,14 +333,15 @@ class StructuringOptimizationPass(OptimizationPass):
|
|
|
331
333
|
def __init__(
|
|
332
334
|
self,
|
|
333
335
|
func,
|
|
334
|
-
prevent_new_gotos=True,
|
|
335
|
-
strictly_less_gotos=False,
|
|
336
|
-
recover_structure_fails=True,
|
|
337
|
-
must_improve_rel_quality=True,
|
|
338
|
-
max_opt_iters=1,
|
|
339
|
-
simplify_ail=True,
|
|
340
|
-
require_gotos=True,
|
|
341
|
-
readd_labels=False,
|
|
336
|
+
prevent_new_gotos: bool = True,
|
|
337
|
+
strictly_less_gotos: bool = False,
|
|
338
|
+
recover_structure_fails: bool = True,
|
|
339
|
+
must_improve_rel_quality: bool = True,
|
|
340
|
+
max_opt_iters: int = 1,
|
|
341
|
+
simplify_ail: bool = True,
|
|
342
|
+
require_gotos: bool = True,
|
|
343
|
+
readd_labels: bool = False,
|
|
344
|
+
edges_to_remove: list[tuple[tuple[int, int | None], tuple[int, int | None]]] | None = None,
|
|
342
345
|
**kwargs,
|
|
343
346
|
):
|
|
344
347
|
super().__init__(func, **kwargs)
|
|
@@ -350,6 +353,7 @@ class StructuringOptimizationPass(OptimizationPass):
|
|
|
350
353
|
self._require_gotos = require_gotos
|
|
351
354
|
self._must_improve_rel_quality = must_improve_rel_quality
|
|
352
355
|
self._readd_labels = readd_labels
|
|
356
|
+
self._edges_to_remove = edges_to_remove or []
|
|
353
357
|
|
|
354
358
|
# relative quality metrics (excludes gotos)
|
|
355
359
|
self._initial_structure_counter = None
|
|
@@ -452,6 +456,8 @@ class StructuringOptimizationPass(OptimizationPass):
|
|
|
452
456
|
if readd_labels:
|
|
453
457
|
graph = add_labels(graph)
|
|
454
458
|
|
|
459
|
+
remove_edges_in_ailgraph(graph, self._edges_to_remove)
|
|
460
|
+
|
|
455
461
|
self._ri = self.project.analyses[RegionIdentifier].prep(kb=self.kb)(
|
|
456
462
|
self._func,
|
|
457
463
|
graph=graph,
|
|
@@ -482,7 +488,7 @@ class StructuringOptimizationPass(OptimizationPass):
|
|
|
482
488
|
if not rs or not rs.result or not rs.result.nodes or rs.result_incomplete:
|
|
483
489
|
return False
|
|
484
490
|
|
|
485
|
-
rs = self.project.analyses.RegionSimplifier(self._func, rs.result,
|
|
491
|
+
rs = self.project.analyses.RegionSimplifier(self._func, rs.result, arg_vvars=self._arg_vvars, kb=self.kb)
|
|
486
492
|
if not rs or rs.goto_manager is None or rs.result is None:
|
|
487
493
|
return False
|
|
488
494
|
|
|
@@ -140,11 +140,11 @@ class ReturnDuplicatorBase:
|
|
|
140
140
|
):
|
|
141
141
|
# every eligible pred gets a new region copy
|
|
142
142
|
self._copy_region([pred_node], region_head, region, graph)
|
|
143
|
+
graph_changed = True
|
|
143
144
|
|
|
144
145
|
if region_head in graph and graph.in_degree(region_head) == 0:
|
|
145
146
|
graph.remove_nodes_from(region)
|
|
146
|
-
|
|
147
|
-
graph_changed = True
|
|
147
|
+
graph_changed = True
|
|
148
148
|
|
|
149
149
|
return graph_changed
|
|
150
150
|
|