3030repo_name = 'binja_native_sigscan'
3131file_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
3972def 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
6598def 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
69104def calculate_hash (file_path ):
@@ -96,63 +131,61 @@ def download_file_to_temp(file_url, file_name):
96131
97132# Function that deletes file
98133def 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
102138def 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)
106144def 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
124148def 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
242289obj = Updater ()
243290obj .start ()
0 commit comments