Skip to content

Commit 5363d1d

Browse files
committed
support for various Binary Ninja versions
1 parent 3843c45 commit 5363d1d

File tree

1 file changed

+96
-49
lines changed

1 file changed

+96
-49
lines changed

__init__.py

Lines changed: 96 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,43 @@
3030
repo_name = 'binja_native_sigscan'
3131
file_url = 'https://github.com/{}/{}/releases/latest/download'.format(repo_owner, repo_name)
3232

33-
# Name of files in release section on github (leave blank if platform not supported)
34-
win_file = 'sigscan.dll'
35-
linux_file = ''
36-
darwin_file = ''
33+
# File names in release section on github along with Binary Ninja versions for which they were compiled (leave whole variable blank if platform not supported)
34+
# Both version variables are inclusive meaning any Binary Ninja version in between is supported, DO NOT include '-dev' suffix so instead of '3.4.4189-dev', use just '3.4.4189')
35+
# Example:
36+
# win_files = [
37+
# ('3.1.3469', '3.3.3996', 'sigscan.dll'),
38+
# ('3.4.4169', '3.4.4189', 'sigscan_dev.dll')
39+
# ]
40+
win_files = [
41+
('3.1.3469', '3.1.3469', 'sigscan_3469.dll'),
42+
('3.2.3814', '3.2.3814', 'sigscan_3814.dll'),
43+
('3.3.3996', '3.3.3996', 'sigscan.dll'),
44+
('3.4.4169', '3.4.4189', 'sigscan_dev.dll')
45+
]
46+
linux_files = []
47+
darwin_files = []
48+
49+
# Function that determines whether Binary Ninja version is supported (returns None if not, according file name if yes)
50+
def is_version_supported(files):
51+
# Get current Binary Ninja version
52+
version_numbers = binaryninja.core_version().split()[0].split('-')[0].split('.')
53+
major, minor, build = map(int, version_numbers)
54+
55+
# Loop through files for current platform and see if our version is supported by any
56+
for entry in files:
57+
min_ver, max_ver, file = entry
58+
59+
min_parts = min_ver.split('.')
60+
max_parts = max_ver.split('.')
61+
62+
major_match = (major >= int(min_parts[0]) and major <= int(max_parts[0]))
63+
minor_match = (minor >= int(min_parts[1]) and minor <= int(max_parts[1]))
64+
build_match = (build >= int(min_parts[2]) and build <= int(max_parts[2]))
65+
66+
if major_match and minor_match and build_match:
67+
return file
68+
69+
return None
3770

3871
# Function that determines whether system is supported
3972
def is_system_supported(file_name):
@@ -57,13 +90,15 @@ def read_data_file():
5790
return f.read().splitlines()
5891

5992
# Function that writes to current_native_plugin_data file
60-
def write_data_file(version, hash):
93+
def write_data_file(version, hash, file_name):
6194
with open(os.path.join(binaryninja.user_plugin_path(), 'native_plugins_data', plugin_name + '.data'), 'w') as f:
62-
f.write(version + '\n' + hash)
95+
f.write(version + '\n' + hash + '\n' + file_name)
6396

6497
# Function that deletes file from current_native_plugin_data
6598
def delete_data_file():
66-
os.remove(os.path.join(binaryninja.user_plugin_path(), 'native_plugins_data', plugin_name + '.data'))
99+
path = os.path.join(binaryninja.user_plugin_path(), 'native_plugins_data', plugin_name + '.data')
100+
if os.path.isfile(path):
101+
os.remove(path)
67102

68103
# Function that calculates hash of file
69104
def calculate_hash(file_path):
@@ -96,63 +131,61 @@ def download_file_to_temp(file_url, file_name):
96131

97132
# Function that deletes file
98133
def delete_file(file_path):
99-
os.remove(file_path)
134+
if os.path.isfile(file_path):
135+
os.remove(file_path)
100136

101137
# Function that deletes file from temp directory
102138
def delete_file_from_temp(file_name):
103-
os.remove(os.path.join(binaryninja.user_plugin_path(), 'temp', file_name))
139+
path = os.path.join(binaryninja.user_plugin_path(), 'temp', file_name)
140+
if os.path.isfile(path):
141+
os.remove(path)
104142

