angr 9.2.143__py3-none-manylinux2014_x86_64.whl → 9.2.145__py3-none-manylinux2014_x86_64.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/calling_convention.py +13 -1
- angr/analyses/calling_convention/fact_collector.py +41 -5
- angr/analyses/cfg/cfg_base.py +7 -2
- angr/analyses/cfg/cfg_emulated.py +13 -4
- angr/analyses/cfg/cfg_fast.py +35 -61
- angr/analyses/cfg/indirect_jump_resolvers/__init__.py +2 -0
- angr/analyses/cfg/indirect_jump_resolvers/constant_value_manager.py +107 -0
- angr/analyses/cfg/indirect_jump_resolvers/default_resolvers.py +2 -1
- angr/analyses/cfg/indirect_jump_resolvers/jumptable.py +2 -101
- angr/analyses/cfg/indirect_jump_resolvers/syscall_resolver.py +92 -0
- angr/analyses/decompiler/ail_simplifier.py +5 -0
- angr/analyses/decompiler/clinic.py +163 -69
- angr/analyses/decompiler/decompiler.py +4 -4
- angr/analyses/decompiler/optimization_passes/base_ptr_save_simplifier.py +1 -1
- angr/analyses/decompiler/optimization_passes/optimization_pass.py +5 -5
- angr/analyses/decompiler/optimization_passes/return_duplicator_base.py +5 -0
- angr/analyses/decompiler/optimization_passes/win_stack_canary_simplifier.py +58 -2
- angr/analyses/decompiler/peephole_optimizations/__init__.py +2 -0
- angr/analyses/decompiler/peephole_optimizations/a_sub_a_shr_const_shr_const.py +37 -0
- angr/analyses/decompiler/ssailification/rewriting_engine.py +2 -0
- angr/analyses/decompiler/ssailification/ssailification.py +10 -2
- angr/analyses/decompiler/ssailification/traversal_engine.py +17 -2
- angr/analyses/decompiler/structured_codegen/c.py +25 -4
- angr/analyses/disassembly.py +3 -3
- angr/analyses/fcp/fcp.py +1 -4
- angr/analyses/s_reaching_definitions/s_reaching_definitions.py +21 -22
- angr/analyses/stack_pointer_tracker.py +61 -25
- angr/analyses/typehoon/dfa.py +13 -3
- angr/analyses/typehoon/typehoon.py +60 -18
- angr/analyses/typehoon/typevars.py +11 -7
- angr/analyses/variable_recovery/engine_ail.py +13 -17
- angr/analyses/variable_recovery/engine_base.py +26 -30
- angr/analyses/variable_recovery/variable_recovery_fast.py +17 -21
- angr/knowledge_plugins/functions/function.py +29 -15
- angr/knowledge_plugins/key_definitions/constants.py +2 -2
- angr/knowledge_plugins/key_definitions/liveness.py +4 -4
- angr/lib/angr_native.so +0 -0
- angr/state_plugins/unicorn_engine.py +24 -8
- angr/storage/memory_mixins/paged_memory/page_backer_mixins.py +1 -2
- angr/storage/memory_mixins/paged_memory/pages/mv_list_page.py +2 -2
- angr/utils/funcid.py +27 -2
- angr/utils/graph.py +26 -20
- {angr-9.2.143.dist-info → angr-9.2.145.dist-info}/METADATA +11 -8
- {angr-9.2.143.dist-info → angr-9.2.145.dist-info}/RECORD +49 -46
- {angr-9.2.143.dist-info → angr-9.2.145.dist-info}/WHEEL +1 -1
- {angr-9.2.143.dist-info → angr-9.2.145.dist-info}/LICENSE +0 -0
- {angr-9.2.143.dist-info → angr-9.2.145.dist-info}/entry_points.txt +0 -0
- {angr-9.2.143.dist-info → angr-9.2.145.dist-info}/top_level.txt +0 -0
|
@@ -28,9 +28,11 @@ ffi = cffi.FFI()
|
|
|
28
28
|
|
|
29
29
|
try:
|
|
30
30
|
import unicorn
|
|
31
|
+
from unicorn.unicorn import _uc
|
|
31
32
|
except ImportError:
|
|
32
|
-
l.
|
|
33
|
-
unicorn = None
|
|
33
|
+
l.info("Unicorn is not installed. Support disabled.")
|
|
34
|
+
unicorn = None # type: ignore
|
|
35
|
+
_uc = None # type: ignore
|
|
34
36
|
|
|
35
37
|
|
|
36
38
|
class MEM_PATCH(ctypes.Structure):
|
|
@@ -418,6 +420,7 @@ def _load_native():
|
|
|
418
420
|
getattr(handle, func).argtypes = argtypes
|
|
419
421
|
|
|
420
422
|
# _setup_prototype_explicit(h, 'logSetLogLevel', None, ctypes.c_uint64)
|
|
423
|
+
_setup_prototype(h, "setup_imports", ctypes.c_bool, ctypes.c_char_p)
|
|
421
424
|
_setup_prototype(
|
|
422
425
|
h,
|
|
423
426
|
"alloc",
|
|
@@ -470,7 +473,8 @@ def _load_native():
|
|
|
470
473
|
_setup_prototype(h, "set_tracking", None, state_t, ctypes.c_bool, ctypes.c_bool)
|
|
471
474
|
_setup_prototype(h, "executed_pages", ctypes.c_uint64, state_t)
|
|
472
475
|
_setup_prototype(h, "in_cache", ctypes.c_bool, state_t, ctypes.c_uint64)
|
|
473
|
-
|
|
476
|
+
if unicorn is not None:
|
|
477
|
+
_setup_prototype(h, "set_map_callback", None, state_t, unicorn.unicorn.UC_HOOK_MEM_INVALID_CB)
|
|
474
478
|
_setup_prototype(
|
|
475
479
|
h,
|
|
476
480
|
"set_vex_to_unicorn_reg_mappings",
|
|
@@ -550,7 +554,7 @@ def _load_native():
|
|
|
550
554
|
|
|
551
555
|
return h
|
|
552
556
|
except (OSError, AttributeError) as e:
|
|
553
|
-
l.
|
|
557
|
+
l.error('failed loading "%s", unicorn support disabled (%s)', libfile, e)
|
|
554
558
|
raise ImportError("Unable to import native SimUnicorn support") from e
|
|
555
559
|
|
|
556
560
|
|
|
@@ -560,6 +564,10 @@ try:
|
|
|
560
564
|
except ImportError:
|
|
561
565
|
_UC_NATIVE = None
|
|
562
566
|
|
|
567
|
+
if _uc is not None and _UC_NATIVE is not None and not _UC_NATIVE.setup_imports(_uc._name.encode()):
|
|
568
|
+
l.error("Unicorn engine has an incompatible API. Support disabled.")
|
|
569
|
+
unicorn = None
|
|
570
|
+
|
|
563
571
|
|
|
564
572
|
class Unicorn(SimStatePlugin):
|
|
565
573
|
"""
|
|
@@ -675,8 +683,12 @@ class Unicorn(SimStatePlugin):
|
|
|
675
683
|
|
|
676
684
|
self.time = None
|
|
677
685
|
|
|
678
|
-
self._bullshit_cb =
|
|
679
|
-
|
|
686
|
+
self._bullshit_cb = (
|
|
687
|
+
ctypes.cast(
|
|
688
|
+
unicorn.unicorn.UC_HOOK_MEM_INVALID_CB(self._hook_mem_unmapped), unicorn.unicorn.UC_HOOK_MEM_INVALID_CB
|
|
689
|
+
)
|
|
690
|
+
if unicorn is not None
|
|
691
|
+
else None
|
|
680
692
|
)
|
|
681
693
|
|
|
682
694
|
@SimStatePlugin.memo
|
|
@@ -777,8 +789,12 @@ class Unicorn(SimStatePlugin):
|
|
|
777
789
|
|
|
778
790
|
def __setstate__(self, s):
|
|
779
791
|
self.__dict__.update(s)
|
|
780
|
-
self._bullshit_cb =
|
|
781
|
-
|
|
792
|
+
self._bullshit_cb = (
|
|
793
|
+
ctypes.cast(
|
|
794
|
+
unicorn.unicorn.UC_HOOK_MEM_INVALID_CB(self._hook_mem_unmapped), unicorn.unicorn.UC_HOOK_MEM_INVALID_CB
|
|
795
|
+
)
|
|
796
|
+
if unicorn is not None
|
|
797
|
+
else None
|
|
782
798
|
)
|
|
783
799
|
self._unicount = next(_unicounter)
|
|
784
800
|
self._uc_state = None
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
from mmap import mmap
|
|
3
|
-
from typing import Union
|
|
4
3
|
from collections.abc import Generator
|
|
5
4
|
import logging
|
|
6
5
|
|
|
@@ -9,7 +8,7 @@ import cle
|
|
|
9
8
|
|
|
10
9
|
l = logging.getLogger(__name__)
|
|
11
10
|
|
|
12
|
-
BackerType =
|
|
11
|
+
BackerType = bytes | bytearray | list[int]
|
|
13
12
|
BackerIterType = Generator[tuple[int, BackerType], None, None]
|
|
14
13
|
|
|
15
14
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# pylint:disable=abstract-method,arguments-differ,assignment-from-no-return
|
|
2
2
|
from __future__ import annotations
|
|
3
3
|
import logging
|
|
4
|
-
from typing import
|
|
4
|
+
from typing import Any
|
|
5
5
|
from collections.abc import Callable
|
|
6
6
|
|
|
7
7
|
from angr.storage.memory_mixins.memory_mixin import MemoryMixin
|
|
@@ -13,7 +13,7 @@ from .cooperation import MemoryObjectSetMixin
|
|
|
13
13
|
|
|
14
14
|
l = logging.getLogger(name=__name__)
|
|
15
15
|
|
|
16
|
-
_MOTYPE =
|
|
16
|
+
_MOTYPE = SimMemoryObject | SimLabeledMemoryObject
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
class MVListPage(
|
angr/utils/funcid.py
CHANGED
|
@@ -17,7 +17,8 @@ def is_function_security_check_cookie(func, project, security_cookie_addr: int)
|
|
|
17
17
|
return False
|
|
18
18
|
ins0 = block.capstone.insns[0]
|
|
19
19
|
if (
|
|
20
|
-
|
|
20
|
+
project.arch.name == "AMD64"
|
|
21
|
+
and ins0.mnemonic == "cmp"
|
|
21
22
|
and len(ins0.operands) == 2
|
|
22
23
|
and ins0.operands[0].type == capstone.x86.X86_OP_REG
|
|
23
24
|
and ins0.operands[0].reg == capstone.x86.X86_REG_RCX
|
|
@@ -29,6 +30,20 @@ def is_function_security_check_cookie(func, project, security_cookie_addr: int)
|
|
|
29
30
|
ins1 = block.capstone.insns[1]
|
|
30
31
|
if ins1.mnemonic == "jne":
|
|
31
32
|
return True
|
|
33
|
+
if (
|
|
34
|
+
project.arch.name == "X86"
|
|
35
|
+
and ins0.mnemonic == "cmp"
|
|
36
|
+
and len(ins0.operands) == 2
|
|
37
|
+
and ins0.operands[0].type == capstone.x86.X86_OP_REG
|
|
38
|
+
and ins0.operands[0].reg == capstone.x86.X86_REG_ECX
|
|
39
|
+
and ins0.operands[1].type == capstone.x86.X86_OP_MEM
|
|
40
|
+
and ins0.operands[1].mem.base == 0
|
|
41
|
+
and ins0.operands[1].mem.disp == security_cookie_addr
|
|
42
|
+
and ins0.operands[1].mem.index == 0
|
|
43
|
+
):
|
|
44
|
+
ins1 = block.capstone.insns[1]
|
|
45
|
+
if ins1.mnemonic == "jne":
|
|
46
|
+
return True
|
|
32
47
|
return False
|
|
33
48
|
|
|
34
49
|
|
|
@@ -63,13 +78,23 @@ def is_function_security_init_cookie(func: Function, project, security_cookie_ad
|
|
|
63
78
|
continue
|
|
64
79
|
last_insn = block.capstone.insns[-1]
|
|
65
80
|
if (
|
|
66
|
-
|
|
81
|
+
project.arch.name == "AMD64"
|
|
82
|
+
and last_insn.mnemonic == "mov"
|
|
67
83
|
and len(last_insn.operands) == 2
|
|
68
84
|
and last_insn.operands[0].type == capstone.x86.X86_OP_MEM
|
|
69
85
|
and last_insn.operands[0].mem.base == capstone.x86.X86_REG_RIP
|
|
70
86
|
and last_insn.operands[0].mem.index == 0
|
|
71
87
|
and last_insn.operands[0].mem.disp + last_insn.address + last_insn.size == security_cookie_addr
|
|
72
88
|
and last_insn.operands[1].type == capstone.x86.X86_OP_REG
|
|
89
|
+
) or (
|
|
90
|
+
project.arch.name == "X86"
|
|
91
|
+
and last_insn.mnemonic == "mov"
|
|
92
|
+
and len(last_insn.operands) == 2
|
|
93
|
+
and last_insn.operands[0].type == capstone.x86.X86_OP_MEM
|
|
94
|
+
and last_insn.operands[0].mem.base == 0
|
|
95
|
+
and last_insn.operands[0].mem.index == 0
|
|
96
|
+
and last_insn.operands[0].mem.disp == security_cookie_addr
|
|
97
|
+
and last_insn.operands[1].type == capstone.x86.X86_OP_REG
|
|
73
98
|
):
|
|
74
99
|
return True
|
|
75
100
|
return False
|
angr/utils/graph.py
CHANGED
|
@@ -276,7 +276,7 @@ class ContainerNode:
|
|
|
276
276
|
|
|
277
277
|
def __init__(self, obj):
|
|
278
278
|
self._obj = obj
|
|
279
|
-
self.index = None
|
|
279
|
+
self.index: int | None = None
|
|
280
280
|
|
|
281
281
|
@property
|
|
282
282
|
def obj(self):
|
|
@@ -308,8 +308,8 @@ class Dominators:
|
|
|
308
308
|
self._reverse = reverse # Set it to True to generate a post-dominator tree.
|
|
309
309
|
|
|
310
310
|
# Temporary variables
|
|
311
|
-
self._ancestor = None
|
|
312
|
-
self._semi = None
|
|
311
|
+
self._ancestor: list[ContainerNode | None] | None = None
|
|
312
|
+
self._semi: list[ContainerNode] | None = None
|
|
313
313
|
self._label = None
|
|
314
314
|
|
|
315
315
|
# Output
|
|
@@ -351,9 +351,11 @@ class Dominators:
|
|
|
351
351
|
# parent is a dict storing the mapping from ContainerNode to ContainerNode
|
|
352
352
|
# Each node in prepared_graph is a ContainerNode instance
|
|
353
353
|
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
354
|
+
assert self._semi is not None
|
|
355
|
+
|
|
356
|
+
bucket: dict[int, set[ContainerNode]] = defaultdict(set)
|
|
357
|
+
dom: list[None | ContainerNode] = [None] * (len(vertices))
|
|
358
|
+
self._ancestor = [None] * (len(vertices) + 1) # type: ignore
|
|
357
359
|
|
|
358
360
|
for i in range(len(vertices) - 1, 0, -1):
|
|
359
361
|
w = vertices[i]
|
|
@@ -376,6 +378,7 @@ class Dominators:
|
|
|
376
378
|
# Step 3
|
|
377
379
|
for v in bucket[parent[w].index]:
|
|
378
380
|
u = self._pd_eval(v)
|
|
381
|
+
assert u.index is not None and v.index is not None
|
|
379
382
|
if self._semi[u.index].index < self._semi[v.index].index:
|
|
380
383
|
dom[v.index] = u
|
|
381
384
|
else:
|
|
@@ -393,7 +396,7 @@ class Dominators:
|
|
|
393
396
|
self.dom = networkx.DiGraph() # The post-dom tree described in a directional graph
|
|
394
397
|
for i in range(1, len(vertices)):
|
|
395
398
|
if dom[i] is not None and vertices[i] is not None:
|
|
396
|
-
self.dom.add_edge(dom[i].obj, vertices[i].obj)
|
|
399
|
+
self.dom.add_edge(dom[i].obj, vertices[i].obj) # type: ignore
|
|
397
400
|
|
|
398
401
|
# Output
|
|
399
402
|
self.prepared_graph = _prepared_graph
|
|
@@ -476,7 +479,7 @@ class Dominators:
|
|
|
476
479
|
all_nodes_count = new_graph.number_of_nodes()
|
|
477
480
|
self._l.debug("There should be %d nodes in all", all_nodes_count)
|
|
478
481
|
counter = 0
|
|
479
|
-
vertices = [ContainerNode("placeholder")]
|
|
482
|
+
vertices: list[Any] = [ContainerNode("placeholder")]
|
|
480
483
|
scanned_nodes = set()
|
|
481
484
|
parent = {}
|
|
482
485
|
while True:
|
|
@@ -526,15 +529,23 @@ class Dominators:
|
|
|
526
529
|
return new_graph, vertices, parent
|
|
527
530
|
|
|
528
531
|
def _pd_link(self, v, w):
|
|
532
|
+
assert self._ancestor is not None
|
|
529
533
|
self._ancestor[w.index] = v
|
|
530
534
|
|
|
531
535
|
def _pd_eval(self, v):
|
|
536
|
+
assert self._ancestor is not None
|
|
537
|
+
assert self._label is not None
|
|
538
|
+
|
|
532
539
|
if self._ancestor[v.index] is None:
|
|
533
540
|
return v
|
|
534
541
|
self._pd_compress(v)
|
|
535
542
|
return self._label[v.index]
|
|
536
543
|
|
|
537
544
|
def _pd_compress(self, v):
|
|
545
|
+
assert self._ancestor is not None
|
|
546
|
+
assert self._semi is not None
|
|
547
|
+
assert self._label is not None
|
|
548
|
+
|
|
538
549
|
if self._ancestor[self._ancestor[v.index].index] is not None:
|
|
539
550
|
self._pd_compress(self._ancestor[v.index])
|
|
540
551
|
if (
|
|
@@ -604,7 +615,7 @@ class GraphUtils:
|
|
|
604
615
|
if graph.in_degree(node) > 1:
|
|
605
616
|
merge_points.add(node)
|
|
606
617
|
|
|
607
|
-
ordered_merge_points = GraphUtils.quasi_topological_sort_nodes(graph, merge_points)
|
|
618
|
+
ordered_merge_points = GraphUtils.quasi_topological_sort_nodes(graph, nodes=list(merge_points))
|
|
608
619
|
|
|
609
620
|
return [n.addr for n in ordered_merge_points]
|
|
610
621
|
|
|
@@ -732,7 +743,7 @@ class GraphUtils:
|
|
|
732
743
|
graph_copy.add_edge(src, dst)
|
|
733
744
|
|
|
734
745
|
# add loners
|
|
735
|
-
out_degree_zero_nodes = [node for (node, degree) in graph.out_degree() if degree == 0]
|
|
746
|
+
out_degree_zero_nodes = [node for (node, degree) in graph.out_degree() if degree == 0] # type:ignore
|
|
736
747
|
for node in out_degree_zero_nodes:
|
|
737
748
|
if graph.in_degree(node) == 0:
|
|
738
749
|
graph_copy.add_node(node)
|
|
@@ -749,9 +760,7 @@ class GraphUtils:
|
|
|
749
760
|
|
|
750
761
|
if nodes is None:
|
|
751
762
|
return ordered_nodes
|
|
752
|
-
|
|
753
|
-
nodes = set(nodes)
|
|
754
|
-
return [n for n in ordered_nodes if n in nodes]
|
|
763
|
+
return [n for n in ordered_nodes if n in set(nodes)]
|
|
755
764
|
|
|
756
765
|
@staticmethod
|
|
757
766
|
def _components_index_node(components, node):
|
|
@@ -820,13 +829,10 @@ class GraphUtils:
|
|
|
820
829
|
# panic mode that will aggressively remove edges
|
|
821
830
|
|
|
822
831
|
if len(subgraph) > 3000 and len(subgraph.edges) > len(subgraph) * 1.4:
|
|
823
|
-
for
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
subgraph.remove_edge(src, n)
|
|
828
|
-
if len(subgraph.edges) <= len(subgraph) * 1.4:
|
|
829
|
-
break
|
|
832
|
+
for n0, n1 in sorted(dfs_back_edges(subgraph, loop_head), key=lambda x: (x[0].addr, x[0].addr)):
|
|
833
|
+
subgraph.remove_edge(n0, n1)
|
|
834
|
+
if len(subgraph.edges) <= len(subgraph) * 1.4:
|
|
835
|
+
break
|
|
830
836
|
|
|
831
837
|
ordered_nodes.extend(GraphUtils.quasi_topological_sort_nodes(subgraph))
|
|
832
838
|
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: angr
|
|
3
|
-
Version: 9.2.
|
|
3
|
+
Version: 9.2.145
|
|
4
4
|
Summary: A multi-architecture binary analysis toolkit, with the ability to perform dynamic symbolic execution and various static analyses on binaries
|
|
5
|
-
Home-page: https://github.com/angr/angr
|
|
6
5
|
License: BSD-2-Clause
|
|
6
|
+
Project-URL: Homepage, https://angr.io/
|
|
7
|
+
Project-URL: Repository, https://github.com/angr/angr
|
|
7
8
|
Classifier: License :: OSI Approved :: BSD License
|
|
8
9
|
Classifier: Programming Language :: Python :: 3
|
|
9
10
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
@@ -16,13 +17,13 @@ Description-Content-Type: text/markdown
|
|
|
16
17
|
License-File: LICENSE
|
|
17
18
|
Requires-Dist: CppHeaderParser
|
|
18
19
|
Requires-Dist: GitPython
|
|
19
|
-
Requires-Dist: ailment==9.2.
|
|
20
|
-
Requires-Dist: archinfo==9.2.
|
|
20
|
+
Requires-Dist: ailment==9.2.145
|
|
21
|
+
Requires-Dist: archinfo==9.2.145
|
|
21
22
|
Requires-Dist: cachetools
|
|
22
23
|
Requires-Dist: capstone==5.0.3
|
|
23
24
|
Requires-Dist: cffi>=1.14.0
|
|
24
|
-
Requires-Dist: claripy==9.2.
|
|
25
|
-
Requires-Dist: cle==9.2.
|
|
25
|
+
Requires-Dist: claripy==9.2.145
|
|
26
|
+
Requires-Dist: cle==9.2.145
|
|
26
27
|
Requires-Dist: mulpyplexer
|
|
27
28
|
Requires-Dist: nampa
|
|
28
29
|
Requires-Dist: networkx!=2.8.1,>=2.0
|
|
@@ -31,11 +32,10 @@ Requires-Dist: psutil
|
|
|
31
32
|
Requires-Dist: pycparser>=2.18
|
|
32
33
|
Requires-Dist: pydemumble
|
|
33
34
|
Requires-Dist: pyformlang
|
|
34
|
-
Requires-Dist: pyvex==9.2.
|
|
35
|
+
Requires-Dist: pyvex==9.2.145
|
|
35
36
|
Requires-Dist: rich>=13.1.0
|
|
36
37
|
Requires-Dist: sortedcontainers
|
|
37
38
|
Requires-Dist: sympy
|
|
38
|
-
Requires-Dist: unicorn==2.0.1.post1
|
|
39
39
|
Requires-Dist: unique-log-filter
|
|
40
40
|
Requires-Dist: colorama; platform_system == "Windows"
|
|
41
41
|
Provides-Extra: angrdb
|
|
@@ -56,6 +56,9 @@ Requires-Dist: pytest; extra == "testing"
|
|
|
56
56
|
Requires-Dist: pytest-split; extra == "testing"
|
|
57
57
|
Requires-Dist: pytest-xdist; extra == "testing"
|
|
58
58
|
Requires-Dist: sqlalchemy; extra == "testing"
|
|
59
|
+
Requires-Dist: unicorn==2.0.1.post1; extra == "testing"
|
|
60
|
+
Provides-Extra: unicorn
|
|
61
|
+
Requires-Dist: unicorn==2.0.1.post1; extra == "unicorn"
|
|
59
62
|
|
|
60
63
|
# angr
|
|
61
64
|
|