angr 9.2.139__py3-none-manylinux2014_x86_64.whl → 9.2.141__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 +136 -53
- angr/analyses/calling_convention/fact_collector.py +44 -18
- angr/analyses/calling_convention/utils.py +3 -1
- angr/analyses/cfg/cfg_base.py +13 -0
- angr/analyses/cfg/cfg_fast.py +11 -0
- angr/analyses/cfg/indirect_jump_resolvers/jumptable.py +9 -8
- angr/analyses/decompiler/ail_simplifier.py +115 -72
- angr/analyses/decompiler/callsite_maker.py +24 -11
- angr/analyses/decompiler/clinic.py +78 -43
- angr/analyses/decompiler/decompiler.py +18 -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/duplication_reverter/duplication_reverter.py +3 -1
- angr/analyses/decompiler/optimization_passes/flip_boolean_cmp.py +21 -2
- angr/analyses/decompiler/optimization_passes/ite_region_converter.py +21 -13
- angr/analyses/decompiler/optimization_passes/lowered_switch_simplifier.py +84 -15
- angr/analyses/decompiler/optimization_passes/optimization_pass.py +92 -11
- angr/analyses/decompiler/optimization_passes/return_duplicator_base.py +53 -9
- angr/analyses/decompiler/peephole_optimizations/eager_eval.py +44 -7
- angr/analyses/decompiler/region_identifier.py +6 -4
- angr/analyses/decompiler/region_simplifiers/expr_folding.py +287 -122
- angr/analyses/decompiler/region_simplifiers/region_simplifier.py +31 -13
- angr/analyses/decompiler/ssailification/rewriting.py +23 -15
- angr/analyses/decompiler/ssailification/rewriting_engine.py +105 -24
- angr/analyses/decompiler/ssailification/ssailification.py +22 -14
- angr/analyses/decompiler/structured_codegen/c.py +73 -137
- angr/analyses/decompiler/structuring/dream.py +22 -18
- angr/analyses/decompiler/structuring/phoenix.py +158 -41
- angr/analyses/decompiler/structuring/recursive_structurer.py +1 -0
- angr/analyses/decompiler/structuring/structurer_base.py +37 -10
- angr/analyses/decompiler/structuring/structurer_nodes.py +4 -1
- angr/analyses/decompiler/utils.py +106 -21
- 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/s_reaching_definitions/s_rda_view.py +43 -25
- 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_ail.py +1 -1
- angr/analyses/variable_recovery/engine_base.py +7 -5
- angr/analyses/variable_recovery/engine_vex.py +20 -4
- angr/block.py +69 -107
- angr/callable.py +14 -7
- angr/calling_conventions.py +30 -11
- 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 +12 -22
- angr/knowledge_plugins/key_definitions/atoms.py +8 -4
- angr/knowledge_plugins/key_definitions/live_definitions.py +41 -103
- angr/knowledge_plugins/variables/variable_manager.py +7 -5
- angr/sim_type.py +19 -17
- angr/simos/simos.py +3 -1
- 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/utils/types.py +48 -0
- {angr-9.2.139.dist-info → angr-9.2.141.dist-info}/METADATA +6 -6
- {angr-9.2.139.dist-info → angr-9.2.141.dist-info}/RECORD +87 -86
- {angr-9.2.139.dist-info → angr-9.2.141.dist-info}/LICENSE +0 -0
- {angr-9.2.139.dist-info → angr-9.2.141.dist-info}/WHEEL +0 -0
- {angr-9.2.139.dist-info → angr-9.2.141.dist-info}/entry_points.txt +0 -0
- {angr-9.2.139.dist-info → angr-9.2.141.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,12 +149,14 @@ 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
|
|
156
155
|
self.vvar_id_start = vvar_id_start
|
|
157
156
|
self.vvar_to_vvar: dict[int, int] | None = None
|
|
157
|
+
# during SSA conversion, we create secondary stack variables because they overlap and are larger than the
|
|
158
|
+
# actual stack variables. these secondary stack variables can be safely eliminated if not used by anything.
|
|
159
|
+
self.secondary_stackvars: set[int] = set()
|
|
158
160
|
|
|
159
161
|
# inlining help
|
|
160
162
|
self._sp_shift = sp_shift
|
|
@@ -167,6 +169,7 @@ class Clinic(Analysis):
|
|
|
167
169
|
self._complete_successors = complete_successors
|
|
168
170
|
|
|
169
171
|
self._register_save_areas_removed: bool = False
|
|
172
|
+
self.edges_to_remove: list[tuple[tuple[int, int | None], tuple[int, int | None]]] = []
|
|
170
173
|
|
|
171
174
|
self._new_block_addrs = set()
|
|
172
175
|
|
|
@@ -209,6 +212,7 @@ class Clinic(Analysis):
|
|
|
209
212
|
|
|
210
213
|
:return:
|
|
211
214
|
"""
|
|
215
|
+
assert self.graph is not None
|
|
212
216
|
|
|
213
217
|
s = ""
|
|
214
218
|
|
|
@@ -297,6 +301,8 @@ class Clinic(Analysis):
|
|
|
297
301
|
return ail_graph
|
|
298
302
|
|
|
299
303
|
def _slice_variables(self, ail_graph):
|
|
304
|
+
assert self.variable_kb is not None
|
|
305
|
+
|
|
300
306
|
nodes_index = {(n.addr, n.idx): n for n in ail_graph.nodes()}
|
|
301
307
|
|
|
302
308
|
vfm = self.variable_kb.variables.function_managers[self.function.addr]
|
|
@@ -344,7 +350,7 @@ class Clinic(Analysis):
|
|
|
344
350
|
optimization_passes=[StackCanarySimplifier],
|
|
345
351
|
sp_shift=self._max_stack_depth,
|
|
346
352
|
vvar_id_start=self.vvar_id_start,
|
|
347
|
-
fail_fast=self._fail_fast,
|
|
353
|
+
fail_fast=self._fail_fast, # type: ignore
|
|
348
354
|
)
|
|
349
355
|
self.vvar_id_start = callee_clinic.vvar_id_start + 1
|
|
350
356
|
self._max_stack_depth = callee_clinic._max_stack_depth
|
|
@@ -618,6 +624,7 @@ class Clinic(Analysis):
|
|
|
618
624
|
|
|
619
625
|
# remove empty nodes from the graph
|
|
620
626
|
ail_graph = self.remove_empty_nodes(ail_graph)
|
|
627
|
+
# note that there are still edges to remove before we can structure this graph!
|
|
621
628
|
|
|
622
629
|
self.arg_list = arg_list
|
|
623
630
|
self.arg_vvars = arg_vvars
|
|
@@ -800,7 +807,7 @@ class Clinic(Analysis):
|
|
|
800
807
|
|
|
801
808
|
# case 2: the callee is a SimProcedure
|
|
802
809
|
if target_func.is_simprocedure:
|
|
803
|
-
cc = self.project.analyses.CallingConvention(target_func, fail_fast=self._fail_fast)
|
|
810
|
+
cc = self.project.analyses.CallingConvention(target_func, fail_fast=self._fail_fast) # type: ignore
|
|
804
811
|
if cc.cc is not None and cc.prototype is not None:
|
|
805
812
|
target_func.calling_convention = cc.cc
|
|
806
813
|
target_func.prototype = cc.prototype
|
|
@@ -808,7 +815,7 @@ class Clinic(Analysis):
|
|
|
808
815
|
|
|
809
816
|
# case 3: the callee is a PLT function
|
|
810
817
|
if target_func.is_plt:
|
|
811
|
-
cc = self.project.analyses.CallingConvention(target_func, fail_fast=self._fail_fast)
|
|
818
|
+
cc = self.project.analyses.CallingConvention(target_func, fail_fast=self._fail_fast) # type: ignore
|
|
812
819
|
if cc.cc is not None and cc.prototype is not None:
|
|
813
820
|
target_func.calling_convention = cc.cc
|
|
814
821
|
target_func.prototype = cc.prototype
|
|
@@ -878,7 +885,7 @@ class Clinic(Analysis):
|
|
|
878
885
|
# finally, recover the calling convention of the current function
|
|
879
886
|
if self.function.prototype is None or self.function.calling_convention is None:
|
|
880
887
|
self.project.analyses.CompleteCallingConventions(
|
|
881
|
-
fail_fast=self._fail_fast,
|
|
888
|
+
fail_fast=self._fail_fast, # type: ignore
|
|
882
889
|
recover_variables=True,
|
|
883
890
|
prioritize_func_addrs=[self.function.addr],
|
|
884
891
|
skip_other_funcs=True,
|
|
@@ -1235,6 +1242,7 @@ class Clinic(Analysis):
|
|
|
1235
1242
|
rewrite_ccalls=rewrite_ccalls,
|
|
1236
1243
|
removed_vvar_ids=removed_vvar_ids,
|
|
1237
1244
|
arg_vvars=arg_vvars,
|
|
1245
|
+
secondary_stackvars=self.secondary_stackvars,
|
|
1238
1246
|
)
|
|
1239
1247
|
# cache the simplifier's RDA analysis
|
|
1240
1248
|
self.reaching_definitions = simp._reaching_definitions
|
|
@@ -1360,6 +1368,7 @@ class Clinic(Analysis):
|
|
|
1360
1368
|
vvar_id_start=self.vvar_id_start,
|
|
1361
1369
|
)
|
|
1362
1370
|
self.vvar_id_start = ssailification.max_vvar_id + 1
|
|
1371
|
+
self.secondary_stackvars = ssailification.secondary_stackvars
|
|
1363
1372
|
return ssailification.out_graph
|
|
1364
1373
|
|
|
1365
1374
|
@timethis
|
|
@@ -1860,6 +1869,11 @@ class Clinic(Analysis):
|
|
|
1860
1869
|
if expr.guard:
|
|
1861
1870
|
self._link_variables_on_expr(variable_manager, global_variables, block, stmt_idx, stmt, expr.guard)
|
|
1862
1871
|
|
|
1872
|
+
elif isinstance(expr, ailment.Expr.Phi):
|
|
1873
|
+
for _, vvar in expr.src_and_vvars:
|
|
1874
|
+
if vvar is not None:
|
|
1875
|
+
self._link_variables_on_expr(variable_manager, global_variables, block, stmt_idx, stmt, vvar)
|
|
1876
|
+
|
|
1863
1877
|
def _function_graph_to_ail_graph(self, func_graph, blocks_by_addr_and_size=None):
|
|
1864
1878
|
if blocks_by_addr_and_size is None:
|
|
1865
1879
|
blocks_by_addr_and_size = self._blocks_by_addr_and_size
|
|
@@ -2109,49 +2123,60 @@ class Clinic(Analysis):
|
|
|
2109
2123
|
# the overlapped instructions and add an unconditional jump so that it jumps to 0x41da9d.
|
|
2110
2124
|
# this is the most common case created by jump threading optimization in compilers. it's easy to handle.
|
|
2111
2125
|
|
|
2112
|
-
# Case 2: the intended head and the other heads do not share the same suffix of instructions. in this
|
|
2113
|
-
#
|
|
2114
|
-
#
|
|
2115
|
-
#
|
|
2126
|
+
# Case 2 & 3: the intended head and the other heads do not share the same suffix of instructions. in this
|
|
2127
|
+
# case, we have two choices:
|
|
2128
|
+
# Case 2: The intended head has two successors, but at least one unintended head has only one successor.
|
|
2129
|
+
# we cannot reliably convert the blocks into a properly structured switch-case construct. we will
|
|
2130
|
+
# last instruction of all other heads to jump to the cmp instruction in the intended head, but do
|
|
2131
|
+
# not remove any other instructions in these other heads. this is unsound, but is the best we can
|
|
2132
|
+
# do in this case.
|
|
2133
|
+
# Case 3: The intended head has only one successor (which is the indirect jump node). during structuring,
|
|
2134
|
+
# we expect it will be structured as a no-default-node switch-case construct. in this case, we
|
|
2135
|
+
# can simply remove the edges from all other heads to the jump node and only leave the edge from
|
|
2136
|
+
# the intended head to the jump node. we will see goto statements in the output, but this will
|
|
2137
|
+
# lead to correct structuring result.
|
|
2116
2138
|
|
|
2117
2139
|
overlaps = [self._get_overlapping_suffix_instructions(intended_head, head) for head in other_heads]
|
|
2118
2140
|
if overlaps and (overlap := min(overlaps)) > 0:
|
|
2119
2141
|
# Case 1
|
|
2120
2142
|
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
|
|
2143
|
+
elif ail_graph.out_degree[intended_head] == 2:
|
|
2144
|
+
# Case 2
|
|
2145
|
+
l.warning("Switch-case at %#x has multiple head nodes but cannot be fixed soundly.", candidate.addr)
|
|
2146
|
+
# find the comparison instruction in the intended head
|
|
2147
|
+
comparison_stmt = None
|
|
2148
|
+
if "cc_op" in self.project.arch.registers:
|
|
2149
|
+
comparison_stmt = next(
|
|
2150
|
+
iter(
|
|
2151
|
+
stmt
|
|
2152
|
+
for stmt in intended_head.statements
|
|
2153
|
+
if isinstance(stmt, ailment.Stmt.Assignment)
|
|
2154
|
+
and isinstance(stmt.dst, ailment.Expr.Register)
|
|
2155
|
+
and stmt.dst.reg_offset == self.project.arch.registers["cc_op"][0]
|
|
2156
|
+
),
|
|
2157
|
+
None,
|
|
2140
2158
|
)
|
|
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,
|
|
2159
|
+
intended_head_block = self.project.factory.block(intended_head.addr, size=intended_head.original_size)
|
|
2160
|
+
if comparison_stmt is not None:
|
|
2161
|
+
cmp_rpos = len(intended_head_block.instruction_addrs) - intended_head_block.instruction_addrs.index(
|
|
2162
|
+
comparison_stmt.ins_addr
|
|
2154
2163
|
)
|
|
2164
|
+
else:
|
|
2165
|
+
cmp_rpos = min(len(intended_head_block.instruction_addrs), 2)
|
|
2166
|
+
self._fix_abnormal_switch_case_heads_case2(
|
|
2167
|
+
ail_graph,
|
|
2168
|
+
candidate,
|
|
2169
|
+
intended_head,
|
|
2170
|
+
other_heads,
|
|
2171
|
+
intended_head_split_insns=cmp_rpos,
|
|
2172
|
+
other_head_split_insns=0,
|
|
2173
|
+
)
|
|
2174
|
+
else:
|
|
2175
|
+
# Case 3
|
|
2176
|
+
self._fix_abnormal_switch_case_heads_case3(
|
|
2177
|
+
candidate,
|
|
2178
|
+
other_heads,
|
|
2179
|
+
)
|
|
2155
2180
|
|
|
2156
2181
|
def _get_overlapping_suffix_instructions(self, ailblock_0: ailment.Block, ailblock_1: ailment.Block) -> int:
|
|
2157
2182
|
# we first compare their ending conditional jumps
|
|
@@ -2360,6 +2385,16 @@ class Clinic(Analysis):
|
|
|
2360
2385
|
# it should be going to the default node. ignore it
|
|
2361
2386
|
pass
|
|
2362
2387
|
|
|
2388
|
+
def _fix_abnormal_switch_case_heads_case3(
|
|
2389
|
+
self, indirect_jump_node: ailment.Block, other_heads: list[ailment.Block]
|
|
2390
|
+
) -> None:
|
|
2391
|
+
# remove all edges from other_heads to the indirect jump node
|
|
2392
|
+
for other_head in other_heads:
|
|
2393
|
+
# delay the edge removal so that we don't mess up the SSA analysis
|
|
2394
|
+
self.edges_to_remove.append(
|
|
2395
|
+
((other_head.addr, other_head.idx), (indirect_jump_node.addr, indirect_jump_node.idx))
|
|
2396
|
+
)
|
|
2397
|
+
|
|
2363
2398
|
@staticmethod
|
|
2364
2399
|
def _remove_redundant_jump_blocks(ail_graph):
|
|
2365
2400
|
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,
|
|
@@ -490,6 +500,7 @@ class Decompiler(Analysis):
|
|
|
490
500
|
scratch=self._optimization_scratch,
|
|
491
501
|
force_loop_single_exit=self._force_loop_single_exit,
|
|
492
502
|
complete_successors=self._complete_successors,
|
|
503
|
+
peephole_optimizations=self._peephole_optimizations,
|
|
493
504
|
**kwargs,
|
|
494
505
|
)
|
|
495
506
|
|
|
@@ -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:
|
|
@@ -950,7 +950,9 @@ class DuplicationReverter(StructuringOptimizationPass):
|
|
|
950
950
|
#
|
|
951
951
|
|
|
952
952
|
def _share_subregion(self, blocks: list[Block]) -> bool:
|
|
953
|
-
return any(
|
|
953
|
+
return any(
|
|
954
|
+
all((block.addr, block.idx) in region for block in blocks) for region in self._ri.regions_by_block_addrs
|
|
955
|
+
)
|
|
954
956
|
|
|
955
957
|
def _is_valid_candidate(self, b0, b1):
|
|
956
958
|
# blocks must have statements
|
|
@@ -6,7 +6,11 @@ import ailment
|
|
|
6
6
|
from ailment.expression import Op
|
|
7
7
|
|
|
8
8
|
from angr.analyses.decompiler.structuring.structurer_nodes import ConditionNode
|
|
9
|
-
from angr.analyses.decompiler.utils import
|
|
9
|
+
from angr.analyses.decompiler.utils import (
|
|
10
|
+
structured_node_is_simple_return,
|
|
11
|
+
sequence_to_statements,
|
|
12
|
+
structured_node_has_multi_predecessors,
|
|
13
|
+
)
|
|
10
14
|
from angr.analyses.decompiler.sequence_walker import SequenceWalker
|
|
11
15
|
from .optimization_pass import SequenceOptimizationPass, OptimizationPassStage
|
|
12
16
|
|
|
@@ -43,7 +47,22 @@ class FlipBooleanWalker(SequenceWalker):
|
|
|
43
47
|
and structured_node_is_simple_return(seq_node.nodes[idx + 1], self._graph)
|
|
44
48
|
and node not in type1_condition_nodes
|
|
45
49
|
):
|
|
46
|
-
|
|
50
|
+
# Type 2: Special Filter:
|
|
51
|
+
# consider code that looks like the following:
|
|
52
|
+
# {if (cond) {LABEL: ... } return;}; goto LABEL;
|
|
53
|
+
#
|
|
54
|
+
# if we were to do the normal flip, this happens:
|
|
55
|
+
# {if (!cond) return; LABEL: ...}; goto LABEL;
|
|
56
|
+
#
|
|
57
|
+
# This is incorrect because we've now created an infinite loop in the event that cond is false,
|
|
58
|
+
# which is not what the original code was. The gist here is that you can't ever flip these cases
|
|
59
|
+
# in the presence of more than one incoming edge to `...` region.
|
|
60
|
+
#
|
|
61
|
+
# To eliminate this illegal case, we simply need to find all the condition nodes of the above structure
|
|
62
|
+
# that have multiple incoming edges to the `...` region.
|
|
63
|
+
illegal_flip = structured_node_has_multi_predecessors(node.true_node, self._graph)
|
|
64
|
+
if not illegal_flip:
|
|
65
|
+
type2_condition_nodes.append((idx, node, seq_node.nodes[idx + 1]))
|
|
47
66
|
|
|
48
67
|
for node in type1_condition_nodes:
|
|
49
68
|
if isinstance(node.condition, Op) and structured_node_is_simple_return(node.false_node, self._graph):
|
|
@@ -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
|
#
|