angr 9.2.123__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/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 +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 +27 -1
- 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 +64 -1
- angr/analyses/decompiler/expression_narrower.py +5 -1
- 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 +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/win_stack_canary_simplifier.py +2 -0
- angr/analyses/decompiler/peephole_optimizations/const_mull_a_shift.py +2 -0
- 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/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/variable_recovery/engine_ail.py +14 -0
- angr/analyses/variable_recovery/engine_base.py +11 -0
- angr/engines/light/engine.py +12 -0
- 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/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.123.dist-info → angr-9.2.124.dist-info}/METADATA +6 -6
- {angr-9.2.123.dist-info → angr-9.2.124.dist-info}/RECORD +84 -83
- {angr-9.2.123.dist-info → angr-9.2.124.dist-info}/WHEEL +1 -1
- {angr-9.2.123.dist-info → angr-9.2.124.dist-info}/LICENSE +0 -0
- {angr-9.2.123.dist-info → angr-9.2.124.dist-info}/entry_points.txt +0 -0
- {angr-9.2.123.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):
|
|
@@ -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):
|
|
@@ -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
|
|
@@ -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):
|
|
@@ -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:
|
|
@@ -9,7 +9,7 @@ import networkx
|
|
|
9
9
|
import ailment
|
|
10
10
|
from ailment import Block
|
|
11
11
|
from ailment.expression import Expression, Phi, VirtualVariable, VirtualVariableCategory
|
|
12
|
-
from ailment.statement import Assignment, Label
|
|
12
|
+
from ailment.statement import Statement, Assignment, Label
|
|
13
13
|
|
|
14
14
|
from angr.code_location import CodeLocation
|
|
15
15
|
from angr.analyses import ForwardAnalysis
|
|
@@ -38,6 +38,7 @@ class RewritingAnalysis(ForwardAnalysis[RewritingState, NodeType, object, object
|
|
|
38
38
|
udef_to_phiid: dict[tuple, set[int]],
|
|
39
39
|
phiid_to_loc: dict[int, tuple[int, int | None]],
|
|
40
40
|
stackvar_locs: dict[int, int],
|
|
41
|
+
rewrite_tmps: bool,
|
|
41
42
|
ail_manager,
|
|
42
43
|
vvar_id_start: int = 0,
|
|
43
44
|
):
|
|
@@ -52,6 +53,7 @@ class RewritingAnalysis(ForwardAnalysis[RewritingState, NodeType, object, object
|
|
|
52
53
|
self._udef_to_phiid = udef_to_phiid
|
|
53
54
|
self._phiid_to_loc = phiid_to_loc
|
|
54
55
|
self._stackvar_locs = stackvar_locs
|
|
56
|
+
self._rewrite_tmps = rewrite_tmps
|
|
55
57
|
self._ail_manager = ail_manager
|
|
56
58
|
self._engine_ail = SimEngineSSARewriting(
|
|
57
59
|
self.project.arch,
|
|
@@ -61,6 +63,7 @@ class RewritingAnalysis(ForwardAnalysis[RewritingState, NodeType, object, object
|
|
|
61
63
|
udef_to_phiid=self._udef_to_phiid,
|
|
62
64
|
phiid_to_loc=self._phiid_to_loc,
|
|
63
65
|
stackvar_locs=self._stackvar_locs,
|
|
66
|
+
rewrite_tmps=self._rewrite_tmps,
|
|
64
67
|
ail_manager=ail_manager,
|
|
65
68
|
vvar_id_start=vvar_id_start,
|
|
66
69
|
)
|
|
@@ -71,7 +74,7 @@ class RewritingAnalysis(ForwardAnalysis[RewritingState, NodeType, object, object
|
|
|
71
74
|
|
|
72
75
|
self._analyze()
|
|
73
76
|
|
|
74
|
-
self.def_to_vvid: dict[Expression, int] = self._engine_ail.def_to_vvid
|
|
77
|
+
self.def_to_vvid: dict[tuple[int, int | None, int, Expression | Statement], int] = self._engine_ail.def_to_vvid
|
|
75
78
|
self.out_graph = self._make_new_graph(ail_graph)
|
|
76
79
|
|
|
77
80
|
@property
|