105-
# Function that determines whether plugin is installed
143+
# Function that determines whether plugin is installed (for current Binary Ninja version)
106144
def is_plugin_installed(file_name):
107145
return os.path.isfile(os.path.join(binaryninja.user_plugin_path(), file_name))
108146

109-
# Function that determines whether plugin is outdated
110-
def is_plugin_outdated(version, hash):
111-
if data_folder_exists():
112-
if data_file_exists(plugin_name):
113-
data = read_data_file(plugin_name)
114-
if data[0] == version and data[1] == hash:
115-
return False
116-
else:
117-
return True
118-
else:
119-
return True
120-
else:
121-
return True
122-
123147
# Function that alerts user
124148
def alert_user(description):
125149
binaryninja.interaction.show_message_box('{} (Native plugin loader)'.format(plugin_name), description, binaryninja.enums.MessageBoxButtonSet.OKButtonSet, binaryninja.enums.MessageBoxIcon.InformationIcon)
126150

127151
# Function that does the actual work
128-
def check_for_updates(repo_owner, repo_name, file_url, win_file, linux_file, darwin_file):
129-
# Retrieve the HTML of the release page
130-
release_url = 'https://github.com/{}/{}/releases/latest'.format(repo_owner, repo_name)
131-
response = requests.get(release_url)
132-
html = response.content
133-
134-
# Parse the HTML to extract the release version
135-
soup = bs4.BeautifulSoup(html, 'html.parser')
136-
latest_version_tag = getattr(soup.find('span', {'class': 'css-truncate-target'}), 'text', None)
137-
latest_version = latest_version_tag.strip() if latest_version_tag else None
138-
152+
def check_for_updates(repo_owner, repo_name, file_url, win_files, linux_files, darwin_files):
139153
# Determine OS we are running on
140154
platform = sys.platform.lower()
141155

142156
# Windows
143157
if platform.startswith('win'):
144-
file_url = '{}/{}'.format(file_url, win_file)
145-
file = win_file
158+
files = win_files
146159
# Linux
147160
elif platform.startswith('linux'):
148-
file_url = '{}/{}'.format(file_url, linux_file)
149-
file = linux_file
161+
files = linux_files
150162
# Mac
151163
elif platform.startswith('darwin'):
152-
file_url = '{}/{}'.format(file_url, darwin_file)
153-
file = darwin_file
164+
files = darwin_files
154165
else:
155166
alert_user(plugin_name, 'Unsupported platform')
167+
return
168+
169+
# Check Binary Ninja version and possible get file name for current version
170+
file = is_version_supported(files)
171+
if not file:
172+
version_numbers = binaryninja.core_version().split()[0].split('-')[0].split('.')
173+
major, minor, build = map(int, version_numbers)
174+
alert_user('Current version of Binary Ninja ({}) is not supported.'.format(str(major) + '.' + str(minor) + '.' + str(build)))
175+
return
176+
177+
# Create url for file we need
178+
file_url = '{}/{}'.format(file_url, file)
179+
180+
# Retrieve the HTML of the release page
181+
release_url = 'https://github.com/{}/{}/releases/latest'.format(repo_owner, repo_name)
182+
response = requests.get(release_url)
183+
html = response.content
184+
185+
# Parse the HTML to extract the release version
186+
soup = bs4.BeautifulSoup(html, 'html.parser')
187+
latest_version_tag = getattr(soup.find('span', {'class': 'css-truncate-target'}), 'text', None)
188+
latest_version = latest_version_tag.strip() if latest_version_tag else None
156189

