angr 9.2.123__py3-none-win_amd64.whl → 9.2.125__py3-none-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/__init__.py +9 -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/codecave.py +77 -0
- 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 +58 -2
- angr/analyses/decompiler/condition_processor.py +10 -3
- angr/analyses/decompiler/decompilation_cache.py +2 -0
- angr/analyses/decompiler/decompiler.py +54 -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/__init__.py +3 -0
- angr/analyses/decompiler/optimization_passes/div_simplifier.py +4 -1
- angr/analyses/decompiler/optimization_passes/inlined_string_transformation_simplifier.py +13 -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/tag_slicer.py +41 -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/constant_derefs.py +2 -2
- 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/patchfinder.py +137 -0
- angr/analyses/pathfinder.py +282 -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/smc.py +159 -0
- angr/analyses/variable_recovery/engine_ail.py +14 -0
- angr/analyses/variable_recovery/engine_base.py +11 -0
- angr/angrdb/models.py +1 -2
- angr/engines/light/engine.py +12 -0
- 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/decompilation.py +45 -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/key_definitions/atoms.py +8 -0
- angr/lib/angr_native.dll +0 -0
- angr/misc/ux.py +2 -2
- angr/procedures/definitions/parse_win32json.py +2 -1
- angr/project.py +17 -1
- angr/state_plugins/history.py +6 -4
- 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/bits.py +4 -0
- angr/utils/constants.py +1 -1
- angr/utils/graph.py +15 -0
- angr/utils/tagged_interval_map.py +112 -0
- angr/vaults.py +2 -2
- {angr-9.2.123.dist-info → angr-9.2.125.dist-info}/METADATA +6 -6
- {angr-9.2.123.dist-info → angr-9.2.125.dist-info}/RECORD +104 -97
- {angr-9.2.123.dist-info → angr-9.2.125.dist-info}/WHEEL +1 -1
- {angr-9.2.123.dist-info → angr-9.2.125.dist-info}/LICENSE +0 -0
- {angr-9.2.123.dist-info → angr-9.2.125.dist-info}/entry_points.txt +0 -0
- {angr-9.2.123.dist-info → angr-9.2.125.dist-info}/top_level.txt +0 -0
|
@@ -7,8 +7,6 @@ import logging
|
|
|
7
7
|
import claripy
|
|
8
8
|
import cle
|
|
9
9
|
|
|
10
|
-
from .paged_memory_mixin import PagedMemoryMixin
|
|
11
|
-
|
|
12
10
|
l = logging.getLogger(__name__)
|
|
13
11
|
|
|
14
12
|
BackerType = Union[bytes, bytearray, list[int]]
|
|
@@ -29,6 +27,9 @@ class NotMemoryview:
|
|
|
29
27
|
memoryview(self.obj)[self.offset : self.offset + self.size][k] = v
|
|
30
28
|
|
|
31
29
|
|
|
30
|
+
from .paged_memory_mixin import PagedMemoryMixin
|
|
31
|
+
|
|
32
|
+
|
|
32
33
|
class ClemoryBackerMixin(PagedMemoryMixin):
|
|
33
34
|
def __init__(self, cle_memory_backer: None | cle.Loader | cle.Clemory = None, **kwargs):
|
|
34
35
|
super().__init__(**kwargs)
|
|
@@ -49,9 +50,9 @@ class ClemoryBackerMixin(PagedMemoryMixin):
|
|
|
49
50
|
o._cle_loader = self._cle_loader
|
|
50
51
|
return o
|
|
51
52
|
|
|
52
|
-
def _initialize_page(self, pageno, force_default=False, **kwargs):
|
|
53
|
+
def _initialize_page(self, pageno, permissions=None, *, force_default=False, **kwargs):
|
|
53
54
|
if self._clemory_backer is None or force_default:
|
|
54
|
-
return super()._initialize_page(pageno, **kwargs)
|
|
55
|
+
return super()._initialize_page(pageno, permissions=permissions, **kwargs)
|
|
55
56
|
|
|
56
57
|
addr = pageno * self.page_size
|
|
57
58
|
|
|
@@ -59,10 +60,10 @@ class ClemoryBackerMixin(PagedMemoryMixin):
|
|
|
59
60
|
backer_iter: BackerIterType = self._clemory_backer.backers(addr)
|
|
60
61
|
backer_start, backer = next(backer_iter)
|
|
61
62
|
except StopIteration:
|
|
62
|
-
return super()._initialize_page(pageno, **kwargs)
|
|
63
|
+
return super()._initialize_page(pageno, permissions=permissions, **kwargs)
|
|
63
64
|
|
|
64
65
|
if backer_start >= addr + self.page_size:
|
|
65
|
-
return super()._initialize_page(pageno, **kwargs)
|
|
66
|
+
return super()._initialize_page(pageno, permissions=permissions, **kwargs)
|
|
66
67
|
|
|
67
68
|
# Load data from backere
|
|
68
69
|
data = self._data_from_backer(addr, backer, backer_start, backer_iter)
|
|
@@ -184,9 +185,9 @@ class ClemoryBackerMixin(PagedMemoryMixin):
|
|
|
184
185
|
|
|
185
186
|
|
|
186
187
|
class ConcreteBackerMixin(ClemoryBackerMixin):
|
|
187
|
-
def _initialize_page(self, pageno, force_default=False, **kwargs):
|
|
188
|
+
def _initialize_page(self, pageno, permissions=None, *, force_default=False, **kwargs):
|
|
188
189
|
if self._clemory_backer is None or force_default:
|
|
189
|
-
return super()._initialize_page(pageno, **kwargs)
|
|
190
|
+
return super()._initialize_page(pageno, permissions=permissions, **kwargs)
|
|
190
191
|
|
|
191
192
|
addr = pageno * self.page_size
|
|
192
193
|
|
|
@@ -194,10 +195,10 @@ class ConcreteBackerMixin(ClemoryBackerMixin):
|
|
|
194
195
|
backer_iter = self._clemory_backer.backers(addr)
|
|
195
196
|
backer_start, backer = next(backer_iter)
|
|
196
197
|
except StopIteration:
|
|
197
|
-
return super()._initialize_page(pageno, **kwargs)
|
|
198
|
+
return super()._initialize_page(pageno, permissions=permissions, **kwargs)
|
|
198
199
|
|
|
199
200
|
if backer_start >= addr + self.page_size:
|
|
200
|
-
return super()._initialize_page(pageno, **kwargs)
|
|
201
|
+
return super()._initialize_page(pageno, permissions=permissions, **kwargs)
|
|
201
202
|
|
|
202
203
|
if self.state.project.concrete_target:
|
|
203
204
|
l.debug("Fetching data from concrete target")
|
|
@@ -207,7 +208,7 @@ class ConcreteBackerMixin(ClemoryBackerMixin):
|
|
|
207
208
|
)
|
|
208
209
|
else:
|
|
209
210
|
# the concrete backer only is here to support concrete loading, defer back to the CleMemoryBacker
|
|
210
|
-
return super()._initialize_page(pageno, **kwargs)
|
|
211
|
+
return super()._initialize_page(pageno, permissions=permissions, **kwargs)
|
|
211
212
|
|
|
212
213
|
permissions = self._cle_permissions_lookup(addr)
|
|
213
214
|
|
|
@@ -237,11 +238,11 @@ class DictBackerMixin(PagedMemoryMixin):
|
|
|
237
238
|
o._dict_memory_backer = self._dict_memory_backer
|
|
238
239
|
return o
|
|
239
240
|
|
|
240
|
-
def _initialize_page(self, pageno: int, force_default=False, **kwargs):
|
|
241
|
+
def _initialize_page(self, pageno: int, permissions=None, *, force_default=False, **kwargs):
|
|
241
242
|
page_addr = pageno * self.page_size
|
|
242
243
|
|
|
243
244
|
if self._dict_memory_backer is None or force_default:
|
|
244
|
-
return super()._initialize_page(pageno, **kwargs)
|
|
245
|
+
return super()._initialize_page(pageno, permissions=permissions, **kwargs)
|
|
245
246
|
|
|
246
247
|
new_page = None
|
|
247
248
|
|
|
@@ -261,6 +262,6 @@ class DictBackerMixin(PagedMemoryMixin):
|
|
|
261
262
|
)
|
|
262
263
|
|
|
263
264
|
if new_page is None:
|
|
264
|
-
return super()._initialize_page(pageno, **kwargs)
|
|
265
|
+
return super()._initialize_page(pageno, permissions=permissions, **kwargs)
|
|
265
266
|
|
|
266
267
|
return new_page
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
import cffi
|
|
3
|
-
from typing import Any
|
|
3
|
+
from typing import Any, Generic, Literal, overload
|
|
4
4
|
from collections.abc import Iterable
|
|
5
5
|
import logging
|
|
6
6
|
from collections import defaultdict
|
|
7
7
|
|
|
8
8
|
import claripy
|
|
9
9
|
|
|
10
|
+
from angr.state_plugins.sim_action_object import SimActionObject
|
|
10
11
|
from angr.storage.memory_mixins.memory_mixin import MemoryMixin
|
|
11
12
|
from angr.storage.memory_mixins.paged_memory.pages import PageType, ListPage, UltraPage, MVListPage
|
|
12
13
|
from angr.errors import SimMemoryError
|
|
@@ -17,14 +18,17 @@ ffi = cffi.FFI()
|
|
|
17
18
|
l = logging.getLogger(__name__)
|
|
18
19
|
|
|
19
20
|
|
|
20
|
-
class PagedMemoryMixin(
|
|
21
|
+
class PagedMemoryMixin(
|
|
22
|
+
Generic[PageType],
|
|
23
|
+
MemoryMixin[int | claripy.ast.BV | SimActionObject, claripy.ast.BV, int | claripy.ast.BV | SimActionObject],
|
|
24
|
+
):
|
|
21
25
|
"""
|
|
22
26
|
A bottom-level storage mechanism. Dispatches reads to individual pages, the type of which is the PAGE_TYPE class
|
|
23
27
|
variable.
|
|
24
28
|
"""
|
|
25
29
|
|
|
26
30
|
SUPPORTS_CONCRETE_LOAD = True
|
|
27
|
-
PAGE_TYPE: type[PageType]
|
|
31
|
+
PAGE_TYPE: type[PageType] # must be provided in subclass
|
|
28
32
|
|
|
29
33
|
def __init__(self, page_size=0x1000, default_permissions=3, permissions_map=None, page_kwargs=None, **kwargs):
|
|
30
34
|
super().__init__(**kwargs)
|
|
@@ -84,7 +88,7 @@ class PagedMemoryMixin(MemoryMixin):
|
|
|
84
88
|
kwargs["allow_default"] = True
|
|
85
89
|
return PagedMemoryMixin._initialize_page(self, pageno, permissions=permissions, **kwargs)
|
|
86
90
|
|
|
87
|
-
def _initialize_page(self, pageno: int, permissions=None, allow_default=True, **kwargs) -> PageType:
|
|
91
|
+
def _initialize_page(self, pageno: int, permissions=None, *, allow_default=True, **kwargs) -> PageType:
|
|
88
92
|
if not allow_default:
|
|
89
93
|
raise SimMemoryError("I have been instructed not to create a default page")
|
|
90
94
|
|
|
@@ -110,7 +114,7 @@ class PagedMemoryMixin(MemoryMixin):
|
|
|
110
114
|
def _divide_addr(self, addr: int) -> tuple[int, int]:
|
|
111
115
|
return divmod(addr, self.page_size)
|
|
112
116
|
|
|
113
|
-
def load(self, addr: int, size: int | None = None, endness=None, **kwargs):
|
|
117
|
+
def load(self, addr: int, size: int | None = None, *, endness=None, **kwargs):
|
|
114
118
|
if endness is None:
|
|
115
119
|
endness = self.endness
|
|
116
120
|
|
|
@@ -164,7 +168,7 @@ class PagedMemoryMixin(MemoryMixin):
|
|
|
164
168
|
l.debug("%s.load(%#x, %d, %s) = %s", self.id, addr, size, endness, out)
|
|
165
169
|
return out
|
|
166
170
|
|
|
167
|
-
def store(self, addr: int, data, size: int | None = None, endness=None, **kwargs):
|
|
171
|
+
def store(self, addr: int, data, size: int | None = None, *, endness=None, **kwargs):
|
|
168
172
|
if endness is None:
|
|
169
173
|
endness = self.endness
|
|
170
174
|
|
|
@@ -253,7 +257,7 @@ class PagedMemoryMixin(MemoryMixin):
|
|
|
253
257
|
pageno = (pageno + 1) % max_pageno
|
|
254
258
|
pageoff = 0
|
|
255
259
|
|
|
256
|
-
def merge(self, others
|
|
260
|
+
def merge(self, others, merge_conditions, common_ancestor=None):
|
|
257
261
|
changed_pages_and_offsets: dict[int, set[int] | None] = {}
|
|
258
262
|
for o in others:
|
|
259
263
|
for changed_page, changed_offsets in self.changed_pages(o).items():
|
|
@@ -315,7 +319,7 @@ class PagedMemoryMixin(MemoryMixin):
|
|
|
315
319
|
return True
|
|
316
320
|
|
|
317
321
|
def permissions(self, addr, permissions=None, **kwargs):
|
|
318
|
-
if
|
|
322
|
+
if not isinstance(addr, int):
|
|
319
323
|
raise TypeError("addr must be an int in paged memory")
|
|
320
324
|
pageno, _ = self._divide_addr(addr)
|
|
321
325
|
try:
|
|
@@ -323,15 +327,15 @@ class PagedMemoryMixin(MemoryMixin):
|
|
|
323
327
|
except SimMemoryError as e:
|
|
324
328
|
raise SimMemoryError(f"{addr:#x} is not mapped") from e
|
|
325
329
|
|
|
326
|
-
if
|
|
330
|
+
if isinstance(permissions, int):
|
|
327
331
|
permissions = claripy.BVV(permissions, 3)
|
|
328
332
|
|
|
329
|
-
result = page.
|
|
333
|
+
result = page.permission_bits
|
|
330
334
|
if permissions is not None:
|
|
331
|
-
page.
|
|
335
|
+
page.permission_bits = permissions
|
|
332
336
|
return result
|
|
333
337
|
|
|
334
|
-
def map_region(self, addr, length, permissions, init_zero=False, **kwargs):
|
|
338
|
+
def map_region(self, addr, length, permissions, *, init_zero=False, **kwargs):
|
|
335
339
|
if type(addr) is not int:
|
|
336
340
|
raise TypeError("addr must be an int in paged memory")
|
|
337
341
|
pageno, pageoff = self._divide_addr(addr)
|
|
@@ -399,6 +403,11 @@ class PagedMemoryMixin(MemoryMixin):
|
|
|
399
403
|
else:
|
|
400
404
|
return True
|
|
401
405
|
|
|
406
|
+
@overload
|
|
407
|
+
def _load_to_memoryview(self, addr, size, with_bitmap: Literal[True]) -> tuple[memoryview, memoryview]: ...
|
|
408
|
+
@overload
|
|
409
|
+
def _load_to_memoryview(self, addr, size, with_bitmap: Literal[False]) -> memoryview: ...
|
|
410
|
+
|
|
402
411
|
def _load_to_memoryview(self, addr, size, with_bitmap):
|
|
403
412
|
result = self.load(addr, size, endness="Iend_BE")
|
|
404
413
|
if result.op == "BVV":
|
|
@@ -469,7 +478,7 @@ class PagedMemoryMixin(MemoryMixin):
|
|
|
469
478
|
return memoryview(bytes(size)), memoryview(b"\x01" * size)
|
|
470
479
|
return memoryview(b"")
|
|
471
480
|
|
|
472
|
-
def concrete_load(self, addr, size, writing=False, with_bitmap=False, **kwargs):
|
|
481
|
+
def concrete_load(self, addr, size, writing=False, *, with_bitmap: bool = False, **kwargs):
|
|
473
482
|
pageno, offset = self._divide_addr(addr)
|
|
474
483
|
subsize = min(size, self.page_size - offset)
|
|
475
484
|
try:
|
|
@@ -481,9 +490,11 @@ class PagedMemoryMixin(MemoryMixin):
|
|
|
481
490
|
|
|
482
491
|
if not page.SUPPORTS_CONCRETE_LOAD:
|
|
483
492
|
# the page does not support concrete_load
|
|
484
|
-
|
|
493
|
+
if with_bitmap:
|
|
494
|
+
return self._load_to_memoryview(addr, size, True)
|
|
495
|
+
return self._load_to_memoryview(addr, size, False)
|
|
485
496
|
|
|
486
|
-
data, bitmap = page.concrete_load(offset, subsize, **kwargs)
|
|
497
|
+
data, bitmap = page.concrete_load(offset, subsize, with_bitmap=True, **kwargs)
|
|
487
498
|
if with_bitmap:
|
|
488
499
|
return data, bitmap
|
|
489
500
|
|
|
@@ -513,7 +524,7 @@ class PagedMemoryMixin(MemoryMixin):
|
|
|
513
524
|
except (SimMemoryError, AttributeError):
|
|
514
525
|
break
|
|
515
526
|
else:
|
|
516
|
-
newdata, bitmap = concrete_load(offset, subsize, **kwargs)
|
|
527
|
+
newdata, bitmap = concrete_load(offset, subsize, with_bitmap=True, **kwargs)
|
|
517
528
|
for i, byte in enumerate(bitmap):
|
|
518
529
|
if byte != 0:
|
|
519
530
|
break
|
|
@@ -1,26 +1,32 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
|
-
from
|
|
2
|
+
from abc import abstractmethod
|
|
3
|
+
from typing import Generic, TypeVar
|
|
4
|
+
from collections.abc import Generator
|
|
3
5
|
|
|
4
6
|
import claripy
|
|
5
7
|
|
|
6
8
|
from angr.storage.memory_object import SimMemoryObject, SimLabeledMemoryObject
|
|
7
9
|
from .multi_values import MultiValues
|
|
8
10
|
|
|
11
|
+
T = TypeVar("T")
|
|
9
12
|
|
|
10
|
-
|
|
13
|
+
|
|
14
|
+
class CooperationBase(Generic[T]):
|
|
11
15
|
"""
|
|
12
16
|
Any given subclass of this class which is not a subclass of MemoryMixin should have the property that any subclass
|
|
13
17
|
it which *is* a subclass of MemoryMixin should all work with the same datatypes
|
|
14
18
|
"""
|
|
15
19
|
|
|
16
20
|
@classmethod
|
|
17
|
-
|
|
21
|
+
@abstractmethod
|
|
22
|
+
def _compose_objects(cls, objects: list[list[tuple[int, T]]], size: str, endness: str, **kwargs) -> T:
|
|
18
23
|
"""
|
|
19
24
|
Provide this a list of the result of several load calls, and it will compose them into a single result.
|
|
20
25
|
"""
|
|
21
26
|
|
|
22
27
|
@classmethod
|
|
23
|
-
|
|
28
|
+
@abstractmethod
|
|
29
|
+
def _decompose_objects(cls, addr, data, endness, **kwargs) -> Generator[tuple[T, int, int], int]:
|
|
24
30
|
"""
|
|
25
31
|
A bidirectional generator. No idea if this is overengineered. Usage is that you send it a size to use
|
|
26
32
|
and it yields a tuple of three elements: the object to store for the next n bytes, the base address of the
|
|
@@ -28,13 +34,14 @@ class CooperationBase:
|
|
|
28
34
|
"""
|
|
29
35
|
|
|
30
36
|
@classmethod
|
|
31
|
-
|
|
37
|
+
@abstractmethod
|
|
38
|
+
def _zero_objects(cls, addr, size, **kwargs) -> Generator[tuple[T, int, int], int]:
|
|
32
39
|
"""
|
|
33
40
|
Like decompose objects, but with a size to zero-fill instead of explicit data
|
|
34
41
|
"""
|
|
35
42
|
|
|
36
43
|
@classmethod
|
|
37
|
-
def _force_store_cooperation(cls, addr, data, size, endness, **kwargs):
|
|
44
|
+
def _force_store_cooperation(cls, addr: int, data: T, size: int, endness: str, **kwargs):
|
|
38
45
|
if data is not None:
|
|
39
46
|
sub_gen = cls._decompose_objects(addr, data, endness, **kwargs)
|
|
40
47
|
else:
|
|
@@ -50,7 +57,7 @@ class CooperationBase:
|
|
|
50
57
|
return cls._compose_objects([results], size, endness, **kwargs)
|
|
51
58
|
|
|
52
59
|
|
|
53
|
-
class MemoryObjectMixin(CooperationBase):
|
|
60
|
+
class MemoryObjectMixin(CooperationBase[SimMemoryObject]):
|
|
54
61
|
"""
|
|
55
62
|
Uses SimMemoryObjects in region storage.
|
|
56
63
|
With this, load will return a list of tuple (address, MO) and store will take a MO.
|
|
@@ -123,11 +130,12 @@ class MemoryObjectMixin(CooperationBase):
|
|
|
123
130
|
else:
|
|
124
131
|
memory_object = SimLabeledMemoryObject(data, cur_addr, endness, byte_width=byte_width, label=label)
|
|
125
132
|
|
|
133
|
+
next_elem_size_left = 0
|
|
134
|
+
next_elem_index = 0
|
|
126
135
|
if data.symbolic and data.op == "Concat":
|
|
127
136
|
next_elem_size_left = data.args[0].size() // 8
|
|
128
|
-
next_elem_index = 0
|
|
129
137
|
|
|
130
|
-
size = yield
|
|
138
|
+
size = yield NotImplemented, NotImplemented, NotImplemented
|
|
131
139
|
max_size = kwargs.get("max_size", size)
|
|
132
140
|
while True:
|
|
133
141
|
if data.symbolic and data.op == "Concat" and data.size() > max_size:
|
|
@@ -235,6 +243,7 @@ class MemoryObjectSetMixin(CooperationBase):
|
|
|
235
243
|
mv.add_value(start_offset, prev_value)
|
|
236
244
|
prev_value = ...
|
|
237
245
|
|
|
246
|
+
assert next(mv.values(), None) is not None, "MultiValues may not be empty"
|
|
238
247
|
return mv
|
|
239
248
|
|
|
240
249
|
@classmethod
|
|
@@ -14,7 +14,7 @@ class ISPOMixin(MemoryMixin):
|
|
|
14
14
|
def set_state(self, state):
|
|
15
15
|
raise Exception("Cannot set state on this stateless object")
|
|
16
16
|
|
|
17
|
-
def _default_value(self, *args, memory
|
|
17
|
+
def _default_value(self, *args, memory, **kwargs):
|
|
18
18
|
try:
|
|
19
19
|
func = memory._default_value
|
|
20
20
|
except AttributeError as e:
|
|
@@ -22,7 +22,7 @@ class ISPOMixin(MemoryMixin):
|
|
|
22
22
|
else:
|
|
23
23
|
return func(*args, **kwargs)
|
|
24
24
|
|
|
25
|
-
def _add_constraints(self, *args, memory
|
|
25
|
+
def _add_constraints(self, *args, memory, **kwargs):
|
|
26
26
|
try:
|
|
27
27
|
func = memory._add_constraints
|
|
28
28
|
except AttributeError as e:
|
|
@@ -30,7 +30,7 @@ class ISPOMixin(MemoryMixin):
|
|
|
30
30
|
else:
|
|
31
31
|
return func(*args, **kwargs)
|
|
32
32
|
|
|
33
|
-
def _merge_values(self, *args, memory
|
|
33
|
+
def _merge_values(self, *args, memory, **kwargs):
|
|
34
34
|
try:
|
|
35
35
|
func = memory._merge_values
|
|
36
36
|
except AttributeError as ex:
|
|
@@ -38,7 +38,7 @@ class ISPOMixin(MemoryMixin):
|
|
|
38
38
|
else:
|
|
39
39
|
return func(*args, **kwargs)
|
|
40
40
|
|
|
41
|
-
def _merge_labels(self, *args, memory
|
|
41
|
+
def _merge_labels(self, *args, memory, **kwargs):
|
|
42
42
|
try:
|
|
43
43
|
func = memory._merge_labels
|
|
44
44
|
except AttributeError as ex:
|
|
@@ -46,7 +46,7 @@ class ISPOMixin(MemoryMixin):
|
|
|
46
46
|
else:
|
|
47
47
|
return func(*args, **kwargs)
|
|
48
48
|
|
|
49
|
-
def _update_mappings(self, *args, memory
|
|
49
|
+
def _update_mappings(self, *args, memory, **kwargs):
|
|
50
50
|
try:
|
|
51
51
|
func = memory._update_mappings
|
|
52
52
|
except AttributeError as ex:
|
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
from collections.abc import Iterator
|
|
3
|
+
from typing import Any, Generic, TypeGuard, TypeVar, cast
|
|
3
4
|
import archinfo
|
|
4
5
|
|
|
5
6
|
import claripy
|
|
6
7
|
|
|
8
|
+
from angr.errors import AngrTypeError
|
|
7
9
|
from angr.storage.memory_object import bv_slice
|
|
8
10
|
|
|
11
|
+
MVType = TypeVar("MVType", bound=claripy.ast.BV | claripy.ast.FP)
|
|
9
12
|
|
|
10
|
-
|
|
13
|
+
|
|
14
|
+
class MultiValues(Generic[MVType]):
|
|
11
15
|
"""
|
|
12
16
|
Represents a byte vector where each byte can have one or multiple values.
|
|
13
17
|
|
|
@@ -20,27 +24,31 @@ class MultiValues:
|
|
|
20
24
|
"_single_value",
|
|
21
25
|
)
|
|
22
26
|
|
|
23
|
-
_single_value:
|
|
24
|
-
_values: dict[int, set[
|
|
27
|
+
_single_value: MVType | None
|
|
28
|
+
_values: dict[int, set[MVType]] | None
|
|
25
29
|
|
|
26
30
|
def __init__(
|
|
27
31
|
self,
|
|
28
|
-
v:
|
|
29
|
-
offset_to_values=None,
|
|
32
|
+
v: MVType | MultiValues[MVType] | None | dict[int, set[MVType]] = None,
|
|
33
|
+
offset_to_values: dict[int, set[MVType]] | None = None,
|
|
30
34
|
):
|
|
31
35
|
if v is not None and offset_to_values is not None:
|
|
32
36
|
raise TypeError("You cannot specify v and offset_to_values at the same time!")
|
|
33
37
|
|
|
34
|
-
|
|
35
|
-
None
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
)
|
|
38
|
+
if v is None:
|
|
39
|
+
self._single_value = None
|
|
40
|
+
self._values = offset_to_values
|
|
41
|
+
elif isinstance(v, (claripy.ast.BV, claripy.ast.FP)):
|
|
42
|
+
self._single_value = v
|
|
43
|
+
self._values = None
|
|
44
|
+
elif isinstance(v, MultiValues):
|
|
45
|
+
self._single_value = v._single_value
|
|
46
|
+
self._values = v._values
|
|
47
|
+
elif isinstance(v, dict):
|
|
48
|
+
self._single_value = None
|
|
49
|
+
self._values = v
|
|
50
|
+
else:
|
|
51
|
+
raise TypeError(type(v))
|
|
44
52
|
|
|
45
53
|
if self._single_value is None and self._values is None:
|
|
46
54
|
self._values = {}
|
|
@@ -56,7 +64,7 @@ class MultiValues:
|
|
|
56
64
|
if not isinstance(vs, set):
|
|
57
65
|
raise TypeError("Each value in offset_to_values must be a set!")
|
|
58
66
|
|
|
59
|
-
def add_value(self, offset: int, value:
|
|
67
|
+
def add_value(self, offset: int, value: MVType) -> None:
|
|
60
68
|
if len(value) == 0:
|
|
61
69
|
return
|
|
62
70
|
if self._single_value is not None:
|
|
@@ -80,32 +88,40 @@ class MultiValues:
|
|
|
80
88
|
|
|
81
89
|
succ_offset = self._adjacent_offset(offset, before=False)
|
|
82
90
|
if succ_offset is not None:
|
|
83
|
-
value_end = offset + value.
|
|
91
|
+
value_end = offset + value.size() // 8
|
|
84
92
|
if value_end > succ_offset:
|
|
93
|
+
if isinstance(value, claripy.ast.FP):
|
|
94
|
+
raise AngrTypeError("Unsupported case. How do we handle floating point values overlapping?")
|
|
85
95
|
# value is too long. we need to break value into two
|
|
86
96
|
mid_value_size = succ_offset - offset
|
|
87
|
-
remaining_value = value[value.
|
|
97
|
+
remaining_value = cast(MVType, value[value.size() - mid_value_size * 8 - 1 : 0])
|
|
88
98
|
# update value
|
|
89
|
-
value = value[value.
|
|
99
|
+
value = cast(MVType, value[value.size() - 1 : value.size() - mid_value_size * 8])
|
|
90
100
|
self.add_value(succ_offset, remaining_value)
|
|
91
101
|
|
|
92
102
|
if self._values[offset]:
|
|
93
|
-
curr_value_size = next(iter(self._values[offset])).
|
|
94
|
-
if curr_value_size > value.
|
|
103
|
+
curr_value_size = next(iter(self._values[offset])).size() // 8
|
|
104
|
+
if curr_value_size > value.size() // 8:
|
|
95
105
|
# we need to break existing values
|
|
96
106
|
new_curr_values = set()
|
|
97
107
|
remaining_values = set()
|
|
98
108
|
for v in self._values[offset]:
|
|
99
|
-
|
|
100
|
-
|
|
109
|
+
if isinstance(v, claripy.ast.FP):
|
|
110
|
+
raise AngrTypeError("Unsupported case. How do we handle floating point values overlapping?")
|
|
111
|
+
|
|
112
|
+
new_curr_values.add(v[v.size() - 1 : v.size() - value.size()])
|
|
113
|
+
remaining_values.add(v[v.size() - value.size() - 1 : 0])
|
|
101
114
|
self._values[offset] = new_curr_values
|
|
102
115
|
for v in remaining_values:
|
|
103
|
-
self.add_value(offset + value.
|
|
104
|
-
elif curr_value_size < value.
|
|
116
|
+
self.add_value(offset + value.size() // 8, v)
|
|
117
|
+
elif curr_value_size < value.size() // 8:
|
|
118
|
+
if isinstance(value, claripy.ast.FP):
|
|
119
|
+
raise AngrTypeError("Unsupported case. How do we handle floating point values overlapping?")
|
|
120
|
+
|
|
105
121
|
# value is too long. we need to break value into two
|
|
106
|
-
remaining_value = value[value.
|
|
122
|
+
remaining_value = cast(MVType, value[value.size() - curr_value_size * 8 - 1 : 0])
|
|
107
123
|
# update value
|
|
108
|
-
value = value[value.
|
|
124
|
+
value = cast(MVType, value[value.size() - 1 : value.size() - curr_value_size * 8])
|
|
109
125
|
self.add_value(offset + curr_value_size, remaining_value)
|
|
110
126
|
|
|
111
127
|
self._values[offset].add(value)
|
|
@@ -113,26 +129,28 @@ class MultiValues:
|
|
|
113
129
|
pre_offset = self._adjacent_offset(offset, before=True)
|
|
114
130
|
if pre_offset is not None:
|
|
115
131
|
pre_values = self._values[pre_offset]
|
|
116
|
-
pre_values_size = next(iter(pre_values)).
|
|
132
|
+
pre_values_size = next(iter(pre_values)).size() // 8
|
|
117
133
|
if pre_offset + pre_values_size > offset:
|
|
118
134
|
# we need to break the preceding values
|
|
119
135
|
new_pre_value_size = offset - pre_offset
|
|
120
136
|
new_pre_values = set()
|
|
121
137
|
remaining_values = set()
|
|
122
138
|
for v in pre_values:
|
|
123
|
-
|
|
124
|
-
|
|
139
|
+
if isinstance(v, claripy.ast.FP):
|
|
140
|
+
raise AngrTypeError("Unsupported case. How do we handle floating point values overlapping?")
|
|
141
|
+
new_pre_values.add(v[v.size() - 1 : v.size() - new_pre_value_size * 8])
|
|
142
|
+
remaining_values.add(v[v.size() - new_pre_value_size * 8 - 1 : 0])
|
|
125
143
|
self._values[pre_offset] = new_pre_values
|
|
126
144
|
for v in remaining_values:
|
|
127
145
|
self.add_value(offset, v)
|
|
128
146
|
|
|
129
|
-
def one_value(self, strip_annotations: bool = False) ->
|
|
147
|
+
def one_value(self, strip_annotations: bool = False) -> MVType | None:
|
|
130
148
|
if self._single_value is not None:
|
|
131
149
|
return self._single_value
|
|
132
150
|
|
|
133
151
|
assert self._values is not None
|
|
134
152
|
|
|
135
|
-
if len(self._values) == 1 and len(self._values[0]) == 1:
|
|
153
|
+
if len(self._values) == 1 and 0 in self._values and len(self._values[0]) == 1:
|
|
136
154
|
return next(iter(self._values[0]))
|
|
137
155
|
if strip_annotations:
|
|
138
156
|
all_values_wo_annotations = {
|
|
@@ -144,7 +162,7 @@ class MultiValues:
|
|
|
144
162
|
|
|
145
163
|
def __len__(self) -> int:
|
|
146
164
|
if self._single_value is not None:
|
|
147
|
-
return self._single_value.
|
|
165
|
+
return self._single_value.size()
|
|
148
166
|
|
|
149
167
|
assert self._values is not None
|
|
150
168
|
|
|
@@ -155,7 +173,7 @@ class MultiValues:
|
|
|
155
173
|
max_len = max(x.size() for x in self._values[max_offset])
|
|
156
174
|
return max_offset * 8 + max_len # FIXME: we are assuming byte_width of 8
|
|
157
175
|
|
|
158
|
-
def merge(self, mv: MultiValues) -> MultiValues:
|
|
176
|
+
def merge(self, mv: MultiValues[MVType]) -> MultiValues[MVType]:
|
|
159
177
|
new_values = {k: set(v) for k, v in self.items()}
|
|
160
178
|
for off, vs in mv.items():
|
|
161
179
|
if off not in new_values:
|
|
@@ -187,47 +205,46 @@ class MultiValues:
|
|
|
187
205
|
def __contains__(self, offset: int) -> bool:
|
|
188
206
|
if self._single_value is not None:
|
|
189
207
|
return offset == 0
|
|
190
|
-
|
|
208
|
+
assert self._values is not None
|
|
209
|
+
return offset in self._values
|
|
191
210
|
|
|
192
|
-
def __getitem__(self, offset: int) -> set[
|
|
211
|
+
def __getitem__(self, offset: int) -> set[MVType]:
|
|
193
212
|
if self._single_value is not None:
|
|
194
213
|
if offset == 0:
|
|
195
214
|
return {self._single_value}
|
|
196
|
-
raise KeyError
|
|
197
|
-
|
|
198
|
-
raise KeyError
|
|
215
|
+
raise KeyError(offset)
|
|
216
|
+
assert self._values is not None
|
|
199
217
|
return self._values[offset]
|
|
200
218
|
|
|
201
219
|
def keys(self) -> set[int]:
|
|
202
220
|
if self._single_value is not None:
|
|
203
221
|
return {0}
|
|
204
|
-
|
|
222
|
+
assert self._values is not None
|
|
223
|
+
return set(self._values.keys())
|
|
205
224
|
|
|
206
|
-
def values(self) -> Iterator[set[
|
|
225
|
+
def values(self) -> Iterator[set[MVType]]:
|
|
207
226
|
if self._single_value is not None:
|
|
208
227
|
yield {self._single_value}
|
|
209
228
|
else:
|
|
210
|
-
|
|
211
|
-
return
|
|
229
|
+
assert self._values is not None
|
|
212
230
|
yield from self._values.values()
|
|
213
231
|
|
|
214
|
-
def items(self) -> Iterator[tuple[int, set[
|
|
232
|
+
def items(self) -> Iterator[tuple[int, set[MVType]]]:
|
|
215
233
|
if self._single_value is not None:
|
|
216
234
|
yield 0, {self._single_value}
|
|
217
235
|
else:
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
else:
|
|
221
|
-
yield from self._values.items()
|
|
236
|
+
assert self._values is not None
|
|
237
|
+
yield from self._values.items()
|
|
222
238
|
|
|
223
239
|
def count(self) -> int:
|
|
224
240
|
if self._single_value is not None:
|
|
225
241
|
return 1
|
|
226
|
-
|
|
227
|
-
return 0
|
|
242
|
+
assert self._values is not None
|
|
228
243
|
return len(self._values)
|
|
229
244
|
|
|
230
|
-
def extract(
|
|
245
|
+
def extract(
|
|
246
|
+
self: MultiValues[claripy.ast.BV], offset: int, length: int, endness: str
|
|
247
|
+
) -> MultiValues[claripy.ast.BV]:
|
|
231
248
|
end = offset + length
|
|
232
249
|
result = MultiValues(claripy.BVV(b""))
|
|
233
250
|
for obj_offset, values in self.items():
|
|
@@ -240,16 +257,19 @@ class MultiValues:
|
|
|
240
257
|
|
|
241
258
|
slice_start = max(0, offset - obj_offset)
|
|
242
259
|
slice_end = min(obj_length, end - obj_offset)
|
|
243
|
-
|
|
260
|
+
bv_value: claripy.ast.BV = claripy.fpToIEEEBV(value) if isinstance(value, claripy.ast.FP) else value
|
|
261
|
+
sliced = bv_slice(bv_value, slice_start, slice_end - slice_start, endness == archinfo.Endness.LE, 8)
|
|
244
262
|
if len(sliced):
|
|
245
263
|
result.add_value(max(0, obj_offset - offset), sliced)
|
|
246
264
|
|
|
247
265
|
return result
|
|
248
266
|
|
|
249
|
-
def concat(
|
|
250
|
-
|
|
267
|
+
def concat(
|
|
268
|
+
self: MultiValues[claripy.ast.BV], other: MultiValues[claripy.ast.BV] | claripy.ast.BV | bytes
|
|
269
|
+
) -> MultiValues[claripy.ast.BV]:
|
|
270
|
+
if isinstance(other, (bytes, bytearray, memoryview)):
|
|
251
271
|
other = claripy.BVV(other)
|
|
252
|
-
if isinstance(other, claripy.ast.
|
|
272
|
+
if isinstance(other, claripy.ast.BV):
|
|
253
273
|
other = MultiValues(other)
|
|
254
274
|
offset = len(self) // 8
|
|
255
275
|
result = MultiValues(self)
|
|
@@ -283,3 +303,17 @@ class MultiValues:
|
|
|
283
303
|
# we missed it...
|
|
284
304
|
return None
|
|
285
305
|
return None
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
def mv_is_bv(mv: MultiValues[Any]) -> TypeGuard[MultiValues[claripy.ast.BV]]:
|
|
309
|
+
if mv._single_value is not None:
|
|
310
|
+
return isinstance(mv._single_value, claripy.ast.BV)
|
|
311
|
+
assert mv._values is not None
|
|
312
|
+
return all(isinstance(value, claripy.ast.BV) for valueset in mv._values.values() for value in valueset)
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
def mv_is_fp(mv: MultiValues[Any]) -> TypeGuard[MultiValues[claripy.ast.FP]]:
|
|
316
|
+
if mv._single_value is not None:
|
|
317
|
+
return isinstance(mv._single_value, claripy.ast.FP)
|
|
318
|
+
assert mv._values is not None
|
|
319
|
+
return all(isinstance(value, claripy.ast.FP) for valueset in mv._values.values() for value in valueset)
|