angr 9.2.171__cp310-abi3-win_amd64.whl → 9.2.173__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/calling_convention/fact_collector.py +6 -1
- angr/analyses/cfg/cfg_fast.py +12 -10
- angr/analyses/cfg/indirect_jump_resolvers/jumptable.py +132 -9
- angr/analyses/decompiler/block_simplifier.py +23 -5
- angr/analyses/decompiler/clinic.py +2 -3
- angr/analyses/decompiler/decompiler.py +7 -1
- angr/analyses/decompiler/optimization_passes/__init__.py +2 -0
- angr/analyses/decompiler/optimization_passes/peephole_simplifier.py +75 -0
- angr/analyses/decompiler/peephole_optimizations/__init__.py +2 -0
- angr/analyses/decompiler/peephole_optimizations/cas_intrinsics.py +15 -5
- angr/analyses/decompiler/peephole_optimizations/inlined_wstrcpy.py +162 -84
- angr/analyses/decompiler/peephole_optimizations/inlined_wstrcpy_consolidation.py +113 -0
- 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/presets/preset.py +2 -2
- angr/analyses/decompiler/structured_codegen/c.py +57 -41
- angr/analyses/s_reaching_definitions/s_rda_view.py +3 -0
- angr/knowledge_plugins/cfg/indirect_jump.py +74 -8
- angr/rustylib.pyd +0 -0
- angr/unicornlib.dll +0 -0
- {angr-9.2.171.dist-info → angr-9.2.173.dist-info}/METADATA +5 -5
- {angr-9.2.171.dist-info → angr-9.2.173.dist-info}/RECORD +28 -26
- {angr-9.2.171.dist-info → angr-9.2.173.dist-info}/WHEEL +0 -0
- {angr-9.2.171.dist-info → angr-9.2.173.dist-info}/entry_points.txt +0 -0
- {angr-9.2.171.dist-info → angr-9.2.173.dist-info}/licenses/LICENSE +0 -0
- {angr-9.2.171.dist-info → angr-9.2.173.dist-info}/top_level.txt +0 -0
|
@@ -4,15 +4,17 @@ import string
|
|
|
4
4
|
|
|
5
5
|
from archinfo import Endness
|
|
6
6
|
|
|
7
|
+
from angr.ailment import BinaryOp
|
|
7
8
|
from angr.ailment.expression import Const, StackBaseOffset, VirtualVariable
|
|
8
|
-
from angr.ailment.statement import Call, Assignment
|
|
9
|
+
from angr.ailment.statement import Call, Assignment, Statement, Store
|
|
9
10
|
|
|
11
|
+
from angr.sim_type import SimTypePointer, SimTypeWideChar
|
|
10
12
|
from angr.utils.endness import ail_const_to_be
|
|
11
13
|
from .base import PeepholeOptimizationStmtBase
|
|
12
14
|
|
|
13
15
|
|
|
14
|
-
ASCII_PRINTABLES =
|
|
15
|
-
ASCII_DIGITS =
|
|
16
|
+
ASCII_PRINTABLES = {ord(x) for x in string.printable}
|
|
17
|
+
ASCII_DIGITS = {ord(x) for x in string.digits}
|
|
16
18
|
|
|
17
19
|
|
|
18
20
|
class InlinedWstrcpy(PeepholeOptimizationStmtBase):
|
|
@@ -23,70 +25,83 @@ class InlinedWstrcpy(PeepholeOptimizationStmtBase):
|
|
|
23
25
|
__slots__ = ()
|
|
24
26
|
|
|
25
27
|
NAME = "Simplifying inlined wstrcpy"
|
|
26
|
-
stmt_classes = (Assignment,)
|
|
28
|
+
stmt_classes = (Assignment, Store)
|
|
27
29
|
|
|
28
|
-
def optimize(self, stmt: Assignment, stmt_idx: int | None = None, block=None, **kwargs):
|
|
30
|
+
def optimize(self, stmt: Assignment | Store, stmt_idx: int | None = None, block=None, **kwargs):
|
|
29
31
|
if (
|
|
30
|
-
isinstance(stmt
|
|
32
|
+
isinstance(stmt, Assignment)
|
|
33
|
+
and isinstance(stmt.dst, VirtualVariable)
|
|
31
34
|
and stmt.dst.was_stack
|
|
32
35
|
and isinstance(stmt.src, Const)
|
|
33
36
|
and isinstance(stmt.src.value, int)
|
|
34
37
|
):
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
38
|
+
dst = StackBaseOffset(None, self.project.arch.bits, stmt.dst.stack_offset)
|
|
39
|
+
value_size = stmt.src.size
|
|
40
|
+
value = stmt.src.value
|
|
41
|
+
elif isinstance(stmt, Store) and isinstance(stmt.data, Const) and isinstance(stmt.data.value, int):
|
|
42
|
+
dst = stmt.addr
|
|
43
|
+
value_size = stmt.data.size
|
|
44
|
+
value = stmt.data.value
|
|
45
|
+
else:
|
|
46
|
+
return None
|
|
47
|
+
|
|
48
|
+
r, s = self.is_integer_likely_a_wide_string(value, value_size, self.project.arch.memory_endness)
|
|
49
|
+
if r:
|
|
50
|
+
# replace it with a call to strncpy
|
|
51
|
+
str_id = self.kb.custom_strings.allocate(s)
|
|
52
|
+
wstr_type = SimTypePointer(SimTypeWideChar()).with_arch(self.project.arch)
|
|
53
|
+
return Call(
|
|
54
|
+
stmt.idx,
|
|
55
|
+
"wstrncpy",
|
|
56
|
+
args=[
|
|
57
|
+
dst,
|
|
58
|
+
Const(None, None, str_id, self.project.arch.bits, custom_string=True, type=wstr_type),
|
|
59
|
+
Const(None, None, len(s) // 2, self.project.arch.bits),
|
|
60
|
+
],
|
|
61
|
+
**stmt.tags,
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
# scan forward in the current block to find all consecutive constant stores
|
|
65
|
+
if block is not None and stmt_idx is not None:
|
|
66
|
+
all_constant_stores: dict[int, tuple[int, Const | None]] = self.collect_constant_stores(block, stmt_idx)
|
|
67
|
+
if all_constant_stores:
|
|
68
|
+
offsets = sorted(all_constant_stores.keys())
|
|
69
|
+
next_offset = min(offsets)
|
|
70
|
+
stride = []
|
|
71
|
+
for offset in offsets:
|
|
72
|
+
if next_offset is not None and offset != next_offset:
|
|
73
|
+
next_offset = None
|
|
74
|
+
stride = []
|
|
75
|
+
stmt_idx_, v = all_constant_stores[offset]
|
|
76
|
+
if v is not None:
|
|
77
|
+
stride.append((offset, stmt_idx_, v))
|
|
78
|
+
next_offset = offset + v.size
|
|
79
|
+
else:
|
|
80
|
+
next_offset = None
|
|
81
|
+
stride = []
|
|
82
|
+
|
|
83
|
+
integer, size = self.stride_to_int(stride)
|
|
84
|
+
r, s = self.is_integer_likely_a_wide_string(integer, size, Endness.BE, min_length=3)
|
|
85
|
+
if r:
|
|
86
|
+
# we remove all involved statements whose statement IDs are greater than the current one
|
|
87
|
+
for _, stmt_idx_, _ in reversed(stride):
|
|
88
|
+
if stmt_idx_ <= stmt_idx:
|
|
89
|
+
continue
|
|
90
|
+
block.statements[stmt_idx_] = None
|
|
91
|
+
block.statements = [ss for ss in block.statements if ss is not None]
|
|
92
|
+
|
|
93
|
+
str_id = self.kb.custom_strings.allocate(s)
|
|
94
|
+
wstr_type = SimTypePointer(SimTypeWideChar()).with_arch(self.project.arch)
|
|
95
|
+
return Call(
|
|
96
|
+
stmt.idx,
|
|
97
|
+
"wstrncpy",
|
|
98
|
+
args=[
|
|
99
|
+
dst,
|
|
100
|
+
Const(None, None, str_id, self.project.arch.bits, custom_string=True, type=wstr_type),
|
|
101
|
+
Const(None, None, len(s) // 2, self.project.arch.bits),
|
|
102
|
+
],
|
|
103
|
+
**stmt.tags,
|
|
104
|
+
)
|
|
90
105
|
|
|
91
106
|
return None
|
|
92
107
|
|
|
@@ -103,68 +118,131 @@ class InlinedWstrcpy(PeepholeOptimizationStmtBase):
|
|
|
103
118
|
|
|
104
119
|
def collect_constant_stores(self, block, starting_stmt_idx: int) -> dict[int, tuple[int, Const | None]]:
|
|
105
120
|
r = {}
|
|
121
|
+
expected_store_varid: int | None = None
|
|
122
|
+
starting_stmt = block.statements[starting_stmt_idx]
|
|
123
|
+
if (
|
|
124
|
+
isinstance(starting_stmt, Assignment)
|
|
125
|
+
and isinstance(starting_stmt.dst, VirtualVariable)
|
|
126
|
+
and starting_stmt.dst.was_stack
|
|
127
|
+
and isinstance(starting_stmt.dst.stack_offset, int)
|
|
128
|
+
):
|
|
129
|
+
expected_type = "stack"
|
|
130
|
+
elif isinstance(starting_stmt, Store):
|
|
131
|
+
if isinstance(starting_stmt.addr, VirtualVariable):
|
|
132
|
+
expected_store_varid = starting_stmt.addr.varid
|
|
133
|
+
elif (
|
|
134
|
+
isinstance(starting_stmt.addr, BinaryOp)
|
|
135
|
+
and starting_stmt.addr.op == "Add"
|
|
136
|
+
and isinstance(starting_stmt.addr.operands[0], VirtualVariable)
|
|
137
|
+
and isinstance(starting_stmt.addr.operands[1], Const)
|
|
138
|
+
):
|
|
139
|
+
expected_store_varid = starting_stmt.addr.operands[0].varid
|
|
140
|
+
else:
|
|
141
|
+
expected_store_varid = None
|
|
142
|
+
expected_type = "store"
|
|
143
|
+
else:
|
|
144
|
+
return r
|
|
145
|
+
|
|
106
146
|
for idx, stmt in enumerate(block.statements):
|
|
107
147
|
if idx < starting_stmt_idx:
|
|
108
148
|
continue
|
|
109
149
|
if (
|
|
110
|
-
|
|
150
|
+
expected_type == "stack"
|
|
151
|
+
and isinstance(stmt, Assignment)
|
|
111
152
|
and isinstance(stmt.dst, VirtualVariable)
|
|
112
153
|
and stmt.dst.was_stack
|
|
113
154
|
and isinstance(stmt.dst.stack_offset, int)
|
|
114
155
|
):
|
|
115
|
-
|
|
116
|
-
|
|
156
|
+
offset = stmt.dst.stack_offset
|
|
157
|
+
value = (
|
|
158
|
+
ail_const_to_be(stmt.src, self.project.arch.memory_endness) if isinstance(stmt.src, Const) else None
|
|
159
|
+
)
|
|
160
|
+
elif expected_type == "store" and isinstance(stmt, Store):
|
|
161
|
+
if isinstance(stmt.addr, VirtualVariable) and stmt.addr.varid == expected_store_varid:
|
|
162
|
+
offset = 0
|
|
163
|
+
elif (
|
|
164
|
+
isinstance(stmt.addr, BinaryOp)
|
|
165
|
+
and stmt.addr.op == "Add"
|
|
166
|
+
and isinstance(stmt.addr.operands[0], VirtualVariable)
|
|
167
|
+
and isinstance(stmt.addr.operands[1], Const)
|
|
168
|
+
and stmt.addr.operands[0].varid == expected_store_varid
|
|
169
|
+
):
|
|
170
|
+
offset = stmt.addr.operands[1].value
|
|
117
171
|
else:
|
|
118
|
-
|
|
172
|
+
offset = None
|
|
173
|
+
value = (
|
|
174
|
+
ail_const_to_be(stmt.data, self.project.arch.memory_endness)
|
|
175
|
+
if isinstance(stmt.data, Const)
|
|
176
|
+
else None
|
|
177
|
+
)
|
|
178
|
+
else:
|
|
179
|
+
continue
|
|
180
|
+
|
|
181
|
+
if offset is not None:
|
|
182
|
+
r[offset] = idx, value
|
|
119
183
|
|
|
120
184
|
return r
|
|
121
185
|
|
|
122
186
|
@staticmethod
|
|
123
|
-
def even_offsets_are_zero(lst: list[
|
|
124
|
-
|
|
187
|
+
def even_offsets_are_zero(lst: list[int]) -> bool:
|
|
188
|
+
if len(lst) >= 2 and lst[-1] == 0 and lst[-2] == 0:
|
|
189
|
+
lst = lst[:-2]
|
|
190
|
+
return all((ch == 0 if i % 2 == 0 else ch != 0) for i, ch in enumerate(lst))
|
|
125
191
|
|
|
126
192
|
@staticmethod
|
|
127
|
-
def odd_offsets_are_zero(lst: list[
|
|
128
|
-
|
|
193
|
+
def odd_offsets_are_zero(lst: list[int]) -> bool:
|
|
194
|
+
if len(lst) >= 2 and lst[-1] == 0 and lst[-2] == 0:
|
|
195
|
+
lst = lst[:-2]
|
|
196
|
+
return all((ch == 0 if i % 2 == 1 else ch != 0) for i, ch in enumerate(lst))
|
|
129
197
|
|
|
130
198
|
@staticmethod
|
|
131
199
|
def is_integer_likely_a_wide_string(
|
|
132
200
|
v: int, size: int, endness: Endness, min_length: int = 4
|
|
133
|
-
) -> tuple[bool,
|
|
201
|
+
) -> tuple[bool, bytes | None]:
|
|
134
202
|
# we need at least four bytes of printable characters
|
|
135
203
|
|
|
136
|
-
chars = []
|
|
204
|
+
chars: list[int] = []
|
|
137
205
|
if endness == Endness.LE:
|
|
138
206
|
while v != 0:
|
|
139
207
|
byt = v & 0xFF
|
|
140
|
-
if byt != 0 and
|
|
208
|
+
if byt != 0 and byt not in ASCII_PRINTABLES:
|
|
141
209
|
return False, None
|
|
142
|
-
chars.append(
|
|
210
|
+
chars.append(byt)
|
|
143
211
|
v >>= 8
|
|
212
|
+
if len(chars) % 2 == 1:
|
|
213
|
+
chars.append(0)
|
|
144
214
|
|
|
145
215
|
elif endness == Endness.BE:
|
|
146
216
|
for _ in range(size):
|
|
147
217
|
byt = v & 0xFF
|
|
148
218
|
v >>= 8
|
|
149
|
-
if byt != 0 and
|
|
219
|
+
if byt != 0 and byt not in ASCII_PRINTABLES:
|
|
150
220
|
return False, None
|
|
151
|
-
chars.append(
|
|
221
|
+
chars.append(byt)
|
|
152
222
|
chars.reverse()
|
|
153
223
|
else:
|
|
154
224
|
# unsupported endness
|
|
155
225
|
return False, None
|
|
156
226
|
|
|
157
|
-
if InlinedWstrcpy.even_offsets_are_zero(chars):
|
|
158
|
-
chars = [ch for i, ch in enumerate(chars) if i % 2 == 1]
|
|
159
|
-
elif InlinedWstrcpy.odd_offsets_are_zero(chars):
|
|
160
|
-
chars = [ch for i, ch in enumerate(chars) if i % 2 == 0]
|
|
161
|
-
else:
|
|
227
|
+
if not (InlinedWstrcpy.even_offsets_are_zero(chars) or InlinedWstrcpy.odd_offsets_are_zero(chars)):
|
|
162
228
|
return False, None
|
|
163
229
|
|
|
164
|
-
if chars and chars[-1] ==
|
|
230
|
+
if chars and len(chars) >= 2 and chars[-1] == 0 and chars[-2] == 0:
|
|
165
231
|
chars = chars[:-1]
|
|
166
|
-
if len(chars) >= min_length and all(ch in ASCII_PRINTABLES for ch in chars):
|
|
167
|
-
if len(chars) <= 4 and all(ch in ASCII_DIGITS for ch in chars):
|
|
232
|
+
if len(chars) >= min_length * 2 and all((ch == 0 or ch in ASCII_PRINTABLES) for ch in chars):
|
|
233
|
+
if len(chars) <= 4 * 2 and all((ch == 0 or ch in ASCII_DIGITS) for ch in chars):
|
|
168
234
|
return False, None
|
|
169
|
-
return True,
|
|
235
|
+
return True, bytes(chars)
|
|
170
236
|
return False, None
|
|
237
|
+
|
|
238
|
+
@staticmethod
|
|
239
|
+
def is_inlined_wstrncpy(stmt: Statement) -> bool:
|
|
240
|
+
return (
|
|
241
|
+
isinstance(stmt, Call)
|
|
242
|
+
and isinstance(stmt.target, str)
|
|
243
|
+
and stmt.target == "wstrncpy"
|
|
244
|
+
and stmt.args is not None
|
|
245
|
+
and len(stmt.args) == 3
|
|
246
|
+
and isinstance(stmt.args[1], Const)
|
|
247
|
+
and hasattr(stmt.args[1], "custom_string")
|
|
248
|
+
)
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# pylint:disable=arguments-differ
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from angr.ailment.expression import Expression, BinaryOp, Const, Register, StackBaseOffset, UnaryOp, VirtualVariable
|
|
5
|
+
from angr.ailment.statement import Call, Store
|
|
6
|
+
|
|
7
|
+
from angr.sim_type import SimTypePointer, SimTypeWideChar
|
|
8
|
+
from .base import PeepholeOptimizationMultiStmtBase
|
|
9
|
+
from .inlined_wstrcpy import InlinedWstrcpy
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class InlinedWstrcpyConsolidation(PeepholeOptimizationMultiStmtBase):
|
|
13
|
+
"""
|
|
14
|
+
Consolidate multiple inlined wstrcpy/wstrncpy calls.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
__slots__ = ()
|
|
18
|
+
|
|
19
|
+
NAME = "Consolidate multiple inlined wstrncpy calls"
|
|
20
|
+
stmt_classes = ((Call, Call), (Call, Store))
|
|
21
|
+
|
|
22
|
+
def optimize( # type:ignore
|
|
23
|
+
self, stmts: list[Call], stmt_idx: int | None = None, block=None, **kwargs
|
|
24
|
+
): # pylint:disable=unused-argument
|
|
25
|
+
last_stmt, stmt = stmts
|
|
26
|
+
if InlinedWstrcpy.is_inlined_wstrncpy(last_stmt):
|
|
27
|
+
assert last_stmt.args is not None
|
|
28
|
+
assert self.kb is not None
|
|
29
|
+
s_last: bytes = self.kb.custom_strings[last_stmt.args[1].value]
|
|
30
|
+
addr_last = last_stmt.args[0]
|
|
31
|
+
new_str = None # will be set if consolidation should happen
|
|
32
|
+
|
|
33
|
+
if isinstance(stmt, Call) and InlinedWstrcpy.is_inlined_wstrncpy(stmt):
|
|
34
|
+
assert stmt.args is not None
|
|
35
|
+
# consolidating two calls
|
|
36
|
+
s_curr: bytes = self.kb.custom_strings[stmt.args[1].value]
|
|
37
|
+
addr_curr = stmt.args[0]
|
|
38
|
+
# determine if the two addresses are consecutive
|
|
39
|
+
delta = self._get_delta(addr_last, addr_curr)
|
|
40
|
+
if delta is not None and delta == len(s_last):
|
|
41
|
+
# consolidate both calls!
|
|
42
|
+
new_str = s_last + s_curr
|
|
43
|
+
elif isinstance(stmt, Store) and isinstance(stmt.data, Const) and isinstance(stmt.data.value, int):
|
|
44
|
+
# consolidating a call and a store, in case the store statement is storing the suffix of a string (but
|
|
45
|
+
# the suffix is too short to qualify an inlined strcpy optimization)
|
|
46
|
+
addr_curr = stmt.addr
|
|
47
|
+
delta = self._get_delta(addr_last, addr_curr)
|
|
48
|
+
if delta is not None and delta == len(s_last):
|
|
49
|
+
if stmt.size == 2 and stmt.data.value == 0:
|
|
50
|
+
# it's probably the terminating null byte
|
|
51
|
+
r, s = True, b"\x00\x00"
|
|
52
|
+
else:
|
|
53
|
+
r, s = InlinedWstrcpy.is_integer_likely_a_wide_string(
|
|
54
|
+
stmt.data.value, stmt.size, stmt.endness, min_length=1 # type:ignore
|
|
55
|
+
)
|
|
56
|
+
if r and s is not None:
|
|
57
|
+
new_str = s_last + s
|
|
58
|
+
|
|
59
|
+
if new_str is not None:
|
|
60
|
+
assert self.project is not None
|
|
61
|
+
wstr_type = SimTypePointer(SimTypeWideChar()).with_arch(self.project.arch)
|
|
62
|
+
if new_str.endswith(b"\x00\x00"):
|
|
63
|
+
call_name = "wstrcpy"
|
|
64
|
+
new_str_idx = self.kb.custom_strings.allocate(new_str[:-2])
|
|
65
|
+
args = [
|
|
66
|
+
last_stmt.args[0],
|
|
67
|
+
Const(None, None, new_str_idx, last_stmt.args[0].bits, custom_string=True, type=wstr_type),
|
|
68
|
+
]
|
|
69
|
+
prototype = None
|
|
70
|
+
else:
|
|
71
|
+
call_name = "wstrncpy"
|
|
72
|
+
new_str_idx = self.kb.custom_strings.allocate(new_str)
|
|
73
|
+
args = [
|
|
74
|
+
last_stmt.args[0],
|
|
75
|
+
Const(None, None, new_str_idx, last_stmt.args[0].bits, custom_string=True, type=wstr_type),
|
|
76
|
+
Const(None, None, len(new_str) // 2, self.project.arch.bits),
|
|
77
|
+
]
|
|
78
|
+
prototype = None
|
|
79
|
+
|
|
80
|
+
return [Call(stmt.idx, call_name, args=args, prototype=prototype, **stmt.tags)]
|
|
81
|
+
|
|
82
|
+
return None
|
|
83
|
+
|
|
84
|
+
@staticmethod
|
|
85
|
+
def _parse_addr(addr: Expression) -> tuple[Expression, int]:
|
|
86
|
+
if isinstance(addr, Register):
|
|
87
|
+
return addr, 0
|
|
88
|
+
if isinstance(addr, StackBaseOffset):
|
|
89
|
+
return StackBaseOffset(None, addr.bits, 0), addr.offset
|
|
90
|
+
if (
|
|
91
|
+
isinstance(addr, UnaryOp)
|
|
92
|
+
and addr.op == "Reference"
|
|
93
|
+
and isinstance(addr.operand, VirtualVariable)
|
|
94
|
+
and addr.operand.was_stack
|
|
95
|
+
):
|
|
96
|
+
return StackBaseOffset(None, addr.bits, 0), addr.operand.stack_offset
|
|
97
|
+
if isinstance(addr, BinaryOp):
|
|
98
|
+
if addr.op == "Add" and isinstance(addr.operands[1], Const) and isinstance(addr.operands[1].value, int):
|
|
99
|
+
base_0, offset_0 = InlinedWstrcpyConsolidation._parse_addr(addr.operands[0])
|
|
100
|
+
return base_0, offset_0 + addr.operands[1].value
|
|
101
|
+
if addr.op == "Sub" and isinstance(addr.operands[1], Const) and isinstance(addr.operands[1].value, int):
|
|
102
|
+
base_0, offset_0 = InlinedWstrcpyConsolidation._parse_addr(addr.operands[0])
|
|
103
|
+
return base_0, offset_0 - addr.operands[1].value
|
|
104
|
+
|
|
105
|
+
return addr, 0
|
|
106
|
+
|
|
107
|
+
@staticmethod
|
|
108
|
+
def _get_delta(addr_0: Expression, addr_1: Expression) -> int | None:
|
|
109
|
+
base_0, offset_0 = InlinedWstrcpyConsolidation._parse_addr(addr_0)
|
|
110
|
+
base_1, offset_1 = InlinedWstrcpyConsolidation._parse_addr(addr_1)
|
|
111
|
+
if base_0.likes(base_1):
|
|
112
|
+
return offset_1 - offset_0
|
|
113
|
+
return None
|
|
@@ -10,6 +10,7 @@ from angr.analyses.decompiler.optimization_passes import (
|
|
|
10
10
|
X86GccGetPcSimplifier,
|
|
11
11
|
CallStatementRewriter,
|
|
12
12
|
SwitchReusedEntryRewriter,
|
|
13
|
+
PostStructuringPeepholeOptimizationPass,
|
|
13
14
|
)
|
|
14
15
|
|
|
15
16
|
|
|
@@ -25,6 +26,7 @@ preset_basic = DecompilationPreset(
|
|
|
25
26
|
X86GccGetPcSimplifier,
|
|
26
27
|
CallStatementRewriter,
|
|
27
28
|
SwitchReusedEntryRewriter,
|
|
29
|
+
PostStructuringPeepholeOptimizationPass,
|
|
28
30
|
],
|
|
29
31
|
)
|
|
30
32
|
|
|
@@ -23,6 +23,7 @@ from angr.analyses.decompiler.optimization_passes import (
|
|
|
23
23
|
SwitchReusedEntryRewriter,
|
|
24
24
|
ConditionConstantPropagation,
|
|
25
25
|
DetermineLoadSizes,
|
|
26
|
+
PostStructuringPeepholeOptimizationPass,
|
|
26
27
|
)
|
|
27
28
|
|
|
28
29
|
|
|
@@ -51,6 +52,7 @@ preset_fast = DecompilationPreset(
|
|
|
51
52
|
CallStatementRewriter,
|
|
52
53
|
ConditionConstantPropagation,
|
|
53
54
|
DetermineLoadSizes,
|
|
55
|
+
PostStructuringPeepholeOptimizationPass,
|
|
54
56
|
],
|
|
55
57
|
)
|
|
56
58
|
|
|
@@ -28,6 +28,7 @@ from angr.analyses.decompiler.optimization_passes import (
|
|
|
28
28
|
SwitchReusedEntryRewriter,
|
|
29
29
|
ConditionConstantPropagation,
|
|
30
30
|
DetermineLoadSizes,
|
|
31
|
+
PostStructuringPeepholeOptimizationPass,
|
|
31
32
|
)
|
|
32
33
|
|
|
33
34
|
|
|
@@ -61,6 +62,7 @@ preset_full = DecompilationPreset(
|
|
|
61
62
|
SwitchReusedEntryRewriter,
|
|
62
63
|
ConditionConstantPropagation,
|
|
63
64
|
DetermineLoadSizes,
|
|
65
|
+
PostStructuringPeepholeOptimizationPass,
|
|
64
66
|
],
|
|
65
67
|
)
|
|
66
68
|
|
|
@@ -2,7 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
from archinfo import Arch
|
|
4
4
|
|
|
5
|
-
from angr.analyses.decompiler.optimization_passes.optimization_pass import
|
|
5
|
+
from angr.analyses.decompiler.optimization_passes.optimization_pass import BaseOptimizationPass
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
class DecompilationPreset:
|
|
@@ -10,7 +10,7 @@ class DecompilationPreset:
|
|
|
10
10
|
A DecompilationPreset provides a preconfigured set of optimizations and configurations for the Decompiler analysis.
|
|
11
11
|
"""
|
|
12
12
|
|
|
13
|
-
def __init__(self, name: str, opt_passes: list[type[
|
|
13
|
+
def __init__(self, name: str, opt_passes: list[type[BaseOptimizationPass]]):
|
|
14
14
|
self.name = name
|
|
15
15
|
self.opt_passes = opt_passes
|
|
16
16
|
|
|
@@ -2230,52 +2230,68 @@ class CConstant(CExpression):
|
|
|
2230
2230
|
return f'{prefix}"{base_str}"'
|
|
2231
2231
|
|
|
2232
2232
|
def c_repr_chunks(self, indent=0, asexpr=False):
|
|
2233
|
+
|
|
2234
|
+
def _default_output(v) -> str | None:
|
|
2235
|
+
if isinstance(v, MemoryData) and v.sort == MemoryDataSort.String:
|
|
2236
|
+
return CConstant.str_to_c_str(v.content.decode("utf-8"))
|
|
2237
|
+
if isinstance(v, Function):
|
|
2238
|
+
return get_cpp_function_name(v.demangled_name)
|
|
2239
|
+
if isinstance(v, str):
|
|
2240
|
+
return CConstant.str_to_c_str(v)
|
|
2241
|
+
if isinstance(v, bytes):
|
|
2242
|
+
return CConstant.str_to_c_str(v.replace(b"\x00", b"").decode("utf-8"))
|
|
2243
|
+
return None
|
|
2244
|
+
|
|
2233
2245
|
if self.collapsed:
|
|
2234
2246
|
yield "...", self
|
|
2235
2247
|
return
|
|
2236
2248
|
|
|
2237
|
-
# default priority: string references -> variables -> other reference values
|
|
2238
2249
|
if self.reference_values is not None:
|
|
2239
|
-
|
|
2240
|
-
if isinstance(
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
yield
|
|
2250
|
+
if self._type is not None and self._type in self.reference_values:
|
|
2251
|
+
if isinstance(self._type, SimTypeInt):
|
|
2252
|
+
if isinstance(self.reference_values[self._type], int):
|
|
2253
|
+
yield self.fmt_int(self.reference_values[self._type]), self
|
|
2254
|
+
return
|
|
2255
|
+
yield hex(self.reference_values[self._type]), self
|
|
2245
2256
|
return
|
|
2246
|
-
elif isinstance(
|
|
2257
|
+
elif isinstance(self._type, SimTypePointer) and isinstance(self._type.pts_to, SimTypeChar):
|
|
2258
|
+
refval = self.reference_values[self._type]
|
|
2259
|
+
if isinstance(refval, MemoryData):
|
|
2260
|
+
v = refval.content.decode("utf-8")
|
|
2261
|
+
elif isinstance(refval, bytes):
|
|
2262
|
+
v = refval.decode("latin1")
|
|
2263
|
+
else:
|
|
2264
|
+
# it must be a string
|
|
2265
|
+
v = refval
|
|
2266
|
+
assert isinstance(v, str)
|
|
2247
2267
|
yield CConstant.str_to_c_str(v), self
|
|
2248
2268
|
return
|
|
2249
|
-
elif isinstance(
|
|
2250
|
-
|
|
2269
|
+
elif isinstance(self._type, SimTypePointer) and isinstance(self._type.pts_to, SimTypeWideChar):
|
|
2270
|
+
refval = self.reference_values[self._type]
|
|
2271
|
+
v = (
|
|
2272
|
+
refval.content.decode("utf_16_le")
|
|
2273
|
+
if isinstance(refval, MemoryData)
|
|
2274
|
+
else refval.decode("utf_16_le")
|
|
2275
|
+
) # it's a string
|
|
2276
|
+
yield CConstant.str_to_c_str(v, prefix="L"), self
|
|
2251
2277
|
return
|
|
2252
|
-
|
|
2253
|
-
if self.reference_values is not None and self._type is not None and self._type in self.reference_values:
|
|
2254
|
-
if isinstance(self._type, SimTypeInt):
|
|
2255
|
-
if isinstance(self.reference_values[self._type], int):
|
|
2256
|
-
yield self.fmt_int(self.reference_values[self._type]), self
|
|
2257
|
-
return
|
|
2258
|
-
yield hex(self.reference_values[self._type]), self
|
|
2259
|
-
elif isinstance(self._type, SimTypePointer) and isinstance(self._type.pts_to, SimTypeChar):
|
|
2260
|
-
refval = self.reference_values[self._type]
|
|
2261
|
-
if isinstance(refval, MemoryData):
|
|
2262
|
-
v = refval.content.decode("utf-8")
|
|
2263
2278
|
else:
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2279
|
+
if isinstance(self.reference_values[self._type], int):
|
|
2280
|
+
yield self.fmt_int(self.reference_values[self._type]), self
|
|
2281
|
+
return
|
|
2282
|
+
o = _default_output(self.reference_values[self.type])
|
|
2283
|
+
if o is not None:
|
|
2284
|
+
yield o, self
|
|
2285
|
+
return
|
|
2286
|
+
|
|
2287
|
+
# default priority: string references -> variables -> other reference values
|
|
2288
|
+
for _ty, v in self.reference_values.items(): # pylint:disable=unused-variable
|
|
2289
|
+
o = _default_output(v)
|
|
2290
|
+
if o is not None:
|
|
2291
|
+
yield o, self
|
|
2275
2292
|
return
|
|
2276
|
-
yield self.reference_values[self.type], self
|
|
2277
2293
|
|
|
2278
|
-
|
|
2294
|
+
if isinstance(self.value, int) and self.value == 0 and isinstance(self.type, SimTypePointer):
|
|
2279
2295
|
# print NULL instead
|
|
2280
2296
|
yield "NULL", self
|
|
2281
2297
|
|
|
@@ -3622,10 +3638,10 @@ class CStructuredCodeGenerator(BaseStructuredCodeGenerator, Analysis):
|
|
|
3622
3638
|
if type_ is None and hasattr(expr, "type"):
|
|
3623
3639
|
type_ = expr.type
|
|
3624
3640
|
|
|
3625
|
-
if
|
|
3641
|
+
if reference_values is None and hasattr(expr, "reference_values"):
|
|
3626
3642
|
reference_values = expr.reference_values.copy()
|
|
3627
|
-
|
|
3628
|
-
|
|
3643
|
+
if type_ is None and reference_values is not None and len(reference_values) == 1: # type: ignore
|
|
3644
|
+
type_ = next(iter(reference_values)) # type: ignore
|
|
3629
3645
|
|
|
3630
3646
|
if reference_values is None:
|
|
3631
3647
|
reference_values = {}
|
|
@@ -3714,10 +3730,10 @@ class CStructuredCodeGenerator(BaseStructuredCodeGenerator, Analysis):
|
|
|
3714
3730
|
offset = getattr(expr, "reference_variable_offset", 0)
|
|
3715
3731
|
var_access = self._access_constant_offset_reference(self._get_variable_reference(cvar), offset, None)
|
|
3716
3732
|
|
|
3717
|
-
if var_access is not None
|
|
3718
|
-
|
|
3719
|
-
|
|
3720
|
-
|
|
3733
|
+
if var_access is not None:
|
|
3734
|
+
if expr.value >= self.min_data_addr:
|
|
3735
|
+
return var_access
|
|
3736
|
+
reference_values["offset"] = var_access
|
|
3721
3737
|
return CConstant(expr.value, type_, reference_values=reference_values, tags=expr.tags, codegen=self)
|
|
3722
3738
|
|
|
3723
3739
|
def _handle_Expr_UnaryOp(self, expr, **kwargs):
|
|
@@ -30,6 +30,9 @@ class RegVVarPredicate:
|
|
|
30
30
|
self.arch = arch
|
|
31
31
|
|
|
32
32
|
def _get_call_clobbered_regs(self, stmt: Call) -> set[int]:
|
|
33
|
+
if isinstance(stmt.target, str):
|
|
34
|
+
# pseudo calls do not clobber any registers
|
|
35
|
+
return set()
|
|
33
36
|
cc = stmt.calling_convention
|
|
34
37
|
if cc is None:
|
|
35
38
|
# get the default calling convention
|