angr 9.2.176__cp310-abi3-win_amd64.whl → 9.2.178__cp310-abi3-win_amd64.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/cfg_fast.py +15 -0
- angr/analyses/decompiler/ail_simplifier.py +69 -1
- angr/analyses/decompiler/ccall_rewriters/amd64_ccalls.py +45 -7
- angr/analyses/decompiler/clinic.py +15 -7
- angr/analyses/decompiler/dirty_rewriters/__init__.py +7 -0
- angr/analyses/decompiler/dirty_rewriters/amd64_dirty.py +69 -0
- angr/analyses/decompiler/dirty_rewriters/rewriter_base.py +27 -0
- angr/analyses/decompiler/optimization_passes/__init__.py +3 -0
- angr/analyses/decompiler/optimization_passes/optimization_pass.py +10 -8
- angr/analyses/decompiler/optimization_passes/register_save_area_simplifier.py +44 -6
- angr/analyses/decompiler/optimization_passes/register_save_area_simplifier_adv.py +198 -0
- angr/analyses/decompiler/optimization_passes/win_stack_canary_simplifier.py +111 -55
- angr/analyses/decompiler/peephole_optimizations/cas_intrinsics.py +69 -12
- angr/analyses/decompiler/peephole_optimizations/inlined_wcscpy_consolidation.py +189 -6
- angr/analyses/decompiler/peephole_optimizations/remove_redundant_shifts_around_comparators.py +72 -1
- angr/analyses/decompiler/presets/basic.py +2 -0
- angr/analyses/decompiler/presets/fast.py +2 -0
- angr/analyses/decompiler/presets/full.py +2 -0
- angr/analyses/decompiler/utils.py +10 -3
- angr/analyses/flirt/flirt.py +5 -4
- angr/analyses/s_propagator.py +23 -21
- angr/analyses/smc.py +2 -3
- angr/analyses/variable_recovery/engine_ail.py +39 -0
- angr/emulator.py +2 -1
- angr/engines/hook.py +1 -1
- angr/engines/icicle.py +19 -3
- angr/knowledge_plugins/functions/function.py +2 -2
- angr/knowledge_plugins/labels.py +4 -4
- angr/procedures/definitions/__init__.py +9 -0
- angr/procedures/definitions/parse_win32json.py +11 -0
- angr/procedures/definitions/wdk/ntoskrnl.json +4 -0
- angr/rustylib.pyd +0 -0
- angr/unicornlib.dll +0 -0
- angr/utils/funcid.py +85 -0
- angr/utils/ssa/__init__.py +2 -6
- {angr-9.2.176.dist-info → angr-9.2.178.dist-info}/METADATA +6 -5
- {angr-9.2.176.dist-info → angr-9.2.178.dist-info}/RECORD +42 -38
- {angr-9.2.176.dist-info → angr-9.2.178.dist-info}/WHEEL +0 -0
- {angr-9.2.176.dist-info → angr-9.2.178.dist-info}/entry_points.txt +0 -0
- {angr-9.2.176.dist-info → angr-9.2.178.dist-info}/licenses/LICENSE +0 -0
- {angr-9.2.176.dist-info → angr-9.2.178.dist-info}/top_level.txt +0 -0
|
@@ -1,13 +1,47 @@
|
|
|
1
1
|
# pylint:disable=arguments-differ
|
|
2
2
|
from __future__ import annotations
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
3
4
|
|
|
4
5
|
from angr.ailment.expression import Expression, BinaryOp, Const, Register, StackBaseOffset, UnaryOp, VirtualVariable
|
|
5
|
-
from angr.ailment.statement import Call, Store
|
|
6
|
+
from angr.ailment.statement import Call, Store, Assignment
|
|
6
7
|
|
|
7
8
|
from angr.sim_type import SimTypePointer, SimTypeWideChar
|
|
8
9
|
from .base import PeepholeOptimizationMultiStmtBase
|
|
9
10
|
from .inlined_wcscpy import InlinedWcscpy
|
|
10
11
|
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from angr.ailment.statement import Statement
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def match_statements(stmts: list[Statement], index: int) -> int:
|
|
17
|
+
ending = index
|
|
18
|
+
has_wcsncpy = False
|
|
19
|
+
for i in range(index, len(stmts)):
|
|
20
|
+
stmt = stmts[i]
|
|
21
|
+
if isinstance(stmt, Call):
|
|
22
|
+
if InlinedWcscpy.is_inlined_wcsncpy(stmt):
|
|
23
|
+
has_wcsncpy = True
|
|
24
|
+
else:
|
|
25
|
+
break
|
|
26
|
+
elif isinstance(stmt, Store):
|
|
27
|
+
if not isinstance(stmt.data, Const):
|
|
28
|
+
break
|
|
29
|
+
_, off = InlinedWcscpyConsolidation._parse_addr(stmt.addr)
|
|
30
|
+
if off is None:
|
|
31
|
+
# unsupported offset - bail
|
|
32
|
+
break
|
|
33
|
+
elif (
|
|
34
|
+
isinstance(stmt, Assignment)
|
|
35
|
+
and isinstance(stmt.dst, VirtualVariable)
|
|
36
|
+
and stmt.dst.was_stack
|
|
37
|
+
and isinstance(stmt.src, Const)
|
|
38
|
+
):
|
|
39
|
+
pass
|
|
40
|
+
else:
|
|
41
|
+
break
|
|
42
|
+
ending = i + 1
|
|
43
|
+
return ending - index if has_wcsncpy and ending - index >= 2 else 0
|
|
44
|
+
|
|
11
45
|
|
|
12
46
|
class InlinedWcscpyConsolidation(PeepholeOptimizationMultiStmtBase):
|
|
13
47
|
"""
|
|
@@ -17,12 +51,141 @@ class InlinedWcscpyConsolidation(PeepholeOptimizationMultiStmtBase):
|
|
|
17
51
|
__slots__ = ()
|
|
18
52
|
|
|
19
53
|
NAME = "Consolidate multiple inlined wcsncpy calls"
|
|
20
|
-
stmt_classes = (
|
|
54
|
+
stmt_classes = (match_statements,)
|
|
21
55
|
|
|
22
56
|
def optimize( # type:ignore
|
|
23
|
-
self, stmts: list[Call], stmt_idx: int | None = None, block=None, **kwargs
|
|
57
|
+
self, stmts: list[Call | Store | Assignment], stmt_idx: int | None = None, block=None, **kwargs
|
|
24
58
|
): # pylint:disable=unused-argument
|
|
25
|
-
|
|
59
|
+
reordered_stmts = self._reorder_stmts(stmts)
|
|
60
|
+
if not reordered_stmts or len(reordered_stmts) <= 1:
|
|
61
|
+
return None
|
|
62
|
+
|
|
63
|
+
new_stmts = []
|
|
64
|
+
optimized = False
|
|
65
|
+
stop = False
|
|
66
|
+
while not stop:
|
|
67
|
+
new_stmts = []
|
|
68
|
+
stop = True
|
|
69
|
+
for i, stmt0 in enumerate(reordered_stmts):
|
|
70
|
+
if i == len(reordered_stmts) - 1:
|
|
71
|
+
new_stmts.append(reordered_stmts[i])
|
|
72
|
+
break
|
|
73
|
+
stmt1 = reordered_stmts[i + 1]
|
|
74
|
+
opt_stmts = self._optimize_pair(stmt0, stmt1)
|
|
75
|
+
if opt_stmts is None:
|
|
76
|
+
new_stmts.append(stmt0)
|
|
77
|
+
else:
|
|
78
|
+
new_stmts += opt_stmts
|
|
79
|
+
# start again from the beginning
|
|
80
|
+
optimized = True
|
|
81
|
+
stop = False
|
|
82
|
+
reordered_stmts = new_stmts + reordered_stmts[i + 2 :]
|
|
83
|
+
break
|
|
84
|
+
|
|
85
|
+
return new_stmts if optimized and new_stmts else None
|
|
86
|
+
|
|
87
|
+
def _reorder_stmts(self, stmts: list[Call | Store | Assignment]) -> list[Call | Store] | None:
|
|
88
|
+
"""
|
|
89
|
+
Order a list of statements based on ascending addresses of their destination buffers.
|
|
90
|
+
"""
|
|
91
|
+
|
|
92
|
+
if not all(
|
|
93
|
+
(
|
|
94
|
+
InlinedWcscpy.is_inlined_wcsncpy(s)
|
|
95
|
+
or (isinstance(s, Store) and isinstance(s.data, Const))
|
|
96
|
+
or (
|
|
97
|
+
isinstance(s, Assignment)
|
|
98
|
+
and isinstance(s.dst, VirtualVariable)
|
|
99
|
+
and s.dst.was_stack
|
|
100
|
+
and isinstance(s.src, Const)
|
|
101
|
+
)
|
|
102
|
+
)
|
|
103
|
+
for s in stmts
|
|
104
|
+
):
|
|
105
|
+
return None
|
|
106
|
+
offset_to_stmt = {}
|
|
107
|
+
updated_offsets: set[int] = set()
|
|
108
|
+
known_base = None
|
|
109
|
+
for stmt in stmts:
|
|
110
|
+
if isinstance(stmt, Call):
|
|
111
|
+
assert (
|
|
112
|
+
stmt.args is not None
|
|
113
|
+
and len(stmt.args) == 3
|
|
114
|
+
and stmt.args[0] is not None
|
|
115
|
+
and stmt.args[2] is not None
|
|
116
|
+
)
|
|
117
|
+
base, off = self._parse_addr(stmt.args[0])
|
|
118
|
+
store_size = stmt.args[2].value * 2 if isinstance(stmt.args[2], Const) else None
|
|
119
|
+
elif isinstance(stmt, Store):
|
|
120
|
+
base, off = self._parse_addr(stmt.addr)
|
|
121
|
+
store_size = stmt.size
|
|
122
|
+
elif isinstance(stmt, Assignment):
|
|
123
|
+
base, off = self._parse_addr(stmt.dst)
|
|
124
|
+
store_size = stmt.dst.size
|
|
125
|
+
else:
|
|
126
|
+
# unexpected!
|
|
127
|
+
return None
|
|
128
|
+
if off is None or store_size is None:
|
|
129
|
+
# bad offset or size - bail
|
|
130
|
+
return None
|
|
131
|
+
if known_base is None:
|
|
132
|
+
known_base = base
|
|
133
|
+
elif not base.likes(known_base):
|
|
134
|
+
# bail
|
|
135
|
+
return None
|
|
136
|
+
if off in offset_to_stmt:
|
|
137
|
+
# duplicate offset - bail
|
|
138
|
+
return None
|
|
139
|
+
assert isinstance(store_size, int)
|
|
140
|
+
for i in range(store_size):
|
|
141
|
+
if off + i in updated_offsets:
|
|
142
|
+
# overlapping store - bail
|
|
143
|
+
return None
|
|
144
|
+
updated_offsets.add(off + i)
|
|
145
|
+
|
|
146
|
+
offset_to_stmt[off] = stmt
|
|
147
|
+
|
|
148
|
+
return [offset_to_stmt[k] for k in sorted(offset_to_stmt)]
|
|
149
|
+
|
|
150
|
+
def _optimize_pair(
|
|
151
|
+
self, last_stmt: Call | Store | Assignment, stmt: Call | Store | Assignment
|
|
152
|
+
) -> list[Call] | None:
|
|
153
|
+
# convert (store, wcsncpy()) to (wcsncpy(), store) if they do not overlap
|
|
154
|
+
if (
|
|
155
|
+
isinstance(stmt, Call)
|
|
156
|
+
and InlinedWcscpy.is_inlined_wcsncpy(stmt)
|
|
157
|
+
and stmt.args is not None
|
|
158
|
+
and len(stmt.args) == 3
|
|
159
|
+
and isinstance(stmt.args[2], Const)
|
|
160
|
+
and isinstance(stmt.args[2].value, int)
|
|
161
|
+
and isinstance(last_stmt, (Store, Assignment))
|
|
162
|
+
):
|
|
163
|
+
if isinstance(last_stmt, Store) and isinstance(last_stmt.data, Const):
|
|
164
|
+
store_addr = last_stmt.addr
|
|
165
|
+
store_size = last_stmt.size
|
|
166
|
+
elif isinstance(last_stmt, Assignment):
|
|
167
|
+
store_addr = last_stmt.dst
|
|
168
|
+
store_size = last_stmt.dst.size
|
|
169
|
+
else:
|
|
170
|
+
return None
|
|
171
|
+
# check if they overlap
|
|
172
|
+
wcsncpy_addr = stmt.args[0]
|
|
173
|
+
wcsncpy_size = stmt.args[2].value * 2
|
|
174
|
+
delta = self._get_delta(store_addr, wcsncpy_addr)
|
|
175
|
+
if delta is not None:
|
|
176
|
+
if (0 <= delta <= store_size) or (delta < 0 and -delta <= wcsncpy_size):
|
|
177
|
+
# they overlap, do not switch
|
|
178
|
+
pass
|
|
179
|
+
else:
|
|
180
|
+
last_stmt, stmt = stmt, last_stmt
|
|
181
|
+
|
|
182
|
+
# swap two statements if they are out of order
|
|
183
|
+
if InlinedWcscpy.is_inlined_wcsncpy(last_stmt) and InlinedWcscpy.is_inlined_wcsncpy(stmt):
|
|
184
|
+
assert last_stmt.args is not None and stmt.args is not None
|
|
185
|
+
delta = self._get_delta(last_stmt.args[0], stmt.args[0])
|
|
186
|
+
if delta is not None and delta < 0:
|
|
187
|
+
last_stmt, stmt = stmt, last_stmt
|
|
188
|
+
|
|
26
189
|
if InlinedWcscpy.is_inlined_wcsncpy(last_stmt):
|
|
27
190
|
assert last_stmt.args is not None
|
|
28
191
|
assert self.kb is not None
|
|
@@ -55,6 +218,22 @@ class InlinedWcscpyConsolidation(PeepholeOptimizationMultiStmtBase):
|
|
|
55
218
|
)
|
|
56
219
|
if r and s is not None:
|
|
57
220
|
new_str = s_last + s
|
|
221
|
+
elif (
|
|
222
|
+
isinstance(stmt, Assignment)
|
|
223
|
+
and isinstance(stmt.dst, VirtualVariable)
|
|
224
|
+
and isinstance(stmt.src, Const)
|
|
225
|
+
and isinstance(stmt.src.value, int)
|
|
226
|
+
):
|
|
227
|
+
# consolidating a call and an assignment, in case the assignment statement is storing the suffix of a
|
|
228
|
+
# string (but the suffix is too short to qualify an inlined strcpy optimization)
|
|
229
|
+
addr_curr = stmt.dst
|
|
230
|
+
delta = self._get_delta(addr_last, addr_curr)
|
|
231
|
+
if delta is not None and delta == len(s_last):
|
|
232
|
+
r, s = InlinedWcscpy.is_integer_likely_a_wide_string(
|
|
233
|
+
stmt.src.value, stmt.dst.size, self.project.arch.memory_endness, min_length=1 # type:ignore
|
|
234
|
+
)
|
|
235
|
+
if r and s is not None:
|
|
236
|
+
new_str = s_last + s
|
|
58
237
|
|
|
59
238
|
if new_str is not None:
|
|
60
239
|
assert self.project is not None
|
|
@@ -83,17 +262,21 @@ class InlinedWcscpyConsolidation(PeepholeOptimizationMultiStmtBase):
|
|
|
83
262
|
|
|
84
263
|
@staticmethod
|
|
85
264
|
def _parse_addr(addr: Expression) -> tuple[Expression, int]:
|
|
265
|
+
# we force the base to 64-bit because it does not really matter when we use it
|
|
266
|
+
|
|
267
|
+
if isinstance(addr, VirtualVariable) and addr.was_stack:
|
|
268
|
+
return StackBaseOffset(None, 64, 0), addr.stack_offset
|
|
86
269
|
if isinstance(addr, Register):
|
|
87
270
|
return addr, 0
|
|
88
271
|
if isinstance(addr, StackBaseOffset):
|
|
89
|
-
return StackBaseOffset(None,
|
|
272
|
+
return StackBaseOffset(None, 64, 0), addr.offset
|
|
90
273
|
if (
|
|
91
274
|
isinstance(addr, UnaryOp)
|
|
92
275
|
and addr.op == "Reference"
|
|
93
276
|
and isinstance(addr.operand, VirtualVariable)
|
|
94
277
|
and addr.operand.was_stack
|
|
95
278
|
):
|
|
96
|
-
return StackBaseOffset(None,
|
|
279
|
+
return StackBaseOffset(None, 64, 0), addr.operand.stack_offset
|
|
97
280
|
if isinstance(addr, BinaryOp):
|
|
98
281
|
if addr.op == "Add" and isinstance(addr.operands[1], Const) and isinstance(addr.operands[1].value, int):
|
|
99
282
|
base_0, offset_0 = InlinedWcscpyConsolidation._parse_addr(addr.operands[0])
|
angr/analyses/decompiler/peephole_optimizations/remove_redundant_shifts_around_comparators.py
CHANGED
|
@@ -16,7 +16,9 @@ class RemoveRedundantShiftsAroundComparators(PeepholeOptimizationExprBase):
|
|
|
16
16
|
NAME = "Remove redundant bitshifts for operands around a comparator"
|
|
17
17
|
expr_classes = (BinaryOp,) # all expressions are allowed
|
|
18
18
|
|
|
19
|
-
def optimize(
|
|
19
|
+
def optimize(
|
|
20
|
+
self, expr: BinaryOp, stmt_idx: int | None = None, block=None, **kwargs
|
|
21
|
+
): # pylint:disable=unused-argument
|
|
20
22
|
# (expr_0 << N) < (expr_1 << N) ==> expr_0 << expr_1
|
|
21
23
|
# FIXME: This optimization is unsafe but seems to work for all existing case
|
|
22
24
|
if expr.op in {"CmpLE", "CmpLT", "CmpEQ", "CmpNE", "CmpGE", "CmpGT"}:
|
|
@@ -41,4 +43,73 @@ class RemoveRedundantShiftsAroundComparators(PeepholeOptimizationExprBase):
|
|
|
41
43
|
**expr.tags,
|
|
42
44
|
)
|
|
43
45
|
|
|
46
|
+
# might have been rewritten to multiplications
|
|
47
|
+
if (
|
|
48
|
+
isinstance(op0, BinaryOp)
|
|
49
|
+
and op0.op == "Mul"
|
|
50
|
+
and isinstance(op0.operands[1], Const)
|
|
51
|
+
and op0.operands[1].is_int
|
|
52
|
+
):
|
|
53
|
+
op0_op = op0.operands[0]
|
|
54
|
+
mul_0 = op0.operands[1].value_int
|
|
55
|
+
mul_1 = None
|
|
56
|
+
op1_op = None
|
|
57
|
+
if (
|
|
58
|
+
isinstance(op1, BinaryOp)
|
|
59
|
+
and op1.op == "Mul"
|
|
60
|
+
and isinstance(op1.operands[1], Const)
|
|
61
|
+
and op1.operands[1].is_int
|
|
62
|
+
):
|
|
63
|
+
op1_op = op1.operands[0]
|
|
64
|
+
mul_1 = op1.operands[1].value_int
|
|
65
|
+
elif isinstance(op1, Const):
|
|
66
|
+
op1_op = None
|
|
67
|
+
mul_1 = op1.value_int
|
|
68
|
+
|
|
69
|
+
if mul_1 is not None:
|
|
70
|
+
common_shift_amount = self._get_common_shift_amount(mul_0, mul_1)
|
|
71
|
+
if common_shift_amount > 0:
|
|
72
|
+
new_mul_0 = Const(None, None, mul_0 >> common_shift_amount, expr.bits)
|
|
73
|
+
new_mul_1 = Const(None, None, mul_1 >> common_shift_amount, expr.bits)
|
|
74
|
+
new_cmp_0 = BinaryOp(op0.idx, "Mul", [op0_op, new_mul_0], op0.signed, bits=op0.bits, **op0.tags)
|
|
75
|
+
new_cmp_1 = (
|
|
76
|
+
BinaryOp(op1.idx, "Mul", [op1_op, new_mul_1], op1.signed, bits=op1.bits, **op1.tags)
|
|
77
|
+
if op1_op is not None
|
|
78
|
+
else new_mul_1
|
|
79
|
+
)
|
|
80
|
+
return BinaryOp(
|
|
81
|
+
expr.idx,
|
|
82
|
+
expr.op,
|
|
83
|
+
[new_cmp_0, new_cmp_1],
|
|
84
|
+
expr.signed,
|
|
85
|
+
bits=expr.bits,
|
|
86
|
+
floating_point=expr.floating_point,
|
|
87
|
+
rounding_mode=expr.rounding_mode,
|
|
88
|
+
**expr.tags,
|
|
89
|
+
)
|
|
90
|
+
|
|
44
91
|
return None
|
|
92
|
+
|
|
93
|
+
@staticmethod
|
|
94
|
+
def _get_common_shift_amount(v0: int, v1: int) -> int:
|
|
95
|
+
if v0 == 0 or v1 == 0:
|
|
96
|
+
return 0
|
|
97
|
+
shift_amount = 0
|
|
98
|
+
while (v0 & 1) == 0 and (v1 & 1) == 0:
|
|
99
|
+
if v0 & 0xFFFF == 0 and v1 & 0xFFFF == 0:
|
|
100
|
+
v0 >>= 16
|
|
101
|
+
v1 >>= 16
|
|
102
|
+
shift_amount += 16
|
|
103
|
+
elif v0 & 0xFF == 0 and v1 & 0xFF == 0:
|
|
104
|
+
v0 >>= 8
|
|
105
|
+
v1 >>= 8
|
|
106
|
+
shift_amount += 8
|
|
107
|
+
elif v0 & 0xF == 0 and v1 & 0xF == 0:
|
|
108
|
+
v0 >>= 4
|
|
109
|
+
v1 >>= 4
|
|
110
|
+
shift_amount += 4
|
|
111
|
+
else:
|
|
112
|
+
v0 >>= 1
|
|
113
|
+
v1 >>= 1
|
|
114
|
+
shift_amount += 1
|
|
115
|
+
return shift_amount
|
|
@@ -7,6 +7,7 @@ from angr.analyses.decompiler.optimization_passes import (
|
|
|
7
7
|
BasePointerSaveSimplifier,
|
|
8
8
|
ConstantDereferencesSimplifier,
|
|
9
9
|
RetAddrSaveSimplifier,
|
|
10
|
+
RegisterSaveAreaSimplifierAdvanced,
|
|
10
11
|
X86GccGetPcSimplifier,
|
|
11
12
|
CallStatementRewriter,
|
|
12
13
|
SwitchReusedEntryRewriter,
|
|
@@ -23,6 +24,7 @@ preset_basic = DecompilationPreset(
|
|
|
23
24
|
BasePointerSaveSimplifier,
|
|
24
25
|
ConstantDereferencesSimplifier,
|
|
25
26
|
RetAddrSaveSimplifier,
|
|
27
|
+
RegisterSaveAreaSimplifierAdvanced,
|
|
26
28
|
X86GccGetPcSimplifier,
|
|
27
29
|
CallStatementRewriter,
|
|
28
30
|
SwitchReusedEntryRewriter,
|
|
@@ -22,6 +22,7 @@ from angr.analyses.decompiler.optimization_passes import (
|
|
|
22
22
|
DeadblockRemover,
|
|
23
23
|
SwitchReusedEntryRewriter,
|
|
24
24
|
ConditionConstantPropagation,
|
|
25
|
+
RegisterSaveAreaSimplifierAdvanced,
|
|
25
26
|
DetermineLoadSizes,
|
|
26
27
|
PostStructuringPeepholeOptimizationPass,
|
|
27
28
|
)
|
|
@@ -33,6 +34,7 @@ preset_fast = DecompilationPreset(
|
|
|
33
34
|
RegisterSaveAreaSimplifier,
|
|
34
35
|
StackCanarySimplifier,
|
|
35
36
|
WinStackCanarySimplifier,
|
|
37
|
+
RegisterSaveAreaSimplifierAdvanced,
|
|
36
38
|
BasePointerSaveSimplifier,
|
|
37
39
|
ConstantDereferencesSimplifier,
|
|
38
40
|
RetAddrSaveSimplifier,
|
|
@@ -4,6 +4,7 @@ from angr.analyses.decompiler.optimization_passes import (
|
|
|
4
4
|
RegisterSaveAreaSimplifier,
|
|
5
5
|
StackCanarySimplifier,
|
|
6
6
|
WinStackCanarySimplifier,
|
|
7
|
+
RegisterSaveAreaSimplifierAdvanced,
|
|
7
8
|
BasePointerSaveSimplifier,
|
|
8
9
|
DivSimplifier,
|
|
9
10
|
ModSimplifier,
|
|
@@ -38,6 +39,7 @@ preset_full = DecompilationPreset(
|
|
|
38
39
|
RegisterSaveAreaSimplifier,
|
|
39
40
|
StackCanarySimplifier,
|
|
40
41
|
WinStackCanarySimplifier,
|
|
42
|
+
RegisterSaveAreaSimplifierAdvanced,
|
|
41
43
|
BasePointerSaveSimplifier,
|
|
42
44
|
DivSimplifier,
|
|
43
45
|
ModSimplifier,
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
from __future__ import annotations
|
|
3
3
|
import pathlib
|
|
4
4
|
import copy
|
|
5
|
+
from types import FunctionType
|
|
5
6
|
from typing import Any
|
|
6
7
|
from collections.abc import Iterable
|
|
7
8
|
import logging
|
|
@@ -958,9 +959,15 @@ def peephole_optimize_multistmts(block, stmt_opts):
|
|
|
958
959
|
for opt in stmt_opts:
|
|
959
960
|
matched = False
|
|
960
961
|
stmt_seq_len = None
|
|
961
|
-
for
|
|
962
|
-
if
|
|
963
|
-
|
|
962
|
+
for stmt_class_seq_or_method in opt.stmt_classes:
|
|
963
|
+
if isinstance(stmt_class_seq_or_method, FunctionType):
|
|
964
|
+
r = stmt_class_seq_or_method(statements, stmt_idx)
|
|
965
|
+
if r > 0:
|
|
966
|
+
stmt_seq_len = r
|
|
967
|
+
matched = True
|
|
968
|
+
break
|
|
969
|
+
elif match_stmt_classes(statements, stmt_idx, stmt_class_seq_or_method):
|
|
970
|
+
stmt_seq_len = len(stmt_class_seq_or_method)
|
|
964
971
|
matched = True
|
|
965
972
|
break
|
|
966
973
|
|
angr/analyses/flirt/flirt.py
CHANGED
|
@@ -35,7 +35,7 @@ class FlirtAnalysis(Analysis):
|
|
|
35
35
|
current binary, and then match all possible signatures for the architecture.
|
|
36
36
|
"""
|
|
37
37
|
|
|
38
|
-
def __init__(self, sig: FlirtSignature | str | None = None, max_mismatched_bytes: int = 0):
|
|
38
|
+
def __init__(self, sig: FlirtSignature | str | None = None, max_mismatched_bytes: int = 0, dry_run: bool = False):
|
|
39
39
|
|
|
40
40
|
from angr.flirt import FLIRT_SIGNATURES_BY_ARCH # pylint:disable=import-outside-toplevel
|
|
41
41
|
|
|
@@ -94,9 +94,10 @@ class FlirtAnalysis(Analysis):
|
|
|
94
94
|
sig_ = path_to_sig.get(max_suggestion_sig_path)
|
|
95
95
|
assert sig_ is not None
|
|
96
96
|
_l.info("Applying FLIRT signature %s for library %s.", sig_, lib)
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
97
|
+
if not dry_run:
|
|
98
|
+
self._apply_changes(
|
|
99
|
+
sig_.sig_name if not self._temporary_sig else None, sig_to_suggestions[max_suggestion_sig_path]
|
|
100
|
+
)
|
|
100
101
|
self.matched_suggestions[lib] = (sig_, sig_to_suggestions[max_suggestion_sig_path])
|
|
101
102
|
|
|
102
103
|
def _find_hits_by_strings(self, regions: list[bytes]) -> Generator[FlirtSignature]:
|
angr/analyses/s_propagator.py
CHANGED
|
@@ -15,6 +15,7 @@ from angr.ailment.expression import (
|
|
|
15
15
|
Load,
|
|
16
16
|
Convert,
|
|
17
17
|
Expression,
|
|
18
|
+
Tmp,
|
|
18
19
|
)
|
|
19
20
|
from angr.ailment.statement import Assignment, Store, Return, Jump, ConditionalJump
|
|
20
21
|
|
|
@@ -164,7 +165,7 @@ class SPropagatorAnalysis(Analysis):
|
|
|
164
165
|
assert v is not None
|
|
165
166
|
const_vvars[vvar_id] = v
|
|
166
167
|
for vvar_at_use, useloc in vvar_uselocs[vvar_id]:
|
|
167
|
-
replacements
|
|
168
|
+
self.replace(replacements, useloc, vvar_at_use, v)
|
|
168
169
|
continue
|
|
169
170
|
|
|
170
171
|
v = phi_assignment_get_src(stmt)
|
|
@@ -185,7 +186,7 @@ class SPropagatorAnalysis(Analysis):
|
|
|
185
186
|
const_value = const_vvars[next(iter(all_int_src_varids))]
|
|
186
187
|
const_vvars[vvar.varid] = const_value
|
|
187
188
|
for vvar_at_use, useloc in vvar_uselocs[vvar.varid]:
|
|
188
|
-
replacements
|
|
189
|
+
self.replace(replacements, useloc, vvar_at_use, const_value)
|
|
189
190
|
|
|
190
191
|
# function mode only
|
|
191
192
|
if self.mode == "function":
|
|
@@ -226,7 +227,7 @@ class SPropagatorAnalysis(Analysis):
|
|
|
226
227
|
if can_replace:
|
|
227
228
|
# we can propagate this load because there is no store between its def and use
|
|
228
229
|
for vvar_used, vvar_useloc in vvar_uselocs_set:
|
|
229
|
-
replacements
|
|
230
|
+
self.replace(replacements, vvar_useloc, vvar_used, stmt.src)
|
|
230
231
|
continue
|
|
231
232
|
|
|
232
233
|
if (
|
|
@@ -241,7 +242,7 @@ class SPropagatorAnalysis(Analysis):
|
|
|
241
242
|
uselocs = {loc for _, loc in vvar_uselocs_set}
|
|
242
243
|
if self.is_vvar_used_for_addr_loading_switch_case(uselocs, blocks) and not has_tmp_expr(stmt.src):
|
|
243
244
|
for vvar_used, vvar_useloc in vvar_uselocs_set:
|
|
244
|
-
replacements
|
|
245
|
+
self.replace(replacements, vvar_useloc, vvar_used, stmt.src)
|
|
245
246
|
# mark the vvar as dead and should be removed
|
|
246
247
|
self.model.dead_vvar_ids.add(vvar.varid)
|
|
247
248
|
continue
|
|
@@ -255,7 +256,7 @@ class SPropagatorAnalysis(Analysis):
|
|
|
255
256
|
and not has_tmp_expr(stmt.src)
|
|
256
257
|
):
|
|
257
258
|
# we can propagate this load because there is no store between its def and use
|
|
258
|
-
replacements
|
|
259
|
+
self.replace(replacements, vvar_useloc, vvar_used, stmt.src)
|
|
259
260
|
continue
|
|
260
261
|
|
|
261
262
|
if is_const_and_vvar_assignment(stmt) and not has_tmp_expr(stmt.src):
|
|
@@ -272,9 +273,9 @@ class SPropagatorAnalysis(Analysis):
|
|
|
272
273
|
and stmt.src.oident == useloc_stmt.dst.oident
|
|
273
274
|
and stmt.src.category == useloc_stmt.dst.category
|
|
274
275
|
):
|
|
275
|
-
replacements
|
|
276
|
+
self.replace(replacements, vvar_useloc, vvar_used, stmt.src)
|
|
276
277
|
else:
|
|
277
|
-
replacements
|
|
278
|
+
self.replace(replacements, vvar_useloc, vvar_used, stmt.src)
|
|
278
279
|
continue
|
|
279
280
|
|
|
280
281
|
else:
|
|
@@ -288,7 +289,7 @@ class SPropagatorAnalysis(Analysis):
|
|
|
288
289
|
# this vvar is used once if we exclude its uses at ret sites or jump sites. we can
|
|
289
290
|
# propagate it
|
|
290
291
|
for vvar_used, vvar_useloc in vvar_uselocs_set:
|
|
291
|
-
replacements
|
|
292
|
+
self.replace(replacements, vvar_useloc, vvar_used, stmt.src)
|
|
292
293
|
continue
|
|
293
294
|
|
|
294
295
|
if (
|
|
@@ -304,7 +305,7 @@ class SPropagatorAnalysis(Analysis):
|
|
|
304
305
|
# remove duplicate use locs (e.g., if the variable is used multiple times by the
|
|
305
306
|
# same statement) - but ensure stmt is simple enough
|
|
306
307
|
for vvar_used, vvar_useloc in vvar_uselocs_set:
|
|
307
|
-
replacements
|
|
308
|
+
self.replace(replacements, vvar_useloc, vvar_used, stmt.src)
|
|
308
309
|
continue
|
|
309
310
|
|
|
310
311
|
# special logic for global variables: if it's used once or multiple times, and the variable is never
|
|
@@ -332,7 +333,7 @@ class SPropagatorAnalysis(Analysis):
|
|
|
332
333
|
)
|
|
333
334
|
if not gv_updated:
|
|
334
335
|
for vvar_used, vvar_useloc in vvar_uselocs_set:
|
|
335
|
-
replacements
|
|
336
|
+
self.replace(replacements, vvar_useloc, vvar_used, stmt.src)
|
|
336
337
|
continue
|
|
337
338
|
|
|
338
339
|
for vvar_id, uselocs in vvar_uselocs.items():
|
|
@@ -353,7 +354,7 @@ class SPropagatorAnalysis(Analysis):
|
|
|
353
354
|
if sp_bits is not None and vvar.bits < sp_bits:
|
|
354
355
|
# truncation needed
|
|
355
356
|
v = Convert(None, sp_bits, vvar.bits, False, v)
|
|
356
|
-
replacements
|
|
357
|
+
self.replace(replacements, useloc, vvar_at_use, v)
|
|
357
358
|
continue
|
|
358
359
|
if not self._bp_as_gpr and vvar.oident == self.project.arch.bp_offset:
|
|
359
360
|
bp_bits = (
|
|
@@ -368,7 +369,7 @@ class SPropagatorAnalysis(Analysis):
|
|
|
368
369
|
if bp_bits is not None and vvar.bits < bp_bits:
|
|
369
370
|
# truncation needed
|
|
370
371
|
v = Convert(None, bp_bits, vvar.bits, False, v)
|
|
371
|
-
replacements
|
|
372
|
+
self.replace(replacements, useloc, vvar_at_use, v)
|
|
372
373
|
continue
|
|
373
374
|
|
|
374
375
|
# find all tmp definitions
|
|
@@ -390,9 +391,8 @@ class SPropagatorAnalysis(Analysis):
|
|
|
390
391
|
if r:
|
|
391
392
|
# we can propagate it!
|
|
392
393
|
for tmp_used, tmp_use_stmtidx in tmp_uses:
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
][tmp_used] = stmt.src
|
|
394
|
+
loc = CodeLocation(block_loc.block_addr, tmp_use_stmtidx, block_idx=block_loc.block_idx)
|
|
395
|
+
self.replace(replacements, loc, tmp_used, stmt.src)
|
|
396
396
|
continue
|
|
397
397
|
|
|
398
398
|
r = is_const_vvar_tmp_assignment(stmt)
|
|
@@ -404,9 +404,8 @@ class SPropagatorAnalysis(Analysis):
|
|
|
404
404
|
v = stmt.src
|
|
405
405
|
|
|
406
406
|
for tmp_used, tmp_use_stmtidx in tmp_uses:
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
][tmp_used] = v
|
|
407
|
+
loc = CodeLocation(block_loc.block_addr, tmp_use_stmtidx, block_idx=block_loc.block_idx)
|
|
408
|
+
self.replace(replacements, loc, tmp_used, v)
|
|
410
409
|
continue
|
|
411
410
|
|
|
412
411
|
if len(tmp_uses) <= 2 and is_const_vvar_load_dirty_assignment(stmt):
|
|
@@ -422,9 +421,8 @@ class SPropagatorAnalysis(Analysis):
|
|
|
422
421
|
# we can propagate this load because either we do not consider memory aliasing problem
|
|
423
422
|
# within the same instruction (blocks must be originally lifted with
|
|
424
423
|
# CROSS_INSN_OPT=False), or there is no store between its def and use.
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
][tmp_used] = stmt.src
|
|
424
|
+
loc = CodeLocation(block_loc.block_addr, tmp_use_stmtidx, block_idx=block_loc.block_idx)
|
|
425
|
+
self.replace(replacements, loc, tmp_used, stmt.src)
|
|
428
426
|
|
|
429
427
|
self.model.replacements = replacements
|
|
430
428
|
|
|
@@ -538,5 +536,9 @@ class SPropagatorAnalysis(Analysis):
|
|
|
538
536
|
|
|
539
537
|
return g
|
|
540
538
|
|
|
539
|
+
@staticmethod
|
|
540
|
+
def replace(replacements: dict, loc, expr: VirtualVariable | Tmp, value: Expression) -> None:
|
|
541
|
+
replacements[loc][expr] = value
|
|
542
|
+
|
|
541
543
|
|
|
542
544
|
register_analysis(SPropagatorAnalysis, "SPropagator")
|
angr/analyses/smc.py
CHANGED
|
@@ -112,9 +112,8 @@ class SelfModifyingCodeAnalysis(Analysis):
|
|
|
112
112
|
if subject is None:
|
|
113
113
|
subject = self.project.entry
|
|
114
114
|
if isinstance(subject, str):
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
except KeyError:
|
|
115
|
+
addr = self.project.kb.labels.lookup(subject)
|
|
116
|
+
if addr is None:
|
|
118
117
|
addr = self.project.kb.functions[subject].addr
|
|
119
118
|
elif isinstance(subject, Function):
|
|
120
119
|
addr = subject.addr
|
|
@@ -171,6 +171,45 @@ class SimEngineVRAIL(
|
|
|
171
171
|
if isinstance(funcaddr_typevar, typevars.TypeVariable):
|
|
172
172
|
load_typevar = self._create_access_typevar(funcaddr_typevar, False, self.arch.bytes, 0)
|
|
173
173
|
self.state.add_type_constraint(typevars.Subtype(funcaddr_typevar, load_typevar))
|
|
174
|
+
elif isinstance(target, str):
|
|
175
|
+
# special handling for some intrinsics
|
|
176
|
+
match target:
|
|
177
|
+
case (
|
|
178
|
+
"InterlockedExchange8"
|
|
179
|
+
| "InterlockedExchange16"
|
|
180
|
+
| "InterlockedExchange"
|
|
181
|
+
| "InterlockedExchange64"
|
|
182
|
+
| "InterlockedCompareExchange16"
|
|
183
|
+
| "InterlockedCompareExchange"
|
|
184
|
+
| "InterlockedCompareExchange64"
|
|
185
|
+
| "InterlockedCompareExchange128"
|
|
186
|
+
| "InterlockedExchangeAdd"
|
|
187
|
+
| "InterlockedExchangeAdd64"
|
|
188
|
+
):
|
|
189
|
+
arg_tv = (
|
|
190
|
+
args[0].typevar
|
|
191
|
+
if args[0].typevar is not None
|
|
192
|
+
else args[1].typevar if args[1].typevar is not None else None
|
|
193
|
+
)
|
|
194
|
+
if arg_tv is not None:
|
|
195
|
+
ret_ty = self._create_access_typevar(
|
|
196
|
+
arg_tv, False, args[0].data.size() // self.arch.byte_width, 0
|
|
197
|
+
)
|
|
198
|
+
return RichR(self.state.top(ret_expr_bits), typevar=ret_ty)
|
|
199
|
+
case (
|
|
200
|
+
"InterlockedIncrement"
|
|
201
|
+
| "InterlockedIncrement16"
|
|
202
|
+
| "InterlockedIncrement64"
|
|
203
|
+
| "InterlockedDecrement"
|
|
204
|
+
| "InterlockedDecrement16"
|
|
205
|
+
| "InterlockedDecrement64"
|
|
206
|
+
):
|
|
207
|
+
arg_tv = args[0].typevar if args[0].typevar is not None else None
|
|
208
|
+
if arg_tv is not None:
|
|
209
|
+
ret_ty = self._create_access_typevar(
|
|
210
|
+
arg_tv, False, args[0].data.size() // self.arch.byte_width, 0
|
|
211
|
+
)
|
|
212
|
+
return RichR(self.state.top(ret_expr_bits), typevar=ret_ty)
|
|
174
213
|
|
|
175
214
|
# discover the prototype
|
|
176
215
|
prototype: SimTypeFunction | None = None
|
angr/emulator.py
CHANGED
|
@@ -95,7 +95,8 @@ class Emulator:
|
|
|
95
95
|
num_inst_executed: int = 0
|
|
96
96
|
while self._state.history.jumpkind != "Ijk_Exit":
|
|
97
97
|
# Check if there is a breakpoint at the current address
|
|
98
|
-
|
|
98
|
+
addr_with_lower_bit_cleared = self._state.addr & ~1
|
|
99
|
+
if completed_engine_execs > 0 and addr_with_lower_bit_cleared in self._engine.get_breakpoints():
|
|
99
100
|
return EmulatorStopReason.BREAKPOINT
|
|
100
101
|
|
|
101
102
|
# Check if we've already executed the requested number of instructions
|
angr/engines/hook.py
CHANGED
|
@@ -54,7 +54,7 @@ class HooksMixin(SuccessorsEngine, ProcedureMixin):
|
|
|
54
54
|
if procedure is None:
|
|
55
55
|
procedure = self._lookup_hook(state, procedure)
|
|
56
56
|
if procedure is None:
|
|
57
|
-
return super().process_successors(successors,
|
|
57
|
+
return super().process_successors(successors, **kwargs)
|
|
58
58
|
|
|
59
59
|
if isinstance(procedure.addr, SootAddressDescriptor):
|
|
60
60
|
l.debug("Running %s (originally at %r)", repr(procedure), procedure.addr)
|