summaryrefslogtreecommitdiff
path: root/python
diff options
context:
space:
mode:
authorJosh Ferrell <josh@vector35.com>2024-01-22 16:11:19 -0500
committerJosh Ferrell <josh@vector35.com>2024-01-22 16:11:19 -0500
commit3dd22f40996fc128ffce6026e8e747ca66bcc21d (patch)
treea5e7fc57dc620fc4d4a408ffdbf114bb66dcf91d /python
parent96053ffc711aa27fcaeeb6cbfa89df0c253361f8 (diff)
Project support
Diffstat (limited to 'python')
-rw-r--r--python/__init__.py1
-rw-r--r--python/binaryview.py79
-rw-r--r--python/exceptions.py4
-rw-r--r--python/externallibrary.py125
-rw-r--r--python/filemetadata.py23
-rw-r--r--python/project.py411
-rw-r--r--python/scriptingprovider.py16
-rw-r--r--python/settings.py2
8 files changed, 649 insertions, 12 deletions
diff --git a/python/__init__.py b/python/__init__.py
index f06b0681..2b12d827 100644
--- a/python/__init__.py
+++ b/python/__init__.py
@@ -71,6 +71,7 @@ from .typeprinter import *
from .component import *
from .typecontainer import *
from .exceptions import *
+from .project import *
# We import each of these by name to prevent conflicts between
# log.py and the function 'log' which we don't import below
from .log import (
diff --git a/python/binaryview.py b/python/binaryview.py
index 0c475445..5b78fca9 100644
--- a/python/binaryview.py
+++ b/python/binaryview.py
@@ -68,6 +68,7 @@ from . import mediumlevelil
from . import highlevelil
from . import debuginfo
from . import flowgraph
+from . import project
# The following are imported as such to allow the type checker disambiguate the module name
# from properties and methods of the same name
from . import workflow as _workflow
@@ -76,6 +77,9 @@ from . import types as _types
from . import platform as _platform
from . import deprecation
from . import typecontainer
+from . import externallibrary
+from . import project
+
PathType = Union[str, os.PathLike]
InstructionsType = Generator[Tuple[List['_function.InstructionTextToken'], int], None, None]
@@ -2322,7 +2326,7 @@ class BinaryView:
return BinaryView(file_metadata=file_metadata, handle=view)
@staticmethod
- def load(source: Union[str, bytes, bytearray, 'databuffer.DataBuffer', 'os.PathLike', 'BinaryView'], update_analysis: Optional[bool] = True,
+ def load(source: Union[str, bytes, bytearray, 'databuffer.DataBuffer', 'os.PathLike', 'BinaryView', 'project.ProjectFile'], update_analysis: Optional[bool] = True,
progress_func: Optional[ProgressFuncType] = None, options: Mapping[str, Any] = {}) -> Optional['BinaryView']:
"""
``load`` opens, generates default load options (which are overridable), and returns the first available \
@@ -2371,6 +2375,8 @@ class BinaryView:
source = str(source)
if isinstance(source, BinaryView):
handle = core.BNLoadBinaryView(source.handle, update_analysis, progress_cfunc, metadata.Metadata(options).handle, source.file.has_database)
+ elif isinstance(source, project.ProjectFile):
+ handle = core.BNLoadProjectFile(source._handle, update_analysis, progress_cfunc, metadata.Metadata(options).handle)
elif isinstance(source, str):
handle = core.BNLoadFilename(source, update_analysis, progress_cfunc, metadata.Metadata(options).handle)
elif isinstance(source, bytes) or isinstance(source, bytearray) or isinstance(source, databuffer.DataBuffer):
@@ -2951,6 +2957,14 @@ class BinaryView:
def new_auto_function_analysis_suppressed(self, suppress: bool) -> None:
core.BNSetNewAutoFunctionAnalysisSuppressed(self.handle, suppress)
+ @property
+ def project(self) -> Optional['project.Project']:
+ return self.file.project
+
+ @property
+ def project_file(self) -> Optional['project.ProjectFile']:
+ return self.file.project_file
+
def _init(self, ctxt):
try:
return self.init()
@@ -8647,6 +8661,69 @@ class BinaryView:
def create_logger(self, logger_name:str) -> Logger:
return Logger(self.file.session_id, logger_name)
+ def add_external_library(self, name: str, backing_file: Optional['project.ProjectFile'] = None, auto: bool = False) -> externallibrary.ExternalLibrary:
+ file_handle = None
+ if backing_file is not None:
+ file_handle = backing_file._handle
+ handle = core.BNBinaryViewAddExternalLibrary(self.handle, name, file_handle, auto)
+ assert handle is not None, "core.BNBinaryViewAddExternalLibrary returned None"
+ return externallibrary.ExternalLibrary(handle)
+
+ def remove_external_library(self, name: str):
+ core.BNBinaryViewRemoveExternalLibrary(self.handle, name)
+
+ def get_external_library(self, name: str) -> Optional[externallibrary.ExternalLibrary]:
+ handle = core.BNBinaryViewGetExternalLibrary(self.handle, name)
+ if handle is None:
+ return None
+ return externallibrary.ExternalLibrary(handle)
+
+ def get_external_libraries(self) -> List[externallibrary.ExternalLibrary]:
+ count = ctypes.c_ulonglong(0)
+ handles = core.BNBinaryViewGetExternalLibraries(self.handle, count)
+ assert handles is not None, "core.BNBinaryViewGetExternalLibraries returned None"
+ result = []
+ try:
+ for i in range(count.value):
+ new_handle = core.BNNewExternalLibraryReference(handles[i])
+ assert new_handle is not None, "core.BNNewExternalLibraryReference returned None"
+ result.append(externallibrary.ExternalLibrary(new_handle))
+ return result
+ finally:
+ core.BNFreeExternalLibraryList(handles, count.value)
+
+ def add_external_location(self, symbol: '_types.CoreSymbol', library: Optional[externallibrary.ExternalLibrary], external_symbol: Optional[str], external_address: Optional[int], auto: bool = False) -> externallibrary.ExternalLocation:
+ c_addr = None
+ if external_address is not None:
+ c_addr = ctypes.c_ulonglong(external_address)
+
+ handle = core.BNBinaryViewAddExternalLocation(self.handle, symbol.handle, library._handle if library else None, external_symbol, c_addr, auto)
+ assert handle is not None, "core.BNBinaryViewAddExternalLocation returned None"
+ return externallibrary.ExternalLocation(handle)
+
+ def remove_external_location(self, symbol: '_types.CoreSymbol'):
+ core.BNBinaryViewRemoveExternalLocation(self.handle, symbol._handle)
+
+ def get_external_location(self, symbol: '_types.CoreSymbol') -> Optional[externallibrary.ExternalLocation]:
+ handle = core.BNBinaryViewGetExternalLocation(self.handle, symbol.handle)
+ if handle is None:
+ return None
+ return externallibrary.ExternalLocation(handle)
+
+ def get_external_locations(self) -> List[externallibrary.ExternalLocation]:
+ count = ctypes.c_ulonglong(0)
+ handles = core.BNBinaryViewGetExternalLocations(self.handle, count)
+ assert handles is not None, "core.BNBinaryViewGetExternalLocations returned None"
+ result = []
+ try:
+ for i in range(count.value):
+ new_handle = core.BNNewExternalLocationReference(handles[i])
+ assert new_handle is not None, "core.BNNewExternalLocationReference returned None"
+ result.append(externallibrary.ExternalLocation(handles[i]))
+ return result
+ finally:
+ core.BNFreeExternalLocationList(handles, count.value)
+
class BinaryReader:
"""
diff --git a/python/exceptions.py b/python/exceptions.py
index cb285253..a2ba197a 100644
--- a/python/exceptions.py
+++ b/python/exceptions.py
@@ -5,3 +5,7 @@ class RelocationWriteException(Exception):
class ILException(Exception):
""" Exception raised when IL operations fail """
pass
+
+class ProjectException(Exception):
+ """ Exception raised when project operations fail """
+ pass
diff --git a/python/externallibrary.py b/python/externallibrary.py
new file mode 100644
index 00000000..6ae7b806
--- /dev/null
+++ b/python/externallibrary.py
@@ -0,0 +1,125 @@
+# coding=utf-8
+# Copyright (c) 2015-2023 Vector 35 Inc
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to
+# deal in the Software without restriction, including without limitation the
+# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+# sell copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+# IN THE SOFTWARE.
+
+import ctypes
+
+from typing import Optional
+
+from . import _binaryninjacore as core
+from . import project
+from . import types
+
+
+class ExternalLibrary:
+ def __init__(self, handle: core.BNExternalLibrary):
+ self._handle = handle
+
+ def __del__(self):
+ if core is not None:
+ core.BNFreeExternalLibrary(self._handle)
+
+ def __repr__(self) -> str:
+ return f'<ExternalLibrary: {self.name}>'
+
+ def __str__(self) -> str:
+ return f'<ExternalLibrary: {self.name}>'
+
+ @property
+ def name(self) -> str:
+ return core.BNExternalLibraryGetName(self._handle) # type: ignore
+
+ @property
+ def backing_file(self) -> Optional[project.ProjectFile]:
+ handle = core.BNExternalLibraryGetBackingFile(self._handle)
+ if handle is None:
+ return None
+ return project.ProjectFile(handle)
+
+ @backing_file.setter
+ def backing_file(self, new_file: Optional[project.ProjectFile]):
+ new_file_handle = None
+ if new_file is not None:
+ new_file_handle = new_file._handle
+ core.BNExternalLibrarySetBackingFile(self._handle, new_file_handle)
+
+
+class ExternalLocation:
+ def __init__(self, handle: core.BNExternalLocation):
+ self._handle = handle
+
+ def __del__(self):
+ if core is not None:
+ core.BNFreeExternalLocation(self._handle)
+
+ def __repr__(self) -> str:
+ return f'<ExternalLocation: {self.internal_symbol}>'
+
+ def __str__(self) -> str:
+ return f'<ExternalLocation: {self.internal_symbol}>'
+
+ @property
+ def internal_symbol(self) -> 'types.CoreSymbol':
+ sym = core.BNExternalLocationGetInternalSymbol(self._handle)
+ assert sym is not None, "core.BNExternalLocationGetInternalSymbol returned None"
+ return types.CoreSymbol(sym)
+
+ @property
+ def has_address(self) -> bool:
+ return core.BNExternalLocationHasAddress(self._handle)
+
+ @property
+ def has_symbol(self) -> bool:
+ return core.BNExternalLocationHasSymbol(self._handle)
+
+ @property
+ def address(self) -> Optional[int]:
+ if not self.has_address:
+ return None
+ return core.BNExternalLocationGetAddress(self._handle)
+
+ @address.setter
+ def address(self, new_address: Optional[int]):
+ c_addr = None
+ if new_address is not None:
+ c_addr = ctypes.c_ulonglong(new_address)
+ return core.BNExternalLocationSetAddress(self._handle, c_addr)
+
+ @property
+ def symbol(self) -> Optional[str]:
+ if not self.has_symbol:
+ return None
+ return core.BNExternalLocationGetSymbol(self._handle)
+
+ @symbol.setter
+ def symbol(self, new_symbol: Optional[str]):
+ return core.BNExternalLocationSetSymbol(self._handle, new_symbol)
+
+ @property
+ def library(self) -> Optional[ExternalLibrary]:
+ handle = core.BNExternalLocationGetExternalLibrary(self._handle)
+ if handle is None:
+ return None
+ return ExternalLibrary(handle)
+
+ @library.setter
+ def library(self, new_library: Optional[ExternalLibrary]):
+ lib_handle = new_library._handle if new_library is not None else None
+ return core.BNExternalLocationSetExternalLibrary(self._handle, lib_handle)
diff --git a/python/filemetadata.py b/python/filemetadata.py
index ec0e16bb..e51436b1 100644
--- a/python/filemetadata.py
+++ b/python/filemetadata.py
@@ -31,6 +31,7 @@ from .log import log_error
from . import binaryview
from . import database
from . import deprecation
+from . import project
ProgressFuncType = Callable[[int, int], bool]
ViewName = str
@@ -298,6 +299,20 @@ class FileMetadata:
def snapshot_data_applied_without_error(self) -> bool:
return core.BNIsSnapshotDataAppliedWithoutError(self.handle)
+ @property
+ def project(self) -> Optional['project.Project']:
+ project_file = self.project_file
+ if project_file is None:
+ return None
+ return project_file.project
+
+ @property
+ def project_file(self) -> Optional['project.ProjectFile']:
+ handle = core.BNGetProjectFile(self.handle)
+ if handle is None:
+ return None
+ return project.ProjectFile(handle)
+
def close(self) -> None:
"""
Closes the underlying file handle. It is recommended that this is done in a
@@ -606,14 +621,6 @@ class FileMetadata:
return None
return binaryview.BinaryView(file_metadata=self, handle=view)
- def open_project(self) -> bool:
- return core.BNOpenProject(self.handle)
-
- def close_project(self) -> None:
- core.BNCloseProject(self.handle)
-
- def is_project_open(self) -> bool:
- return core.BNIsProjectOpen(self.handle)
@property
def existing_views(self) -> List[ViewName]:
diff --git a/python/project.py b/python/project.py
new file mode 100644
index 00000000..e1f7171d
--- /dev/null
+++ b/python/project.py
@@ -0,0 +1,411 @@
+# Copyright (c) 2015-2023 Vector 35 Inc
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to
+# deal in the Software without restriction, including without limitation the
+# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+# sell copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+# IN THE SOFTWARE.
+
+import ctypes
+
+from contextlib import contextmanager
+from os import PathLike
+from typing import Callable, List, Optional, Union
+
+from . import _binaryninjacore as core
+from .exceptions import ProjectException
+from .metadata import Metadata, MetadataValueType
+
+
+ProgressFuncType = Callable[[int, int], bool]
+AsPath = Union[PathLike, str]
+
+#TODO: notifications
+
+def nop(*args, **kwargs):
+ """
+ Function that just returns True, used as default for callbacks
+
+ :return: True
+ """
+ return True
+
+
+def wrap_progress(progress_func: ProgressFuncType):
+ """
+ Wraps a progress function in a ctypes function for passing to the FFI
+
+ :param progress_func: Python progress function
+ :return: Wrapped ctypes function
+ """
+ return ctypes.CFUNCTYPE(ctypes.c_bool, ctypes.c_void_p, ctypes.c_ulonglong, ctypes.c_ulonglong)(
+ lambda ctxt, cur, total: progress_func(cur, total))
+
+
+class ProjectFile:
+ def __init__(self, handle: core.BNProjectFileHandle):
+ self._handle = handle
+
+ def __del__(self):
+ if core is not None:
+ core.BNFreeProjectFile(self._handle)
+
+ def __repr__(self) -> str:
+ path = self.name
+ parent = self.folder
+ while parent is not None:
+ path = parent.name + '/' + path
+ parent = parent.parent
+ return f'<ProjectFile: {self.project.name}/{path}>'
+
+ def __str__(self) -> str:
+ path = self.name
+ parent = self.folder
+ while parent is not None:
+ path = parent.name + '/' + path
+ parent = parent.parent
+ return f'<ProjectFile: {self.project.name}/{path}>'
+
+ @property
+ def project(self):
+ proj_handle = core.BNProjectFileGetProject(self._handle)
+
+ if proj_handle is None:
+ raise ProjectException("Failed to get project for file")
+
+ return Project(handle=proj_handle)
+
+ @property
+ def path_on_disk(self) -> str:
+ return core.BNProjectFileGetPathOnDisk(self._handle) # type: ignore
+
+ @property
+ def exists_on_disk(self) -> bool:
+ return core.BNProjectFileExistsOnDisk(self._handle)
+
+ @property
+ def id(self) -> str:
+ return core.BNProjectFileGetId(self._handle) # type: ignore
+
+ @property
+ def name(self) -> str:
+ return core.BNProjectFileGetName(self._handle) # type: ignore
+
+ @name.setter
+ def name(self, new_name: str):
+ return core.BNProjectFileSetName(self._handle, new_name)
+
+ @property
+ def description(self) -> str:
+ return core.BNProjectFileGetDescription(self._handle) # type: ignore
+
+ @description.setter
+ def description(self, new_description: str):
+ return core.BNProjectFileSetDescription(self._handle, new_description)
+
+ @property
+ def folder(self) -> Optional['ProjectFolder']:
+ folder_handle = core.BNProjectFileGetFolder(self._handle)
+ if folder_handle is None:
+ return None
+ return ProjectFolder(handle=folder_handle)
+
+ @folder.setter
+ def folder(self, new_folder: Optional['ProjectFolder']):
+ folder_handle = None if new_folder is None else new_folder._handle
+ core.BNProjectFileSetFolder(self._handle, folder_handle)
+
+ def export(self, dest: AsPath) -> bool:
+ return core.BNProjectFileExport(self._handle, str(dest))
+
+
+class ProjectFolder:
+ def __init__(self, handle: core.BNProjectFolderHandle):
+ self._handle = handle
+
+ def __del__(self):
+ if core is not None:
+ core.BNFreeProjectFolder(self._handle)
+
+ def __repr__(self) -> str:
+ path = self.name
+ parent = self.parent
+ while parent is not None:
+ path = parent.name + '/' + path
+ parent = parent.parent
+ return f'<ProjectFolder: {self.project.name}/{path}>'
+
+ def __str__(self) -> str:
+ path = self.name
+ parent = self.parent
+ while parent is not None:
+ path = parent.name + '/' + path
+ parent = parent.parent
+ return f'<ProjectFolder: {self.project.name}/{path}>'
+
+ @property
+ def project(self):
+ proj_handle = core.BNProjectFolderGetProject(self._handle)
+
+ if proj_handle is None:
+ raise ProjectException("Failed to get project for folder")
+
+ return Project(handle=proj_handle)
+
+ @property
+ def id(self) -> str:
+ return core.BNProjectFolderGetId(self._handle) # type: ignore
+
+ @property
+ def name(self) -> str:
+ return core.BNProjectFolderGetName(self._handle) # type: ignore
+
+ @name.setter
+ def name(self, new_name: str):
+ return core.BNProjectFolderSetName(self._handle, new_name)
+
+ @property
+ def description(self) -> str:
+ return core.BNProjectFolderGetDescription(self._handle) # type: ignore
+
+ @description.setter
+ def description(self, new_description: str):
+ return core.BNProjectFolderSetDescription(self._handle, new_description)
+
+ @property
+ def parent(self) -> Optional['ProjectFolder']:
+ folder_handle = core.BNProjectFolderGetParent(self._handle)
+ if folder_handle is None:
+ return None
+ return ProjectFolder(handle=folder_handle)
+
+ @parent.setter
+ def parent(self, new_parent: Optional['ProjectFolder']):
+ parent_handle = None if new_parent is None else new_parent._handle
+ core.BNProjectFolderSetParent(self._handle, parent_handle)
+
+ def export(self, dest: AsPath, progress_func: ProgressFuncType = nop) -> bool:
+ return core.BNProjectFolderExport(self._handle, str(dest), None, wrap_progress(progress_func))
+
+
+class Project:
+ def __init__(self, handle: core.BNProjectHandle):
+ self._handle = handle
+
+ def __del__(self):
+ if core is not None:
+ core.BNFreeProject(self._handle)
+
+ def __repr__(self) -> str:
+ return f'<Project: {self.name}>'
+
+ def __str__(self) -> str:
+ return f'<Project: {self.name}>'
+
+ @staticmethod
+ def open_project(path: AsPath) -> 'Project':
+ project_handle = core.BNOpenProject(str(path))
+ if project_handle is None:
+ raise ProjectException("Failed to open project")
+ return Project(handle=project_handle)
+
+ @staticmethod
+ def create_project(path: AsPath, name: str) -> 'Project':
+ project_handle = core.BNCreateProject(str(path), name)
+ if project_handle is None:
+ raise ProjectException("Failed to create project")
+ return Project(handle=project_handle)
+
+ def open(self) -> bool:
+ return core.BNProjectOpen(self._handle)
+
+ def close(self) -> bool:
+ return core.BNProjectClose(self._handle)
+
+ @property
+ def id(self) -> str:
+ return core.BNProjectGetId(self._handle) # type: ignore
+
+ @property
+ def is_open(self) -> bool:
+ return core.BNProjectIsOpen(self._handle)
+
+ @property
+ def path(self) -> str:
+ return core.BNProjectGetPath(self._handle) # type: ignore
+
+ @property
+ def name(self) -> str:
+ return core.BNProjectGetName(self._handle) # type: ignore
+
+ @name.setter
+ def name(self, new_name: str):
+ core.BNProjectSetName(self._handle, new_name)
+
+ @property
+ def description(self) -> str:
+ return core.BNProjectGetDescription(self._handle) # type: ignore
+
+ @description.setter
+ def description(self, new_description: str):
+ core.BNProjectSetDescription(self._handle, new_description)
+
+ def query_metadata(self, key: str) -> MetadataValueType:
+ md_handle = core.BNProjectQueryMetadata(self._handle, key)
+ if md_handle is None:
+ raise KeyError(key)
+ return Metadata(handle=md_handle).value
+
+ def store_metadata(self, key: str, value: MetadataValueType):
+ _val = value
+ if not isinstance(_val, Metadata):
+ _val = Metadata(_val)
+ core.BNProjectStoreMetadata(self._handle, key, _val.handle)
+
+ def remove_metadata(self, key: str):
+ core.BNProjectRemoveMetadata(self._handle, key)
+
+ def create_folder_from_path(self, path: Union[PathLike, str], parent: Optional[ProjectFolder] = None, description: str = "", progress_func: ProgressFuncType = nop) -> ProjectFolder:
+ parent_handle = parent._handle if parent is not None else None
+ folder_handle = core.BNProjectCreateFolderFromPath(
+ project=self._handle,
+ path=str(path),
+ parent=parent_handle,
+ description=description,
+ ctxt=None,
+ progress=wrap_progress(progress_func)
+ )
+
+ if folder_handle is None:
+ raise ProjectException("Failed to create folder")
+
+ return ProjectFolder(handle=folder_handle)
+
+ def create_folder(self, parent: Optional[ProjectFolder], name: str, description: str = "") -> ProjectFolder:
+ parent_handle = parent._handle if parent is not None else None
+ folder_handle = core.BNProjectCreateFolder(
+ project=self._handle,
+ parent=parent_handle,
+ name=name,
+ description=description,
+ )
+
+ if folder_handle is None:
+ raise ProjectException("Failed to create folder")
+
+ return ProjectFolder(handle=folder_handle)
+
+ @property
+ def folders(self) -> List[ProjectFolder]:
+ count = ctypes.c_size_t()
+ value = core.BNProjectGetFolders(self._handle, count)
+ if value is None:
+ raise ProjectException("Failed to get list of project folders")
+ result = []
+ try:
+ for i in range(count.value):
+ folder_handle = core.BNNewProjectFolderReference(value[i])
+ if folder_handle is None:
+ raise ProjectException("core.BNNewProjectFolderReference returned None")
+ result.append(ProjectFolder(folder_handle))
+ return result
+ finally:
+ core.BNFreeProjectFolderList(value, count.value)
+
+ def get_folder_by_id(self, id: str) -> Optional[ProjectFolder]:
+ handle = core.BNProjectGetFolderById(self._handle, id)
+ if handle is None:
+ return None
+ folder = ProjectFolder(handle)
+ return folder
+
+ def push_folder(self, folder: ProjectFolder):
+ core.BNProjectPushFolder(self._handle, folder._handle)
+
+ def delete_folder(self, folder: ProjectFolder, progress_func: ProgressFuncType = nop):
+ core.BNProjectDeleteFolder(self._handle, folder._handle, None, wrap_progress(progress_func))
+
+ def create_file_from_path(self, path: AsPath, folder: Optional[ProjectFile], name: str, description: str = "", progress_func: ProgressFuncType = nop) -> ProjectFile:
+ folder_handle = folder._handle if folder is not None else None
+ file_handle = core.BNProjectCreateFileFromPath(
+ project=self._handle,
+ path=str(path),
+ folder=folder_handle,
+ name=name,
+ description=description,
+ ctxt=None,
+ progress=wrap_progress(progress_func)
+ )
+
+ if file_handle is None:
+ raise ProjectException("Failed to create file")
+
+ return ProjectFile(handle=file_handle)
+
+ def create_file(self, contents: bytes, folder: Optional[ProjectFile], name: str, description: str = "", progress_func: ProgressFuncType = nop) -> ProjectFile:
+ folder_handle = folder._handle if folder is not None else None
+ buf = (ctypes.c_ubyte * len(contents))()
+ ctypes.memmove(buf, contents, len(contents))
+ file_handle = core.BNProjectCreateFile(
+ project=self._handle,
+ contents=buf,
+ contentsSize=len(contents),
+ folder=folder_handle,
+ name=name,
+ description=description,
+ ctxt=None,
+ progress=wrap_progress(progress_func)
+ )
+
+ if file_handle is None:
+ raise ProjectException("Failed to create file")
+
+ return ProjectFile(handle=file_handle)
+
+ @property
+ def files(self) -> List[ProjectFile]:
+ count = ctypes.c_size_t()
+ value = core.BNProjectGetFiles(self._handle, count)
+ if value is None:
+ raise ProjectException("Failed to get list of project files")
+ result = []
+ try:
+ for i in range(count.value):
+ file_handle = core.BNNewProjectFileReference(value[i])
+ if file_handle is None:
+ raise ProjectException("core.BNNewProjectFileReference returned None")
+ result.append(ProjectFile(file_handle))
+ return result
+ finally:
+ core.BNFreeProjectFileList(value, count.value)
+
+ def get_file_by_id(self, id: str) -> Optional[ProjectFile]:
+ handle = core.BNProjectGetFileById(self._handle, id)
+ if handle is None:
+ return None
+ file = ProjectFile(handle)
+ return file
+
+ def push_file(self, file: ProjectFile):
+ core.BNProjectPushFile(self._handle, file._handle)
+
+ def delete_file(self, file: ProjectFile):
+ core.BNProjectDeleteFile(self._handle, file._handle)
+
+ @contextmanager
+ def bulk_operation(self):
+ core.BNProjectBeginBulkOperation(self._handle)
+ yield
+ core.BNProjectEndBulkOperation(self._handle)
diff --git a/python/scriptingprovider.py b/python/scriptingprovider.py
index 7042b988..d7d5b5dc 100644
--- a/python/scriptingprovider.py
+++ b/python/scriptingprovider.py
@@ -660,6 +660,7 @@ class PythonScriptingInstance(ScriptingInstance):
blacklisted_vars = {
"current_thread",
"current_view",
+ "current_project",
"bv",
"current_function",
"current_basic_block",
@@ -705,6 +706,7 @@ class PythonScriptingInstance(ScriptingInstance):
self.current_selection_begin = 0
self.current_selection_end = 0
self.current_dbg = None
+ self.current_project = None
# Selections that were current as of last issued command
self.active_view = None
@@ -821,6 +823,7 @@ from binaryninja import *
self.active_selection_begin = self.current_selection_begin
self.active_selection_end = self.current_selection_end
self.active_dbg = self.current_dbg
+ self.active_project = self.current_project
if self.active_view is not None:
self.active_file_offset = self.active_view.get_data_offset_for_address(self.active_addr)
else:
@@ -829,6 +832,7 @@ from binaryninja import *
self.locals.blacklist_enabled = False
self.locals["current_thread"] = self.interpreter
self.locals["current_view"] = self.active_view
+ self.locals["current_project"] = self.active_project
self.locals["bv"] = self.active_view
self.locals["current_function"] = self.active_func
self.locals["current_basic_block"] = self.active_block
@@ -895,10 +899,12 @@ from binaryninja import *
action_handler = None
view_frame = None
view = None
+ project = None
if context is not None:
action_handler = context.getCurrentActionHandler()
view_frame = context.getCurrentViewFrame()
view = context.getCurrentView()
+ project = context.getProject()
view_location = view_frame.getViewLocation() if view_frame is not None else None
action_context = None
@@ -970,6 +976,7 @@ from binaryninja import *
self.active_il_function = None
self.locals["current_ui_context"] = context
+ self.locals["current_project"] = project
self.locals["current_ui_view_frame"] = view_frame
self.locals["current_ui_view"] = view
self.locals["current_ui_action_handler"] = action_handler
@@ -983,6 +990,7 @@ from binaryninja import *
if not ui_locals_valid:
self.locals["current_ui_context"] = None
+ self.locals["current_project"] = None
self.locals["current_ui_view_frame"] = None
self.locals["current_ui_view"] = None
self.locals["current_ui_action_handler"] = None
@@ -1193,10 +1201,14 @@ from binaryninja import *
@abc.abstractmethod
def perform_set_current_binary_view(self, view):
self.interpreter.current_view = view
- if view is not None and self.debugger_imported:
- self.interpreter.current_dbg = self.DebuggerController(view)
+ if view is not None:
+ if self.debugger_imported:
+ self.interpreter.current_dbg = self.DebuggerController(view)
+ self.interpreter.current_project = view.project
+
else:
self.interpreter.current_dbg = None
+ self.interpreter.current_project = None
# This is a workaround that allows BN to properly free up resources when the last tab of a binary view is closed.
# Without this update, the interpreter local variables will NOT be updated until the user interacts with the
diff --git a/python/settings.py b/python/settings.py
index 8ddfc945..486d3d2f 100644
--- a/python/settings.py
+++ b/python/settings.py
@@ -50,7 +50,7 @@ class Settings:
================= ========================== ============== ==============================================
Default SettingsDefaultScope Lowest Settings Schema
User SettingsUserScope - <User Directory>/settings.json
- Project SettingsProjectScope - <Project Directory>/.binaryninja/settings.json
+ Project SettingsProjectScope - <Project Directory>/settings.json
Resource SettingsResourceScope Highest Raw BinaryView (Storage in BNDB)
================= ========================== ============== ==============================================