157190
# Make sure we have data folder
158191
if not data_folder_exists():
@@ -166,21 +199,35 @@ def check_for_updates(repo_owner, repo_name, file_url, win_file, linux_file, dar
166199
for file in os.listdir(os.path.join(binaryninja.user_plugin_path(), 'temp')):
167200
delete_file_from_temp(file)
168201

169-
# Do the thing
202+
# Verify we have correct file
170203
if (is_system_supported(file) and latest_version != None):
171204
plugin_data = (read_data_file() if data_file_exists() else None) if data_folder_exists() else None
172-
# Check if we have both version and hash of the plugin
173-
if plugin_data == None or len(plugin_data) != 2 or plugin_data[0] == None or plugin_data[1] == None:
205+
# Check if we have all required data (version, hash, file name)
206+
if plugin_data == None or len(plugin_data) != 3 or plugin_data[0] == None or plugin_data[1] == None or plugin_data[2] == None:
174207
delete_data_file() if data_file_exists() else None
175208
plugin_data = None
176209

177210
data_version = plugin_data[0] if plugin_data != None else None
178211
data_hash = plugin_data[1] if plugin_data != None else None
212+
data_file_name = plugin_data[2] if plugin_data != None else None
213+
214+
# Check if we there is a binary for different Binary Ninja version
215+
if (data_file_name != None and data_file_name != file):
216+
# Delete old file
217+
delete_file(os.path.join(binaryninja.user_plugin_path(), data_file_name))
218+
# Delete data file
219+
delete_data_file()
220+
# Reset data
221+
plugin_data = None
222+
data_version = None
223+
data_hash = None
224+
data_file_name = None
225+
179226
if not is_plugin_installed(file):
180227
# Plugin not installed, just download it
181228
if download_file(file_url, file):
182229
# Register plugin in data directory
183-
write_data_file(latest_version, calculate_hash(os.path.join(binaryninja.user_plugin_path(), file)))
230+
write_data_file(latest_version, calculate_hash(os.path.join(binaryninja.user_plugin_path(), file)), file)
184231
alert_user('Plugin downloaded successfully, please restart Binary Ninja to load it')
185232
else:
186233
alert_user('Failed to download plugin')
@@ -191,7 +238,7 @@ def check_for_updates(repo_owner, repo_name, file_url, win_file, linux_file, dar
191238
download_file_to_temp(file_url, file)
192239
if (calculate_hash(os.path.join(binaryninja.user_plugin_path(), file)) == calculate_hash(os.path.join(binaryninja.user_plugin_path(), 'temp', file))):
193240
# We have the latest version, register it in data directory
194-
write_data_file(latest_version, calculate_hash(os.path.join(binaryninja.user_plugin_path(), file)))
241+
write_data_file(latest_version, calculate_hash(os.path.join(binaryninja.user_plugin_path(), file)), file)
195242
delete_file_from_temp(file)
196243
else:
197244
# We don't have the latest version, alert user
@@ -209,7 +256,7 @@ def check_for_updates(repo_owner, repo_name, file_url, win_file, linux_file, dar
209256
download_file_to_temp(file_url, file)
210257
if (calculate_hash(os.path.join(binaryninja.user_plugin_path(), file)) == calculate_hash(os.path.join(binaryninja.user_plugin_path(), 'temp', file))):
211258
# We have the latest version, register it in data directory so user is not prompted to update as he probably already did
212-
write_data_file(latest_version, calculate_hash(os.path.join(binaryninja.user_plugin_path(), file)))
259+
write_data_file(latest_version, calculate_hash(os.path.join(binaryninja.user_plugin_path(), file)), file)
213260
delete_file_from_temp(file)
214261
else:
215262
# We don't have the latest version, alert user
@@ -224,7 +271,7 @@ def check_for_updates(repo_owner, repo_name, file_url, win_file, linux_file, dar
224271
download_file_to_temp(file_url, file)
225272
if (calculate_hash(os.path.join(binaryninja.user_plugin_path(), file)) == calculate_hash(os.path.join(binaryninja.user_plugin_path(), 'temp', file))):
226273
# Yep, hash of the plugin in the github release corresponds to the hash of currently installed plugin so we have the latest one
227-
write_data_file(latest_version, calculate_hash(os.path.join(binaryninja.user_plugin_path(), file)))
274+
write_data_file(latest_version, calculate_hash(os.path.join(binaryninja.user_plugin_path(), file)), file)
228275
delete_file_from_temp(file)
229276
else:
230277
# Not the latest one (according to the hash in the github release), but user might be intending to test different version of the plugin, add ignore option
@@ -237,7 +284,7 @@ def __init__(self):
237284
binaryninja.BackgroundTaskThread.__init__(self, 'Native plugin loader - checking for updates on: {}'.format(plugin_name), True)
238285

239286
def run(self):
240-
check_for_updates(repo_owner, repo_name, file_url, win_file, linux_file, darwin_file)
287+
check_for_updates(repo_owner, repo_name, file_url, win_files, linux_files, darwin_files)
241288

242289
obj = Updater()
243290
obj.start()

0 commit comments

Comments
 (0)