angr 9.2.123__py3-none-manylinux2014_aarch64.whl → 9.2.125__py3-none-manylinux2014_aarch64.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/__init__.py +9 -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/codecave.py +77 -0
- angr/analyses/decompiler/ail_simplifier.py +16 -19
- angr/analyses/decompiler/callsite_maker.py +8 -7
- angr/analyses/decompiler/ccall_rewriters/amd64_ccalls.py +24 -2
- angr/analyses/decompiler/clinic.py +58 -2
- angr/analyses/decompiler/condition_processor.py +10 -3
- angr/analyses/decompiler/decompilation_cache.py +2 -0
- angr/analyses/decompiler/decompiler.py +54 -8
- angr/analyses/decompiler/dephication/graph_vvar_mapping.py +10 -2
- angr/analyses/decompiler/dephication/rewriting_engine.py +64 -1
- angr/analyses/decompiler/expression_narrower.py +5 -1
- angr/analyses/decompiler/optimization_passes/__init__.py +3 -0
- angr/analyses/decompiler/optimization_passes/div_simplifier.py +4 -1
- angr/analyses/decompiler/optimization_passes/inlined_string_transformation_simplifier.py +13 -0
- angr/analyses/decompiler/optimization_passes/ite_region_converter.py +23 -4
- 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/tag_slicer.py +41 -0
- angr/analyses/decompiler/optimization_passes/win_stack_canary_simplifier.py +2 -0
- angr/analyses/decompiler/peephole_optimizations/const_mull_a_shift.py +2 -0
- angr/analyses/decompiler/peephole_optimizations/constant_derefs.py +2 -2
- angr/analyses/decompiler/peephole_optimizations/remove_cascading_conversions.py +8 -2
- angr/analyses/decompiler/region_identifier.py +36 -0
- angr/analyses/decompiler/region_simplifiers/loop.py +2 -8
- angr/analyses/decompiler/region_simplifiers/switch_cluster_simplifier.py +9 -3
- 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/patchfinder.py +137 -0
- angr/analyses/pathfinder.py +282 -0
- angr/analyses/propagator/engine_ail.py +10 -3
- angr/analyses/reaching_definitions/engine_ail.py +10 -15
- angr/analyses/s_propagator.py +16 -9
- angr/analyses/s_reaching_definitions/s_rda_view.py +127 -63
- angr/analyses/smc.py +159 -0
- angr/analyses/variable_recovery/engine_ail.py +14 -0
- angr/analyses/variable_recovery/engine_base.py +11 -0
- angr/angrdb/models.py +1 -2
- angr/engines/light/engine.py +12 -0
- angr/engines/vex/heavy/heavy.py +2 -0
- angr/exploration_techniques/spiller_db.py +1 -2
- angr/knowledge_plugins/__init__.py +2 -0
- angr/knowledge_plugins/decompilation.py +45 -0
- angr/knowledge_plugins/functions/function.py +4 -0
- angr/knowledge_plugins/functions/function_manager.py +18 -9
- angr/knowledge_plugins/functions/function_parser.py +1 -1
- angr/knowledge_plugins/functions/soot_function.py +1 -0
- angr/knowledge_plugins/key_definitions/atoms.py +8 -0
- angr/misc/ux.py +2 -2
- angr/procedures/definitions/parse_win32json.py +2 -1
- angr/project.py +17 -1
- angr/state_plugins/history.py +6 -4
- 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/bits.py +4 -0
- angr/utils/constants.py +1 -1
- angr/utils/graph.py +15 -0
- angr/utils/tagged_interval_map.py +112 -0
- angr/vaults.py +2 -2
- {angr-9.2.123.dist-info → angr-9.2.125.dist-info}/METADATA +6 -6
- {angr-9.2.123.dist-info → angr-9.2.125.dist-info}/RECORD +103 -96
- {angr-9.2.123.dist-info → angr-9.2.125.dist-info}/WHEEL +1 -1
- {angr-9.2.123.dist-info → angr-9.2.125.dist-info}/LICENSE +0 -0
- {angr-9.2.123.dist-info → angr-9.2.125.dist-info}/entry_points.txt +0 -0
- {angr-9.2.123.dist-info → angr-9.2.125.dist-info}/top_level.txt +0 -0
|
@@ -66,14 +66,16 @@ class Decompiler(Analysis):
|
|
|
66
66
|
decompile=True,
|
|
67
67
|
regen_clinic=True,
|
|
68
68
|
inline_functions=frozenset(),
|
|
69
|
+
desired_variables=frozenset(),
|
|
69
70
|
update_memory_data: bool = True,
|
|
70
71
|
generate_code: bool = True,
|
|
72
|
+
use_cache: bool = True,
|
|
71
73
|
):
|
|
72
74
|
if not isinstance(func, Function):
|
|
73
75
|
func = self.kb.functions[func]
|
|
74
76
|
self.func: Function = func
|
|
75
77
|
self._cfg = cfg.model if isinstance(cfg, CFGFast) else cfg
|
|
76
|
-
self._options = options
|
|
78
|
+
self._options = options or []
|
|
77
79
|
|
|
78
80
|
if preset is None and optimization_passes:
|
|
79
81
|
self._optimization_passes = optimization_passes
|
|
@@ -102,6 +104,27 @@ class Decompiler(Analysis):
|
|
|
102
104
|
self._update_memory_data = update_memory_data
|
|
103
105
|
self._generate_code = generate_code
|
|
104
106
|
self._inline_functions = inline_functions
|
|
107
|
+
self._desired_variables = desired_variables
|
|
108
|
+
self._cache_parameters = (
|
|
109
|
+
{
|
|
110
|
+
"cfg": self._cfg,
|
|
111
|
+
"variable_kb": self._variable_kb,
|
|
112
|
+
"options": {(o, v) for o, v in self._options if o.category != "Display" and v != o.default_value},
|
|
113
|
+
"optimization_passes": self._optimization_passes,
|
|
114
|
+
"sp_tracker_track_memory": self._sp_tracker_track_memory,
|
|
115
|
+
"peephole_optimizations": self._peephole_optimizations,
|
|
116
|
+
"vars_must_struct": self._vars_must_struct,
|
|
117
|
+
"flavor": self._flavor,
|
|
118
|
+
"expr_comments": self._expr_comments,
|
|
119
|
+
"stmt_comments": self._stmt_comments,
|
|
120
|
+
"ite_exprs": self._ite_exprs,
|
|
121
|
+
"binop_operators": self._binop_operators,
|
|
122
|
+
"inline_functions": self._inline_functions,
|
|
123
|
+
"desired_variables": self._desired_variables,
|
|
124
|
+
}
|
|
125
|
+
if use_cache
|
|
126
|
+
else None
|
|
127
|
+
)
|
|
105
128
|
|
|
106
129
|
self.clinic = None # mostly for debugging purposes
|
|
107
130
|
self.codegen: CStructuredCodeGenerator | None = None
|
|
@@ -111,27 +134,43 @@ class Decompiler(Analysis):
|
|
|
111
134
|
self.unoptimized_ail_graph: networkx.DiGraph | None = None
|
|
112
135
|
self.ail_graph: networkx.DiGraph | None = None
|
|
113
136
|
self.vvar_id_start = None
|
|
137
|
+
self._optimization_scratch: dict[str, Any] = {}
|
|
114
138
|
|
|
115
139
|
if decompile:
|
|
116
140
|
self._decompile()
|
|
117
141
|
|
|
142
|
+
def _can_use_decompilation_cache(self, cache: DecompilationCache) -> bool:
|
|
143
|
+
a, b = self._cache_parameters, cache.parameters
|
|
144
|
+
id_checks = {"cfg", "variable_kb"}
|
|
145
|
+
return all(a[k] is b[k] if k in id_checks else a[k] == b[k] for k in self._cache_parameters)
|
|
146
|
+
|
|
118
147
|
@timethis
|
|
119
148
|
def _decompile(self):
|
|
120
149
|
if self.func.is_simprocedure:
|
|
121
150
|
return
|
|
122
151
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
152
|
+
cache = None
|
|
153
|
+
|
|
154
|
+
if self._cache_parameters is not None:
|
|
155
|
+
try:
|
|
156
|
+
cache = self.kb.decompilations[self.func.addr]
|
|
157
|
+
if not self._can_use_decompilation_cache(cache):
|
|
158
|
+
cache = None
|
|
159
|
+
except KeyError:
|
|
160
|
+
pass
|
|
161
|
+
|
|
162
|
+
if cache:
|
|
126
163
|
old_codegen = cache.codegen
|
|
127
164
|
old_clinic = cache.clinic
|
|
128
165
|
ite_exprs = cache.ite_exprs if self._ite_exprs is None else self._ite_exprs
|
|
129
166
|
binop_operators = cache.binop_operators if self._binop_operators is None else self._binop_operators
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
binop_operators = self._binop_operators
|
|
167
|
+
l.debug("Decompilation cache hit")
|
|
168
|
+
else:
|
|
133
169
|
old_codegen = None
|
|
134
170
|
old_clinic = None
|
|
171
|
+
ite_exprs = self._ite_exprs
|
|
172
|
+
binop_operators = self._binop_operators
|
|
173
|
+
l.debug("Decompilation cache miss")
|
|
135
174
|
|
|
136
175
|
self.options_by_class = defaultdict(list)
|
|
137
176
|
|
|
@@ -167,6 +206,7 @@ class Decompiler(Analysis):
|
|
|
167
206
|
fold_callexprs_into_conditions = True
|
|
168
207
|
|
|
169
208
|
cache = DecompilationCache(self.func.addr)
|
|
209
|
+
cache.parameters = self._cache_parameters
|
|
170
210
|
cache.ite_exprs = ite_exprs
|
|
171
211
|
cache.binop_operators = binop_operators
|
|
172
212
|
|
|
@@ -189,6 +229,8 @@ class Decompiler(Analysis):
|
|
|
189
229
|
cache=cache,
|
|
190
230
|
progress_callback=progress_callback,
|
|
191
231
|
inline_functions=self._inline_functions,
|
|
232
|
+
desired_variables=self._desired_variables,
|
|
233
|
+
optimization_scratch=self._optimization_scratch,
|
|
192
234
|
**self.options_to_params(self.options_by_class["clinic"]),
|
|
193
235
|
)
|
|
194
236
|
else:
|
|
@@ -302,6 +344,8 @@ class Decompiler(Analysis):
|
|
|
302
344
|
self.cache.codegen = codegen
|
|
303
345
|
self.cache.clinic = self.clinic
|
|
304
346
|
|
|
347
|
+
self.kb.decompilations[self.func.addr] = self.cache
|
|
348
|
+
|
|
305
349
|
def _recover_regions(self, graph: networkx.DiGraph, condition_processor, update_graph: bool = True):
|
|
306
350
|
return self.project.analyses[RegionIdentifier].prep(kb=self.kb)(
|
|
307
351
|
self.func,
|
|
@@ -355,6 +399,7 @@ class Decompiler(Analysis):
|
|
|
355
399
|
variable_kb=self._variable_kb,
|
|
356
400
|
reaching_definitions=reaching_definitions,
|
|
357
401
|
entry_node_addr=self.clinic.entry_node_addr,
|
|
402
|
+
scratch=self._optimization_scratch,
|
|
358
403
|
**kwargs,
|
|
359
404
|
)
|
|
360
405
|
|
|
@@ -414,6 +459,7 @@ class Decompiler(Analysis):
|
|
|
414
459
|
reaching_definitions=reaching_definitions,
|
|
415
460
|
vvar_id_start=self.vvar_id_start,
|
|
416
461
|
entry_node_addr=self.clinic.entry_node_addr,
|
|
462
|
+
scratch=self._optimization_scratch,
|
|
417
463
|
**kwargs,
|
|
418
464
|
)
|
|
419
465
|
|
|
@@ -442,7 +488,7 @@ class Decompiler(Analysis):
|
|
|
442
488
|
continue
|
|
443
489
|
|
|
444
490
|
pass_ = timethis(pass_)
|
|
445
|
-
a = pass_(self.func, seq=seq_node, **kwargs)
|
|
491
|
+
a = pass_(self.func, seq=seq_node, scratch=self._optimization_scratch, **kwargs)
|
|
446
492
|
if a.out_seq:
|
|
447
493
|
seq_node = a.out_seq
|
|
448
494
|
|
|
@@ -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
|
|
@@ -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
|
|
@@ -105,7 +105,11 @@ class NarrowingInfoExtractor(AILBlockWalkerBase):
|
|
|
105
105
|
def _handle_DirtyExpression(
|
|
106
106
|
self, expr_idx: int, expr: DirtyExpression, stmt_idx: int, stmt: Statement, block: Block | None
|
|
107
107
|
):
|
|
108
|
-
|
|
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
|
|
109
113
|
|
|
110
114
|
def _handle_VEXCCallExpression(
|
|
111
115
|
self, expr_idx: int, expr: VEXCCallExpression, stmt_idx: int, stmt: Statement, block: Block | None
|
|
@@ -26,6 +26,7 @@ from .cross_jump_reverter import CrossJumpReverter
|
|
|
26
26
|
from .code_motion import CodeMotionOptimization
|
|
27
27
|
from .switch_default_case_duplicator import SwitchDefaultCaseDuplicator
|
|
28
28
|
from .deadblock_remover import DeadblockRemover
|
|
29
|
+
from .tag_slicer import TagSlicer
|
|
29
30
|
from .inlined_string_transformation_simplifier import InlinedStringTransformationSimplifier
|
|
30
31
|
from .const_prop_reverter import ConstPropOptReverter
|
|
31
32
|
from .call_stmt_rewriter import CallStatementRewriter
|
|
@@ -59,6 +60,7 @@ ALL_OPTIMIZATION_PASSES = [
|
|
|
59
60
|
FlipBooleanCmp,
|
|
60
61
|
InlinedStringTransformationSimplifier,
|
|
61
62
|
CallStatementRewriter,
|
|
63
|
+
TagSlicer,
|
|
62
64
|
]
|
|
63
65
|
|
|
64
66
|
# these passes may duplicate code to remove gotos or improve the structure of the graph
|
|
@@ -118,6 +120,7 @@ __all__ = (
|
|
|
118
120
|
"ConstPropOptReverter",
|
|
119
121
|
"CallStatementRewriter",
|
|
120
122
|
"DuplicationReverter",
|
|
123
|
+
"TagSlicer",
|
|
121
124
|
"ALL_OPTIMIZATION_PASSES",
|
|
122
125
|
"DUPLICATING_OPTS",
|
|
123
126
|
"CONDENSING_OPTS",
|
|
@@ -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
|
|
@@ -13,6 +13,7 @@ from ailment.expression import (
|
|
|
13
13
|
BinaryOp,
|
|
14
14
|
VirtualVariable,
|
|
15
15
|
Phi,
|
|
16
|
+
UnaryOp,
|
|
16
17
|
VirtualVariableCategory,
|
|
17
18
|
)
|
|
18
19
|
from ailment.statement import ConditionalJump, Jump, Assignment
|
|
@@ -238,6 +239,18 @@ class InlinedStringTransformationAILEngine(SimEngineLightAILMixin):
|
|
|
238
239
|
return self.state.vvar_load(vvar)
|
|
239
240
|
return None
|
|
240
241
|
|
|
242
|
+
def _handle_Neg(self, expr: UnaryOp):
|
|
243
|
+
v = self._expr(expr.operand)
|
|
244
|
+
if isinstance(v, claripy.ast.Bits):
|
|
245
|
+
return -v
|
|
246
|
+
return None
|
|
247
|
+
|
|
248
|
+
def _handle_BitwiseNeg(self, expr: UnaryOp):
|
|
249
|
+
v = self._expr(expr.operand)
|
|
250
|
+
if isinstance(v, claripy.ast.Bits):
|
|
251
|
+
return ~v
|
|
252
|
+
return None
|
|
253
|
+
|
|
241
254
|
def _handle_Convert(self, expr: Convert):
|
|
242
255
|
v = self._expr(expr.operand)
|
|
243
256
|
if isinstance(v, claripy.ast.Bits):
|
|
@@ -202,6 +202,7 @@ class ITERegionConverter(OptimizationPass):
|
|
|
202
202
|
true_stmt_src = true_stmt.src if isinstance(true_stmt, Assignment) else true_stmt
|
|
203
203
|
true_stmt_dst = true_stmt.dst if isinstance(true_stmt, Assignment) else true_stmt.ret_expr
|
|
204
204
|
false_stmt_src = false_stmt.src if isinstance(false_stmt, Assignment) else false_stmt
|
|
205
|
+
false_stmt_dst = false_stmt.dst if isinstance(false_stmt, Assignment) else false_stmt.ret_expr
|
|
205
206
|
|
|
206
207
|
addr_obj = true_stmt_src if "ins_addr" in true_stmt_src.tags else true_stmt
|
|
207
208
|
ternary_expr = ITE(
|
|
@@ -209,9 +210,7 @@ class ITERegionConverter(OptimizationPass):
|
|
|
209
210
|
conditional_jump.condition,
|
|
210
211
|
false_stmt_src,
|
|
211
212
|
true_stmt_src,
|
|
212
|
-
|
|
213
|
-
vex_block_addr=addr_obj.vex_block_addr,
|
|
214
|
-
vex_stmt_idx=addr_obj.vex_stmt_idx,
|
|
213
|
+
**addr_obj.tags,
|
|
215
214
|
)
|
|
216
215
|
dst = VirtualVariable(
|
|
217
216
|
true_stmt_dst.idx,
|
|
@@ -261,12 +260,32 @@ class ITERegionConverter(OptimizationPass):
|
|
|
261
260
|
if not is_phi_assignment(stmt):
|
|
262
261
|
stmts.append(stmt)
|
|
263
262
|
continue
|
|
263
|
+
|
|
264
|
+
# is this the statement that we are looking for?
|
|
265
|
+
found_true_src_vvar, found_false_src_vvar = False, False
|
|
266
|
+
for src, vvar in stmt.src.src_and_vvars:
|
|
267
|
+
if vvar is not None:
|
|
268
|
+
if vvar.varid == true_stmt_dst.varid:
|
|
269
|
+
found_true_src_vvar = True
|
|
270
|
+
elif vvar.varid == false_stmt_dst.varid:
|
|
271
|
+
found_false_src_vvar = True
|
|
272
|
+
# we should only update the vvars of this phi statement if we found both true and false source vvars
|
|
273
|
+
update_vars = found_true_src_vvar and found_false_src_vvar
|
|
274
|
+
|
|
264
275
|
new_src_and_vvars = []
|
|
276
|
+
original_vvars = []
|
|
265
277
|
for src, vvar in stmt.src.src_and_vvars:
|
|
266
278
|
if src not in region_tail_pred_srcs:
|
|
267
279
|
new_src_and_vvars.append((src, vvar))
|
|
280
|
+
else:
|
|
281
|
+
original_vvars.append(vvar)
|
|
268
282
|
new_vvar = new_assignment.dst.copy()
|
|
269
|
-
|
|
283
|
+
if update_vars:
|
|
284
|
+
new_src_and_vvars.append(((region_head.addr, region_head.idx), new_vvar))
|
|
285
|
+
else:
|
|
286
|
+
new_src_and_vvars.append(
|
|
287
|
+
((region_head.addr, region_head.idx), original_vvars[0] if original_vvars else None)
|
|
288
|
+
)
|
|
270
289
|
|
|
271
290
|
new_phi = Phi(
|
|
272
291
|
stmt.src.idx,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# pylint:disable=unused-argument
|
|
2
2
|
from __future__ import annotations
|
|
3
3
|
import logging
|
|
4
|
-
from typing import TYPE_CHECKING
|
|
4
|
+
from typing import Any, TYPE_CHECKING
|
|
5
5
|
from collections.abc import Generator
|
|
6
6
|
from enum import Enum
|
|
7
7
|
|
|
@@ -117,6 +117,7 @@ class OptimizationPass(BaseOptimizationPass):
|
|
|
117
117
|
reaching_definitions=None,
|
|
118
118
|
vvar_id_start=None,
|
|
119
119
|
entry_node_addr=None,
|
|
120
|
+
scratch: dict[str, Any] | None = None,
|
|
120
121
|
**kwargs,
|
|
121
122
|
):
|
|
122
123
|
super().__init__(func)
|
|
@@ -127,6 +128,7 @@ class OptimizationPass(BaseOptimizationPass):
|
|
|
127
128
|
self._variable_kb = variable_kb
|
|
128
129
|
self._ri = region_identifier
|
|
129
130
|
self._rd = reaching_definitions
|
|
131
|
+
self._scratch = scratch if scratch is not None else {}
|
|
130
132
|
self._new_block_addrs = set()
|
|
131
133
|
self.vvar_id_start = vvar_id_start
|
|
132
134
|
self.entry_node_addr: tuple[int, int | None] = (
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
from typing import Any
|
|
3
|
-
from itertools import count
|
|
4
3
|
import copy
|
|
5
4
|
import logging
|
|
6
5
|
|
|
@@ -78,23 +77,27 @@ class ReturnDuplicatorBase:
|
|
|
78
77
|
def __init__(
|
|
79
78
|
self,
|
|
80
79
|
func,
|
|
81
|
-
node_idx_start: int = 0,
|
|
82
80
|
max_calls_in_regions: int = 2,
|
|
83
81
|
minimize_copies_for_regions: bool = True,
|
|
84
82
|
ri: RegionIdentifier | None = None,
|
|
85
83
|
vvar_id_start: int | None = None,
|
|
86
|
-
|
|
84
|
+
scratch: dict[str, Any] | None = None,
|
|
87
85
|
):
|
|
88
|
-
self.node_idx = count(start=node_idx_start)
|
|
89
86
|
self._max_calls_in_region = max_calls_in_regions
|
|
90
87
|
self._minimize_copies_for_regions = minimize_copies_for_regions
|
|
91
88
|
self._supergraph = None
|
|
92
89
|
|
|
93
90
|
# this should also be set by the optimization passes initer
|
|
91
|
+
self.scratch = scratch if scratch is not None else {}
|
|
94
92
|
self._func = func
|
|
95
93
|
self._ri: RegionIdentifier | None = ri
|
|
96
94
|
self.vvar_id_start = vvar_id_start
|
|
97
95
|
|
|
96
|
+
def next_node_idx(self) -> int:
|
|
97
|
+
node_idx = self.scratch.get("returndup_node_idx", 0) + 1
|
|
98
|
+
self.scratch["returndup_node_idx"] = node_idx
|
|
99
|
+
return node_idx
|
|
100
|
+
|
|
98
101
|
#
|
|
99
102
|
# must implement these methods
|
|
100
103
|
#
|
|
@@ -208,7 +211,7 @@ class ReturnDuplicatorBase:
|
|
|
208
211
|
else:
|
|
209
212
|
node_copy = copy.deepcopy(node)
|
|
210
213
|
node_copy = self._use_fresh_virtual_variables(node_copy, vvar_mapping)
|
|
211
|
-
node_copy.idx =
|
|
214
|
+
node_copy.idx = self.next_node_idx()
|
|
212
215
|
self._fix_copied_node_labels(node_copy)
|
|
213
216
|
copies[node] = node_copy
|
|
214
217
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
import logging
|
|
3
|
+
from typing import Any
|
|
3
4
|
|
|
4
5
|
import networkx
|
|
5
6
|
|
|
@@ -26,22 +27,26 @@ class ReturnDuplicatorHigh(OptimizationPass, ReturnDuplicatorBase):
|
|
|
26
27
|
def __init__(
|
|
27
28
|
self,
|
|
28
29
|
func,
|
|
29
|
-
# internal parameters that should be used by Clinic
|
|
30
|
-
node_idx_start: int = 0,
|
|
31
30
|
# settings
|
|
32
31
|
max_calls_in_regions: int = 2,
|
|
33
32
|
minimize_copies_for_regions: bool = True,
|
|
33
|
+
region_identifier=None,
|
|
34
|
+
vvar_id_start: int | None = None,
|
|
35
|
+
scratch: dict[str, Any] | None = None,
|
|
34
36
|
**kwargs,
|
|
35
37
|
):
|
|
38
|
+
OptimizationPass.__init__(
|
|
39
|
+
self, func, vvar_id_start=vvar_id_start, scratch=scratch, region_identifier=region_identifier, **kwargs
|
|
40
|
+
)
|
|
36
41
|
ReturnDuplicatorBase.__init__(
|
|
37
42
|
self,
|
|
38
43
|
func,
|
|
39
|
-
node_idx_start=node_idx_start,
|
|
40
44
|
max_calls_in_regions=max_calls_in_regions,
|
|
41
45
|
minimize_copies_for_regions=minimize_copies_for_regions,
|
|
42
|
-
|
|
46
|
+
ri=region_identifier,
|
|
47
|
+
vvar_id_start=vvar_id_start,
|
|
48
|
+
scratch=scratch,
|
|
43
49
|
)
|
|
44
|
-
OptimizationPass.__init__(self, func, **kwargs)
|
|
45
50
|
# since we run before the RegionIdentification pass in the decompiler, we need to collect it early here
|
|
46
51
|
self._ri = self._recover_regions(self._graph)
|
|
47
52
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
import logging
|
|
3
3
|
import inspect
|
|
4
|
+
from typing import Any
|
|
4
5
|
|
|
5
6
|
import networkx
|
|
6
7
|
|
|
@@ -46,25 +47,35 @@ class ReturnDuplicatorLow(StructuringOptimizationPass, ReturnDuplicatorBase):
|
|
|
46
47
|
def __init__(
|
|
47
48
|
self,
|
|
48
49
|
func,
|
|
49
|
-
# internal parameters that should be used by Clinic
|
|
50
|
-
node_idx_start: int = 0,
|
|
51
50
|
# settings
|
|
52
51
|
max_opt_iters: int = 4,
|
|
53
52
|
max_calls_in_regions: int = 2,
|
|
54
53
|
prevent_new_gotos: bool = True,
|
|
55
54
|
minimize_copies_for_regions: bool = True,
|
|
55
|
+
region_identifier=None,
|
|
56
|
+
vvar_id_start: int | None = None,
|
|
57
|
+
scratch: dict[str, Any] | None = None,
|
|
56
58
|
**kwargs,
|
|
57
59
|
):
|
|
60
|
+
StructuringOptimizationPass.__init__(
|
|
61
|
+
self,
|
|
62
|
+
func,
|
|
63
|
+
max_opt_iters=max_opt_iters,
|
|
64
|
+
prevent_new_gotos=prevent_new_gotos,
|
|
65
|
+
require_gotos=True,
|
|
66
|
+
vvar_id_start=vvar_id_start,
|
|
67
|
+
scratch=scratch,
|
|
68
|
+
region_identifier=region_identifier,
|
|
69
|
+
**kwargs,
|
|
70
|
+
)
|
|
58
71
|
ReturnDuplicatorBase.__init__(
|
|
59
72
|
self,
|
|
60
73
|
func,
|
|
61
|
-
node_idx_start=node_idx_start,
|
|
62
74
|
max_calls_in_regions=max_calls_in_regions,
|
|
63
75
|
minimize_copies_for_regions=minimize_copies_for_regions,
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
self, func, max_opt_iters=max_opt_iters, prevent_new_gotos=prevent_new_gotos, require_gotos=True, **kwargs
|
|
76
|
+
ri=region_identifier,
|
|
77
|
+
vvar_id_start=vvar_id_start,
|
|
78
|
+
scratch=scratch,
|
|
68
79
|
)
|
|
69
80
|
self.analyze()
|
|
70
81
|
|
|
@@ -75,8 +75,12 @@ class SwitchDefaultCaseDuplicator(OptimizationPass):
|
|
|
75
75
|
default_case_node_addrs = cache["default_case_node_addrs"]
|
|
76
76
|
|
|
77
77
|
out_graph = None
|
|
78
|
+
duplicated_default_addrs: set[int] = set()
|
|
78
79
|
|
|
79
80
|
for switch_head_addr, jump_node_addr, default_addr in default_case_node_addrs:
|
|
81
|
+
if default_addr in duplicated_default_addrs:
|
|
82
|
+
continue
|
|
83
|
+
|
|
80
84
|
default_case_node = self._func.get_node(default_addr)
|
|
81
85
|
unexpected_pred_addrs = {
|
|
82
86
|
pred.addr
|
|
@@ -92,6 +96,8 @@ class SwitchDefaultCaseDuplicator(OptimizationPass):
|
|
|
92
96
|
for jump_node in jump_nodes:
|
|
93
97
|
jump_node_descedents |= networkx.descendants(self._graph, jump_node)
|
|
94
98
|
|
|
99
|
+
duplicated_default_addrs.add(default_addr)
|
|
100
|
+
|
|
95
101
|
# duplicate default_case_node for each unexpected predecessor
|
|
96
102
|
for unexpected_pred_addr in unexpected_pred_addrs:
|
|
97
103
|
for unexpected_pred in self._get_blocks(unexpected_pred_addr):
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# pylint:disable=too-many-boolean-expressions
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
import logging
|
|
4
|
+
|
|
5
|
+
from ailment.statement import ConditionalJump, Jump, Label
|
|
6
|
+
from .optimization_pass import OptimizationPass, OptimizationPassStage
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
_l = logging.getLogger(name=__name__)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class TagSlicer(OptimizationPass):
|
|
13
|
+
"""
|
|
14
|
+
Removes unmarked statements from the graph.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
ARCHES = None
|
|
18
|
+
PLATFORMS = None
|
|
19
|
+
STAGE = OptimizationPassStage.AFTER_VARIABLE_RECOVERY
|
|
20
|
+
NAME = "Remove unmarked statements from the graph."
|
|
21
|
+
DESCRIPTION = __doc__.strip()
|
|
22
|
+
|
|
23
|
+
def __init__(self, func, **kwargs):
|
|
24
|
+
super().__init__(func, **kwargs)
|
|
25
|
+
self.analyze()
|
|
26
|
+
|
|
27
|
+
def _check(self):
|
|
28
|
+
return True, {}
|
|
29
|
+
|
|
30
|
+
def _analyze(self, cache=None):
|
|
31
|
+
for n in self._graph.nodes():
|
|
32
|
+
for i, s in enumerate(n.statements):
|
|
33
|
+
if isinstance(s, (ConditionalJump, Jump, Label)):
|
|
34
|
+
continue
|
|
35
|
+
if not s.tags.get("keep_in_slice", False):
|
|
36
|
+
n.statements[i] = None
|
|
37
|
+
|
|
38
|
+
for n in self._graph.nodes():
|
|
39
|
+
n.statements = [s for s in n.statements if s is not None]
|
|
40
|
+
|
|
41
|
+
self.out_graph = self._graph
|
|
@@ -100,6 +100,8 @@ class WinStackCanarySimplifier(OptimizationPass):
|
|
|
100
100
|
for pred_addr in pred_addr_to_endpoint_addrs:
|
|
101
101
|
# the predecessor should call _security_check_cookie
|
|
102
102
|
endpoint_preds = list(self._get_blocks(pred_addr))
|
|
103
|
+
if not endpoint_preds:
|
|
104
|
+
continue
|
|
103
105
|
if self._find_stmt_calling_security_check_cookie(endpoint_preds[0]) is None:
|
|
104
106
|
_l.debug("The predecessor does not call _security_check_cookie().")
|
|
105
107
|
continue
|
|
@@ -182,6 +182,8 @@ class ConstMullAShift(PeepholeOptimizationExprBase):
|
|
|
182
182
|
|
|
183
183
|
@staticmethod
|
|
184
184
|
def _check_divisor(a, b, ndigits=6):
|
|
185
|
+
if b == 0:
|
|
186
|
+
return None
|
|
185
187
|
divisor_1 = 1 + (a // b)
|
|
186
188
|
divisor_2 = int(round(a / float(b), ndigits))
|
|
187
189
|
return divisor_1 if divisor_1 == divisor_2 else None
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
from ailment.expression import Load, Const
|
|
3
|
-
from cle.backends import Blob
|
|
3
|
+
from cle.backends import Blob, Hex
|
|
4
4
|
|
|
5
5
|
from .base import PeepholeOptimizationExprBase
|
|
6
6
|
|
|
@@ -32,7 +32,7 @@ class ConstantDereferences(PeepholeOptimizationExprBase):
|
|
|
32
32
|
|
|
33
33
|
# is it loading from a blob?
|
|
34
34
|
obj = self.project.loader.find_object_containing(expr.addr.value)
|
|
35
|
-
if obj is not None and isinstance(obj, Blob):
|
|
35
|
+
if obj is not None and isinstance(obj, (Blob, Hex)):
|
|
36
36
|
# do we know the value that it's reading?
|
|
37
37
|
try:
|
|
38
38
|
val = self.project.loader.memory.unpack_word(expr.addr.value, size=self.project.arch.bytes)
|