angr 9.2.124__py3-none-manylinux2014_aarch64.whl → 9.2.126__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 +13 -1
- angr/analyses/codecave.py +77 -0
- angr/analyses/decompiler/ail_simplifier.py +1 -0
- angr/analyses/decompiler/callsite_maker.py +9 -1
- angr/analyses/decompiler/clinic.py +32 -2
- angr/analyses/decompiler/condition_processor.py +104 -66
- angr/analyses/decompiler/decompiler.py +7 -0
- angr/analyses/decompiler/optimization_passes/__init__.py +18 -1
- angr/analyses/decompiler/optimization_passes/inlined_string_transformation_simplifier.py +6 -0
- angr/analyses/decompiler/optimization_passes/tag_slicer.py +41 -0
- angr/analyses/decompiler/peephole_optimizations/constant_derefs.py +2 -2
- angr/analyses/decompiler/return_maker.py +1 -0
- angr/analyses/decompiler/ssailification/rewriting.py +4 -0
- angr/analyses/decompiler/ssailification/rewriting_engine.py +10 -3
- angr/analyses/decompiler/structured_codegen/c.py +18 -2
- angr/analyses/deobfuscator/__init__.py +18 -0
- angr/analyses/deobfuscator/api_obf_finder.py +313 -0
- angr/analyses/deobfuscator/api_obf_peephole_optimizer.py +51 -0
- angr/analyses/deobfuscator/irsb_reg_collector.py +85 -0
- angr/analyses/deobfuscator/string_obf_finder.py +774 -0
- angr/analyses/deobfuscator/string_obf_opt_passes.py +133 -0
- angr/analyses/deobfuscator/string_obf_peephole_optimizer.py +47 -0
- angr/analyses/patchfinder.py +137 -0
- angr/analyses/pathfinder.py +282 -0
- angr/analyses/reaching_definitions/function_handler_library/stdio.py +8 -1
- angr/analyses/smc.py +159 -0
- angr/analyses/unpacker/__init__.py +6 -0
- angr/analyses/unpacker/obfuscation_detector.py +103 -0
- angr/analyses/unpacker/packing_detector.py +138 -0
- angr/angrdb/models.py +1 -2
- angr/calling_conventions.py +3 -1
- angr/engines/vex/claripy/irop.py +10 -5
- 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/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/obfuscations.py +36 -0
- angr/misc/ux.py +2 -2
- angr/project.py +17 -1
- angr/state_plugins/history.py +6 -4
- angr/utils/bits.py +4 -0
- angr/utils/tagged_interval_map.py +112 -0
- {angr-9.2.124.dist-info → angr-9.2.126.dist-info}/METADATA +6 -6
- {angr-9.2.124.dist-info → angr-9.2.126.dist-info}/RECORD +52 -35
- {angr-9.2.124.dist-info → angr-9.2.126.dist-info}/WHEEL +1 -1
- {angr-9.2.124.dist-info → angr-9.2.126.dist-info}/LICENSE +0 -0
- {angr-9.2.124.dist-info → angr-9.2.126.dist-info}/entry_points.txt +0 -0
- {angr-9.2.124.dist-info → angr-9.2.126.dist-info}/top_level.txt +0 -0
angr/__init__.py
CHANGED
angr/analyses/__init__.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# pylint:disable=wrong-import-position
|
|
1
|
+
# " pylint:disable=wrong-import-position
|
|
2
2
|
from __future__ import annotations
|
|
3
3
|
|
|
4
4
|
from .analysis import Analysis, AnalysesHub
|
|
@@ -49,6 +49,12 @@ from .flirt import FlirtAnalysis
|
|
|
49
49
|
from .s_propagator import SPropagatorAnalysis
|
|
50
50
|
from .s_reaching_definitions import SReachingDefinitionsAnalysis
|
|
51
51
|
from .s_liveness import SLivenessAnalysis
|
|
52
|
+
from .codecave import CodeCaveAnalysis
|
|
53
|
+
from .patchfinder import PatchFinderAnalysis
|
|
54
|
+
from .pathfinder import Pathfinder
|
|
55
|
+
from .smc import SelfModifyingCodeAnalysis
|
|
56
|
+
from .unpacker import PackingDetector
|
|
57
|
+
from . import deobfuscator
|
|
52
58
|
|
|
53
59
|
|
|
54
60
|
__all__ = (
|
|
@@ -101,4 +107,10 @@ __all__ = (
|
|
|
101
107
|
"SPropagatorAnalysis",
|
|
102
108
|
"SReachingDefinitionsAnalysis",
|
|
103
109
|
"SLivenessAnalysis",
|
|
110
|
+
"CodeCaveAnalysis",
|
|
111
|
+
"PatchFinderAnalysis",
|
|
112
|
+
"Pathfinder",
|
|
113
|
+
"SelfModifyingCodeAnalysis",
|
|
114
|
+
"PackingDetector",
|
|
115
|
+
"deobfuscator",
|
|
104
116
|
)
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
import logging
|
|
3
|
+
from enum import Enum, auto
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
|
|
7
|
+
from angr.analyses import Analysis, AnalysesHub
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from angr.knowledge_plugins import Function
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
log = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class CodeCaveClassification(Enum):
|
|
18
|
+
"""
|
|
19
|
+
Type of code caves.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
ALIGNMENT = auto()
|
|
23
|
+
UNREACHABLE = auto()
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@dataclass
|
|
27
|
+
class CodeCave:
|
|
28
|
+
"""
|
|
29
|
+
Describes a code cave in a binary.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
func: Function | None
|
|
33
|
+
addr: int
|
|
34
|
+
size: int
|
|
35
|
+
classification: CodeCaveClassification
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class CodeCaveAnalysis(Analysis):
|
|
39
|
+
"""
|
|
40
|
+
Best-effort static location of potential vacant code caves for possible code injection:
|
|
41
|
+
- Padding functions
|
|
42
|
+
- Unreachable code
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
codecaves: list[CodeCave]
|
|
46
|
+
|
|
47
|
+
def __init__(self):
|
|
48
|
+
self.codecaves = []
|
|
49
|
+
|
|
50
|
+
if len(self.project.kb.functions) == 0 and self.project.kb.cfgs.get_most_accurate() is None:
|
|
51
|
+
log.warning("Please run CFGFast analysis first, to identify functions")
|
|
52
|
+
return
|
|
53
|
+
|
|
54
|
+
# Alignment functions
|
|
55
|
+
for func in self.project.kb.functions.values():
|
|
56
|
+
if func.is_alignment:
|
|
57
|
+
for block in func.blocks:
|
|
58
|
+
self.codecaves.append(CodeCave(func, block.addr, block.size, CodeCaveClassification.ALIGNMENT))
|
|
59
|
+
|
|
60
|
+
# Unreachable code
|
|
61
|
+
for func in self.project.kb.functions.values():
|
|
62
|
+
if func.is_alignment or func.is_plt or func.is_simprocedure or func.addr in self.project.kb.labels:
|
|
63
|
+
continue
|
|
64
|
+
|
|
65
|
+
in_degree = self.project.kb.callgraph.in_degree(func.addr)
|
|
66
|
+
if in_degree == 0 or (
|
|
67
|
+
in_degree == 1
|
|
68
|
+
and self.project.kb.functions[next(self.project.kb.callgraph.predecessors(func.addr))].is_alignment
|
|
69
|
+
):
|
|
70
|
+
for block in func.blocks:
|
|
71
|
+
self.codecaves.append(CodeCave(func, block.addr, block.size, CodeCaveClassification.UNREACHABLE))
|
|
72
|
+
|
|
73
|
+
# FIXME: find dead blocks with argument propagation
|
|
74
|
+
# FIXME: find dead blocks with external coverage info
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
AnalysesHub.register_default("CodeCaves", CodeCaveAnalysis)
|
|
@@ -872,6 +872,7 @@ class AILSimplifier(Analysis):
|
|
|
872
872
|
Const(None, None, eq.atom0.addr, self.project.arch.bits),
|
|
873
873
|
eq.atom0.size,
|
|
874
874
|
endness=self.project.arch.memory_endness,
|
|
875
|
+
**eq.atom1.tags,
|
|
875
876
|
)
|
|
876
877
|
elif isinstance(eq.atom0, VirtualVariable) and eq.atom0.was_reg:
|
|
877
878
|
if isinstance(eq.atom1, VirtualVariable) and eq.atom1.was_reg:
|
|
@@ -157,7 +157,15 @@ class CallSiteMaker(Analysis):
|
|
|
157
157
|
)
|
|
158
158
|
args.append(vvar_use)
|
|
159
159
|
else:
|
|
160
|
-
|
|
160
|
+
reg = Expr.Register(
|
|
161
|
+
self._atom_idx(),
|
|
162
|
+
None,
|
|
163
|
+
offset,
|
|
164
|
+
size * 8,
|
|
165
|
+
reg_name=arg_loc.reg_name,
|
|
166
|
+
ins_addr=last_stmt.ins_addr,
|
|
167
|
+
)
|
|
168
|
+
args.append(reg)
|
|
161
169
|
elif isinstance(arg_loc, SimStackArg):
|
|
162
170
|
stack_arg_locs.append(arg_loc)
|
|
163
171
|
_, the_arg = self._resolve_stack_argument(call_stmt, arg_loc)
|
|
@@ -42,6 +42,7 @@ from .optimization_passes import (
|
|
|
42
42
|
OptimizationPassStage,
|
|
43
43
|
RegisterSaveAreaSimplifier,
|
|
44
44
|
StackCanarySimplifier,
|
|
45
|
+
TagSlicer,
|
|
45
46
|
DUPLICATING_OPTS,
|
|
46
47
|
CONDENSING_OPTS,
|
|
47
48
|
)
|
|
@@ -111,6 +112,7 @@ class Clinic(Analysis):
|
|
|
111
112
|
inlining_parents: set[int] | None = None,
|
|
112
113
|
vvar_id_start: int = 0,
|
|
113
114
|
optimization_scratch: dict[str, Any] | None = None,
|
|
115
|
+
desired_variables: set[str] | None = None,
|
|
114
116
|
):
|
|
115
117
|
if not func.normalized and mode == ClinicMode.DECOMPILE:
|
|
116
118
|
raise ValueError("Decompilation must work on normalized function graphs.")
|
|
@@ -154,6 +156,7 @@ class Clinic(Analysis):
|
|
|
154
156
|
self._inline_functions = inline_functions
|
|
155
157
|
self._inlined_counts = {} if inlined_counts is None else inlined_counts
|
|
156
158
|
self._inlining_parents = inlining_parents or ()
|
|
159
|
+
self._desired_variables = desired_variables
|
|
157
160
|
|
|
158
161
|
self._register_save_areas_removed: bool = False
|
|
159
162
|
|
|
@@ -220,6 +223,9 @@ class Clinic(Analysis):
|
|
|
220
223
|
ail_graph = self._inline_child_functions(ail_graph)
|
|
221
224
|
|
|
222
225
|
ail_graph = self._decompilation_simplifications(ail_graph)
|
|
226
|
+
|
|
227
|
+
if self._desired_variables:
|
|
228
|
+
ail_graph = self._slice_variables(ail_graph)
|
|
223
229
|
self.graph = ail_graph
|
|
224
230
|
|
|
225
231
|
def _decompilation_graph_recovery(self):
|
|
@@ -279,6 +285,27 @@ class Clinic(Analysis):
|
|
|
279
285
|
|
|
280
286
|
return ail_graph
|
|
281
287
|
|
|
288
|
+
def _slice_variables(self, ail_graph):
|
|
289
|
+
nodes_index = {(n.addr, n.idx): n for n in ail_graph.nodes()}
|
|
290
|
+
|
|
291
|
+
vfm = self.variable_kb.variables.function_managers[self.function.addr]
|
|
292
|
+
for v_name in self._desired_variables:
|
|
293
|
+
v = next(iter(vv for vv in vfm._unified_variables if vv.name == v_name))
|
|
294
|
+
for va in vfm.get_variable_accesses(v):
|
|
295
|
+
nodes_index[(va.location.block_addr, va.location.block_idx)].statements[va.location.stmt_idx].tags[
|
|
296
|
+
"keep_in_slice"
|
|
297
|
+
] = True
|
|
298
|
+
|
|
299
|
+
a = TagSlicer(
|
|
300
|
+
self.function,
|
|
301
|
+
graph=ail_graph,
|
|
302
|
+
variable_kb=self.variable_kb,
|
|
303
|
+
)
|
|
304
|
+
if a.out_graph:
|
|
305
|
+
# use the new graph
|
|
306
|
+
ail_graph = a.out_graph
|
|
307
|
+
return ail_graph
|
|
308
|
+
|
|
282
309
|
def _inline_child_functions(self, ail_graph):
|
|
283
310
|
for blk in ail_graph.nodes():
|
|
284
311
|
for idx, stmt in enumerate(blk.statements):
|
|
@@ -909,13 +936,16 @@ class Clinic(Analysis):
|
|
|
909
936
|
Convert a VEX block to an AIL block.
|
|
910
937
|
|
|
911
938
|
:param block_node: A BlockNode instance.
|
|
912
|
-
:return:
|
|
939
|
+
:return: A converted AIL block.
|
|
913
940
|
:rtype: ailment.Block
|
|
914
941
|
"""
|
|
915
942
|
|
|
916
943
|
if type(block_node) is not BlockNode:
|
|
917
944
|
return block_node
|
|
918
945
|
|
|
946
|
+
if block_node.size == 0:
|
|
947
|
+
return ailment.Block(block_node.addr, 0, statements=[])
|
|
948
|
+
|
|
919
949
|
block = self.project.factory.block(block_node.addr, block_node.size, cross_insn_opt=False)
|
|
920
950
|
if block.vex.jumpkind not in {"Ijk_Call", "Ijk_Boring", "Ijk_Ret"} and not block.vex.jumpkind.startswith(
|
|
921
951
|
"Ijk_Sys"
|
|
@@ -1782,7 +1812,7 @@ class Clinic(Analysis):
|
|
|
1782
1812
|
s = self.kb.custom_strings[expr.value]
|
|
1783
1813
|
expr.tags["reference_values"] = {
|
|
1784
1814
|
SimTypePointer(SimTypeChar().with_arch(self.project.arch)).with_arch(self.project.arch): s.decode(
|
|
1785
|
-
"
|
|
1815
|
+
"latin-1"
|
|
1786
1816
|
),
|
|
1787
1817
|
}
|
|
1788
1818
|
else:
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
from collections import defaultdict, OrderedDict
|
|
3
3
|
from typing import Any
|
|
4
|
+
from collections.abc import Callable
|
|
4
5
|
from collections.abc import Generator
|
|
5
6
|
import operator
|
|
6
7
|
import logging
|
|
@@ -80,17 +81,17 @@ _INVERSE_OPERATIONS = {
|
|
|
80
81
|
#
|
|
81
82
|
|
|
82
83
|
|
|
83
|
-
def _op_with_unified_size(op, conv, operand0, operand1):
|
|
84
|
+
def _op_with_unified_size(op, conv: Callable, operand0, operand1, ins_addr: int):
|
|
84
85
|
# ensure operand1 is of the same size as operand0
|
|
85
86
|
if isinstance(operand1, ailment.Expr.Const):
|
|
86
87
|
# amazing - we do the easy thing here
|
|
87
|
-
return op(conv(operand0, nobool=True), operand1.value)
|
|
88
|
+
return op(conv(operand0, nobool=True, ins_addr=ins_addr), operand1.value)
|
|
88
89
|
if operand1.bits == operand0.bits:
|
|
89
|
-
return op(conv(operand0, nobool=True), conv(operand1))
|
|
90
|
+
return op(conv(operand0, nobool=True, ins_addr=ins_addr), conv(operand1, ins_addr=ins_addr))
|
|
90
91
|
# extension is required
|
|
91
92
|
assert operand1.bits < operand0.bits
|
|
92
93
|
operand1 = ailment.Expr.Convert(None, operand1.bits, operand0.bits, False, operand1)
|
|
93
|
-
return op(conv(operand0, nobool=True), conv(operand1, nobool=True))
|
|
94
|
+
return op(conv(operand0, nobool=True, ins_addr=ins_addr), conv(operand1, nobool=True, ins_addr=ins_addr))
|
|
94
95
|
|
|
95
96
|
|
|
96
97
|
def _dummy_bvs(condition, condition_mapping, name_suffix=""):
|
|
@@ -106,62 +107,94 @@ def _dummy_bools(condition, condition_mapping, name_suffix=""):
|
|
|
106
107
|
|
|
107
108
|
|
|
108
109
|
_ail2claripy_op_mapping = {
|
|
109
|
-
"LogicalAnd": lambda expr, conv, _: claripy.And(
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
"
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
"
|
|
116
|
-
"
|
|
117
|
-
"
|
|
118
|
-
"
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
"
|
|
122
|
-
"
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
"
|
|
126
|
-
"
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
"
|
|
130
|
-
"
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
"
|
|
134
|
-
"
|
|
135
|
-
"
|
|
136
|
-
"
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
"
|
|
140
|
-
"
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
"
|
|
144
|
-
"
|
|
145
|
-
|
|
110
|
+
"LogicalAnd": lambda expr, conv, _, ia: claripy.And(
|
|
111
|
+
conv(expr.operands[0], ins_addr=ia), conv(expr.operands[1], ins_addr=ia)
|
|
112
|
+
),
|
|
113
|
+
"LogicalOr": lambda expr, conv, _, ia: claripy.Or(
|
|
114
|
+
conv(expr.operands[0], ins_addr=ia), conv(expr.operands[1], ins_addr=ia)
|
|
115
|
+
),
|
|
116
|
+
"CmpEQ": lambda expr, conv, _, ia: conv(expr.operands[0], ins_addr=ia) == conv(expr.operands[1], ins_addr=ia),
|
|
117
|
+
"CmpNE": lambda expr, conv, _, ia: conv(expr.operands[0], ins_addr=ia) != conv(expr.operands[1], ins_addr=ia),
|
|
118
|
+
"CmpLE": lambda expr, conv, _, ia: conv(expr.operands[0], ins_addr=ia) <= conv(expr.operands[1], ins_addr=ia),
|
|
119
|
+
"CmpLEs": lambda expr, conv, _, ia: claripy.SLE(
|
|
120
|
+
conv(expr.operands[0], ins_addr=ia), conv(expr.operands[1], ins_addr=ia)
|
|
121
|
+
),
|
|
122
|
+
"CmpLT": lambda expr, conv, _, ia: conv(expr.operands[0], ins_addr=ia) < conv(expr.operands[1], ins_addr=ia),
|
|
123
|
+
"CmpLTs": lambda expr, conv, _, ia: claripy.SLT(
|
|
124
|
+
conv(expr.operands[0], ins_addr=ia), conv(expr.operands[1], ins_addr=ia)
|
|
125
|
+
),
|
|
126
|
+
"CmpGE": lambda expr, conv, _, ia: conv(expr.operands[0], ins_addr=ia) >= conv(expr.operands[1], ins_addr=ia),
|
|
127
|
+
"CmpGEs": lambda expr, conv, _, ia: claripy.SGE(
|
|
128
|
+
conv(expr.operands[0], ins_addr=ia), conv(expr.operands[1], ins_addr=ia)
|
|
129
|
+
),
|
|
130
|
+
"CmpGT": lambda expr, conv, _, ia: conv(expr.operands[0], ins_addr=ia) > conv(expr.operands[1], ins_addr=ia),
|
|
131
|
+
"CmpGTs": lambda expr, conv, _, ia: claripy.SGT(
|
|
132
|
+
conv(expr.operands[0], ins_addr=ia), conv(expr.operands[1], ins_addr=ia)
|
|
133
|
+
),
|
|
134
|
+
"CasCmpEQ": lambda expr, conv, _, ia: conv(expr.operands[0], ins_addr=ia) == conv(expr.operands[1], ins_addr=ia),
|
|
135
|
+
"CasCmpNE": lambda expr, conv, _, ia: conv(expr.operands[0], ins_addr=ia) != conv(expr.operands[1], ins_addr=ia),
|
|
136
|
+
"CasCmpLE": lambda expr, conv, _, ia: conv(expr.operands[0], ins_addr=ia) <= conv(expr.operands[1], ins_addr=ia),
|
|
137
|
+
"CasCmpLEs": lambda expr, conv, _, ia: claripy.SLE(
|
|
138
|
+
conv(expr.operands[0], ins_addr=ia), conv(expr.operands[1], ins_addr=ia)
|
|
139
|
+
),
|
|
140
|
+
"CasCmpLT": lambda expr, conv, _, ia: conv(expr.operands[0], ins_addr=ia) < conv(expr.operands[1], ins_addr=ia),
|
|
141
|
+
"CasCmpLTs": lambda expr, conv, _, ia: claripy.SLT(
|
|
142
|
+
conv(expr.operands[0], ins_addr=ia), conv(expr.operands[1], ins_addr=ia)
|
|
143
|
+
),
|
|
144
|
+
"CasCmpGE": lambda expr, conv, _, ia: conv(expr.operands[0], ins_addr=ia) >= conv(expr.operands[1], ins_addr=ia),
|
|
145
|
+
"CasCmpGEs": lambda expr, conv, _, ia: claripy.SGE(
|
|
146
|
+
conv(expr.operands[0], ins_addr=ia), conv(expr.operands[1], ins_addr=ia)
|
|
147
|
+
),
|
|
148
|
+
"CasCmpGT": lambda expr, conv, _, ia: conv(expr.operands[0], ins_addr=ia) > conv(expr.operands[1], ins_addr=ia),
|
|
149
|
+
"CasCmpGTs": lambda expr, conv, _, ia: claripy.SGT(
|
|
150
|
+
conv(expr.operands[0], ins_addr=ia), conv(expr.operands[1], ins_addr=ia)
|
|
151
|
+
),
|
|
152
|
+
"Add": lambda expr, conv, _, ia: conv(expr.operands[0], nobool=True, ins_addr=ia)
|
|
153
|
+
+ conv(expr.operands[1], nobool=True, ins_addr=ia),
|
|
154
|
+
"Sub": lambda expr, conv, _, ia: conv(expr.operands[0], nobool=True, ins_addr=ia)
|
|
155
|
+
- conv(expr.operands[1], nobool=True, ins_addr=ia),
|
|
156
|
+
"Mul": lambda expr, conv, _, ia: conv(expr.operands[0], nobool=True, ins_addr=ia)
|
|
157
|
+
* conv(expr.operands[1], nobool=True, ins_addr=ia),
|
|
158
|
+
"Div": lambda expr, conv, _, ia: conv(expr.operands[0], nobool=True, ins_addr=ia)
|
|
159
|
+
/ conv(expr.operands[1], nobool=True, ins_addr=ia),
|
|
160
|
+
"Mod": lambda expr, conv, _, ia: conv(expr.operands[0], nobool=True, ins_addr=ia)
|
|
161
|
+
% conv(expr.operands[1], nobool=True, ins_addr=ia),
|
|
162
|
+
"Not": lambda expr, conv, _, ia: claripy.Not(conv(expr.operand, ins_addr=ia)),
|
|
163
|
+
"Neg": lambda expr, conv, _, ia: -conv(expr.operand, ins_addr=ia),
|
|
164
|
+
"BitwiseNeg": lambda expr, conv, _, ia: ~conv(expr.operand, ins_addr=ia),
|
|
165
|
+
"Xor": lambda expr, conv, _, ia: conv(expr.operands[0], nobool=True, ins_addr=ia)
|
|
166
|
+
^ conv(expr.operands[1], nobool=True, ins_addr=ia),
|
|
167
|
+
"And": lambda expr, conv, _, ia: conv(expr.operands[0], nobool=True, ins_addr=ia)
|
|
168
|
+
& conv(expr.operands[1], nobool=True, ins_addr=ia),
|
|
169
|
+
"Or": lambda expr, conv, _, ia: conv(expr.operands[0], nobool=True, ins_addr=ia)
|
|
170
|
+
| conv(expr.operands[1], nobool=True, ins_addr=ia),
|
|
171
|
+
"Shr": lambda expr, conv, _, ia: _op_with_unified_size(claripy.LShR, conv, expr.operands[0], expr.operands[1], ia),
|
|
172
|
+
"Shl": lambda expr, conv, _, ia: _op_with_unified_size(
|
|
173
|
+
operator.lshift, conv, expr.operands[0], expr.operands[1], ia
|
|
174
|
+
),
|
|
175
|
+
"Sar": lambda expr, conv, _, ia: _op_with_unified_size(
|
|
176
|
+
operator.rshift, conv, expr.operands[0], expr.operands[1], ia
|
|
177
|
+
),
|
|
178
|
+
"Concat": lambda expr, conv, _, ia: claripy.Concat(*[conv(operand, ins_addr=ia) for operand in expr.operands]),
|
|
146
179
|
# There are no corresponding claripy operations for the following operations
|
|
147
|
-
"DivMod": lambda expr, _, m: _dummy_bvs(expr, m),
|
|
148
|
-
"CmpF": lambda expr, _, m: _dummy_bvs(expr, m),
|
|
149
|
-
"Mull": lambda expr, _, m: _dummy_bvs(expr, m),
|
|
150
|
-
"Mulls": lambda expr, _, m: _dummy_bvs(expr, m),
|
|
151
|
-
"Reinterpret": lambda expr, _, m: _dummy_bvs(expr, m),
|
|
152
|
-
"Rol": lambda expr, _, m: _dummy_bvs(expr, m),
|
|
153
|
-
"Ror": lambda expr, _, m: _dummy_bvs(expr, m),
|
|
154
|
-
"LogicalXor": lambda expr, _, m: _dummy_bvs(expr, m),
|
|
155
|
-
"Carry": lambda expr, _, m: _dummy_bvs(expr, m),
|
|
156
|
-
"SCarry": lambda expr, _, m: _dummy_bvs(expr, m),
|
|
157
|
-
"SBorrow": lambda expr, _, m: _dummy_bvs(expr, m),
|
|
158
|
-
"ExpCmpNE": lambda expr, _, m: _dummy_bools(expr, m),
|
|
159
|
-
"CmpORD": lambda expr, _, m: _dummy_bvs(expr, m), # in case CmpORDRewriter fails
|
|
160
|
-
"GetMSBs": lambda expr, _, m: _dummy_bvs(expr, m),
|
|
161
|
-
"InterleaveLOV": lambda expr, _, m: _dummy_bvs(expr, m),
|
|
162
|
-
"InterleaveHIV": lambda expr, _, m: _dummy_bvs(expr, m),
|
|
180
|
+
"DivMod": lambda expr, _, m, *args: _dummy_bvs(expr, m),
|
|
181
|
+
"CmpF": lambda expr, _, m, *args: _dummy_bvs(expr, m),
|
|
182
|
+
"Mull": lambda expr, _, m, *args: _dummy_bvs(expr, m),
|
|
183
|
+
"Mulls": lambda expr, _, m, *args: _dummy_bvs(expr, m),
|
|
184
|
+
"Reinterpret": lambda expr, _, m, *args: _dummy_bvs(expr, m),
|
|
185
|
+
"Rol": lambda expr, _, m, *args: _dummy_bvs(expr, m),
|
|
186
|
+
"Ror": lambda expr, _, m, *args: _dummy_bvs(expr, m),
|
|
187
|
+
"LogicalXor": lambda expr, _, m, *args: _dummy_bvs(expr, m),
|
|
188
|
+
"Carry": lambda expr, _, m, *args: _dummy_bvs(expr, m),
|
|
189
|
+
"SCarry": lambda expr, _, m, *args: _dummy_bvs(expr, m),
|
|
190
|
+
"SBorrow": lambda expr, _, m, *args: _dummy_bvs(expr, m),
|
|
191
|
+
"ExpCmpNE": lambda expr, _, m, *args: _dummy_bools(expr, m),
|
|
192
|
+
"CmpORD": lambda expr, _, m, *args: _dummy_bvs(expr, m), # in case CmpORDRewriter fails
|
|
193
|
+
"GetMSBs": lambda expr, _, m, *args: _dummy_bvs(expr, m),
|
|
194
|
+
"InterleaveLOV": lambda expr, _, m, *args: _dummy_bvs(expr, m),
|
|
195
|
+
"InterleaveHIV": lambda expr, _, m, *args: _dummy_bvs(expr, m),
|
|
163
196
|
# catch-all
|
|
164
|
-
"_DUMMY_": lambda expr, _, m: _dummy_bvs(expr, m),
|
|
197
|
+
"_DUMMY_": lambda expr, _, m, *args: _dummy_bvs(expr, m),
|
|
165
198
|
}
|
|
166
199
|
|
|
167
200
|
#
|
|
@@ -610,6 +643,7 @@ class ConditionProcessor:
|
|
|
610
643
|
),
|
|
611
644
|
False,
|
|
612
645
|
),
|
|
646
|
+
ins_addr=dst_block.addr,
|
|
613
647
|
)
|
|
614
648
|
|
|
615
649
|
if type(src_block) is ConditionalBreakNode:
|
|
@@ -638,10 +672,10 @@ class ConditionProcessor:
|
|
|
638
672
|
if isinstance(last_stmt.target, ailment.Expr.Const):
|
|
639
673
|
return claripy.true()
|
|
640
674
|
# indirect jump
|
|
641
|
-
target_ast = self.claripy_ast_from_ail_condition(last_stmt.target)
|
|
675
|
+
target_ast = self.claripy_ast_from_ail_condition(last_stmt.target, ins_addr=last_stmt.ins_addr)
|
|
642
676
|
return target_ast == dst_block.addr
|
|
643
677
|
if type(last_stmt) is ailment.Stmt.ConditionalJump:
|
|
644
|
-
bool_var = self.claripy_ast_from_ail_condition(last_stmt.condition)
|
|
678
|
+
bool_var = self.claripy_ast_from_ail_condition(last_stmt.condition, ins_addr=last_stmt.ins_addr)
|
|
645
679
|
if isinstance(last_stmt.true_target, ailment.Expr.Const) and last_stmt.true_target.value == dst_block.addr:
|
|
646
680
|
return bool_var
|
|
647
681
|
return claripy.Not(bool_var)
|
|
@@ -766,7 +800,7 @@ class ConditionProcessor:
|
|
|
766
800
|
f"Condition variable {cond} has an unsupported operator {cond.op}. Consider implementing."
|
|
767
801
|
)
|
|
768
802
|
|
|
769
|
-
def claripy_ast_from_ail_condition(self, condition, nobool: bool = False) -> claripy.ast.Bool:
|
|
803
|
+
def claripy_ast_from_ail_condition(self, condition, nobool: bool = False, *, ins_addr: int = 0) -> claripy.ast.Bool:
|
|
770
804
|
# Unpack a condition all the way to the leaves
|
|
771
805
|
if isinstance(condition, claripy.ast.Base): # pylint:disable=isinstance-second-argument-not-valid-type
|
|
772
806
|
return condition
|
|
@@ -782,20 +816,24 @@ class ConditionProcessor:
|
|
|
782
816
|
# does it have a variable associated?
|
|
783
817
|
if condition.variable is not None:
|
|
784
818
|
var = claripy.BVS(
|
|
785
|
-
f"ailexpr_{condition!r}-{condition.variable.ident}",
|
|
819
|
+
f"ailexpr_{condition!r}-{condition.variable.ident}-{ins_addr:x}",
|
|
820
|
+
condition.bits,
|
|
821
|
+
explicit_name=True,
|
|
786
822
|
)
|
|
787
823
|
else:
|
|
788
|
-
var = claripy.BVS(
|
|
824
|
+
var = claripy.BVS(
|
|
825
|
+
f"ailexpr_{condition!r}-{condition.idx}-{ins_addr:x}", condition.bits, explicit_name=True
|
|
826
|
+
)
|
|
789
827
|
self._condition_mapping[var.args[0]] = condition
|
|
790
828
|
return var
|
|
791
829
|
if isinstance(condition, ailment.Expr.Convert):
|
|
792
830
|
# convert is special. if it generates a 1-bit variable, it should be treated as a BoolS
|
|
793
831
|
if condition.to_bits == 1:
|
|
794
|
-
var_ = self.claripy_ast_from_ail_condition(condition.operands[0])
|
|
832
|
+
var_ = self.claripy_ast_from_ail_condition(condition.operands[0], ins_addr=ins_addr)
|
|
795
833
|
name = "ailcond_Conv(%d->%d, %d)" % (condition.from_bits, condition.to_bits, hash(var_))
|
|
796
834
|
var = claripy.BoolS(name, explicit_name=True)
|
|
797
835
|
else:
|
|
798
|
-
var_ = self.claripy_ast_from_ail_condition(condition.operands[0])
|
|
836
|
+
var_ = self.claripy_ast_from_ail_condition(condition.operands[0], ins_addr=ins_addr)
|
|
799
837
|
name = "ailexpr_Conv(%d->%d, %d)" % (condition.from_bits, condition.to_bits, hash(var_))
|
|
800
838
|
var = claripy.BVS(name, condition.to_bits, explicit_name=True)
|
|
801
839
|
self._condition_mapping[var.args[0]] = condition
|
|
@@ -838,7 +876,7 @@ class ConditionProcessor:
|
|
|
838
876
|
condition.verbose_op,
|
|
839
877
|
)
|
|
840
878
|
lambda_expr = _ail2claripy_op_mapping["_DUMMY_"]
|
|
841
|
-
r = lambda_expr(condition, self.claripy_ast_from_ail_condition, self._condition_mapping)
|
|
879
|
+
r = lambda_expr(condition, self.claripy_ast_from_ail_condition, self._condition_mapping, ins_addr)
|
|
842
880
|
|
|
843
881
|
if isinstance(r, claripy.ast.Bool) and nobool:
|
|
844
882
|
r = claripy.BVS(f"ailexpr_from_bool_{r!r}", 1, explicit_name=True)
|
|
@@ -66,9 +66,11 @@ 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,
|
|
71
72
|
use_cache: bool = True,
|
|
73
|
+
expr_collapse_depth: int = 16,
|
|
72
74
|
):
|
|
73
75
|
if not isinstance(func, Function):
|
|
74
76
|
func = self.kb.functions[func]
|
|
@@ -103,6 +105,7 @@ class Decompiler(Analysis):
|
|
|
103
105
|
self._update_memory_data = update_memory_data
|
|
104
106
|
self._generate_code = generate_code
|
|
105
107
|
self._inline_functions = inline_functions
|
|
108
|
+
self._desired_variables = desired_variables
|
|
106
109
|
self._cache_parameters = (
|
|
107
110
|
{
|
|
108
111
|
"cfg": self._cfg,
|
|
@@ -118,6 +121,7 @@ class Decompiler(Analysis):
|
|
|
118
121
|
"ite_exprs": self._ite_exprs,
|
|
119
122
|
"binop_operators": self._binop_operators,
|
|
120
123
|
"inline_functions": self._inline_functions,
|
|
124
|
+
"desired_variables": self._desired_variables,
|
|
121
125
|
}
|
|
122
126
|
if use_cache
|
|
123
127
|
else None
|
|
@@ -132,6 +136,7 @@ class Decompiler(Analysis):
|
|
|
132
136
|
self.ail_graph: networkx.DiGraph | None = None
|
|
133
137
|
self.vvar_id_start = None
|
|
134
138
|
self._optimization_scratch: dict[str, Any] = {}
|
|
139
|
+
self.expr_collapse_depth = expr_collapse_depth
|
|
135
140
|
|
|
136
141
|
if decompile:
|
|
137
142
|
self._decompile()
|
|
@@ -226,6 +231,7 @@ class Decompiler(Analysis):
|
|
|
226
231
|
cache=cache,
|
|
227
232
|
progress_callback=progress_callback,
|
|
228
233
|
inline_functions=self._inline_functions,
|
|
234
|
+
desired_variables=self._desired_variables,
|
|
229
235
|
optimization_scratch=self._optimization_scratch,
|
|
230
236
|
**self.options_to_params(self.options_by_class["clinic"]),
|
|
231
237
|
)
|
|
@@ -329,6 +335,7 @@ class Decompiler(Analysis):
|
|
|
329
335
|
stmt_comments=old_codegen.stmt_comments if old_codegen is not None else None,
|
|
330
336
|
const_formats=old_codegen.const_formats if old_codegen is not None else None,
|
|
331
337
|
externs=clinic.externs,
|
|
338
|
+
binop_depth_cutoff=self.expr_collapse_depth,
|
|
332
339
|
**self.options_to_params(self.options_by_class["codegen"]),
|
|
333
340
|
)
|
|
334
341
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# pylint:disable=import-outside-toplevel
|
|
2
2
|
from __future__ import annotations
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
3
4
|
|
|
4
5
|
from archinfo import Arch
|
|
5
6
|
|
|
@@ -26,11 +27,16 @@ from .cross_jump_reverter import CrossJumpReverter
|
|
|
26
27
|
from .code_motion import CodeMotionOptimization
|
|
27
28
|
from .switch_default_case_duplicator import SwitchDefaultCaseDuplicator
|
|
28
29
|
from .deadblock_remover import DeadblockRemover
|
|
30
|
+
from .tag_slicer import TagSlicer
|
|
29
31
|
from .inlined_string_transformation_simplifier import InlinedStringTransformationSimplifier
|
|
30
32
|
from .const_prop_reverter import ConstPropOptReverter
|
|
31
33
|
from .call_stmt_rewriter import CallStatementRewriter
|
|
32
34
|
from .duplication_reverter import DuplicationReverter
|
|
33
35
|
|
|
36
|
+
if TYPE_CHECKING:
|
|
37
|
+
from angr.analyses.decompiler.presets import DecompilationPreset
|
|
38
|
+
|
|
39
|
+
|
|
34
40
|
# order matters!
|
|
35
41
|
ALL_OPTIMIZATION_PASSES = [
|
|
36
42
|
RegisterSaveAreaSimplifier,
|
|
@@ -59,6 +65,7 @@ ALL_OPTIMIZATION_PASSES = [
|
|
|
59
65
|
FlipBooleanCmp,
|
|
60
66
|
InlinedStringTransformationSimplifier,
|
|
61
67
|
CallStatementRewriter,
|
|
68
|
+
TagSlicer,
|
|
62
69
|
]
|
|
63
70
|
|
|
64
71
|
# these passes may duplicate code to remove gotos or improve the structure of the graph
|
|
@@ -86,9 +93,18 @@ def get_optimization_passes(arch, platform):
|
|
|
86
93
|
return passes
|
|
87
94
|
|
|
88
95
|
|
|
89
|
-
def register_optimization_pass(opt_pass):
|
|
96
|
+
def register_optimization_pass(opt_pass, *, presets: list[str | DecompilationPreset] | None = None):
|
|
90
97
|
ALL_OPTIMIZATION_PASSES.append(opt_pass)
|
|
91
98
|
|
|
99
|
+
if presets:
|
|
100
|
+
from angr.analyses.decompiler.presets import DECOMPILATION_PRESETS
|
|
101
|
+
|
|
102
|
+
for preset in presets:
|
|
103
|
+
if isinstance(preset, str):
|
|
104
|
+
preset = DECOMPILATION_PRESETS[preset] # intentionally raise a KeyError if the preset is not found
|
|
105
|
+
if opt_pass not in preset.opt_passes:
|
|
106
|
+
preset.opt_passes.append(opt_pass)
|
|
107
|
+
|
|
92
108
|
|
|
93
109
|
__all__ = (
|
|
94
110
|
"OptimizationPassStage",
|
|
@@ -118,6 +134,7 @@ __all__ = (
|
|
|
118
134
|
"ConstPropOptReverter",
|
|
119
135
|
"CallStatementRewriter",
|
|
120
136
|
"DuplicationReverter",
|
|
137
|
+
"TagSlicer",
|
|
121
138
|
"ALL_OPTIMIZATION_PASSES",
|
|
122
139
|
"DUPLICATING_OPTS",
|
|
123
140
|
"CONDENSING_OPTS",
|
|
@@ -240,6 +240,12 @@ class InlinedStringTransformationAILEngine(SimEngineLightAILMixin):
|
|
|
240
240
|
return None
|
|
241
241
|
|
|
242
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):
|
|
243
249
|
v = self._expr(expr.operand)
|
|
244
250
|
if isinstance(v, claripy.ast.Bits):
|
|
245
251
|
return ~v
|
|
@@ -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
|