-
Notifications
You must be signed in to change notification settings - Fork 218
/
install_api.py
executable file
·230 lines (180 loc) · 7.01 KB
/
install_api.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
#!/usr/bin/env python3
# Note, you probably want to run this script directly
# so you can be explicit about _which_ python version you
# wish to add binaryninja to the path of. For example:
# /usr/bin/python3 install_api.py
#
# Do not run this from a github clone. Should be used
# by running the system native python and pointing it at
# the copy of this script in your install path.
#
# Based on a prior script by @withzombies
import importlib.util
import os
import sys
from site import check_enableusersite
from typing import Optional
if sys.version_info[0] < 3:
sys.stderr.print("Python 3 is required.")
sys.exit(1)
# Handle both normal environments and virtualenvs
try:
from site import getusersitepackages, getsitepackages
except ImportError:
from sysconfig import get_path
getsitepackages = lambda: get_path("purelib")
getusersitepackages = getsitepackages
def print_error(*args, file=None, **kwargs):
print(*args, file=sys.stderr, **kwargs)
def check_virtual_environment() -> bool:
if sys.prefix != sys.base_prefix:
if os.environ.get("VIRTUAL_ENV"):
return True
return False
def getsitepackage() -> str:
"""
gets the site package and creates it if needed
returns empty if fails
"""
install_path = getsitepackages()[0]
if not os.path.exists(install_path):
try:
os.makedirs(install_path, exist_ok=True)
except OSError:
print_error(f"Root install specified but cannot create {install_path}")
return ''
return install_path
def binaryninja_installed() -> bool:
try:
binaryninja = importlib.util.find_spec("binaryninja")
assert binaryninja is not None
binaryninjaui = importlib.util.find_spec("binaryninjaui")
assert binaryninjaui is not None
return True
except ModuleNotFoundError:
return False
except AssertionError:
return False
def get_expected_binaryninja_installed_directory() -> Optional[str]:
dir_name = os.path.dirname(os.path.abspath(__file__))
api_path = os.path.abspath(os.path.join(dir_name, "..", "python"))
return api_path
def validate_path(path: str) -> bool:
try:
os.stat(path)
except OSError:
return False
old_path = sys.path
sys.path.append(path)
try:
from binaryninja import core_version
print("Found Binary Ninja core version: {}".format(core_version()))
except ModuleNotFoundError as e:
sys.path = old_path
if e.name == "packaging" and sys.version_info.minor >= 10:
raise e
return False
except ImportError as e:
sys.path = old_path
return False
return True
def install(interactive=False, on_root=False, on_pyenv=False, force=False) -> bool:
if binaryninja_installed() and not force:
print_error("Binary Ninja API already in the path. Use --force to overwrite.")
return False
api_path = get_expected_binaryninja_installed_directory()
if not os.path.isdir(api_path):
print_error("Failed to find installed python expected at {}".format(api_path))
return False
print(f"Found install folder of {api_path}")
while not validate_path(api_path):
print(f"Binary Ninja not found: {api_path}")
if not interactive:
print_error("silent mode selected (-s, --silent), failing.")
return False
try:
new_path = input("Please provide the path to Binary Ninja's install directory: \n [{}] : ".format(api_path))
except KeyboardInterrupt:
print_error("KeyboardInterrupt detected.")
return False
if not new_path:
print("Invalid path.")
continue
if not new_path.endswith("python"):
os.path.join(new_path, "python")
api_path = new_path
if on_root:
install_path = getsitepackage()
if not install_path or not os.access(install_path, os.W_OK):
print_error(f"Root install specified but cannot write to \"{install_path}\"")
return False
else:
print(f"Installing on root site: \"{install_path}\"")
elif on_pyenv:
install_path = getsitepackage()
print(f"Installing on pyenv site: \"{install_path}")
elif check_virtual_environment():
install_path = getsitepackage()
print(f"Installing on virtual environment site: \"{install_path}\"")
else:
if not check_enableusersite():
print_error("Warning, trying to write to user site packages, but check_enableusersite fails.")
return False
else:
install_path = getusersitepackages()
if not os.path.exists(install_path):
os.makedirs(install_path)
print(f"Installing on user site: {install_path}")
if not install_path:
print_error("empty site packages path")
return False
binaryninja_pth_path = os.path.join(install_path, "binaryninja.pth")
with open(binaryninja_pth_path, 'wb') as pth_file:
pth_file.write((api_path + '\n').encode("charmap"))
pth_file.write((api_path + sys.version[0] + '\n').encode("charmap")) # support for QT bindings
return True
def uninstall() -> bool:
if not binaryninja_installed():
print_error("Uninstall specified, but binaryninja not in the current path.")
return False
user_path = os.path.join(getusersitepackages(), "binaryninja.pth")
site_path = os.path.join(getsitepackages()[0], "binaryninja.pth")
for path_to_unlink in [user_path, site_path]:
if not os.path.exists(path_to_unlink):
print_error(f"{path_to_unlink} not found.")
continue
print(f"Removing {path_to_unlink}...")
try:
os.unlink(path_to_unlink)
except OSError as e:
print_error(f"Unable to unlink, please re-run with appropriate permissions: {str(e)}")
return False
return True
if __name__ == '__main__':
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("-s", "--silent", action='store_true')
parser.add_argument("-u", "--uninstall", action='store_true')
parser.add_argument("-f", "--force", action='store_true')
parser.add_argument("--install-on-root", action='store_true')
parser.add_argument("--install-on-pyenv", action='store_true')
args = parser.parse_args()
if args.uninstall:
uninstall_result = uninstall()
if not uninstall_result:
print(f"Binary Ninja API uninstallation failed.")
sys.exit(-1)
else:
print(f"Binary Ninja API uninstallation success.")
else:
install_result = install(
interactive=(not args.silent),
on_root=args.install_on_root,
on_pyenv=args.install_on_pyenv,
force=args.force
)
if not install_result:
print(f"Binary Ninja API installation failed.")
sys.exit(-1)
else:
print(f"Binary Ninja API installation success.")