From 5ab92a703fe01634e086cbf278dc8ca36c438a54 Mon Sep 17 00:00:00 2001 From: Josh Ferrell Date: Wed, 27 Aug 2025 21:02:59 -0400 Subject: Add auto downloading of project file dependencies, clean up download APIs --- .../collaboration/examples/download_everything.py | 14 +++------- python/collaboration/examples/multitool.py | 2 +- python/collaboration/examples/sync_test.py | 30 +++++++++++----------- python/collaboration/file.py | 17 ++++++++++-- 4 files changed, 35 insertions(+), 28 deletions(-) (limited to 'python') diff --git a/python/collaboration/examples/download_everything.py b/python/collaboration/examples/download_everything.py index ef804e39..61199daa 100644 --- a/python/collaboration/examples/download_everything.py +++ b/python/collaboration/examples/download_everything.py @@ -19,7 +19,6 @@ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # IN THE SOFTWARE. -from pathlib import Path import sys import binaryninja @@ -40,18 +39,13 @@ def main(): # Pull every file from every project for project in remote.projects: for file in project.files: - bndb_path = file.default_path - print(f"{project.name}/{file.name} BNDB at {bndb_path}") + print(f"{project.name}/{file.name} BNDB at {file.default_path}") try: - metadata: binaryninja.FileMetadata = file.download_to_bndb(bndb_path) - - for v in metadata.existing_views: - if v == 'Raw': - continue - bv = metadata.get_view_of_type(v) + file.download() + with binaryninja.load(file.core_file) as bv: # Show the entry point to demonstrate we have pulled the analyzed file - print(f"{project.name}/{file.name} {v} Entrypoint @ 0x{bv.entry_point:08x}") + print(f"{project.name}/{file.name} {bv.view_type} Entrypoint @ 0x{bv.entry_point:08x}") except InterruptedError as e: # In case of ^C raise e diff --git a/python/collaboration/examples/multitool.py b/python/collaboration/examples/multitool.py index 313e537b..6195fe32 100644 --- a/python/collaboration/examples/multitool.py +++ b/python/collaboration/examples/multitool.py @@ -237,7 +237,7 @@ def main(): print(f"Unknown File: {args.name}") sys.exit(1) with TqdmProgress(desc="", leave=False) as t: - file.download(lambda cur, max: t.progress(cur, max)) + file.download_contents(lambda cur, max: t.progress(cur, max)) print_file_info(file) elif args.file_command == "delete": file = project.get_file_by_name(args.name) diff --git a/python/collaboration/examples/sync_test.py b/python/collaboration/examples/sync_test.py index 31b4518a..fdebe8c3 100644 --- a/python/collaboration/examples/sync_test.py +++ b/python/collaboration/examples/sync_test.py @@ -56,27 +56,27 @@ def main(): project_dir = Path(file.default_path).parent print(f'Snapshots: {[snapshot.id for snapshot in file.snapshots]}') - bv = binaryninja.load(file.default_path) - view_type = bv.view_type - assert collaboration.RemoteFile.get_for_bv(bv) == file + with binaryninja.load(file.default_path) as bv: + assert collaboration.RemoteFile.get_for_bv(bv) == file + assert bv.entry_function is not None, "Failed to get binary entry function" - print(f'Setting entry function at 0x{bv.entry_function.start:08x} name to \'entry_function\'') - bv.entry_function.name = 'entry_function' - bv.file.save_auto_snapshot() + print(f'Setting entry function at 0x{bv.entry_function.start:08x} name to \'entry_function\'') + bv.entry_function.name = 'entry_function' + bv.file.save_auto_snapshot() - file.sync(bv, lambda conflicts: False) - print(f'Snapshots: {[snapshot.id for snapshot in file.snapshots]}') + file.sync(bv, lambda conflicts: False) + print(f'Snapshots: {[snapshot.id for snapshot in file.snapshots]}') - # Try deleting the bndb, redownload and see if the function name is preserved - bv.file.close() + # Delete the bndb, redownload and see if the function name is preserved Path(file.default_path).unlink() print(f'Redownloading {project.name}/{file.name}...') - metadata = file.download_to_bndb() - bv = metadata.get_view_of_type(view_type) - print(f'Entry function name: {bv.entry_function.name}') - assert bv.entry_function.name == 'entry_function' - bv.file.close() + file.download() + with binaryninja.load(file.core_file) as bv: + assert bv.entry_function is not None, "Failed to get binary entry function after redownload" + + print(f'Entry function name: {bv.entry_function.name}') + assert bv.entry_function.name == 'entry_function' finally: # Clean up diff --git a/python/collaboration/file.py b/python/collaboration/file.py index 0cc3717d..ab566c84 100644 --- a/python/collaboration/file.py +++ b/python/collaboration/file.py @@ -374,7 +374,19 @@ class RemoteFile: if not core.BNRemoteFileDeleteSnapshot(self._handle, snapshot._handle): raise RuntimeError(util._last_error()) - def download(self, progress: 'util.ProgressFuncType' = util.nop) -> bytes: + def download(self, progress: 'util.ProgressFuncType' = util.nop): + """ + Download a remote file and possibly dependencies to its project + Dependency download behavior depends on the value of the collaboration.autoDownloadFileDependencies setting + + :param progress: Function to call on progress updates + :raises: RuntimeError if there was an error + """ + value = core.BNRemoteFileDownload(self._handle, util.wrap_progress(progress), None) + if not value: + raise RuntimeError(util._last_error()) + + def download_contents(self, progress: 'util.ProgressFuncType' = util.nop) -> bytes: """ Download the contents of a remote file @@ -384,7 +396,7 @@ class RemoteFile: """ data = (ctypes.POINTER(ctypes.c_ubyte))() size = ctypes.c_size_t() - value = core.BNRemoteFileDownload(self._handle, util.wrap_progress(progress), None, data, size) + value = core.BNRemoteFileDownloadContents(self._handle, util.wrap_progress(progress), None, data, size) if not value: raise RuntimeError(util._last_error()) return bytes(ctypes.cast(data, ctypes.POINTER(ctypes.c_uint8 * size.value)).contents) @@ -399,6 +411,7 @@ class RemoteFile: :return: Constructed FileMetadata object :raises: RuntimeError if there was an error """ + # TODO: deprecated, use RemoteFile.download() and ProjectFile.export() if path is None: path = self.default_path file = databasesync.download_file(self, path, util.split_progress(progress, 0, [0.5, 0.5])) -- cgit v1.3.1