angr 9.2.122__py3-none-manylinux2014_x86_64.whl → 9.2.124__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.py +6 -1
- angr/analyses/cfg/indirect_jump_resolvers/mips_elf_fast.py +11 -8
- angr/analyses/cfg/indirect_jump_resolvers/mips_elf_got.py +2 -2
- angr/analyses/decompiler/ail_simplifier.py +38 -342
- angr/analyses/decompiler/callsite_maker.py +8 -7
- angr/analyses/decompiler/ccall_rewriters/amd64_ccalls.py +24 -2
- angr/analyses/decompiler/clinic.py +30 -3
- angr/analyses/decompiler/condition_processor.py +10 -3
- angr/analyses/decompiler/decompilation_cache.py +2 -0
- angr/analyses/decompiler/decompiler.py +50 -8
- angr/analyses/decompiler/dephication/graph_vvar_mapping.py +10 -2
- angr/analyses/decompiler/dephication/rewriting_engine.py +65 -2
- angr/analyses/decompiler/expression_narrower.py +206 -6
- angr/analyses/decompiler/optimization_passes/div_simplifier.py +4 -1
- angr/analyses/decompiler/optimization_passes/inlined_string_transformation_simplifier.py +7 -0
- angr/analyses/decompiler/optimization_passes/ite_region_converter.py +34 -11
- angr/analyses/decompiler/optimization_passes/lowered_switch_simplifier.py +10 -1
- angr/analyses/decompiler/optimization_passes/optimization_pass.py +3 -1
- angr/analyses/decompiler/optimization_passes/return_duplicator_base.py +8 -5
- angr/analyses/decompiler/optimization_passes/return_duplicator_high.py +10 -5
- angr/analyses/decompiler/optimization_passes/return_duplicator_low.py +18 -7
- angr/analyses/decompiler/optimization_passes/switch_default_case_duplicator.py +6 -0
- angr/analyses/decompiler/optimization_passes/win_stack_canary_simplifier.py +2 -0
- angr/analyses/decompiler/peephole_optimizations/const_mull_a_shift.py +75 -42
- angr/analyses/decompiler/peephole_optimizations/remove_cascading_conversions.py +8 -2
- angr/analyses/decompiler/region_identifier.py +36 -0
- angr/analyses/decompiler/region_simplifiers/expr_folding.py +4 -0
- angr/analyses/decompiler/region_simplifiers/loop.py +2 -8
- angr/analyses/decompiler/region_simplifiers/switch_cluster_simplifier.py +9 -3
- angr/analyses/decompiler/sequence_walker.py +20 -4
- angr/analyses/decompiler/ssailification/rewriting.py +5 -2
- angr/analyses/decompiler/ssailification/rewriting_engine.py +151 -25
- angr/analyses/decompiler/ssailification/rewriting_state.py +1 -0
- angr/analyses/decompiler/ssailification/ssailification.py +17 -9
- angr/analyses/decompiler/ssailification/traversal.py +3 -1
- angr/analyses/decompiler/ssailification/traversal_engine.py +35 -8
- angr/analyses/decompiler/ssailification/traversal_state.py +1 -0
- angr/analyses/decompiler/structured_codegen/c.py +42 -4
- angr/analyses/decompiler/structuring/phoenix.py +3 -0
- angr/analyses/propagator/engine_ail.py +10 -3
- angr/analyses/reaching_definitions/engine_ail.py +10 -15
- angr/analyses/s_propagator.py +26 -15
- angr/analyses/s_reaching_definitions/s_rda_view.py +127 -63
- angr/analyses/variable_recovery/engine_ail.py +14 -0
- angr/analyses/variable_recovery/engine_base.py +11 -0
- angr/calling_conventions.py +2 -2
- angr/engines/light/engine.py +24 -2
- angr/engines/soot/expressions/instanceOf.py +4 -1
- angr/engines/successors.py +1 -1
- angr/engines/vex/heavy/concretizers.py +47 -47
- angr/engines/vex/heavy/dirty.py +4 -4
- angr/knowledge_plugins/__init__.py +2 -0
- angr/knowledge_plugins/decompilation.py +45 -0
- angr/knowledge_plugins/key_definitions/atoms.py +8 -0
- angr/procedures/definitions/parse_win32json.py +2 -1
- angr/procedures/java_lang/getsimplename.py +4 -1
- angr/procedures/linux_kernel/iovec.py +5 -2
- angr/sim_type.py +3 -1
- angr/storage/memory_mixins/actions_mixin.py +7 -7
- angr/storage/memory_mixins/address_concretization_mixin.py +5 -5
- angr/storage/memory_mixins/bvv_conversion_mixin.py +1 -1
- angr/storage/memory_mixins/clouseau_mixin.py +3 -3
- angr/storage/memory_mixins/conditional_store_mixin.py +3 -3
- angr/storage/memory_mixins/default_filler_mixin.py +3 -3
- angr/storage/memory_mixins/memory_mixin.py +45 -34
- angr/storage/memory_mixins/paged_memory/page_backer_mixins.py +15 -14
- angr/storage/memory_mixins/paged_memory/paged_memory_mixin.py +27 -16
- angr/storage/memory_mixins/paged_memory/pages/cooperation.py +18 -9
- angr/storage/memory_mixins/paged_memory/pages/ispo_mixin.py +5 -5
- angr/storage/memory_mixins/paged_memory/pages/multi_values.py +89 -55
- angr/storage/memory_mixins/paged_memory/pages/mv_list_page.py +16 -25
- angr/storage/memory_mixins/paged_memory/pages/permissions_mixin.py +11 -9
- angr/storage/memory_mixins/paged_memory/pages/ultra_page.py +23 -7
- angr/storage/memory_mixins/paged_memory/privileged_mixin.py +1 -1
- angr/storage/memory_mixins/regioned_memory/region_meta_mixin.py +9 -7
- angr/storage/memory_mixins/regioned_memory/regioned_memory_mixin.py +9 -9
- angr/storage/memory_mixins/regioned_memory/static_find_mixin.py +1 -0
- angr/storage/memory_mixins/simple_interface_mixin.py +2 -2
- angr/storage/memory_mixins/simplification_mixin.py +2 -2
- angr/storage/memory_mixins/size_resolution_mixin.py +1 -1
- angr/storage/memory_mixins/slotted_memory.py +3 -3
- angr/storage/memory_mixins/smart_find_mixin.py +1 -0
- angr/storage/memory_mixins/underconstrained_mixin.py +5 -5
- angr/storage/memory_mixins/unwrapper_mixin.py +4 -4
- angr/storage/memory_object.py +4 -3
- angr/utils/constants.py +1 -1
- angr/utils/graph.py +15 -0
- angr/vaults.py +2 -2
- {angr-9.2.122.dist-info → angr-9.2.124.dist-info}/METADATA +7 -6
- {angr-9.2.122.dist-info → angr-9.2.124.dist-info}/RECORD +95 -94
- {angr-9.2.122.dist-info → angr-9.2.124.dist-info}/WHEEL +1 -1
- {angr-9.2.122.dist-info → angr-9.2.124.dist-info}/LICENSE +0 -0
- {angr-9.2.122.dist-info → angr-9.2.124.dist-info}/entry_points.txt +0 -0
- {angr-9.2.122.dist-info → angr-9.2.124.dist-info}/top_level.txt +0 -0
|
@@ -110,6 +110,7 @@ class Clinic(Analysis):
|
|
|
110
110
|
inlined_counts: dict[int, int] | None = None,
|
|
111
111
|
inlining_parents: set[int] | None = None,
|
|
112
112
|
vvar_id_start: int = 0,
|
|
113
|
+
optimization_scratch: dict[str, Any] | None = None,
|
|
113
114
|
):
|
|
114
115
|
if not func.normalized and mode == ClinicMode.DECOMPILE:
|
|
115
116
|
raise ValueError("Decompilation must work on normalized function graphs.")
|
|
@@ -124,6 +125,7 @@ class Clinic(Analysis):
|
|
|
124
125
|
self.variable_kb = variable_kb
|
|
125
126
|
self.externs: set[SimMemoryVariable] = set()
|
|
126
127
|
self.data_refs: dict[int, int] = {} # data address to instruction address
|
|
128
|
+
self.optimization_scratch = optimization_scratch if optimization_scratch is not None else {}
|
|
127
129
|
|
|
128
130
|
self._func_graph: networkx.DiGraph | None = None
|
|
129
131
|
self._ail_manager = None
|
|
@@ -749,8 +751,9 @@ class Clinic(Analysis):
|
|
|
749
751
|
continue
|
|
750
752
|
|
|
751
753
|
call_sites = []
|
|
752
|
-
for pred in self.function.transition_graph.
|
|
753
|
-
|
|
754
|
+
for pred, _, data in self.function.transition_graph.in_edges(node, data=True):
|
|
755
|
+
if data.get("type", None) != "return":
|
|
756
|
+
call_sites.append(pred)
|
|
754
757
|
# case 1: calling conventions and prototypes are available at every single call site
|
|
755
758
|
if call_sites and all(self.kb.callsite_prototypes.has_prototype(callsite.addr) for callsite in call_sites):
|
|
756
759
|
continue
|
|
@@ -918,10 +921,16 @@ class Clinic(Analysis):
|
|
|
918
921
|
"Ijk_Sys"
|
|
919
922
|
):
|
|
920
923
|
# we don't support lifting this block. use a dummy block instead
|
|
924
|
+
dirty_expr = ailment.Expr.DirtyExpression(
|
|
925
|
+
self._ail_manager.next_atom,
|
|
926
|
+
f"Unsupported jumpkind {block.vex.jumpkind} at address {block_node.addr}",
|
|
927
|
+
[],
|
|
928
|
+
bits=0,
|
|
929
|
+
)
|
|
921
930
|
statements = [
|
|
922
931
|
ailment.Stmt.DirtyStatement(
|
|
923
932
|
self._ail_manager.next_atom(),
|
|
924
|
-
|
|
933
|
+
dirty_expr,
|
|
925
934
|
ins_addr=block_node.addr,
|
|
926
935
|
)
|
|
927
936
|
]
|
|
@@ -1218,6 +1227,7 @@ class Clinic(Analysis):
|
|
|
1218
1227
|
variable_kb=variable_kb,
|
|
1219
1228
|
vvar_id_start=self.vvar_id_start,
|
|
1220
1229
|
entry_node_addr=self.entry_node_addr,
|
|
1230
|
+
scratch=self.optimization_scratch,
|
|
1221
1231
|
**kwargs,
|
|
1222
1232
|
)
|
|
1223
1233
|
if a.out_graph:
|
|
@@ -1255,6 +1265,7 @@ class Clinic(Analysis):
|
|
|
1255
1265
|
ailment.Expr.VirtualVariableCategory.PARAMETER,
|
|
1256
1266
|
oident=arg.reg,
|
|
1257
1267
|
ins_addr=self.function.addr,
|
|
1268
|
+
vex_block_addr=self.function.addr,
|
|
1258
1269
|
)
|
|
1259
1270
|
self.vvar_id_start += 1
|
|
1260
1271
|
arg_vvars[arg_vvar.varid] = arg_vvar, arg
|
|
@@ -1268,6 +1279,7 @@ class Clinic(Analysis):
|
|
|
1268
1279
|
False,
|
|
1269
1280
|
arg_vvar,
|
|
1270
1281
|
ins_addr=self.function.addr,
|
|
1282
|
+
vex_block_addr=self.function.addr,
|
|
1271
1283
|
)
|
|
1272
1284
|
|
|
1273
1285
|
fullreg_dst = ailment.Expr.Register(
|
|
@@ -1276,12 +1288,14 @@ class Clinic(Analysis):
|
|
|
1276
1288
|
basereg_offset,
|
|
1277
1289
|
basereg_size * self.project.arch.byte_width,
|
|
1278
1290
|
ins_addr=self.function.addr,
|
|
1291
|
+
vex_block_addr=self.function.addr,
|
|
1279
1292
|
)
|
|
1280
1293
|
stmt = ailment.Stmt.Assignment(
|
|
1281
1294
|
self._ail_manager.next_atom(),
|
|
1282
1295
|
fullreg_dst,
|
|
1283
1296
|
arg_vvar,
|
|
1284
1297
|
ins_addr=self.function.addr,
|
|
1298
|
+
vex_block_addr=self.function.addr,
|
|
1285
1299
|
)
|
|
1286
1300
|
new_stmts.append(stmt)
|
|
1287
1301
|
|
|
@@ -1312,6 +1326,7 @@ class Clinic(Analysis):
|
|
|
1312
1326
|
ail_graph,
|
|
1313
1327
|
entry=next(iter(bb for bb in ail_graph if (bb.addr, bb.idx) == self.entry_node_addr)),
|
|
1314
1328
|
ail_manager=self._ail_manager,
|
|
1329
|
+
ssa_tmps=True,
|
|
1315
1330
|
ssa_stackvars=True,
|
|
1316
1331
|
vvar_id_start=self.vvar_id_start,
|
|
1317
1332
|
)
|
|
@@ -1791,6 +1806,18 @@ class Clinic(Analysis):
|
|
|
1791
1806
|
elif isinstance(expr, ailment.Stmt.Call):
|
|
1792
1807
|
self._link_variables_on_call(variable_manager, global_variables, block, stmt_idx, expr, is_expr=True)
|
|
1793
1808
|
|
|
1809
|
+
elif isinstance(expr, ailment.Expr.VEXCCallExpression):
|
|
1810
|
+
for operand in expr.operands:
|
|
1811
|
+
self._link_variables_on_expr(variable_manager, global_variables, block, stmt_idx, stmt, operand)
|
|
1812
|
+
|
|
1813
|
+
elif isinstance(expr, ailment.Expr.DirtyExpression):
|
|
1814
|
+
for operand in expr.operands:
|
|
1815
|
+
self._link_variables_on_expr(variable_manager, global_variables, block, stmt_idx, stmt, operand)
|
|
1816
|
+
if expr.maddr:
|
|
1817
|
+
self._link_variables_on_expr(variable_manager, global_variables, block, stmt_idx, stmt, expr.maddr)
|
|
1818
|
+
if expr.guard:
|
|
1819
|
+
self._link_variables_on_expr(variable_manager, global_variables, block, stmt_idx, stmt, expr.guard)
|
|
1820
|
+
|
|
1794
1821
|
def _function_graph_to_ail_graph(self, func_graph, blocks_by_addr_and_size=None):
|
|
1795
1822
|
if blocks_by_addr_and_size is None:
|
|
1796
1823
|
blocks_by_addr_and_size = self._blocks_by_addr_and_size
|
|
@@ -160,6 +160,8 @@ _ail2claripy_op_mapping = {
|
|
|
160
160
|
"GetMSBs": lambda expr, _, m: _dummy_bvs(expr, m),
|
|
161
161
|
"InterleaveLOV": lambda expr, _, m: _dummy_bvs(expr, m),
|
|
162
162
|
"InterleaveHIV": lambda expr, _, m: _dummy_bvs(expr, m),
|
|
163
|
+
# catch-all
|
|
164
|
+
"_DUMMY_": lambda expr, _, m: _dummy_bvs(expr, m),
|
|
163
165
|
}
|
|
164
166
|
|
|
165
167
|
#
|
|
@@ -771,7 +773,7 @@ class ConditionProcessor:
|
|
|
771
773
|
|
|
772
774
|
if isinstance(
|
|
773
775
|
condition,
|
|
774
|
-
(ailment.Expr.
|
|
776
|
+
(ailment.Expr.VEXCCallExpression, ailment.Expr.BasePointerOffset, ailment.Expr.ITE),
|
|
775
777
|
):
|
|
776
778
|
return _dummy_bvs(condition, self._condition_mapping)
|
|
777
779
|
if isinstance(condition, ailment.Stmt.Call):
|
|
@@ -828,9 +830,14 @@ class ConditionProcessor:
|
|
|
828
830
|
# fall back to op
|
|
829
831
|
lambda_expr = _ail2claripy_op_mapping.get(condition.op, None)
|
|
830
832
|
if lambda_expr is None:
|
|
831
|
-
|
|
832
|
-
|
|
833
|
+
# fall back to the catch-all option
|
|
834
|
+
l.debug(
|
|
835
|
+
"Unsupported AIL expression operation %s (or verbose: %s). Fall back to the default catch-all dummy "
|
|
836
|
+
"option. Consider implementing.",
|
|
837
|
+
condition.op,
|
|
838
|
+
condition.verbose_op,
|
|
833
839
|
)
|
|
840
|
+
lambda_expr = _ail2claripy_op_mapping["_DUMMY_"]
|
|
834
841
|
r = lambda_expr(condition, self.claripy_ast_from_ail_condition, self._condition_mapping)
|
|
835
842
|
|
|
836
843
|
if isinstance(r, claripy.ast.Bool) and nobool:
|
|
@@ -14,6 +14,7 @@ class DecompilationCache:
|
|
|
14
14
|
"""
|
|
15
15
|
|
|
16
16
|
__slots__ = (
|
|
17
|
+
"parameters",
|
|
17
18
|
"addr",
|
|
18
19
|
"type_constraints",
|
|
19
20
|
"func_typevar",
|
|
@@ -25,6 +26,7 @@ class DecompilationCache:
|
|
|
25
26
|
)
|
|
26
27
|
|
|
27
28
|
def __init__(self, addr):
|
|
29
|
+
self.parameters: dict[str, Any] = {}
|
|
28
30
|
self.addr = addr
|
|
29
31
|
self.type_constraints: set | None = None
|
|
30
32
|
self.func_typevar = None
|
|
@@ -68,12 +68,13 @@ class Decompiler(Analysis):
|
|
|
68
68
|
inline_functions=frozenset(),
|
|
69
69
|
update_memory_data: bool = True,
|
|
70
70
|
generate_code: bool = True,
|
|
71
|
+
use_cache: bool = True,
|
|
71
72
|
):
|
|
72
73
|
if not isinstance(func, Function):
|
|
73
74
|
func = self.kb.functions[func]
|
|
74
75
|
self.func: Function = func
|
|
75
76
|
self._cfg = cfg.model if isinstance(cfg, CFGFast) else cfg
|
|
76
|
-
self._options = options
|
|
77
|
+
self._options = options or []
|
|
77
78
|
|
|
78
79
|
if preset is None and optimization_passes:
|
|
79
80
|
self._optimization_passes = optimization_passes
|
|
@@ -102,6 +103,25 @@ class Decompiler(Analysis):
|
|
|
102
103
|
self._update_memory_data = update_memory_data
|
|
103
104
|
self._generate_code = generate_code
|
|
104
105
|
self._inline_functions = inline_functions
|
|
106
|
+
self._cache_parameters = (
|
|
107
|
+
{
|
|
108
|
+
"cfg": self._cfg,
|
|
109
|
+
"variable_kb": self._variable_kb,
|
|
110
|
+
"options": {(o, v) for o, v in self._options if o.category != "Display" and v != o.default_value},
|
|
111
|
+
"optimization_passes": self._optimization_passes,
|
|
112
|
+
"sp_tracker_track_memory": self._sp_tracker_track_memory,
|
|
113
|
+
"peephole_optimizations": self._peephole_optimizations,
|
|
114
|
+
"vars_must_struct": self._vars_must_struct,
|
|
115
|
+
"flavor": self._flavor,
|
|
116
|
+
"expr_comments": self._expr_comments,
|
|
117
|
+
"stmt_comments": self._stmt_comments,
|
|
118
|
+
"ite_exprs": self._ite_exprs,
|
|
119
|
+
"binop_operators": self._binop_operators,
|
|
120
|
+
"inline_functions": self._inline_functions,
|
|
121
|
+
}
|
|
122
|
+
if use_cache
|
|
123
|
+
else None
|
|
124
|
+
)
|
|
105
125
|
|
|
106
126
|
self.clinic = None # mostly for debugging purposes
|
|
107
127
|
self.codegen: CStructuredCodeGenerator | None = None
|
|
@@ -111,27 +131,43 @@ class Decompiler(Analysis):
|
|
|
111
131
|
self.unoptimized_ail_graph: networkx.DiGraph | None = None
|
|
112
132
|
self.ail_graph: networkx.DiGraph | None = None
|
|
113
133
|
self.vvar_id_start = None
|
|
134
|
+
self._optimization_scratch: dict[str, Any] = {}
|
|
114
135
|
|
|
115
136
|
if decompile:
|
|
116
137
|
self._decompile()
|
|
117
138
|
|
|
139
|
+
def _can_use_decompilation_cache(self, cache: DecompilationCache) -> bool:
|
|
140
|
+
a, b = self._cache_parameters, cache.parameters
|
|
141
|
+
id_checks = {"cfg", "variable_kb"}
|
|
142
|
+
return all(a[k] is b[k] if k in id_checks else a[k] == b[k] for k in self._cache_parameters)
|
|
143
|
+
|
|
118
144
|
@timethis
|
|
119
145
|
def _decompile(self):
|
|
120
146
|
if self.func.is_simprocedure:
|
|
121
147
|
return
|
|
122
148
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
149
|
+
cache = None
|
|
150
|
+
|
|
151
|
+
if self._cache_parameters is not None:
|
|
152
|
+
try:
|
|
153
|
+
cache = self.kb.decompilations[self.func.addr]
|
|
154
|
+
if not self._can_use_decompilation_cache(cache):
|
|
155
|
+
cache = None
|
|
156
|
+
except KeyError:
|
|
157
|
+
pass
|
|
158
|
+
|
|
159
|
+
if cache:
|
|
126
160
|
old_codegen = cache.codegen
|
|
127
161
|
old_clinic = cache.clinic
|
|
128
162
|
ite_exprs = cache.ite_exprs if self._ite_exprs is None else self._ite_exprs
|
|
129
163
|
binop_operators = cache.binop_operators if self._binop_operators is None else self._binop_operators
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
binop_operators = self._binop_operators
|
|
164
|
+
l.debug("Decompilation cache hit")
|
|
165
|
+
else:
|
|
133
166
|
old_codegen = None
|
|
134
167
|
old_clinic = None
|
|
168
|
+
ite_exprs = self._ite_exprs
|
|
169
|
+
binop_operators = self._binop_operators
|
|
170
|
+
l.debug("Decompilation cache miss")
|
|
135
171
|
|
|
136
172
|
self.options_by_class = defaultdict(list)
|
|
137
173
|
|
|
@@ -167,6 +203,7 @@ class Decompiler(Analysis):
|
|
|
167
203
|
fold_callexprs_into_conditions = True
|
|
168
204
|
|
|
169
205
|
cache = DecompilationCache(self.func.addr)
|
|
206
|
+
cache.parameters = self._cache_parameters
|
|
170
207
|
cache.ite_exprs = ite_exprs
|
|
171
208
|
cache.binop_operators = binop_operators
|
|
172
209
|
|
|
@@ -189,6 +226,7 @@ class Decompiler(Analysis):
|
|
|
189
226
|
cache=cache,
|
|
190
227
|
progress_callback=progress_callback,
|
|
191
228
|
inline_functions=self._inline_functions,
|
|
229
|
+
optimization_scratch=self._optimization_scratch,
|
|
192
230
|
**self.options_to_params(self.options_by_class["clinic"]),
|
|
193
231
|
)
|
|
194
232
|
else:
|
|
@@ -302,6 +340,8 @@ class Decompiler(Analysis):
|
|
|
302
340
|
self.cache.codegen = codegen
|
|
303
341
|
self.cache.clinic = self.clinic
|
|
304
342
|
|
|
343
|
+
self.kb.decompilations[self.func.addr] = self.cache
|
|
344
|
+
|
|
305
345
|
def _recover_regions(self, graph: networkx.DiGraph, condition_processor, update_graph: bool = True):
|
|
306
346
|
return self.project.analyses[RegionIdentifier].prep(kb=self.kb)(
|
|
307
347
|
self.func,
|
|
@@ -355,6 +395,7 @@ class Decompiler(Analysis):
|
|
|
355
395
|
variable_kb=self._variable_kb,
|
|
356
396
|
reaching_definitions=reaching_definitions,
|
|
357
397
|
entry_node_addr=self.clinic.entry_node_addr,
|
|
398
|
+
scratch=self._optimization_scratch,
|
|
358
399
|
**kwargs,
|
|
359
400
|
)
|
|
360
401
|
|
|
@@ -414,6 +455,7 @@ class Decompiler(Analysis):
|
|
|
414
455
|
reaching_definitions=reaching_definitions,
|
|
415
456
|
vvar_id_start=self.vvar_id_start,
|
|
416
457
|
entry_node_addr=self.clinic.entry_node_addr,
|
|
458
|
+
scratch=self._optimization_scratch,
|
|
417
459
|
**kwargs,
|
|
418
460
|
)
|
|
419
461
|
|
|
@@ -442,7 +484,7 @@ class Decompiler(Analysis):
|
|
|
442
484
|
continue
|
|
443
485
|
|
|
444
486
|
pass_ = timethis(pass_)
|
|
445
|
-
a = pass_(self.func, seq=seq_node, **kwargs)
|
|
487
|
+
a = pass_(self.func, seq=seq_node, scratch=self._optimization_scratch, **kwargs)
|
|
446
488
|
if a.out_seq:
|
|
447
489
|
seq_node = a.out_seq
|
|
448
490
|
|
|
@@ -3,7 +3,7 @@ import logging
|
|
|
3
3
|
from collections import defaultdict
|
|
4
4
|
|
|
5
5
|
from ailment.block import Block
|
|
6
|
-
from ailment.expression import Phi, VirtualVariable
|
|
6
|
+
from ailment.expression import Phi, VirtualVariable, VirtualVariableCategory
|
|
7
7
|
from ailment.statement import Assignment, Jump, ConditionalJump, Label
|
|
8
8
|
|
|
9
9
|
from angr.analyses import Analysis
|
|
@@ -213,8 +213,16 @@ class GraphDephicationVVarMapping(Analysis): # pylint:disable=abstract-method
|
|
|
213
213
|
|
|
214
214
|
the_block = self._blocks[(src_block_addr, src_block_idx)]
|
|
215
215
|
ins_addr = the_block.addr + the_block.original_size - 1
|
|
216
|
+
if vvar.category == VirtualVariableCategory.PARAMETER:
|
|
217
|
+
# it's a parameter, so we copy the variable into a dummy register vvar
|
|
218
|
+
# a parameter vvar should never be assigned to
|
|
219
|
+
new_category = VirtualVariableCategory.REGISTER
|
|
220
|
+
new_oident = 4096
|
|
221
|
+
else:
|
|
222
|
+
new_category = vvar.category
|
|
223
|
+
new_oident = vvar.oident
|
|
216
224
|
new_vvar = VirtualVariable(
|
|
217
|
-
None, new_vvar_id, vvar.bits,
|
|
225
|
+
None, new_vvar_id, vvar.bits, new_category, oident=new_oident, ins_addr=ins_addr
|
|
218
226
|
)
|
|
219
227
|
assignment = Assignment(None, new_vvar, vvar, ins_addr=ins_addr)
|
|
220
228
|
|
|
@@ -3,7 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
import logging
|
|
4
4
|
|
|
5
5
|
from ailment.block import Block
|
|
6
|
-
from ailment.statement import Statement, Assignment, Store, Call, Return, ConditionalJump
|
|
6
|
+
from ailment.statement import Statement, Assignment, Store, Call, Return, ConditionalJump, DirtyStatement
|
|
7
7
|
from ailment.expression import (
|
|
8
8
|
Register,
|
|
9
9
|
VirtualVariable,
|
|
@@ -14,6 +14,8 @@ from ailment.expression import (
|
|
|
14
14
|
Convert,
|
|
15
15
|
StackBaseOffset,
|
|
16
16
|
ITE,
|
|
17
|
+
VEXCCallExpression,
|
|
18
|
+
DirtyExpression,
|
|
17
19
|
)
|
|
18
20
|
|
|
19
21
|
from angr.engines.light import SimEngineLight, SimEngineLightAILMixin
|
|
@@ -126,7 +128,7 @@ class SimEngineDephiRewriting(
|
|
|
126
128
|
return None
|
|
127
129
|
|
|
128
130
|
def _handle_Call(self, stmt: Call) -> Call | None:
|
|
129
|
-
new_target = self._expr(stmt.target) if stmt.target is not None else None
|
|
131
|
+
new_target = self._expr(stmt.target) if stmt.target is not None and not isinstance(stmt.target, str) else None
|
|
130
132
|
new_ret_expr = self._expr(stmt.ret_expr) if stmt.ret_expr is not None else None
|
|
131
133
|
new_fp_ret_expr = self._expr(stmt.fp_ret_expr) if stmt.fp_ret_expr is not None else None
|
|
132
134
|
|
|
@@ -139,10 +141,19 @@ class SimEngineDephiRewriting(
|
|
|
139
141
|
args=stmt.args,
|
|
140
142
|
ret_expr=stmt.ret_expr if new_ret_expr is None else new_ret_expr,
|
|
141
143
|
fp_ret_expr=stmt.fp_ret_expr if new_fp_ret_expr is None else new_fp_ret_expr,
|
|
144
|
+
bits=stmt.bits,
|
|
142
145
|
**stmt.tags,
|
|
143
146
|
)
|
|
144
147
|
return None
|
|
145
148
|
|
|
149
|
+
_handle_CallExpr = _handle_Call
|
|
150
|
+
|
|
151
|
+
def _handle_DirtyStatement(self, stmt: DirtyStatement) -> DirtyStatement | None:
|
|
152
|
+
dirty = self._expr(stmt.dirty)
|
|
153
|
+
if dirty is None or dirty is stmt.dirty:
|
|
154
|
+
return None
|
|
155
|
+
return DirtyStatement(stmt.idx, dirty, **stmt.tags)
|
|
156
|
+
|
|
146
157
|
def _handle_Register(self, expr: Register) -> None:
|
|
147
158
|
return None
|
|
148
159
|
|
|
@@ -243,5 +254,57 @@ class SimEngineDephiRewriting(
|
|
|
243
254
|
)
|
|
244
255
|
return None
|
|
245
256
|
|
|
257
|
+
def _handle_VEXCCallExpression(self, expr: VEXCCallExpression) -> VEXCCallExpression | None:
|
|
258
|
+
new_operands = []
|
|
259
|
+
updated = False
|
|
260
|
+
for o in expr.operands:
|
|
261
|
+
new_o = self._expr(o)
|
|
262
|
+
if new_o is not None:
|
|
263
|
+
updated = True
|
|
264
|
+
new_operands.append(new_o)
|
|
265
|
+
else:
|
|
266
|
+
new_operands.append(o)
|
|
267
|
+
|
|
268
|
+
if updated:
|
|
269
|
+
return VEXCCallExpression(
|
|
270
|
+
expr.idx,
|
|
271
|
+
expr.callee,
|
|
272
|
+
new_operands,
|
|
273
|
+
bits=expr.bits,
|
|
274
|
+
**expr.tags,
|
|
275
|
+
)
|
|
276
|
+
return None
|
|
277
|
+
|
|
278
|
+
def _handle_DirtyExpression(self, expr: DirtyExpression) -> DirtyExpression | None:
|
|
279
|
+
new_operands = []
|
|
280
|
+
updated = False
|
|
281
|
+
for o in expr.operands:
|
|
282
|
+
new_o = self._expr(o)
|
|
283
|
+
if new_o is not None:
|
|
284
|
+
updated = True
|
|
285
|
+
new_operands.append(new_o)
|
|
286
|
+
else:
|
|
287
|
+
new_operands.append(o)
|
|
288
|
+
|
|
289
|
+
new_guard = None
|
|
290
|
+
if expr.guard is not None:
|
|
291
|
+
new_guard = self._expr(expr.guard)
|
|
292
|
+
if new_guard is not None:
|
|
293
|
+
updated = True
|
|
294
|
+
|
|
295
|
+
if updated:
|
|
296
|
+
return DirtyExpression(
|
|
297
|
+
expr.idx,
|
|
298
|
+
expr.callee,
|
|
299
|
+
new_operands,
|
|
300
|
+
guard=new_guard,
|
|
301
|
+
mfx=expr.mfx,
|
|
302
|
+
maddr=expr.maddr,
|
|
303
|
+
msize=expr.msize,
|
|
304
|
+
bits=expr.bits,
|
|
305
|
+
**expr.tags,
|
|
306
|
+
)
|
|
307
|
+
return None
|
|
308
|
+
|
|
246
309
|
def _handle_StackBaseOffset(self, expr: StackBaseOffset) -> None:
|
|
247
310
|
return None
|
|
@@ -1,23 +1,52 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
from typing import Any, TYPE_CHECKING
|
|
3
|
-
from
|
|
3
|
+
from collections import defaultdict
|
|
4
|
+
import logging
|
|
5
|
+
|
|
6
|
+
from ailment import AILBlockWalkerBase, AILBlockWalker
|
|
7
|
+
from ailment.statement import Assignment, Call
|
|
8
|
+
from ailment.expression import VirtualVariable, Convert, BinaryOp, Phi
|
|
9
|
+
|
|
10
|
+
from angr.knowledge_plugins.key_definitions import atoms
|
|
11
|
+
from angr.code_location import CodeLocation
|
|
4
12
|
|
|
5
13
|
if TYPE_CHECKING:
|
|
6
14
|
from ailment.expression import (
|
|
7
15
|
Expression,
|
|
8
|
-
BinaryOp,
|
|
9
16
|
Load,
|
|
10
17
|
UnaryOp,
|
|
11
|
-
Convert,
|
|
12
18
|
ITE,
|
|
13
19
|
DirtyExpression,
|
|
14
20
|
VEXCCallExpression,
|
|
15
21
|
)
|
|
16
|
-
from ailment.statement import
|
|
22
|
+
from ailment.statement import Statement
|
|
17
23
|
from ailment.block import Block
|
|
18
24
|
|
|
19
25
|
|
|
20
|
-
|
|
26
|
+
_l = logging.getLogger(__name__)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class ExprNarrowingInfo:
|
|
30
|
+
"""
|
|
31
|
+
Stores the analysis result of _narrowing_needed().
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
__slots__ = ("narrowable", "to_size", "use_exprs", "phi_vars")
|
|
35
|
+
|
|
36
|
+
def __init__(
|
|
37
|
+
self,
|
|
38
|
+
narrowable: bool,
|
|
39
|
+
to_size: int | None = None,
|
|
40
|
+
use_exprs: list[tuple[atoms.VirtualVariable, CodeLocation, tuple[str, tuple[Expression, ...]]]] | None = None,
|
|
41
|
+
phi_vars: set[atoms.VirtualVariable] | None = None,
|
|
42
|
+
):
|
|
43
|
+
self.narrowable = narrowable
|
|
44
|
+
self.to_size = to_size
|
|
45
|
+
self.use_exprs = use_exprs
|
|
46
|
+
self.phi_vars = phi_vars
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class NarrowingInfoExtractor(AILBlockWalkerBase):
|
|
21
50
|
"""
|
|
22
51
|
Walks a statement or an expression and extracts the operations that are applied on the given expression.
|
|
23
52
|
|
|
@@ -76,7 +105,11 @@ class ExpressionNarrowingWalker(AILBlockWalkerBase):
|
|
|
76
105
|
def _handle_DirtyExpression(
|
|
77
106
|
self, expr_idx: int, expr: DirtyExpression, stmt_idx: int, stmt: Statement, block: Block | None
|
|
78
107
|
):
|
|
79
|
-
|
|
108
|
+
r = False
|
|
109
|
+
if expr.operands:
|
|
110
|
+
for i, op in enumerate(expr.operands):
|
|
111
|
+
r |= self._handle_expr(i, op, stmt_idx, stmt, block)
|
|
112
|
+
return r
|
|
80
113
|
|
|
81
114
|
def _handle_VEXCCallExpression(
|
|
82
115
|
self, expr_idx: int, expr: VEXCCallExpression, stmt_idx: int, stmt: Statement, block: Block | None
|
|
@@ -85,3 +118,170 @@ class ExpressionNarrowingWalker(AILBlockWalkerBase):
|
|
|
85
118
|
for idx, operand in enumerate(expr.operands):
|
|
86
119
|
r |= self._handle_expr(idx, operand, stmt_idx, stmt, block)
|
|
87
120
|
return r
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
class ExpressionNarrower(AILBlockWalker):
|
|
124
|
+
"""
|
|
125
|
+
Narrows an expression regardless of whether the expression is a definition or a use.
|
|
126
|
+
"""
|
|
127
|
+
|
|
128
|
+
def __init__(
|
|
129
|
+
self, project, rd, narrowables, addr2blocks: dict[tuple[int, int | None], Block], new_blocks: dict[Block, Block]
|
|
130
|
+
):
|
|
131
|
+
super().__init__(update_block=False)
|
|
132
|
+
|
|
133
|
+
self.project = project
|
|
134
|
+
self._rd = rd
|
|
135
|
+
self._addr2blocks = addr2blocks
|
|
136
|
+
self._new_blocks = new_blocks
|
|
137
|
+
|
|
138
|
+
self.new_vvar_sizes: dict[int, int] = {}
|
|
139
|
+
self.replacement_core_vvars: dict[int, list[VirtualVariable]] = defaultdict(list)
|
|
140
|
+
self.narrowed_any = False
|
|
141
|
+
|
|
142
|
+
for def_, narrow_info in narrowables:
|
|
143
|
+
self.new_vvar_sizes[def_.atom.varid] = narrow_info.to_size
|
|
144
|
+
|
|
145
|
+
def walk(self, block: Block):
|
|
146
|
+
self.narrowed_any = False
|
|
147
|
+
return super().walk(block)
|
|
148
|
+
|
|
149
|
+
def _handle_Assignment(self, stmt_idx: int, stmt: Assignment, block: Block | None) -> Assignment | None:
|
|
150
|
+
|
|
151
|
+
if isinstance(stmt.src, Phi):
|
|
152
|
+
changed = False
|
|
153
|
+
|
|
154
|
+
src_and_vvars = []
|
|
155
|
+
for src, vvar in stmt.src.src_and_vvars:
|
|
156
|
+
if vvar is None:
|
|
157
|
+
src_and_vvars.append((src, None))
|
|
158
|
+
continue
|
|
159
|
+
if vvar.varid in self.new_vvar_sizes and self.new_vvar_sizes[vvar.varid] != vvar.size:
|
|
160
|
+
self.narrowed_any = True
|
|
161
|
+
changed = True
|
|
162
|
+
new_var = VirtualVariable(
|
|
163
|
+
vvar.idx,
|
|
164
|
+
vvar.varid,
|
|
165
|
+
self.new_vvar_sizes[vvar.varid] * self.project.arch.byte_width,
|
|
166
|
+
category=vvar.category,
|
|
167
|
+
oident=vvar.oident,
|
|
168
|
+
**vvar.tags,
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
self.replacement_core_vvars[new_var.varid].append(new_var)
|
|
172
|
+
else:
|
|
173
|
+
new_var = None
|
|
174
|
+
|
|
175
|
+
src_and_vvars.append((src, new_var))
|
|
176
|
+
|
|
177
|
+
new_src = Phi(stmt.src.idx, stmt.src.bits, src_and_vvars, **stmt.src.tags)
|
|
178
|
+
|
|
179
|
+
else:
|
|
180
|
+
new_src = self._handle_expr(1, stmt.src, stmt_idx, stmt, block)
|
|
181
|
+
if new_src is None:
|
|
182
|
+
changed = False
|
|
183
|
+
new_src = stmt.src
|
|
184
|
+
else:
|
|
185
|
+
changed = True
|
|
186
|
+
|
|
187
|
+
if isinstance(stmt.dst, VirtualVariable) and stmt.dst.varid in self.new_vvar_sizes:
|
|
188
|
+
changed = True
|
|
189
|
+
new_dst = VirtualVariable(
|
|
190
|
+
stmt.dst.idx,
|
|
191
|
+
stmt.dst.varid,
|
|
192
|
+
self.new_vvar_sizes[stmt.dst.varid] * self.project.arch.byte_width,
|
|
193
|
+
category=stmt.dst.category,
|
|
194
|
+
oident=stmt.dst.oident,
|
|
195
|
+
**stmt.dst.tags,
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
self.replacement_core_vvars[new_dst.varid].append(new_dst)
|
|
199
|
+
|
|
200
|
+
if isinstance(new_src, Phi):
|
|
201
|
+
new_src.bits = self.new_vvar_sizes[stmt.dst.varid] * self.project.arch.byte_width
|
|
202
|
+
else:
|
|
203
|
+
new_src = Convert(
|
|
204
|
+
None,
|
|
205
|
+
stmt.src.bits,
|
|
206
|
+
self.new_vvar_sizes[stmt.dst.varid] * self.project.arch.byte_width,
|
|
207
|
+
False,
|
|
208
|
+
new_src,
|
|
209
|
+
**new_src.tags,
|
|
210
|
+
)
|
|
211
|
+
else:
|
|
212
|
+
new_dst = self._handle_expr(0, stmt.dst, stmt_idx, stmt, block)
|
|
213
|
+
if new_dst is not None:
|
|
214
|
+
changed = True
|
|
215
|
+
else:
|
|
216
|
+
new_dst = stmt.dst
|
|
217
|
+
|
|
218
|
+
if changed:
|
|
219
|
+
self.narrowed_any = True
|
|
220
|
+
return Assignment(stmt.idx, new_dst, new_src, **stmt.tags)
|
|
221
|
+
|
|
222
|
+
return None
|
|
223
|
+
|
|
224
|
+
def _handle_VirtualVariable(
|
|
225
|
+
self, expr_idx: int, expr: VirtualVariable, stmt_idx: int, stmt: Statement, block: Block | None
|
|
226
|
+
) -> Convert | None:
|
|
227
|
+
if expr.varid in self.new_vvar_sizes and self.new_vvar_sizes[expr.varid] != expr.size:
|
|
228
|
+
self.narrowed_any = True
|
|
229
|
+
new_expr = VirtualVariable(
|
|
230
|
+
expr.idx,
|
|
231
|
+
expr.varid,
|
|
232
|
+
self.new_vvar_sizes[expr.varid] * self.project.arch.byte_width,
|
|
233
|
+
category=expr.category,
|
|
234
|
+
oident=expr.oident,
|
|
235
|
+
**expr.tags,
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
self.replacement_core_vvars[expr.varid].append(new_expr)
|
|
239
|
+
|
|
240
|
+
return Convert(
|
|
241
|
+
None,
|
|
242
|
+
new_expr.bits,
|
|
243
|
+
expr.bits,
|
|
244
|
+
False,
|
|
245
|
+
new_expr,
|
|
246
|
+
**new_expr.tags,
|
|
247
|
+
)
|
|
248
|
+
return None
|
|
249
|
+
|
|
250
|
+
def _handle_Call(self, stmt_idx: int, stmt: Call, block: Block | None) -> Call | None:
|
|
251
|
+
new_stmt = super()._handle_Call(stmt_idx, stmt, block)
|
|
252
|
+
if new_stmt is None:
|
|
253
|
+
changed = False
|
|
254
|
+
new_stmt = stmt
|
|
255
|
+
else:
|
|
256
|
+
changed = True
|
|
257
|
+
|
|
258
|
+
if (
|
|
259
|
+
stmt.ret_expr is not None
|
|
260
|
+
and isinstance(stmt.ret_expr, VirtualVariable)
|
|
261
|
+
and stmt.ret_expr.was_reg
|
|
262
|
+
and stmt.ret_expr.varid in self.new_vvar_sizes
|
|
263
|
+
and stmt.ret_expr.size != self.new_vvar_sizes[stmt.ret_expr.varid]
|
|
264
|
+
):
|
|
265
|
+
changed = True
|
|
266
|
+
|
|
267
|
+
# update reg name
|
|
268
|
+
tags = dict(stmt.ret_expr.tags)
|
|
269
|
+
tags["reg_name"] = self.project.arch.translate_register_name(
|
|
270
|
+
stmt.ret_expr.reg_offset, size=self.new_vvar_sizes[stmt.ret_expr.varid]
|
|
271
|
+
)
|
|
272
|
+
new_ret_expr = VirtualVariable(
|
|
273
|
+
stmt.ret_expr.idx,
|
|
274
|
+
stmt.ret_expr.varid,
|
|
275
|
+
self.new_vvar_sizes[stmt.ret_expr.varid] * self.project.arch.byte_width,
|
|
276
|
+
category=stmt.ret_expr.category,
|
|
277
|
+
oident=stmt.ret_expr.oident,
|
|
278
|
+
**tags,
|
|
279
|
+
)
|
|
280
|
+
self.replacement_core_vvars[new_ret_expr.varid].append(new_ret_expr)
|
|
281
|
+
new_stmt.ret_expr = new_ret_expr
|
|
282
|
+
|
|
283
|
+
if changed:
|
|
284
|
+
self.narrowed_any = True
|
|
285
|
+
return new_stmt
|
|
286
|
+
|
|
287
|
+
return None
|
|
@@ -18,7 +18,10 @@ class DivSimplifierAILEngine(SimplifierAILEngine):
|
|
|
18
18
|
An AIL pass for the div simplifier
|
|
19
19
|
"""
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
@staticmethod
|
|
22
|
+
def _check_divisor(a, b, ndigits=6):
|
|
23
|
+
if b == 0:
|
|
24
|
+
return None
|
|
22
25
|
divisor_1 = 1 + (a // b)
|
|
23
26
|
divisor_2 = int(round(a / float(b), ndigits))
|
|
24
27
|
return divisor_1 if divisor_1 == divisor_2 else None
|