angr 9.2.139__py3-none-manylinux2014_x86_64.whl → 9.2.140__py3-none-manylinux2014_x86_64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of angr might be problematic. Click here for more details.
- angr/__init__.py +1 -1
- angr/analyses/calling_convention/calling_convention.py +48 -21
- angr/analyses/cfg/cfg_base.py +13 -0
- angr/analyses/cfg/cfg_fast.py +11 -0
- angr/analyses/decompiler/ail_simplifier.py +67 -52
- angr/analyses/decompiler/clinic.py +68 -43
- angr/analyses/decompiler/decompiler.py +17 -7
- angr/analyses/decompiler/expression_narrower.py +1 -1
- angr/analyses/decompiler/optimization_passes/const_prop_reverter.py +8 -7
- angr/analyses/decompiler/optimization_passes/ite_region_converter.py +21 -13
- angr/analyses/decompiler/optimization_passes/optimization_pass.py +16 -10
- angr/analyses/decompiler/optimization_passes/return_duplicator_base.py +2 -2
- angr/analyses/decompiler/region_simplifiers/expr_folding.py +259 -108
- angr/analyses/decompiler/region_simplifiers/region_simplifier.py +27 -12
- angr/analyses/decompiler/structuring/dream.py +21 -17
- angr/analyses/decompiler/structuring/phoenix.py +152 -40
- angr/analyses/decompiler/structuring/recursive_structurer.py +1 -0
- angr/analyses/decompiler/structuring/structurer_base.py +36 -10
- angr/analyses/decompiler/structuring/structurer_nodes.py +4 -1
- angr/analyses/decompiler/utils.py +60 -1
- angr/analyses/deobfuscator/api_obf_finder.py +8 -5
- angr/analyses/deobfuscator/api_obf_type2_finder.py +18 -10
- angr/analyses/deobfuscator/string_obf_finder.py +105 -18
- angr/analyses/forward_analysis/forward_analysis.py +1 -1
- angr/analyses/propagator/top_checker_mixin.py +6 -6
- angr/analyses/reaching_definitions/__init__.py +2 -1
- angr/analyses/reaching_definitions/dep_graph.py +1 -12
- angr/analyses/reaching_definitions/engine_vex.py +36 -31
- angr/analyses/reaching_definitions/function_handler.py +15 -2
- angr/analyses/reaching_definitions/rd_state.py +1 -37
- angr/analyses/reaching_definitions/reaching_definitions.py +13 -24
- angr/analyses/s_propagator.py +6 -41
- angr/analyses/s_reaching_definitions/s_rda_model.py +7 -1
- angr/analyses/stack_pointer_tracker.py +36 -22
- angr/analyses/typehoon/simple_solver.py +45 -7
- angr/analyses/typehoon/typeconsts.py +18 -5
- angr/analyses/variable_recovery/engine_base.py +7 -5
- angr/block.py +69 -107
- angr/callable.py +14 -7
- angr/calling_conventions.py +15 -1
- angr/distributed/__init__.py +1 -1
- angr/engines/__init__.py +7 -8
- angr/engines/engine.py +1 -120
- angr/engines/failure.py +2 -2
- angr/engines/hook.py +2 -2
- angr/engines/light/engine.py +2 -2
- angr/engines/pcode/engine.py +2 -14
- angr/engines/procedure.py +2 -2
- angr/engines/soot/engine.py +2 -2
- angr/engines/soot/statements/switch.py +1 -1
- angr/engines/successors.py +124 -11
- angr/engines/syscall.py +2 -2
- angr/engines/unicorn.py +3 -3
- angr/engines/vex/heavy/heavy.py +3 -15
- angr/factory.py +4 -19
- angr/knowledge_plugins/key_definitions/atoms.py +8 -4
- angr/knowledge_plugins/key_definitions/live_definitions.py +41 -103
- angr/sim_type.py +19 -17
- angr/state_plugins/plugin.py +19 -4
- angr/storage/memory_mixins/memory_mixin.py +1 -1
- angr/storage/memory_mixins/paged_memory/pages/multi_values.py +10 -5
- angr/utils/ssa/__init__.py +119 -4
- {angr-9.2.139.dist-info → angr-9.2.140.dist-info}/METADATA +6 -6
- {angr-9.2.139.dist-info → angr-9.2.140.dist-info}/RECORD +68 -68
- {angr-9.2.139.dist-info → angr-9.2.140.dist-info}/LICENSE +0 -0
- {angr-9.2.139.dist-info → angr-9.2.140.dist-info}/WHEEL +0 -0
- {angr-9.2.139.dist-info → angr-9.2.140.dist-info}/entry_points.txt +0 -0
- {angr-9.2.139.dist-info → angr-9.2.140.dist-info}/top_level.txt +0 -0
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
# pylint:disable=missing-class-docstring,unused-argument
|
|
1
|
+
# pylint:disable=missing-class-docstring,unused-argument,consider-using-dict-items
|
|
2
2
|
from __future__ import annotations
|
|
3
3
|
from collections import defaultdict
|
|
4
|
-
from typing import Any, TYPE_CHECKING
|
|
5
4
|
from collections.abc import Iterable
|
|
5
|
+
from typing import Any, TYPE_CHECKING
|
|
6
6
|
|
|
7
7
|
import ailment
|
|
8
8
|
from ailment import Expression, Block, AILBlockWalker
|
|
9
|
-
from ailment.expression import ITE
|
|
10
|
-
from ailment.statement import Statement, Assignment, Call
|
|
9
|
+
from ailment.expression import ITE, Load
|
|
10
|
+
from ailment.statement import Statement, Assignment, Call, Return
|
|
11
11
|
|
|
12
12
|
from angr.utils.ail import is_phi_assignment
|
|
13
|
+
from angr.utils.ssa import VVarUsesCollector
|
|
13
14
|
from angr.analyses.decompiler.sequence_walker import SequenceWalker
|
|
14
15
|
from angr.analyses.decompiler.structuring.structurer_nodes import (
|
|
15
16
|
ConditionNode,
|
|
@@ -20,8 +21,6 @@ from angr.analyses.decompiler.structuring.structurer_nodes import (
|
|
|
20
21
|
)
|
|
21
22
|
|
|
22
23
|
if TYPE_CHECKING:
|
|
23
|
-
from angr.sim_variable import SimVariable
|
|
24
|
-
from angr.knowledge_plugins.variables.variable_manager import VariableManagerInternal
|
|
25
24
|
from ailment.expression import MultiStatementExpression
|
|
26
25
|
|
|
27
26
|
|
|
@@ -30,6 +29,10 @@ class LocationBase:
|
|
|
30
29
|
|
|
31
30
|
|
|
32
31
|
class StatementLocation(LocationBase):
|
|
32
|
+
"""
|
|
33
|
+
Describes the location of a statement.
|
|
34
|
+
"""
|
|
35
|
+
|
|
33
36
|
__slots__ = (
|
|
34
37
|
"block_addr",
|
|
35
38
|
"block_idx",
|
|
@@ -60,6 +63,10 @@ class StatementLocation(LocationBase):
|
|
|
60
63
|
|
|
61
64
|
|
|
62
65
|
class ExpressionLocation(LocationBase):
|
|
66
|
+
"""
|
|
67
|
+
Describes the location of an expression.
|
|
68
|
+
"""
|
|
69
|
+
|
|
63
70
|
__slots__ = (
|
|
64
71
|
"block_addr",
|
|
65
72
|
"block_idx",
|
|
@@ -93,6 +100,10 @@ class ExpressionLocation(LocationBase):
|
|
|
93
100
|
|
|
94
101
|
|
|
95
102
|
class ConditionLocation(LocationBase):
|
|
103
|
+
"""
|
|
104
|
+
Describes the location of a condition.
|
|
105
|
+
"""
|
|
106
|
+
|
|
96
107
|
__slots__ = (
|
|
97
108
|
"case_idx",
|
|
98
109
|
"node_addr",
|
|
@@ -117,6 +128,10 @@ class ConditionLocation(LocationBase):
|
|
|
117
128
|
|
|
118
129
|
|
|
119
130
|
class ConditionalBreakLocation(LocationBase):
|
|
131
|
+
"""
|
|
132
|
+
Describes the location of a conditional break.
|
|
133
|
+
"""
|
|
134
|
+
|
|
120
135
|
__slots__ = ("node_addr",)
|
|
121
136
|
|
|
122
137
|
def __init__(self, node_addr):
|
|
@@ -177,18 +192,18 @@ class ExpressionUseFinder(AILBlockWalker):
|
|
|
177
192
|
|
|
178
193
|
def __init__(self):
|
|
179
194
|
super().__init__()
|
|
180
|
-
self.uses: defaultdict[
|
|
195
|
+
self.uses: defaultdict[int, set[tuple[Expression, ExpressionLocation | None]]] = defaultdict(set)
|
|
181
196
|
self.has_load = False
|
|
182
197
|
|
|
183
198
|
def _handle_expr(
|
|
184
199
|
self, expr_idx: int, expr: Expression, stmt_idx: int, stmt: Statement | None, block: Block | None
|
|
185
200
|
) -> Any:
|
|
186
|
-
if isinstance(expr, ailment.Expr.VirtualVariable) and expr.was_reg
|
|
201
|
+
if isinstance(expr, ailment.Expr.VirtualVariable) and expr.was_reg:
|
|
187
202
|
if not (isinstance(stmt, ailment.Stmt.Assignment) and stmt.dst is expr):
|
|
188
203
|
if block is not None:
|
|
189
|
-
self.uses[expr.
|
|
204
|
+
self.uses[expr.varid].add((expr, ExpressionLocation(block.addr, block.idx, stmt_idx, expr_idx)))
|
|
190
205
|
else:
|
|
191
|
-
self.uses[expr.
|
|
206
|
+
self.uses[expr.varid].add((expr, None))
|
|
192
207
|
return None
|
|
193
208
|
return super()._handle_expr(expr_idx, expr, stmt_idx, stmt, block)
|
|
194
209
|
|
|
@@ -202,7 +217,7 @@ class ExpressionCounter(SequenceWalker):
|
|
|
202
217
|
Find all expressions that are assigned once and only used once.
|
|
203
218
|
"""
|
|
204
219
|
|
|
205
|
-
def __init__(self, node
|
|
220
|
+
def __init__(self, node):
|
|
206
221
|
handlers = {
|
|
207
222
|
ConditionalBreakNode: self._handle_ConditionalBreak,
|
|
208
223
|
ConditionNode: self._handle_Condition,
|
|
@@ -215,20 +230,12 @@ class ExpressionCounter(SequenceWalker):
|
|
|
215
230
|
# the current assignment depends on, StatementLocation of the assignment statement, a Boolean variable that
|
|
216
231
|
# indicates if ExpressionUseFinder has succeeded or not)
|
|
217
232
|
self.assignments: defaultdict[Any, set[tuple]] = defaultdict(set)
|
|
218
|
-
self.uses: dict[
|
|
219
|
-
self._variable_manager: VariableManagerInternal = variable_manager
|
|
233
|
+
self.uses: dict[int, set[tuple[Expression, LocationBase | None]]] = {}
|
|
220
234
|
|
|
221
235
|
super().__init__(handlers)
|
|
222
236
|
self.walk(node)
|
|
223
237
|
|
|
224
|
-
def
|
|
225
|
-
"""
|
|
226
|
-
Get unified variable for a given variable.
|
|
227
|
-
"""
|
|
228
|
-
|
|
229
|
-
return self._variable_manager.unified_variable(v)
|
|
230
|
-
|
|
231
|
-
def _handle_Statement(self, idx: int, stmt: ailment.Stmt, node: ailment.Block | LoopNode):
|
|
238
|
+
def _handle_Statement(self, idx: int, stmt: Statement, node: ailment.Block | LoopNode):
|
|
232
239
|
if isinstance(stmt, ailment.Stmt.Assignment):
|
|
233
240
|
if is_phi_assignment(stmt):
|
|
234
241
|
return
|
|
@@ -237,72 +244,65 @@ class ExpressionCounter(SequenceWalker):
|
|
|
237
244
|
and stmt.dst.was_reg
|
|
238
245
|
and stmt.dst.variable is not None
|
|
239
246
|
):
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
StatementLocation(node.addr, node.idx if isinstance(node, ailment.Block) else None, idx),
|
|
251
|
-
dependency_finder.has_load,
|
|
252
|
-
)
|
|
247
|
+
# dependency
|
|
248
|
+
dependency_finder = ExpressionUseFinder()
|
|
249
|
+
dependency_finder.walk_expression(stmt.src)
|
|
250
|
+
dependencies = tuple(dependency_finder.uses)
|
|
251
|
+
self.assignments[stmt.dst.varid].add(
|
|
252
|
+
(
|
|
253
|
+
stmt.src,
|
|
254
|
+
dependencies,
|
|
255
|
+
StatementLocation(node.addr, node.idx if isinstance(node, ailment.Block) else None, idx),
|
|
256
|
+
dependency_finder.has_load,
|
|
253
257
|
)
|
|
258
|
+
)
|
|
254
259
|
if (
|
|
255
260
|
isinstance(stmt, ailment.Stmt.Call)
|
|
256
261
|
and isinstance(stmt.ret_expr, ailment.Expr.VirtualVariable)
|
|
257
262
|
and stmt.ret_expr.was_reg
|
|
258
263
|
and stmt.ret_expr.variable is not None
|
|
259
264
|
):
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
StatementLocation(node.addr, node.idx if isinstance(node, ailment.Block) else None, idx),
|
|
270
|
-
dependency_finder.has_load,
|
|
271
|
-
)
|
|
265
|
+
dependency_finder = ExpressionUseFinder()
|
|
266
|
+
dependency_finder.walk_expression(stmt)
|
|
267
|
+
dependencies = tuple(dependency_finder.uses)
|
|
268
|
+
self.assignments[stmt.ret_expr.varid].add(
|
|
269
|
+
(
|
|
270
|
+
stmt,
|
|
271
|
+
dependencies,
|
|
272
|
+
StatementLocation(node.addr, node.idx if isinstance(node, ailment.Block) else None, idx),
|
|
273
|
+
dependency_finder.has_load,
|
|
272
274
|
)
|
|
275
|
+
)
|
|
273
276
|
|
|
274
277
|
def _handle_Block(self, node: ailment.Block, **kwargs):
|
|
275
|
-
# find assignments
|
|
278
|
+
# find assignments and uses of variables
|
|
279
|
+
use_finder = ExpressionUseFinder()
|
|
276
280
|
for idx, stmt in enumerate(node.statements):
|
|
277
281
|
self._handle_Statement(idx, stmt, node)
|
|
282
|
+
if not is_phi_assignment(stmt):
|
|
283
|
+
use_finder.walk_statement(stmt)
|
|
278
284
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
for v, content in use_finder.uses.items():
|
|
284
|
-
u = self._u(v)
|
|
285
|
-
if u is not None:
|
|
286
|
-
if u not in self.uses:
|
|
287
|
-
self.uses[u] = set()
|
|
288
|
-
self.uses[u] |= content
|
|
285
|
+
for varid, content in use_finder.uses.items():
|
|
286
|
+
if varid not in self.uses:
|
|
287
|
+
self.uses[varid] = set()
|
|
288
|
+
self.uses[varid] |= content
|
|
289
289
|
|
|
290
|
-
def _collect_assignments(self, expr:
|
|
290
|
+
def _collect_assignments(self, expr: Expression, node) -> None:
|
|
291
291
|
finder = MultiStatementExpressionAssignmentFinder(self._handle_Statement)
|
|
292
292
|
finder.walk_expression(expr, None, None, node)
|
|
293
293
|
|
|
294
|
-
def _collect_uses(self, expr: Expression, loc: LocationBase):
|
|
294
|
+
def _collect_uses(self, expr: Expression | Statement, loc: LocationBase):
|
|
295
295
|
use_finder = ExpressionUseFinder()
|
|
296
|
-
|
|
296
|
+
if isinstance(expr, Statement):
|
|
297
|
+
use_finder.walk_statement(expr)
|
|
298
|
+
else:
|
|
299
|
+
use_finder.walk_expression(expr, stmt_idx=-1)
|
|
297
300
|
|
|
298
|
-
for
|
|
299
|
-
u = self._u(var)
|
|
300
|
-
if u is None:
|
|
301
|
-
continue
|
|
301
|
+
for varid, uses in use_finder.uses.items():
|
|
302
302
|
for use in uses:
|
|
303
|
-
if
|
|
304
|
-
self.uses[
|
|
305
|
-
self.uses[
|
|
303
|
+
if varid not in self.uses:
|
|
304
|
+
self.uses[varid] = set()
|
|
305
|
+
self.uses[varid].add((use[0], loc))
|
|
306
306
|
|
|
307
307
|
def _handle_ConditionalBreak(self, node: ConditionalBreakNode, **kwargs):
|
|
308
308
|
# collect uses on the condition expression
|
|
@@ -338,22 +338,185 @@ class ExpressionCounter(SequenceWalker):
|
|
|
338
338
|
return super()._handle_SwitchCase(node, **kwargs)
|
|
339
339
|
|
|
340
340
|
|
|
341
|
-
class
|
|
342
|
-
|
|
341
|
+
class ExpressionSpotter(VVarUsesCollector):
|
|
342
|
+
"""
|
|
343
|
+
ExpressionSpotter collects uses of vvars and existence of Call expressions.
|
|
344
|
+
"""
|
|
345
|
+
|
|
346
|
+
def __init__(self):
|
|
343
347
|
super().__init__()
|
|
348
|
+
self.has_calls: bool = False
|
|
349
|
+
self.has_loads: bool = False
|
|
350
|
+
|
|
351
|
+
def _handle_CallExpr(self, expr_idx: int, expr: Call, stmt_idx: int, stmt: Statement, block: Block | None):
|
|
352
|
+
self.has_calls = True
|
|
353
|
+
return super()._handle_CallExpr(expr_idx, expr, stmt_idx, stmt, block)
|
|
354
|
+
|
|
355
|
+
def _handle_Load(self, expr_idx: int, expr: Load, stmt_idx: int, stmt: Statement, block: Block | None):
|
|
356
|
+
self.has_loads = True
|
|
357
|
+
return super()._handle_Load(expr_idx, expr, stmt_idx, stmt, block)
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
class InterferenceChecker(SequenceWalker):
|
|
361
|
+
"""
|
|
362
|
+
Detect for every pair of definition (assignment) - use if there is anything that may interfere with the definition.
|
|
363
|
+
|
|
364
|
+
Interferences may be caused by:
|
|
365
|
+
|
|
366
|
+
- another call
|
|
367
|
+
- function return
|
|
368
|
+
- store statements
|
|
369
|
+
- load expressions
|
|
370
|
+
- Condition and CascadingCondition nodes
|
|
371
|
+
"""
|
|
372
|
+
|
|
373
|
+
def __init__(self, assignments: dict[int, Any], uses: dict[int, Any], node):
|
|
374
|
+
handlers = {
|
|
375
|
+
ailment.Block: self._handle_Block,
|
|
376
|
+
ConditionNode: self._handle_Condition,
|
|
377
|
+
ConditionalBreakNode: self._handle_ConditionalBreak,
|
|
378
|
+
SwitchCaseNode: self._handle_SwitchCase,
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
super().__init__(handlers, update_seqnode_in_place=False, force_forward_scan=True)
|
|
344
382
|
self._assignments = assignments
|
|
345
383
|
self._uses = uses
|
|
346
|
-
self.
|
|
384
|
+
self._assignment_interferences: dict[int, list] = {}
|
|
385
|
+
self.interfered_assignments: set[int] = set()
|
|
386
|
+
self.walk(node)
|
|
347
387
|
|
|
348
|
-
def
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
388
|
+
def _after_spotting(self, obj, spotter: ExpressionSpotter) -> None:
|
|
389
|
+
if spotter.has_calls or spotter.has_loads:
|
|
390
|
+
# mark all existing assignments as interfered
|
|
391
|
+
for vid in list(self._assignment_interferences):
|
|
392
|
+
self._assignment_interferences[vid].append(obj)
|
|
393
|
+
if set(self._assignment_interferences).intersection(spotter.vvars):
|
|
394
|
+
for used_vvar_id in spotter.vvars:
|
|
395
|
+
if used_vvar_id in self._assignment_interferences:
|
|
396
|
+
if self._assignment_interferences[used_vvar_id]:
|
|
397
|
+
self.interfered_assignments.add(used_vvar_id)
|
|
398
|
+
del self._assignment_interferences[used_vvar_id]
|
|
353
399
|
|
|
354
|
-
def
|
|
400
|
+
def _handle_Block(self, node: ailment.Block, **kwargs):
|
|
401
|
+
for stmt in node.statements:
|
|
402
|
+
|
|
403
|
+
# deal with uses
|
|
404
|
+
spotter = ExpressionSpotter()
|
|
405
|
+
# special case: we process the call arguments first, then the call itself. this is to allow more expression
|
|
406
|
+
# folding opportunities.
|
|
407
|
+
the_call = None
|
|
408
|
+
if isinstance(stmt, Assignment) and isinstance(stmt.src, ailment.Stmt.Call):
|
|
409
|
+
the_call = stmt.src
|
|
410
|
+
elif isinstance(stmt, ailment.Stmt.Call):
|
|
411
|
+
the_call = stmt
|
|
412
|
+
if the_call is not None:
|
|
413
|
+
spotter.walk_expression(the_call.target)
|
|
414
|
+
if the_call.args:
|
|
415
|
+
for arg in the_call.args:
|
|
416
|
+
spotter.walk_expression(arg)
|
|
417
|
+
self._after_spotting(the_call, spotter)
|
|
418
|
+
spotter.walk_statement(stmt)
|
|
419
|
+
self._after_spotting(stmt, spotter)
|
|
420
|
+
|
|
421
|
+
if isinstance(stmt, Return):
|
|
422
|
+
# mark all existing assignments as interfered
|
|
423
|
+
for vid in self._assignment_interferences:
|
|
424
|
+
self._assignment_interferences[vid].append(stmt)
|
|
425
|
+
|
|
426
|
+
if isinstance(stmt, ailment.Stmt.Store):
|
|
427
|
+
# mark all existing assignments as interfered
|
|
428
|
+
for vid in self._assignment_interferences:
|
|
429
|
+
self._assignment_interferences[vid].append(stmt)
|
|
430
|
+
|
|
431
|
+
if isinstance(stmt, ailment.Stmt.Call):
|
|
432
|
+
# mark all existing assignments as interfered
|
|
433
|
+
for vid in self._assignment_interferences:
|
|
434
|
+
self._assignment_interferences[vid].append(stmt)
|
|
435
|
+
|
|
436
|
+
# deal with defs
|
|
437
|
+
if (
|
|
438
|
+
isinstance(stmt, ailment.Stmt.Assignment)
|
|
439
|
+
and isinstance(stmt.dst, ailment.Expr.VirtualVariable)
|
|
440
|
+
and stmt.dst.was_reg
|
|
441
|
+
and stmt.dst.varid in self._assignments
|
|
442
|
+
):
|
|
443
|
+
# we found this def
|
|
444
|
+
self._assignment_interferences[stmt.dst.varid] = []
|
|
445
|
+
|
|
446
|
+
if (
|
|
447
|
+
isinstance(stmt, ailment.Stmt.Call)
|
|
448
|
+
and isinstance(stmt.ret_expr, ailment.Expr.VirtualVariable)
|
|
449
|
+
and stmt.ret_expr.was_reg
|
|
450
|
+
and stmt.ret_expr.variable is not None
|
|
451
|
+
and stmt.ret_expr.varid in self._assignments
|
|
452
|
+
):
|
|
453
|
+
# we found this def
|
|
454
|
+
self._assignment_interferences[stmt.ret_expr.varid] = []
|
|
455
|
+
|
|
456
|
+
def _handle_ConditionalBreak(self, node: ConditionalBreakNode, **kwargs):
|
|
457
|
+
spotter = ExpressionSpotter()
|
|
458
|
+
spotter.walk_expression(node.condition)
|
|
459
|
+
self._after_spotting(node, spotter)
|
|
460
|
+
return super()._handle_ConditionalBreak(node, **kwargs)
|
|
461
|
+
|
|
462
|
+
def _handle_Condition(self, node: ConditionNode, **kwargs):
|
|
463
|
+
spotter = ExpressionSpotter()
|
|
464
|
+
spotter.walk_expression(node.condition)
|
|
465
|
+
self._after_spotting(node, spotter)
|
|
466
|
+
|
|
467
|
+
# mark all existing assignments as interfered
|
|
468
|
+
for vid in self._assignment_interferences:
|
|
469
|
+
self._assignment_interferences[vid].append(node)
|
|
470
|
+
|
|
471
|
+
return super()._handle_Condition(node, **kwargs)
|
|
472
|
+
|
|
473
|
+
def _handle_CascadingCondition(self, node: CascadingConditionNode, **kwargs):
|
|
474
|
+
spotter = ExpressionSpotter()
|
|
475
|
+
for cond, _ in node.condition_and_nodes: # pylint:disable=consider-using-enumerate
|
|
476
|
+
spotter.walk_expression(cond)
|
|
477
|
+
self._after_spotting(node, spotter)
|
|
478
|
+
|
|
479
|
+
# mark all existing assignments as interfered
|
|
480
|
+
for vid in self._assignment_interferences:
|
|
481
|
+
self._assignment_interferences[vid].append(node)
|
|
482
|
+
|
|
483
|
+
return super()._handle_CascadingCondition(node, **kwargs)
|
|
484
|
+
|
|
485
|
+
def _handle_Loop(self, node: LoopNode, **kwargs):
|
|
486
|
+
spotter = ExpressionSpotter()
|
|
487
|
+
|
|
488
|
+
# iterator
|
|
489
|
+
if node.iterator is not None:
|
|
490
|
+
spotter.walk_statement(node.iterator)
|
|
491
|
+
|
|
492
|
+
# initializer
|
|
493
|
+
if node.initializer is not None:
|
|
494
|
+
spotter.walk_statement(node.initializer)
|
|
495
|
+
|
|
496
|
+
# condition
|
|
497
|
+
if node.condition is not None:
|
|
498
|
+
spotter.walk_expression(node.condition)
|
|
499
|
+
|
|
500
|
+
self._after_spotting(node, spotter)
|
|
501
|
+
|
|
502
|
+
return super()._handle_Loop(node, **kwargs)
|
|
503
|
+
|
|
504
|
+
def _handle_SwitchCase(self, node: SwitchCaseNode, **kwargs):
|
|
505
|
+
spotter = ExpressionSpotter()
|
|
506
|
+
spotter.walk_expression(node.switch_expr)
|
|
507
|
+
self._after_spotting(node, spotter)
|
|
508
|
+
return super()._handle_SwitchCase(node, **kwargs)
|
|
509
|
+
|
|
510
|
+
|
|
511
|
+
class ExpressionReplacer(AILBlockWalker):
|
|
512
|
+
def __init__(self, assignments: dict[int, Any], uses: dict[int, Any]):
|
|
513
|
+
super().__init__()
|
|
514
|
+
self._assignments = assignments
|
|
515
|
+
self._uses = uses
|
|
516
|
+
|
|
517
|
+
def _handle_MultiStatementExpression( # type: ignore
|
|
355
518
|
self, expr_idx, expr: MultiStatementExpression, stmt_idx: int, stmt: Statement, block: Block | None
|
|
356
|
-
):
|
|
519
|
+
) -> Expression | None:
|
|
357
520
|
changed = False
|
|
358
521
|
new_statements = []
|
|
359
522
|
for idx, stmt_ in enumerate(expr.stmts):
|
|
@@ -386,7 +549,7 @@ class ExpressionReplacer(AILBlockWalker):
|
|
|
386
549
|
if changed:
|
|
387
550
|
if not new_statements:
|
|
388
551
|
# it is no longer a multi-statement expression
|
|
389
|
-
return new_expr
|
|
552
|
+
return new_expr # type: ignore
|
|
390
553
|
expr_ = expr.copy()
|
|
391
554
|
expr_.expr = new_expr
|
|
392
555
|
expr_.stmts = new_statements
|
|
@@ -424,16 +587,14 @@ class ExpressionReplacer(AILBlockWalker):
|
|
|
424
587
|
def _handle_expr(
|
|
425
588
|
self, expr_idx: int, expr: Expression, stmt_idx: int, stmt: Statement | None, block: Block | None
|
|
426
589
|
) -> Any:
|
|
427
|
-
if isinstance(expr, ailment.Expr.VirtualVariable) and expr.was_reg and expr.
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
replace_with, _ = self._assignments[unified_var]
|
|
431
|
-
return replace_with
|
|
590
|
+
if isinstance(expr, ailment.Expr.VirtualVariable) and expr.was_reg and expr.varid in self._uses:
|
|
591
|
+
replace_with, _ = self._assignments[expr.varid]
|
|
592
|
+
return replace_with
|
|
432
593
|
return super()._handle_expr(expr_idx, expr, stmt_idx, stmt, block)
|
|
433
594
|
|
|
434
595
|
|
|
435
596
|
class ExpressionFolder(SequenceWalker):
|
|
436
|
-
def __init__(self, assignments: dict, uses: dict,
|
|
597
|
+
def __init__(self, assignments: dict[int, Any], uses: dict[int, Any], node):
|
|
437
598
|
handlers = {
|
|
438
599
|
ailment.Block: self._handle_Block,
|
|
439
600
|
ConditionNode: self._handle_Condition,
|
|
@@ -444,15 +605,8 @@ class ExpressionFolder(SequenceWalker):
|
|
|
444
605
|
super().__init__(handlers)
|
|
445
606
|
self._assignments = assignments
|
|
446
607
|
self._uses = uses
|
|
447
|
-
self._variable_manager = variable_manager
|
|
448
608
|
self.walk(node)
|
|
449
609
|
|
|
450
|
-
def _u(self, v) -> SimVariable | None:
|
|
451
|
-
"""
|
|
452
|
-
Get unified variable for a given variable.
|
|
453
|
-
"""
|
|
454
|
-
return self._variable_manager.unified_variable(v)
|
|
455
|
-
|
|
456
610
|
def _handle_Block(self, node: ailment.Block, **kwargs):
|
|
457
611
|
# Walk the block to remove each assignment and replace uses of each variable
|
|
458
612
|
new_stmts = []
|
|
@@ -461,45 +615,42 @@ class ExpressionFolder(SequenceWalker):
|
|
|
461
615
|
isinstance(stmt, ailment.Stmt.Assignment)
|
|
462
616
|
and isinstance(stmt.dst, ailment.Expr.VirtualVariable)
|
|
463
617
|
and stmt.dst.was_reg
|
|
464
|
-
and stmt.dst.
|
|
618
|
+
and stmt.dst.varid in self._assignments
|
|
465
619
|
):
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
# remove this statement
|
|
469
|
-
continue
|
|
620
|
+
# remove this statement
|
|
621
|
+
continue
|
|
470
622
|
if (
|
|
471
623
|
isinstance(stmt, ailment.Stmt.Call)
|
|
472
624
|
and isinstance(stmt.ret_expr, ailment.Expr.VirtualVariable)
|
|
473
625
|
and stmt.ret_expr.was_reg
|
|
474
626
|
and stmt.ret_expr.variable is not None
|
|
627
|
+
and stmt.ret_expr.varid in self._assignments
|
|
475
628
|
):
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
# remove this statement
|
|
479
|
-
continue
|
|
629
|
+
# remove this statement
|
|
630
|
+
continue
|
|
480
631
|
new_stmts.append(stmt)
|
|
481
632
|
node.statements = new_stmts
|
|
482
633
|
|
|
483
634
|
# Walk the block to replace the use of each variable
|
|
484
|
-
replacer = ExpressionReplacer(self._assignments, self._uses
|
|
635
|
+
replacer = ExpressionReplacer(self._assignments, self._uses)
|
|
485
636
|
replacer.walk(node)
|
|
486
637
|
|
|
487
638
|
def _handle_ConditionalBreak(self, node: ConditionalBreakNode, **kwargs):
|
|
488
|
-
replacer = ExpressionReplacer(self._assignments, self._uses
|
|
639
|
+
replacer = ExpressionReplacer(self._assignments, self._uses)
|
|
489
640
|
r = replacer.walk_expression(node.condition)
|
|
490
641
|
if r is not None and r is not node.condition:
|
|
491
642
|
node.condition = r
|
|
492
643
|
return super()._handle_ConditionalBreak(node, **kwargs)
|
|
493
644
|
|
|
494
645
|
def _handle_Condition(self, node: ConditionNode, **kwargs):
|
|
495
|
-
replacer = ExpressionReplacer(self._assignments, self._uses
|
|
646
|
+
replacer = ExpressionReplacer(self._assignments, self._uses)
|
|
496
647
|
r = replacer.walk_expression(node.condition)
|
|
497
648
|
if r is not None and r is not node.condition:
|
|
498
649
|
node.condition = r
|
|
499
650
|
return super()._handle_Condition(node, **kwargs)
|
|
500
651
|
|
|
501
652
|
def _handle_CascadingCondition(self, node: CascadingConditionNode, **kwargs):
|
|
502
|
-
replacer = ExpressionReplacer(self._assignments, self._uses
|
|
653
|
+
replacer = ExpressionReplacer(self._assignments, self._uses)
|
|
503
654
|
for idx in range(len(node.condition_and_nodes)): # pylint:disable=consider-using-enumerate
|
|
504
655
|
cond, _ = node.condition_and_nodes[idx]
|
|
505
656
|
r = replacer.walk_expression(cond)
|
|
@@ -508,17 +659,17 @@ class ExpressionFolder(SequenceWalker):
|
|
|
508
659
|
return super()._handle_CascadingCondition(node, **kwargs)
|
|
509
660
|
|
|
510
661
|
def _handle_Loop(self, node: LoopNode, **kwargs):
|
|
511
|
-
replacer = ExpressionReplacer(self._assignments, self._uses
|
|
662
|
+
replacer = ExpressionReplacer(self._assignments, self._uses)
|
|
512
663
|
|
|
513
664
|
# iterator
|
|
514
665
|
if node.iterator is not None:
|
|
515
|
-
r = replacer.
|
|
666
|
+
r = replacer.walk_statement(node.iterator)
|
|
516
667
|
if r is not None and r is not node.iterator:
|
|
517
668
|
node.iterator = r
|
|
518
669
|
|
|
519
670
|
# initializer
|
|
520
671
|
if node.initializer is not None:
|
|
521
|
-
r = replacer.
|
|
672
|
+
r = replacer.walk_statement(node.initializer)
|
|
522
673
|
if r is not None and r is not node.initializer:
|
|
523
674
|
node.initializer = r
|
|
524
675
|
|
|
@@ -531,7 +682,7 @@ class ExpressionFolder(SequenceWalker):
|
|
|
531
682
|
return super()._handle_Loop(node, **kwargs)
|
|
532
683
|
|
|
533
684
|
def _handle_SwitchCase(self, node: SwitchCaseNode, **kwargs):
|
|
534
|
-
replacer = ExpressionReplacer(self._assignments, self._uses
|
|
685
|
+
replacer = ExpressionReplacer(self._assignments, self._uses)
|
|
535
686
|
|
|
536
687
|
r = replacer.walk_expression(node.switch_expr)
|
|
537
688
|
if r is not None and r is not node.switch_expr:
|
|
@@ -12,7 +12,13 @@ from .if_ import IfSimplifier
|
|
|
12
12
|
from .cascading_ifs import CascadingIfsRemover
|
|
13
13
|
from .ifelse import IfElseFlattener
|
|
14
14
|
from .loop import LoopSimplifier
|
|
15
|
-
from .expr_folding import
|
|
15
|
+
from .expr_folding import (
|
|
16
|
+
ExpressionCounter,
|
|
17
|
+
ExpressionFolder,
|
|
18
|
+
StoreStatementFinder,
|
|
19
|
+
ExpressionLocation,
|
|
20
|
+
InterferenceChecker,
|
|
21
|
+
)
|
|
16
22
|
from .cascading_cond_transformer import CascadingConditionTransformer
|
|
17
23
|
from .switch_expr_simplifier import SwitchExpressionSimplifier
|
|
18
24
|
from .switch_cluster_simplifier import SwitchClusterFinder, simplify_switch_clusters, simplify_lowered_switches
|
|
@@ -23,10 +29,17 @@ class RegionSimplifier(Analysis):
|
|
|
23
29
|
Simplifies a given region.
|
|
24
30
|
"""
|
|
25
31
|
|
|
26
|
-
def __init__(
|
|
32
|
+
def __init__(
|
|
33
|
+
self,
|
|
34
|
+
func,
|
|
35
|
+
region,
|
|
36
|
+
arg_vvars: set[int] | None = None,
|
|
37
|
+
simplify_switches: bool = True,
|
|
38
|
+
simplify_ifelse: bool = True,
|
|
39
|
+
):
|
|
27
40
|
self.func = func
|
|
28
41
|
self.region = region
|
|
29
|
-
self.
|
|
42
|
+
self.arg_vvars = arg_vvars
|
|
30
43
|
self._simplify_switches = simplify_switches
|
|
31
44
|
self._should_simplify_ifelses = simplify_ifelse
|
|
32
45
|
|
|
@@ -54,7 +67,7 @@ class RegionSimplifier(Analysis):
|
|
|
54
67
|
# Remove empty nodes again
|
|
55
68
|
r = self._remove_empty_nodes(r)
|
|
56
69
|
|
|
57
|
-
if self.
|
|
70
|
+
if self.arg_vvars is not None:
|
|
58
71
|
# Fold expressions that are only used once into their use sites
|
|
59
72
|
r = self._fold_oneuse_expressions(r)
|
|
60
73
|
r = self._remove_empty_nodes(r)
|
|
@@ -88,12 +101,8 @@ class RegionSimplifier(Analysis):
|
|
|
88
101
|
#
|
|
89
102
|
|
|
90
103
|
def _fold_oneuse_expressions(self, region):
|
|
91
|
-
# Disabled until https://github.com/angr/angr/issues/5110 and related folding issues fixed
|
|
92
|
-
return region
|
|
93
|
-
|
|
94
104
|
# pylint:disable=unreachable
|
|
95
|
-
|
|
96
|
-
expr_counter = ExpressionCounter(region, variable_manager)
|
|
105
|
+
expr_counter = ExpressionCounter(region)
|
|
97
106
|
|
|
98
107
|
variable_assignments = {}
|
|
99
108
|
variable_uses = {}
|
|
@@ -130,7 +139,7 @@ class RegionSimplifier(Analysis):
|
|
|
130
139
|
# make sure all variables that var depends on has been assigned at most once
|
|
131
140
|
fail = False
|
|
132
141
|
for dep_var in deps:
|
|
133
|
-
if dep_var.
|
|
142
|
+
if self.arg_vvars is not None and dep_var in self.arg_vvars:
|
|
134
143
|
continue
|
|
135
144
|
if dep_var in expr_counter.assignments and len(expr_counter.assignments[dep_var]) > 1:
|
|
136
145
|
fail = True
|
|
@@ -155,8 +164,14 @@ class RegionSimplifier(Analysis):
|
|
|
155
164
|
del variable_assignments[var]
|
|
156
165
|
del variable_uses[var]
|
|
157
166
|
|
|
158
|
-
#
|
|
159
|
-
|
|
167
|
+
# ensure there is no interference between the call site and the use site
|
|
168
|
+
checker = InterferenceChecker(variable_assignments, variable_uses, region)
|
|
169
|
+
for varid in checker.interfered_assignments:
|
|
170
|
+
if varid in variable_assignments:
|
|
171
|
+
del variable_assignments[varid]
|
|
172
|
+
del variable_uses[varid]
|
|
173
|
+
# fold these expressions if possible
|
|
174
|
+
ExpressionFolder(variable_assignments, variable_uses, region)
|
|
160
175
|
return region
|
|
161
176
|
|
|
162
177
|
@staticmethod
|