angr 9.2.122__py3-none-macosx_11_0_arm64.whl → 9.2.124__py3-none-macosx_11_0_arm64.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/lib/angr_native.dylib +0 -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 +96 -95
- {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
|
@@ -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,12 @@ 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
|
+
|
|
241
248
|
def _handle_Convert(self, expr: Convert):
|
|
242
249
|
v = self._expr(expr.operand)
|
|
243
250
|
if isinstance(v, claripy.ast.Bits):
|
|
@@ -42,10 +42,8 @@ class ITERegionConverter(OptimizationPass):
|
|
|
42
42
|
if not ite_assign_regions:
|
|
43
43
|
break
|
|
44
44
|
|
|
45
|
-
for region_head, region_tail,
|
|
46
|
-
round_update |= self._convert_region_to_ternary_expr(
|
|
47
|
-
region_head, region_tail, true_block, true_stmt, false_block, false_stmt
|
|
48
|
-
)
|
|
45
|
+
for region_head, region_tail, _, true_stmt, _, false_stmt in ite_assign_regions:
|
|
46
|
+
round_update |= self._convert_region_to_ternary_expr(region_head, region_tail, true_stmt, false_stmt)
|
|
49
47
|
|
|
50
48
|
if not round_update:
|
|
51
49
|
break
|
|
@@ -188,9 +186,7 @@ class ITERegionConverter(OptimizationPass):
|
|
|
188
186
|
self,
|
|
189
187
|
region_head,
|
|
190
188
|
region_tail,
|
|
191
|
-
true_block,
|
|
192
189
|
true_stmt: Assignment | Call,
|
|
193
|
-
false_block,
|
|
194
190
|
false_stmt: Assignment | Call,
|
|
195
191
|
):
|
|
196
192
|
if region_head not in self._graph or region_tail not in self._graph:
|
|
@@ -206,6 +202,7 @@ class ITERegionConverter(OptimizationPass):
|
|
|
206
202
|
true_stmt_src = true_stmt.src if isinstance(true_stmt, Assignment) else true_stmt
|
|
207
203
|
true_stmt_dst = true_stmt.dst if isinstance(true_stmt, Assignment) else true_stmt.ret_expr
|
|
208
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
|
|
209
206
|
|
|
210
207
|
addr_obj = true_stmt_src if "ins_addr" in true_stmt_src.tags else true_stmt
|
|
211
208
|
ternary_expr = ITE(
|
|
@@ -213,9 +210,7 @@ class ITERegionConverter(OptimizationPass):
|
|
|
213
210
|
conditional_jump.condition,
|
|
214
211
|
false_stmt_src,
|
|
215
212
|
true_stmt_src,
|
|
216
|
-
|
|
217
|
-
vex_block_addr=addr_obj.vex_block_addr,
|
|
218
|
-
vex_stmt_idx=addr_obj.vex_stmt_idx,
|
|
213
|
+
**addr_obj.tags,
|
|
219
214
|
)
|
|
220
215
|
dst = VirtualVariable(
|
|
221
216
|
true_stmt_dst.idx,
|
|
@@ -242,6 +237,14 @@ class ITERegionConverter(OptimizationPass):
|
|
|
242
237
|
#
|
|
243
238
|
|
|
244
239
|
region_nodes = subgraph_between_nodes(self._graph, region_head, [region_tail])
|
|
240
|
+
|
|
241
|
+
# we must obtain the predecessors of the region tail instead of using true_block and false_block because
|
|
242
|
+
# true_block and false_block may have other successors before reaching the region tail!
|
|
243
|
+
region_tail_preds = [pred for pred in self._graph.predecessors(region_tail) if pred in region_nodes]
|
|
244
|
+
if len(region_tail_preds) != 2:
|
|
245
|
+
return False
|
|
246
|
+
region_tail_pred_srcs = {(pred.addr, pred.idx) for pred in region_tail_preds}
|
|
247
|
+
|
|
245
248
|
for node in region_nodes:
|
|
246
249
|
if node is region_head or node is region_tail:
|
|
247
250
|
continue
|
|
@@ -257,12 +260,32 @@ class ITERegionConverter(OptimizationPass):
|
|
|
257
260
|
if not is_phi_assignment(stmt):
|
|
258
261
|
stmts.append(stmt)
|
|
259
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
|
+
|
|
260
275
|
new_src_and_vvars = []
|
|
276
|
+
original_vvars = []
|
|
261
277
|
for src, vvar in stmt.src.src_and_vvars:
|
|
262
|
-
if src not in
|
|
278
|
+
if src not in region_tail_pred_srcs:
|
|
263
279
|
new_src_and_vvars.append((src, vvar))
|
|
280
|
+
else:
|
|
281
|
+
original_vvars.append(vvar)
|
|
264
282
|
new_vvar = new_assignment.dst.copy()
|
|
265
|
-
|
|
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
|
+
)
|
|
266
289
|
|
|
267
290
|
new_phi = Phi(
|
|
268
291
|
stmt.src.idx,
|
|
@@ -396,7 +396,16 @@ class LoweredSwitchSimplifier(StructuringOptimizationPass):
|
|
|
396
396
|
default_case_candidates = {}
|
|
397
397
|
last_comp = None
|
|
398
398
|
stack = [(head, 0, 0xFFFF_FFFF_FFFF_FFFF)]
|
|
399
|
-
|
|
399
|
+
|
|
400
|
+
# cursed: there is an infinite loop in the following loop that
|
|
401
|
+
# occurs rarely. we need to keep track of the nodes we've seen
|
|
402
|
+
# to break out of the loop.
|
|
403
|
+
# See https://github.com/angr/angr/pull/4953
|
|
404
|
+
#
|
|
405
|
+
# FIXME: the root cause should be fixed and this workaround removed
|
|
406
|
+
seen = set()
|
|
407
|
+
while stack and tuple(stack) not in seen:
|
|
408
|
+
seen.add(tuple(stack))
|
|
400
409
|
comp, min_, max_ = stack.pop(0)
|
|
401
410
|
(
|
|
402
411
|
comp_type,
|
|
@@ -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):
|
|
@@ -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
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# pylint:disable=too-many-boolean-expressions
|
|
2
2
|
from __future__ import annotations
|
|
3
3
|
|
|
4
|
-
from ailment.expression import Convert, BinaryOp, Const
|
|
4
|
+
from ailment.expression import Convert, BinaryOp, Const, Expression
|
|
5
5
|
|
|
6
6
|
from .base import PeepholeOptimizationExprBase
|
|
7
7
|
|
|
@@ -56,47 +56,10 @@ class ConstMullAShift(PeepholeOptimizationExprBase):
|
|
|
56
56
|
|
|
57
57
|
elif isinstance(expr, BinaryOp) and expr.op in {"Add", "Sub"}:
|
|
58
58
|
expr0, expr1 = expr.operands
|
|
59
|
-
if (
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
and isinstance(expr1, BinaryOp)
|
|
64
|
-
and expr1.op in {"Shr", "Sar"}
|
|
65
|
-
and isinstance(expr1.operands[1], Const)
|
|
66
|
-
):
|
|
67
|
-
if (
|
|
68
|
-
isinstance(expr0.operands[0], BinaryOp)
|
|
69
|
-
and expr0.operands[0].op in {"Mull", "Mul"}
|
|
70
|
-
and isinstance(expr0.operands[0].operands[1], Const)
|
|
71
|
-
):
|
|
72
|
-
a0 = expr0.operands[0].operands[0]
|
|
73
|
-
a1 = expr1.operands[0]
|
|
74
|
-
elif (
|
|
75
|
-
isinstance(expr1.operands[0], BinaryOp)
|
|
76
|
-
and expr1.operands[0].op in {"Mull", "Mul"}
|
|
77
|
-
and isinstance(expr1.operands[0].operands[1], Const)
|
|
78
|
-
):
|
|
79
|
-
a1 = expr0.operands[0].operands[0]
|
|
80
|
-
a0 = expr1.operands[0]
|
|
81
|
-
else:
|
|
82
|
-
a0, a1 = None, None
|
|
83
|
-
if a0 is not None and a1 is not None and a0.likes(a1):
|
|
84
|
-
# (a * x >> M1) +/- (a >> M2) ==> a / N
|
|
85
|
-
C = expr0.operands[0].operands[1].value
|
|
86
|
-
X = a0
|
|
87
|
-
V = expr0.operands[1].value
|
|
88
|
-
ndigits = 5 if V == 32 else 6
|
|
89
|
-
divisor = self._check_divisor(pow(2, V), C, ndigits)
|
|
90
|
-
if divisor is not None:
|
|
91
|
-
new_const = Const(None, None, divisor, X.bits)
|
|
92
|
-
# we cannot drop the convert in this case
|
|
93
|
-
return BinaryOp(
|
|
94
|
-
expr0.operands[0].idx,
|
|
95
|
-
"Div",
|
|
96
|
-
[X, new_const],
|
|
97
|
-
expr0.operands[0].signed,
|
|
98
|
-
**expr0.operands[0].tags,
|
|
99
|
-
)
|
|
59
|
+
if isinstance(expr1, Convert) and expr1.from_bits == 32 and expr1.to_bits == 64:
|
|
60
|
+
r = self._match_case_a(expr0, expr1)
|
|
61
|
+
if r is not None:
|
|
62
|
+
return r
|
|
100
63
|
|
|
101
64
|
# with Convert in consideration
|
|
102
65
|
if (
|
|
@@ -149,8 +112,78 @@ class ConstMullAShift(PeepholeOptimizationExprBase):
|
|
|
149
112
|
|
|
150
113
|
return None
|
|
151
114
|
|
|
115
|
+
def _match_case_a(self, expr0: Expression, expr1: Convert) -> BinaryOp | None:
|
|
116
|
+
# (
|
|
117
|
+
# (((Conv(32->64, vvar_44{reg 32}) * 0x4325c53f<64>) >>a 0x24<8>) & 0xffffffff<64>) -
|
|
118
|
+
# Conv(32->s64, (vvar_44{reg 32} >>a 0x1f<8>))
|
|
119
|
+
# )
|
|
120
|
+
|
|
121
|
+
expr1 = expr1.operand
|
|
122
|
+
|
|
123
|
+
if (
|
|
124
|
+
isinstance(expr0, BinaryOp)
|
|
125
|
+
and expr0.op == "And"
|
|
126
|
+
and isinstance(expr0.operands[1], Const)
|
|
127
|
+
and expr0.operands[1].value == 0xFFFFFFFF
|
|
128
|
+
):
|
|
129
|
+
expr0 = expr0.operands[0]
|
|
130
|
+
else:
|
|
131
|
+
return None
|
|
132
|
+
|
|
133
|
+
if (
|
|
134
|
+
isinstance(expr0, BinaryOp)
|
|
135
|
+
and expr0.op in {"Shr", "Sar"}
|
|
136
|
+
and isinstance(expr0.operands[1], Const)
|
|
137
|
+
and isinstance(expr1, BinaryOp)
|
|
138
|
+
and expr1.op in {"Shr", "Sar"}
|
|
139
|
+
and isinstance(expr1.operands[1], Const)
|
|
140
|
+
):
|
|
141
|
+
if (
|
|
142
|
+
isinstance(expr0.operands[0], BinaryOp)
|
|
143
|
+
and expr0.operands[0].op in {"Mull", "Mul"}
|
|
144
|
+
and isinstance(expr0.operands[0].operands[1], Const)
|
|
145
|
+
):
|
|
146
|
+
a0 = expr0.operands[0].operands[0]
|
|
147
|
+
a1 = expr1.operands[0]
|
|
148
|
+
elif (
|
|
149
|
+
isinstance(expr1.operands[0], BinaryOp)
|
|
150
|
+
and expr1.operands[0].op in {"Mull", "Mul"}
|
|
151
|
+
and isinstance(expr1.operands[0].operands[1], Const)
|
|
152
|
+
):
|
|
153
|
+
a1 = expr0.operands[0].operands[0]
|
|
154
|
+
a0 = expr1.operands[0]
|
|
155
|
+
else:
|
|
156
|
+
a0, a1 = None, None
|
|
157
|
+
|
|
158
|
+
# a0: Conv(32->64, vvar_44{reg 32})
|
|
159
|
+
# a1: vvar_44{reg 32}
|
|
160
|
+
if isinstance(a0, Convert) and a0.from_bits == a1.bits:
|
|
161
|
+
a0 = a0.operand
|
|
162
|
+
|
|
163
|
+
if a0 is not None and a1 is not None and a0.likes(a1):
|
|
164
|
+
# (a * x >> M1) +/- (a >> M2) ==> a / N
|
|
165
|
+
C = expr0.operands[0].operands[1].value
|
|
166
|
+
X = a0
|
|
167
|
+
V = expr0.operands[1].value
|
|
168
|
+
ndigits = 5 if V == 32 else 6
|
|
169
|
+
divisor = self._check_divisor(pow(2, V), C, ndigits)
|
|
170
|
+
if divisor is not None:
|
|
171
|
+
new_const = Const(None, None, divisor, X.bits)
|
|
172
|
+
# we cannot drop the convert in this case
|
|
173
|
+
return BinaryOp(
|
|
174
|
+
expr0.operands[0].idx,
|
|
175
|
+
"Div",
|
|
176
|
+
[X, new_const],
|
|
177
|
+
expr0.operands[0].signed,
|
|
178
|
+
**expr0.operands[0].tags,
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
return None
|
|
182
|
+
|
|
152
183
|
@staticmethod
|
|
153
184
|
def _check_divisor(a, b, ndigits=6):
|
|
185
|
+
if b == 0:
|
|
186
|
+
return None
|
|
154
187
|
divisor_1 = 1 + (a // b)
|
|
155
188
|
divisor_2 = int(round(a / float(b), ndigits))
|
|
156
189
|
return divisor_1 if divisor_1 == divisor_2 else None
|
|
@@ -11,9 +11,15 @@ class RemoveCascadingConversions(PeepholeOptimizationExprBase):
|
|
|
11
11
|
expr_classes = (Convert,)
|
|
12
12
|
|
|
13
13
|
def optimize(self, expr: Convert, **kwargs):
|
|
14
|
-
if
|
|
14
|
+
if (
|
|
15
|
+
expr.from_type == Convert.TYPE_INT
|
|
16
|
+
and expr.to_type == Convert.TYPE_INT
|
|
17
|
+
and isinstance(expr.operand, Convert)
|
|
18
|
+
and expr.operand.from_type == Convert.TYPE_INT
|
|
19
|
+
and expr.operand.to_type == Convert.TYPE_INT
|
|
20
|
+
):
|
|
15
21
|
inner = expr.operand
|
|
16
|
-
if inner.from_bits == expr.to_bits:
|
|
22
|
+
if inner.from_bits == expr.to_bits and inner.from_type == expr.to_type:
|
|
17
23
|
if inner.from_bits < inner.to_bits:
|
|
18
24
|
# extension -> truncation
|
|
19
25
|
return inner.operand
|
|
@@ -163,6 +163,22 @@ class RegionIdentifier(Analysis):
|
|
|
163
163
|
raise AngrRuntimeError("Cannot find the start node from the graph!") from ex
|
|
164
164
|
raise AngrRuntimeError("Cannot find the start node from the graph!")
|
|
165
165
|
|
|
166
|
+
def _get_entry_node(self, graph: networkx.DiGraph):
|
|
167
|
+
if self.entry_node_addr is None:
|
|
168
|
+
return None
|
|
169
|
+
return next(
|
|
170
|
+
(
|
|
171
|
+
n
|
|
172
|
+
for n in graph.nodes()
|
|
173
|
+
if (
|
|
174
|
+
(n.addr, n.idx) == self.entry_node_addr
|
|
175
|
+
if isinstance(n, Block)
|
|
176
|
+
else n.addr == self.entry_node_addr[0]
|
|
177
|
+
)
|
|
178
|
+
),
|
|
179
|
+
None,
|
|
180
|
+
)
|
|
181
|
+
|
|
166
182
|
def _test_reducibility(self):
|
|
167
183
|
# make a copy of the graph
|
|
168
184
|
graph = networkx.DiGraph(self._graph)
|
|
@@ -188,8 +204,19 @@ class RegionIdentifier(Analysis):
|
|
|
188
204
|
return len(graph.nodes) == 1
|
|
189
205
|
|
|
190
206
|
def _make_supergraph(self, graph: networkx.DiGraph):
|
|
207
|
+
|
|
208
|
+
entry_node = None
|
|
209
|
+
if self.entry_node_addr is not None:
|
|
210
|
+
entry_node = next(iter(nn for nn in graph if nn.addr == self.entry_node_addr[0]), None)
|
|
211
|
+
|
|
191
212
|
while True:
|
|
192
213
|
for src, dst, data in graph.edges(data=True):
|
|
214
|
+
if entry_node is not None and dst is entry_node:
|
|
215
|
+
# the entry node must be kept instead of merged with its predecessor (which can happen in real
|
|
216
|
+
# binaries! e.g., 444a401b900eb825f216e95111dcb6ef94b01a81fc7b88a48599867db8c50365, function
|
|
217
|
+
# 0x1802BEA28, block 0x1802BEA05 and 0x1802BEA28)
|
|
218
|
+
continue
|
|
219
|
+
|
|
193
220
|
type_ = data.get("type", None)
|
|
194
221
|
if type_ == "fake_return":
|
|
195
222
|
if len(list(graph.successors(src))) == 1 and len(list(graph.predecessors(dst))) == 1:
|
|
@@ -452,6 +479,8 @@ class RegionIdentifier(Analysis):
|
|
|
452
479
|
#
|
|
453
480
|
|
|
454
481
|
def _make_cyclic_region(self, head, graph: networkx.DiGraph):
|
|
482
|
+
original_entry = self._get_entry_node(graph)
|
|
483
|
+
|
|
455
484
|
l.debug("Found cyclic region at %#08x", head.addr)
|
|
456
485
|
initial_loop_nodes = self._find_initial_loop_nodes(graph, head)
|
|
457
486
|
l.debug("Initial loop nodes %s", self._dbg_block_list(initial_loop_nodes))
|
|
@@ -505,6 +534,13 @@ class RegionIdentifier(Analysis):
|
|
|
505
534
|
# multi-successor region. refinement is required
|
|
506
535
|
self._refine_loop_successors(region, graph)
|
|
507
536
|
|
|
537
|
+
# if the head node is in the graph and it's not the head of the graph, we will need to update the head node
|
|
538
|
+
# address.
|
|
539
|
+
if original_entry is not None and original_entry in region.graph and region.head is not original_entry:
|
|
540
|
+
self.entry_node_addr = (head.addr, None)
|
|
541
|
+
# FIXME: the identified region will probably be incorrect. we may need to add a jump block that jumps to
|
|
542
|
+
# original_entry.
|
|
543
|
+
|
|
508
544
|
return region
|
|
509
545
|
|
|
510
546
|
def _refine_loop_successors(self, region, graph: networkx.DiGraph):
|
|
@@ -395,6 +395,10 @@ class ExpressionReplacer(AILBlockWalker):
|
|
|
395
395
|
|
|
396
396
|
def _handle_Assignment(self, stmt_idx: int, stmt: Assignment, block: Block | None):
|
|
397
397
|
# override the base handler and make sure we do not replace .dst with a Call expression or an ITE expression
|
|
398
|
+
|
|
399
|
+
if is_phi_assignment(stmt):
|
|
400
|
+
return None
|
|
401
|
+
|
|
398
402
|
changed = False
|
|
399
403
|
|
|
400
404
|
dst = self._handle_expr(0, stmt.dst, stmt_idx, stmt, block)
|
|
@@ -16,6 +16,7 @@ from angr.analyses.decompiler.structuring.structurer_nodes import (
|
|
|
16
16
|
CascadingConditionNode,
|
|
17
17
|
)
|
|
18
18
|
from angr.analyses.decompiler.utils import is_statement_terminating, has_nonlabel_nonphi_statements
|
|
19
|
+
from angr.utils.ail import is_phi_assignment
|
|
19
20
|
|
|
20
21
|
|
|
21
22
|
class LoopSimplifier(SequenceWalker):
|
|
@@ -104,14 +105,7 @@ class LoopSimplifier(SequenceWalker):
|
|
|
104
105
|
)
|
|
105
106
|
and (
|
|
106
107
|
all(has_nonlabel_nonphi_statements(block) for block in self.continue_preludes[node])
|
|
107
|
-
and all(
|
|
108
|
-
not self._control_transferring_statement(block.statements[-1])
|
|
109
|
-
for block in self.continue_preludes[node]
|
|
110
|
-
)
|
|
111
|
-
and all(
|
|
112
|
-
block.statements[-1] == self.continue_preludes[node][0].statements[-1]
|
|
113
|
-
for block in self.continue_preludes[node]
|
|
114
|
-
)
|
|
108
|
+
and all(not is_phi_assignment(block.statements[-1]) for block in self.continue_preludes[node])
|
|
115
109
|
)
|
|
116
110
|
):
|
|
117
111
|
node.sort = "for"
|
|
@@ -284,6 +284,10 @@ def simplify_switch_clusters(
|
|
|
284
284
|
|
|
285
285
|
for variable in var2switches:
|
|
286
286
|
switch_regions = var2switches[variable]
|
|
287
|
+
if len(switch_regions) <= 1:
|
|
288
|
+
# nothing to simplify or merge if there is only one switch region
|
|
289
|
+
continue
|
|
290
|
+
|
|
287
291
|
cond_regions = list(var2condnodes[variable])
|
|
288
292
|
|
|
289
293
|
if not cond_regions:
|
|
@@ -449,10 +453,10 @@ def simplify_switch_clusters(
|
|
|
449
453
|
# build the SwitchCase node and replace old nodes in the parent node
|
|
450
454
|
cases_dict = OrderedDict(cases)
|
|
451
455
|
new_switchcase = SwitchCaseNode(
|
|
452
|
-
|
|
456
|
+
switch_regions[0].node.switch_expr,
|
|
453
457
|
cases_dict,
|
|
454
458
|
default_node,
|
|
455
|
-
addr=
|
|
459
|
+
addr=switch_regions[0].node.addr,
|
|
456
460
|
)
|
|
457
461
|
|
|
458
462
|
# what are we trying to replace?
|
|
@@ -525,13 +529,15 @@ def simplify_lowered_switches_core(
|
|
|
525
529
|
|
|
526
530
|
if outermost_node is None:
|
|
527
531
|
return False
|
|
532
|
+
if not isinstance(outermost_node, ConditionNode):
|
|
533
|
+
return False
|
|
528
534
|
if isinstance(outermost_node.condition, UnaryOp) and outermost_node.condition.op == "Not":
|
|
529
535
|
# attempt to flip any simple negated comparison for normalized operations
|
|
530
536
|
outermost_node.condition = negate(outermost_node.condition.operand)
|
|
531
537
|
|
|
532
538
|
caseno_to_node = {}
|
|
533
539
|
default_node_candidates: list[tuple[BaseNode, BaseNode]] = [] # parent to default node candidate
|
|
534
|
-
stack: list[
|
|
540
|
+
stack: list[tuple[BaseNode, int, int]] = [(outermost_node, 0, 0xFFFF_FFFF_FFFF_FFFF)]
|
|
535
541
|
while stack:
|
|
536
542
|
node, min_, max_ = stack.pop(0)
|
|
537
543
|
if node not in node_to_condnode:
|
|
@@ -216,11 +216,27 @@ class SequenceWalker:
|
|
|
216
216
|
)
|
|
217
217
|
|
|
218
218
|
def _handle_CascadingCondition(self, node: CascadingConditionNode, **kwargs):
|
|
219
|
-
|
|
220
|
-
|
|
219
|
+
cond_nodes_changed = False
|
|
220
|
+
new_condition_and_nodes = []
|
|
221
|
+
for index, (cond, child_node) in enumerate(node.condition_and_nodes):
|
|
222
|
+
new_child = self._handle(child_node, parent=node, index=index)
|
|
223
|
+
if new_child is not None:
|
|
224
|
+
cond_nodes_changed = True
|
|
225
|
+
new_condition_and_nodes.append((cond, new_child))
|
|
226
|
+
else:
|
|
227
|
+
new_condition_and_nodes.append((cond, child_node))
|
|
228
|
+
|
|
229
|
+
new_else = None
|
|
221
230
|
if node.else_node is not None:
|
|
222
|
-
self._handle(node.else_node, parent=node, index=-1)
|
|
223
|
-
|
|
231
|
+
new_else = self._handle(node.else_node, parent=node, index=-1)
|
|
232
|
+
|
|
233
|
+
if cond_nodes_changed or new_else is not None:
|
|
234
|
+
return CascadingConditionNode(
|
|
235
|
+
node.addr,
|
|
236
|
+
new_condition_and_nodes if cond_nodes_changed else node.condition_and_nodes,
|
|
237
|
+
else_node=new_else if new_else is not None else node.else_node,
|
|
238
|
+
)
|
|
239
|
+
return None
|
|
224
240
|
|
|
225
241
|
def _handle_ConditionalBreak(self, node: ConditionalBreakNode, **kwargs): # pylint:disable=no-self-use
|
|
226
242
|
return None
|