summaryrefslogtreecommitdiff
path: root/view/sharedcache/api/python
diff options
context:
space:
mode:
Diffstat (limited to 'view/sharedcache/api/python')
-rw-r--r--view/sharedcache/api/python/CMakeLists.txt50
-rw-r--r--view/sharedcache/api/python/__init__.py8
-rw-r--r--view/sharedcache/api/python/_sharedcachecore.py659
-rw-r--r--view/sharedcache/api/python/_sharedcachecore_template.py43
-rw-r--r--view/sharedcache/api/python/generator.cpp617
-rw-r--r--view/sharedcache/api/python/sharedcache.py260
-rw-r--r--view/sharedcache/api/python/sharedcache_enums.py14
7 files changed, 1651 insertions, 0 deletions
diff --git a/view/sharedcache/api/python/CMakeLists.txt b/view/sharedcache/api/python/CMakeLists.txt
new file mode 100644
index 00000000..c04a57ff
--- /dev/null
+++ b/view/sharedcache/api/python/CMakeLists.txt
@@ -0,0 +1,50 @@
+cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
+
+project(sharedcache-python-api)
+
+file(GLOB PYTHON_SOURCES ${PROJECT_SOURCE_DIR}/*.py)
+list(REMOVE_ITEM PYTHON_SOURCES ${PROJECT_SOURCE_DIR}/_sharedcachecore.py)
+list(REMOVE_ITEM PYTHON_SOURCES ${PROJECT_SOURCE_DIR}/enums.py)
+
+add_executable(sharedcache_generator
+ ${PROJECT_SOURCE_DIR}/generator.cpp)
+target_link_libraries(sharedcache_generator binaryninjaapi)
+target_include_directories(sharedcache_generator PUBLIC {PROJECT_SOURCE_DIR}/../../api)
+
+set_target_properties(sharedcache_generator PROPERTIES
+ CXX_STANDARD 17
+ CXX_STANDARD_REQUIRED ON
+ BUILD_WITH_INSTALL_RPATH OFF
+ RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR})
+
+if(BN_INTERNAL_BUILD)
+ set(PYTHON_OUTPUT_DIRECTORY ${BN_RESOURCE_DIR}/python/binaryninja/sharedcache/)
+else()
+ set(PYTHON_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/out/plugins/sharedcache/)
+endif()
+
+if(WIN32)
+ if (BN_INTERNAL_BUILD)
+ add_custom_command(TARGET sharedcache_generator PRE_BUILD
+ COMMAND ${CMAKE_COMMAND} -E copy ${BN_CORE_OUTPUT_DIR}/binaryninjacore.dll ${PROJECT_BINARY_DIR}/)
+ else()
+ add_custom_command(TARGET sharedcache_generator PRE_BUILD
+ COMMAND ${CMAKE_COMMAND} -E copy ${BN_INSTALL_DIR}/binaryninjacore.dll ${PROJECT_BINARY_DIR}/)
+ endif()
+endif()
+
+add_custom_target(sharedcache_generator_copy ALL
+ BYPRODUCTS ${PROJECT_SOURCE_DIR}/_sharedcachecore.py ${PROJECT_SOURCE_DIR}/enums.py
+ DEPENDS ${PYTHON_SOURCES} ${PROJECT_SOURCE_DIR}/../sharedcachecore.h $<TARGET_FILE:sharedcache_generator>
+ COMMAND ${CMAKE_COMMAND} -E echo "Copying SharedCache Python Sources"
+ COMMAND ${CMAKE_COMMAND} -E make_directory ${PYTHON_OUTPUT_DIRECTORY}
+ COMMAND ${CMAKE_COMMAND} -E env ASAN_OPTIONS=detect_leaks=0 $<TARGET_FILE:sharedcache_generator>
+ ${PROJECT_SOURCE_DIR}/../sharedcachecore.h
+ ${PROJECT_SOURCE_DIR}/_sharedcachecore.py
+ ${PROJECT_SOURCE_DIR}/_sharedcachecore_template.py
+ ${PROJECT_SOURCE_DIR}/sharedcache_enums.py
+
+ COMMAND ${CMAKE_COMMAND} -E copy ${PYTHON_SOURCES} ${PYTHON_OUTPUT_DIRECTORY}
+ COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_SOURCE_DIR}/_sharedcachecore.py ${PYTHON_OUTPUT_DIRECTORY}
+ COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_SOURCE_DIR}/sharedcache_enums.py ${PYTHON_OUTPUT_DIRECTORY})
+
diff --git a/view/sharedcache/api/python/__init__.py b/view/sharedcache/api/python/__init__.py
new file mode 100644
index 00000000..1f65af93
--- /dev/null
+++ b/view/sharedcache/api/python/__init__.py
@@ -0,0 +1,8 @@
+import os
+
+from binaryninja._binaryninjacore import BNGetUserPluginDirectory
+user_plugin_dir = os.path.realpath(BNGetUserPluginDirectory())
+current_path = os.path.realpath(__file__)
+
+from .sharedcache import *
+
diff --git a/view/sharedcache/api/python/_sharedcachecore.py b/view/sharedcache/api/python/_sharedcachecore.py
new file mode 100644
index 00000000..93228468
--- /dev/null
+++ b/view/sharedcache/api/python/_sharedcachecore.py
@@ -0,0 +1,659 @@
+import binaryninja
+import ctypes, os
+
+from typing import Optional
+from . import sharedcache_enums
+# Load core module
+import platform
+core = None
+core_platform = platform.system()
+
+# By the time the debugger is loaded, binaryninja has not fully initialized.
+# So we cannot call binaryninja.bundled_plugin_path()
+from binaryninja._binaryninjacore import BNGetBundledPluginDirectory, BNFreeString
+if core_platform == "Darwin":
+ _base_path = BNGetBundledPluginDirectory()
+ core = ctypes.CDLL(os.path.join(_base_path, "libsharedcache.dylib"))
+
+elif core_platform == "Linux":
+ _base_path = BNGetBundledPluginDirectory()
+ core = ctypes.CDLL(os.path.join(_base_path, "libsharedcache.so"))
+
+elif (core_platform == "Windows") or (core_platform.find("CYGWIN_NT") == 0):
+ _base_path = BNGetBundledPluginDirectory()
+ core = ctypes.CDLL(os.path.join(_base_path, "sharedcache.dll"))
+else:
+ raise Exception("OS not supported")
+
+def cstr(var) -> Optional[ctypes.c_char_p]:
+ if var is None:
+ return None
+ if isinstance(var, bytes):
+ return var
+ return var.encode("utf-8")
+
+def pyNativeStr(arg):
+ if isinstance(arg, str):
+ return arg
+ else:
+ return arg.decode('utf8')
+
+def free_string(value:ctypes.c_char_p) -> None:
+ BNFreeString(ctypes.cast(value, ctypes.POINTER(ctypes.c_byte)))
+
+# Type definitions
+from binaryninja._binaryninjacore import BNBinaryView, BNBinaryViewHandle
+class BNDSCBackingCache(ctypes.Structure):
+ @property
+ def path(self):
+ return pyNativeStr(self._path)
+ @path.setter
+ def path(self, value):
+ self._path = cstr(value)
+BNDSCBackingCacheHandle = ctypes.POINTER(BNDSCBackingCache)
+class BNDSCBackingCacheMapping(ctypes.Structure):
+ pass
+BNDSCBackingCacheMappingHandle = ctypes.POINTER(BNDSCBackingCacheMapping)
+class BNDSCImage(ctypes.Structure):
+ @property
+ def name(self):
+ return pyNativeStr(self._name)
+ @name.setter
+ def name(self, value):
+ self._name = cstr(value)
+BNDSCImageHandle = ctypes.POINTER(BNDSCImage)
+class BNDSCImageMemoryMapping(ctypes.Structure):
+ @property
+ def filePath(self):
+ return pyNativeStr(self._filePath)
+ @filePath.setter
+ def filePath(self, value):
+ self._filePath = cstr(value)
+ @property
+ def name(self):
+ return pyNativeStr(self._name)
+ @name.setter
+ def name(self, value):
+ self._name = cstr(value)
+BNDSCImageMemoryMappingHandle = ctypes.POINTER(BNDSCImageMemoryMapping)
+class BNDSCMappedMemoryRegion(ctypes.Structure):
+ @property
+ def name(self):
+ return pyNativeStr(self._name)
+ @name.setter
+ def name(self, value):
+ self._name = cstr(value)
+BNDSCMappedMemoryRegionHandle = ctypes.POINTER(BNDSCMappedMemoryRegion)
+class BNDSCMemoryUsageInfo(ctypes.Structure):
+ pass
+BNDSCMemoryUsageInfoHandle = ctypes.POINTER(BNDSCMemoryUsageInfo)
+class BNDSCSymbolRep(ctypes.Structure):
+ @property
+ def name(self):
+ return pyNativeStr(self._name)
+ @name.setter
+ def name(self, value):
+ self._name = cstr(value)
+ @property
+ def image(self):
+ return pyNativeStr(self._image)
+ @image.setter
+ def image(self, value):
+ self._image = cstr(value)
+BNDSCSymbolRepHandle = ctypes.POINTER(BNDSCSymbolRep)
+DSCViewLoadProgressEnum = ctypes.c_int
+DSCViewStateEnum = ctypes.c_int
+class BNSharedCache(ctypes.Structure):
+ pass
+BNSharedCacheHandle = ctypes.POINTER(BNSharedCache)
+
+# Structure definitions
+BNDSCBackingCache._fields_ = [
+ ("_path", ctypes.c_char_p),
+ ("isPrimary", ctypes.c_bool),
+ ("mappings", ctypes.POINTER(BNDSCBackingCacheMapping)),
+ ("mappingCount", ctypes.c_ulonglong),
+ ]
+BNDSCBackingCacheMapping._fields_ = [
+ ("vmAddress", ctypes.c_ulonglong),
+ ("size", ctypes.c_ulonglong),
+ ("fileOffset", ctypes.c_ulonglong),
+ ]
+BNDSCImage._fields_ = [
+ ("_name", ctypes.c_char_p),
+ ("headerAddress", ctypes.c_ulonglong),
+ ("mappings", ctypes.POINTER(BNDSCImageMemoryMapping)),
+ ("mappingCount", ctypes.c_ulonglong),
+ ]
+BNDSCImageMemoryMapping._fields_ = [
+ ("_filePath", ctypes.c_char_p),
+ ("_name", ctypes.c_char_p),
+ ("vmAddress", ctypes.c_ulonglong),
+ ("size", ctypes.c_ulonglong),
+ ("loaded", ctypes.c_bool),
+ ("rawViewOffset", ctypes.c_ulonglong),
+ ]
+BNDSCMappedMemoryRegion._fields_ = [
+ ("vmAddress", ctypes.c_ulonglong),
+ ("size", ctypes.c_ulonglong),
+ ("_name", ctypes.c_char_p),
+ ]
+BNDSCMemoryUsageInfo._fields_ = [
+ ("sharedCacheRefs", ctypes.c_ulonglong),
+ ("mmapRefs", ctypes.c_ulonglong),
+ ]
+BNDSCSymbolRep._fields_ = [
+ ("address", ctypes.c_ulonglong),
+ ("_name", ctypes.c_char_p),
+ ("_image", ctypes.c_char_p),
+ ]
+
+# Function definitions
+# -------------------------------------------------------
+# _BNDSCFindSymbolAtAddressAndApplyToAddress
+
+_BNDSCFindSymbolAtAddressAndApplyToAddress = core.BNDSCFindSymbolAtAddressAndApplyToAddress
+_BNDSCFindSymbolAtAddressAndApplyToAddress.restype = None
+_BNDSCFindSymbolAtAddressAndApplyToAddress.argtypes = [
+ ctypes.POINTER(BNSharedCache),
+ ctypes.c_ulonglong,
+ ctypes.c_ulonglong,
+ ctypes.c_bool,
+ ]
+
+
+# noinspection PyPep8Naming
+def BNDSCFindSymbolAtAddressAndApplyToAddress(
+ cache: ctypes.POINTER(BNSharedCache),
+ symbolLocation: int,
+ targetLocation: int,
+ triggerReanalysis: bool
+ ) -> None:
+ return _BNDSCFindSymbolAtAddressAndApplyToAddress(cache, symbolLocation, targetLocation, triggerReanalysis)
+
+
+# -------------------------------------------------------
+# _BNDSCViewFastGetBackingCacheCount
+
+_BNDSCViewFastGetBackingCacheCount = core.BNDSCViewFastGetBackingCacheCount
+_BNDSCViewFastGetBackingCacheCount.restype = ctypes.c_ulonglong
+_BNDSCViewFastGetBackingCacheCount.argtypes = [
+ ctypes.POINTER(BNBinaryView),
+ ]
+
+
+# noinspection PyPep8Naming
+def BNDSCViewFastGetBackingCacheCount(
+ view: ctypes.POINTER(BNBinaryView)
+ ) -> int:
+ return _BNDSCViewFastGetBackingCacheCount(view)
+
+
+# -------------------------------------------------------
+# _BNDSCViewFreeAllImages
+
+_BNDSCViewFreeAllImages = core.BNDSCViewFreeAllImages
+_BNDSCViewFreeAllImages.restype = None
+_BNDSCViewFreeAllImages.argtypes = [
+ ctypes.POINTER(BNDSCImage),
+ ctypes.c_ulonglong,
+ ]
+
+
+# noinspection PyPep8Naming
+def BNDSCViewFreeAllImages(
+ images: ctypes.POINTER(BNDSCImage),
+ count: int
+ ) -> None:
+ return _BNDSCViewFreeAllImages(images, count)
+
+
+# -------------------------------------------------------
+# _BNDSCViewFreeBackingCaches
+
+_BNDSCViewFreeBackingCaches = core.BNDSCViewFreeBackingCaches
+_BNDSCViewFreeBackingCaches.restype = None
+_BNDSCViewFreeBackingCaches.argtypes = [
+ ctypes.POINTER(BNDSCBackingCache),
+ ctypes.c_ulonglong,
+ ]
+
+
+# noinspection PyPep8Naming
+def BNDSCViewFreeBackingCaches(
+ caches: ctypes.POINTER(BNDSCBackingCache),
+ count: int
+ ) -> None:
+ return _BNDSCViewFreeBackingCaches(caches, count)
+
+
+# -------------------------------------------------------
+# _BNDSCViewFreeLoadedRegions
+
+_BNDSCViewFreeLoadedRegions = core.BNDSCViewFreeLoadedRegions
+_BNDSCViewFreeLoadedRegions.restype = None
+_BNDSCViewFreeLoadedRegions.argtypes = [
+ ctypes.POINTER(BNDSCMappedMemoryRegion),
+ ctypes.c_ulonglong,
+ ]
+
+
+# noinspection PyPep8Naming
+def BNDSCViewFreeLoadedRegions(
+ images: ctypes.POINTER(BNDSCMappedMemoryRegion),
+ count: int
+ ) -> None:
+ return _BNDSCViewFreeLoadedRegions(images, count)
+
+
+# -------------------------------------------------------
+# _BNDSCViewFreeSymbols
+
+_BNDSCViewFreeSymbols = core.BNDSCViewFreeSymbols
+_BNDSCViewFreeSymbols.restype = None
+_BNDSCViewFreeSymbols.argtypes = [
+ ctypes.POINTER(BNDSCSymbolRep),
+ ctypes.c_ulonglong,
+ ]
+
+
+# noinspection PyPep8Naming
+def BNDSCViewFreeSymbols(
+ symbols: ctypes.POINTER(BNDSCSymbolRep),
+ count: int
+ ) -> None:
+ return _BNDSCViewFreeSymbols(symbols, count)
+
+
+# -------------------------------------------------------
+# _BNDSCViewGetAllImages
+
+_BNDSCViewGetAllImages = core.BNDSCViewGetAllImages
+_BNDSCViewGetAllImages.restype = ctypes.POINTER(BNDSCImage)
+_BNDSCViewGetAllImages.argtypes = [
+ ctypes.POINTER(BNSharedCache),
+ ctypes.POINTER(ctypes.c_ulonglong),
+ ]
+
+
+# noinspection PyPep8Naming
+def BNDSCViewGetAllImages(
+ cache: ctypes.POINTER(BNSharedCache),
+ count: ctypes.POINTER(ctypes.c_ulonglong)
+ ) -> Optional[ctypes.POINTER(BNDSCImage)]:
+ result = _BNDSCViewGetAllImages(cache, count)
+ if not result:
+ return None
+ return result
+
+
+# -------------------------------------------------------
+# _BNDSCViewGetBackingCaches
+
+_BNDSCViewGetBackingCaches = core.BNDSCViewGetBackingCaches
+_BNDSCViewGetBackingCaches.restype = ctypes.POINTER(BNDSCBackingCache)
+_BNDSCViewGetBackingCaches.argtypes = [
+ ctypes.POINTER(BNSharedCache),
+ ctypes.POINTER(ctypes.c_ulonglong),
+ ]
+
+
+# noinspection PyPep8Naming
+def BNDSCViewGetBackingCaches(
+ cache: ctypes.POINTER(BNSharedCache),
+ count: ctypes.POINTER(ctypes.c_ulonglong)
+ ) -> Optional[ctypes.POINTER(BNDSCBackingCache)]:
+ result = _BNDSCViewGetBackingCaches(cache, count)
+ if not result:
+ return None
+ return result
+
+
+# -------------------------------------------------------
+# _BNDSCViewGetImageHeaderForAddress
+
+_BNDSCViewGetImageHeaderForAddress = core.BNDSCViewGetImageHeaderForAddress
+_BNDSCViewGetImageHeaderForAddress.restype = ctypes.POINTER(ctypes.c_byte)
+_BNDSCViewGetImageHeaderForAddress.argtypes = [
+ ctypes.POINTER(BNSharedCache),
+ ctypes.c_ulonglong,
+ ]
+
+
+# noinspection PyPep8Naming
+def BNDSCViewGetImageHeaderForAddress(
+ cache: ctypes.POINTER(BNSharedCache),
+ address: int
+ ) -> Optional[Optional[str]]:
+ result = _BNDSCViewGetImageHeaderForAddress(cache, address)
+ if not result:
+ return None
+ string = str(pyNativeStr(ctypes.cast(result, ctypes.c_char_p).value))
+ BNFreeString(result)
+ return string
+
+
+# -------------------------------------------------------
+# _BNDSCViewGetImageHeaderForName
+
+_BNDSCViewGetImageHeaderForName = core.BNDSCViewGetImageHeaderForName
+_BNDSCViewGetImageHeaderForName.restype = ctypes.POINTER(ctypes.c_byte)
+_BNDSCViewGetImageHeaderForName.argtypes = [
+ ctypes.POINTER(BNSharedCache),
+ ctypes.c_char_p,
+ ]
+
+
+# noinspection PyPep8Naming
+def BNDSCViewGetImageHeaderForName(
+ cache: ctypes.POINTER(BNSharedCache),
+ name: Optional[str]
+ ) -> Optional[Optional[str]]:
+ result = _BNDSCViewGetImageHeaderForName(cache, cstr(name))
+ if not result:
+ return None
+ string = str(pyNativeStr(ctypes.cast(result, ctypes.c_char_p).value))
+ BNFreeString(result)
+ return string
+
+
+# -------------------------------------------------------
+# _BNDSCViewGetImageNameForAddress
+
+_BNDSCViewGetImageNameForAddress = core.BNDSCViewGetImageNameForAddress
+_BNDSCViewGetImageNameForAddress.restype = ctypes.POINTER(ctypes.c_byte)
+_BNDSCViewGetImageNameForAddress.argtypes = [
+ ctypes.POINTER(BNSharedCache),
+ ctypes.c_ulonglong,
+ ]
+
+
+# noinspection PyPep8Naming
+def BNDSCViewGetImageNameForAddress(
+ cache: ctypes.POINTER(BNSharedCache),
+ address: int
+ ) -> Optional[Optional[str]]:
+ result = _BNDSCViewGetImageNameForAddress(cache, address)
+ if not result:
+ return None
+ string = str(pyNativeStr(ctypes.cast(result, ctypes.c_char_p).value))
+ BNFreeString(result)
+ return string
+
+
+# -------------------------------------------------------
+# _BNDSCViewGetInstallNames
+
+_BNDSCViewGetInstallNames = core.BNDSCViewGetInstallNames
+_BNDSCViewGetInstallNames.restype = ctypes.POINTER(ctypes.c_char_p)
+_BNDSCViewGetInstallNames.argtypes = [
+ ctypes.POINTER(BNSharedCache),
+ ctypes.POINTER(ctypes.c_ulonglong),
+ ]
+
+
+# noinspection PyPep8Naming
+def BNDSCViewGetInstallNames(
+ cache: ctypes.POINTER(BNSharedCache),
+ count: ctypes.POINTER(ctypes.c_ulonglong)
+ ) -> Optional[ctypes.POINTER(ctypes.c_char_p)]:
+ result = _BNDSCViewGetInstallNames(cache, count)
+ if not result:
+ return None
+ return result
+
+
+# -------------------------------------------------------
+# _BNDSCViewGetLoadProgress
+
+_BNDSCViewGetLoadProgress = core.BNDSCViewGetLoadProgress
+_BNDSCViewGetLoadProgress.restype = DSCViewLoadProgressEnum
+_BNDSCViewGetLoadProgress.argtypes = [
+ ctypes.c_ulonglong,
+ ]
+
+
+# noinspection PyPep8Naming
+def BNDSCViewGetLoadProgress(
+ sessionID: int
+ ) -> DSCViewLoadProgressEnum:
+ return _BNDSCViewGetLoadProgress(sessionID)
+
+
+# -------------------------------------------------------
+# _BNDSCViewGetLoadedRegions
+
+_BNDSCViewGetLoadedRegions = core.BNDSCViewGetLoadedRegions
+_BNDSCViewGetLoadedRegions.restype = ctypes.POINTER(BNDSCMappedMemoryRegion)
+_BNDSCViewGetLoadedRegions.argtypes = [
+ ctypes.POINTER(BNSharedCache),
+ ctypes.POINTER(ctypes.c_ulonglong),
+ ]
+
+
+# noinspection PyPep8Naming
+def BNDSCViewGetLoadedRegions(
+ cache: ctypes.POINTER(BNSharedCache),
+ count: ctypes.POINTER(ctypes.c_ulonglong)
+ ) -> Optional[ctypes.POINTER(BNDSCMappedMemoryRegion)]:
+ result = _BNDSCViewGetLoadedRegions(cache, count)
+ if not result:
+ return None
+ return result
+
+
+# -------------------------------------------------------
+# _BNDSCViewGetMemoryUsageInfo
+
+_BNDSCViewGetMemoryUsageInfo = core.BNDSCViewGetMemoryUsageInfo
+_BNDSCViewGetMemoryUsageInfo.restype = BNDSCMemoryUsageInfo
+_BNDSCViewGetMemoryUsageInfo.argtypes = [
+ ]
+
+
+# noinspection PyPep8Naming
+def BNDSCViewGetMemoryUsageInfo(
+ ) -> BNDSCMemoryUsageInfo:
+ return _BNDSCViewGetMemoryUsageInfo()
+
+
+# -------------------------------------------------------
+# _BNDSCViewGetNameForAddress
+
+_BNDSCViewGetNameForAddress = core.BNDSCViewGetNameForAddress
+_BNDSCViewGetNameForAddress.restype = ctypes.POINTER(ctypes.c_byte)
+_BNDSCViewGetNameForAddress.argtypes = [
+ ctypes.POINTER(BNSharedCache),
+ ctypes.c_ulonglong,
+ ]
+
+
+# noinspection PyPep8Naming
+def BNDSCViewGetNameForAddress(
+ cache: ctypes.POINTER(BNSharedCache),
+ address: int
+ ) -> Optional[Optional[str]]:
+ result = _BNDSCViewGetNameForAddress(cache, address)
+ if not result:
+ return None
+ string = str(pyNativeStr(ctypes.cast(result, ctypes.c_char_p).value))
+ BNFreeString(result)
+ return string
+
+
+# -------------------------------------------------------
+# _BNDSCViewGetState
+
+_BNDSCViewGetState = core.BNDSCViewGetState
+_BNDSCViewGetState.restype = DSCViewStateEnum
+_BNDSCViewGetState.argtypes = [
+ ctypes.POINTER(BNSharedCache),
+ ]
+
+
+# noinspection PyPep8Naming
+def BNDSCViewGetState(
+ cache: ctypes.POINTER(BNSharedCache)
+ ) -> DSCViewStateEnum:
+ return _BNDSCViewGetState(cache)
+
+
+# -------------------------------------------------------
+# _BNDSCViewLoadAllSymbolsAndWait
+
+_BNDSCViewLoadAllSymbolsAndWait = core.BNDSCViewLoadAllSymbolsAndWait
+_BNDSCViewLoadAllSymbolsAndWait.restype = ctypes.POINTER(BNDSCSymbolRep)
+_BNDSCViewLoadAllSymbolsAndWait.argtypes = [
+ ctypes.POINTER(BNSharedCache),
+ ctypes.POINTER(ctypes.c_ulonglong),
+ ]
+
+
+# noinspection PyPep8Naming
+def BNDSCViewLoadAllSymbolsAndWait(
+ cache: ctypes.POINTER(BNSharedCache),
+ count: ctypes.POINTER(ctypes.c_ulonglong)
+ ) -> Optional[ctypes.POINTER(BNDSCSymbolRep)]:
+ result = _BNDSCViewLoadAllSymbolsAndWait(cache, count)
+ if not result:
+ return None
+ return result
+
+
+# -------------------------------------------------------
+# _BNDSCViewLoadImageContainingAddress
+
+_BNDSCViewLoadImageContainingAddress = core.BNDSCViewLoadImageContainingAddress
+_BNDSCViewLoadImageContainingAddress.restype = ctypes.c_bool
+_BNDSCViewLoadImageContainingAddress.argtypes = [
+ ctypes.POINTER(BNSharedCache),
+ ctypes.c_ulonglong,
+ ]
+
+
+# noinspection PyPep8Naming
+def BNDSCViewLoadImageContainingAddress(
+ cache: ctypes.POINTER(BNSharedCache),
+ address: int
+ ) -> bool:
+ return _BNDSCViewLoadImageContainingAddress(cache, address)
+
+
+# -------------------------------------------------------
+# _BNDSCViewLoadImageWithInstallName
+
+_BNDSCViewLoadImageWithInstallName = core.BNDSCViewLoadImageWithInstallName
+_BNDSCViewLoadImageWithInstallName.restype = ctypes.c_bool
+_BNDSCViewLoadImageWithInstallName.argtypes = [
+ ctypes.POINTER(BNSharedCache),
+ ctypes.c_char_p,
+ ]
+
+
+# noinspection PyPep8Naming
+def BNDSCViewLoadImageWithInstallName(
+ cache: ctypes.POINTER(BNSharedCache),
+ name: Optional[str]
+ ) -> bool:
+ return _BNDSCViewLoadImageWithInstallName(cache, cstr(name))
+
+
+# -------------------------------------------------------
+# _BNDSCViewLoadSectionAtAddress
+
+_BNDSCViewLoadSectionAtAddress = core.BNDSCViewLoadSectionAtAddress
+_BNDSCViewLoadSectionAtAddress.restype = ctypes.c_bool
+_BNDSCViewLoadSectionAtAddress.argtypes = [
+ ctypes.POINTER(BNSharedCache),
+ ctypes.c_ulonglong,
+ ]
+
+
+# noinspection PyPep8Naming
+def BNDSCViewLoadSectionAtAddress(
+ cache: ctypes.POINTER(BNSharedCache),
+ name: int
+ ) -> bool:
+ return _BNDSCViewLoadSectionAtAddress(cache, name)
+
+
+# -------------------------------------------------------
+# _BNDSCViewLoadedImageCount
+
+_BNDSCViewLoadedImageCount = core.BNDSCViewLoadedImageCount
+_BNDSCViewLoadedImageCount.restype = ctypes.c_ulonglong
+_BNDSCViewLoadedImageCount.argtypes = [
+ ctypes.POINTER(BNSharedCache),
+ ]
+
+
+# noinspection PyPep8Naming
+def BNDSCViewLoadedImageCount(
+ cache: ctypes.POINTER(BNSharedCache)
+ ) -> int:
+ return _BNDSCViewLoadedImageCount(cache)
+
+
+# -------------------------------------------------------
+# _BNFreeSharedCacheReference
+
+_BNFreeSharedCacheReference = core.BNFreeSharedCacheReference
+_BNFreeSharedCacheReference.restype = None
+_BNFreeSharedCacheReference.argtypes = [
+ ctypes.POINTER(BNSharedCache),
+ ]
+
+
+# noinspection PyPep8Naming
+def BNFreeSharedCacheReference(
+ cache: ctypes.POINTER(BNSharedCache)
+ ) -> None:
+ return _BNFreeSharedCacheReference(cache)
+
+
+# -------------------------------------------------------
+# _BNGetSharedCache
+
+_BNGetSharedCache = core.BNGetSharedCache
+_BNGetSharedCache.restype = ctypes.POINTER(BNSharedCache)
+_BNGetSharedCache.argtypes = [
+ ctypes.POINTER(BNBinaryView),
+ ]
+
+
+# noinspection PyPep8Naming
+def BNGetSharedCache(
+ data: ctypes.POINTER(BNBinaryView)
+ ) -> Optional[ctypes.POINTER(BNSharedCache)]:
+ result = _BNGetSharedCache(data)
+ if not result:
+ return None
+ return result
+
+
+# -------------------------------------------------------
+# _BNNewSharedCacheReference
+
+_BNNewSharedCacheReference = core.BNNewSharedCacheReference
+_BNNewSharedCacheReference.restype = ctypes.POINTER(BNSharedCache)
+_BNNewSharedCacheReference.argtypes = [
+ ctypes.POINTER(BNSharedCache),
+ ]
+
+
+# noinspection PyPep8Naming
+def BNNewSharedCacheReference(
+ cache: ctypes.POINTER(BNSharedCache)
+ ) -> Optional[ctypes.POINTER(BNSharedCache)]:
+ result = _BNNewSharedCacheReference(cache)
+ if not result:
+ return None
+ return result
+
+
+
+# Helper functions
+def handle_of_type(value, handle_type):
+ if isinstance(value, ctypes.POINTER(handle_type)) or isinstance(value, ctypes.c_void_p):
+ return ctypes.cast(value, ctypes.POINTER(handle_type))
+ raise ValueError('expected pointer to %s' % str(handle_type))
diff --git a/view/sharedcache/api/python/_sharedcachecore_template.py b/view/sharedcache/api/python/_sharedcachecore_template.py
new file mode 100644
index 00000000..6b5b174f
--- /dev/null
+++ b/view/sharedcache/api/python/_sharedcachecore_template.py
@@ -0,0 +1,43 @@
+import binaryninja
+import ctypes, os
+
+from typing import Optional
+from . import sharedcache_enums
+# Load core module
+import platform
+core = None
+core_platform = platform.system()
+
+# By the time the debugger is loaded, binaryninja has not fully initialized.
+# So we cannot call binaryninja.bundled_plugin_path()
+from binaryninja._binaryninjacore import BNGetBundledPluginDirectory, BNFreeString
+if core_platform == "Darwin":
+ _base_path = BNGetBundledPluginDirectory()
+ core = ctypes.CDLL(os.path.join(_base_path, "libsharedcache.dylib"))
+
+elif core_platform == "Linux":
+ _base_path = BNGetBundledPluginDirectory()
+ core = ctypes.CDLL(os.path.join(_base_path, "libsharedcache.so"))
+
+elif (core_platform == "Windows") or (core_platform.find("CYGWIN_NT") == 0):
+ _base_path = BNGetBundledPluginDirectory()
+ core = ctypes.CDLL(os.path.join(_base_path, "sharedcache.dll"))
+else:
+ raise Exception("OS not supported")
+
+def cstr(var) -> Optional[ctypes.c_char_p]:
+ if var is None:
+ return None
+ if isinstance(var, bytes):
+ return var
+ return var.encode("utf-8")
+
+def pyNativeStr(arg):
+ if isinstance(arg, str):
+ return arg
+ else:
+ return arg.decode('utf8')
+
+def free_string(value:ctypes.c_char_p) -> None:
+ BNFreeString(ctypes.cast(value, ctypes.POINTER(ctypes.c_byte)))
+
diff --git a/view/sharedcache/api/python/generator.cpp b/view/sharedcache/api/python/generator.cpp
new file mode 100644
index 00000000..91ff88c0
--- /dev/null
+++ b/view/sharedcache/api/python/generator.cpp
@@ -0,0 +1,617 @@
+/*
+Copyright 2020-2024 Vector 35 Inc.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+
+
+#include <stdio.h>
+#include <inttypes.h>
+#include "binaryninjaapi.h"
+
+using namespace BinaryNinja;
+using namespace std;
+
+
+map<string, string> g_pythonKeywordReplacements = {
+ {"False", "False_"},
+ {"True", "True_"},
+ {"None", "None_"},
+ {"and", "and_"},
+ {"as", "as_"},
+ {"assert", "assert_"},
+ {"async", "async_"},
+ {"await", "await_"},
+ {"break", "break_"},
+ {"class", "class_"},
+ {"continue", "continue_"},
+ {"def", "def_"},
+ {"del", "del_"},
+ {"elif", "elif_"},
+ {"else", "else_"},
+ {"except", "except_"},
+ {"finally", "finally_"},
+ {"for", "for_"},
+ {"from", "from_"},
+ {"global", "global_"},
+ {"if", "if_"},
+ {"import", "import_"},
+ {"in", "in_"},
+ {"is", "is_"},
+ {"lambda", "lambda_"},
+ {"nonlocal", "nonlocal_"},
+ {"not", "not_"},
+ {"or", "or_"},
+ {"pass", "pass_"},
+ {"raise", "raise_"},
+ {"return", "return_"},
+ {"try", "try_"},
+ {"while", "while_"},
+ {"with", "with_"},
+ {"yield", "yield_"},
+};
+
+
+void OutputType(FILE* out, Type* type, bool isReturnType = false, bool isCallback = false)
+{
+ switch (type->GetClass())
+ {
+ case BoolTypeClass:
+ fprintf(out, "ctypes.c_bool");
+ break;
+ case IntegerTypeClass:
+ switch (type->GetWidth())
+ {
+ case 1:
+ if (type->IsSigned())
+ fprintf(out, "ctypes.c_byte");
+ else
+ fprintf(out, "ctypes.c_ubyte");
+ break;
+ case 2:
+ if (type->IsSigned())
+ fprintf(out, "ctypes.c_short");
+ else
+ fprintf(out, "ctypes.c_ushort");
+ break;
+ case 4:
+ if (type->IsSigned())
+ fprintf(out, "ctypes.c_int");
+ else
+ fprintf(out, "ctypes.c_uint");
+ break;
+ default:
+ if (type->IsSigned())
+ fprintf(out, "ctypes.c_longlong");
+ else
+ fprintf(out, "ctypes.c_ulonglong");
+ break;
+ }
+ break;
+ case FloatTypeClass:
+ if (type->GetWidth() == 4)
+ fprintf(out, "ctypes.c_float");
+ else
+ fprintf(out, "ctypes.c_double");
+ break;
+ case NamedTypeReferenceClass:
+ if (type->GetNamedTypeReference()->GetTypeReferenceClass() == EnumNamedTypeClass)
+ {
+ string name = type->GetNamedTypeReference()->GetName().GetString();
+ if (name.size() > 16 && name.substr(0, 11) == "_BNDebugger")
+ name = name.substr(3);
+ else if (name.size() > 15 && name.substr(0, 10) == "BNDebugger")
+ name = name.substr(2);
+ else if (name.size() > 15 && name.substr(0, 7) == "BNDebug")
+ name = name.substr(2);
+ else if (name.size() > 2 && name.substr(0, 2) == "BN")
+ name = name.substr(2);
+ fprintf(out, "%sEnum", name.c_str());
+ }
+ else
+ {
+ fprintf(out, "%s", type->GetNamedTypeReference()->GetName().GetString().c_str());
+ }
+ break;
+ case PointerTypeClass:
+ if (isCallback || (type->GetChildType()->GetClass() == VoidTypeClass))
+ {
+ fprintf(out, "ctypes.c_void_p");
+ break;
+ }
+ else if ((type->GetChildType()->GetClass() == IntegerTypeClass) &&
+ (type->GetChildType()->GetWidth() == 1) && (type->GetChildType()->IsSigned()))
+ {
+ if (isReturnType)
+ fprintf(out, "ctypes.POINTER(ctypes.c_byte)");
+ else
+ fprintf(out, "ctypes.c_char_p");
+ break;
+ }
+ else if (type->GetChildType()->GetClass() == FunctionTypeClass)
+ {
+ fprintf(out, "ctypes.CFUNCTYPE(");
+ OutputType(out, type->GetChildType()->GetChildType(), true, true);
+ for (auto& i : type->GetChildType()->GetParameters())
+ {
+ fprintf(out, ", ");
+ OutputType(out, i.type);
+ }
+ fprintf(out, ")");
+ break;
+ }
+ fprintf(out, "ctypes.POINTER(");
+ OutputType(out, type->GetChildType());
+ fprintf(out, ")");
+ break;
+ case ArrayTypeClass:
+ OutputType(out, type->GetChildType());
+ fprintf(out, " * %" PRId64, type->GetElementCount());
+ break;
+ default:
+ fprintf(out, "None");
+ break;
+ }
+}
+
+
+void OutputSwizzledType(FILE* out, Type* type)
+{
+ switch (type->GetClass())
+ {
+ case BoolTypeClass:
+ fprintf(out, "bool");
+ break;
+ case IntegerTypeClass:
+ fprintf(out, "int");
+ break;
+ case FloatTypeClass:
+ fprintf(out, "float");
+ break;
+ case NamedTypeReferenceClass:
+ if (type->GetNamedTypeReference()->GetTypeReferenceClass() == EnumNamedTypeClass)
+ {
+ string name = type->GetNamedTypeReference()->GetName().GetString();
+ if (name.size() > 16 && name.substr(0, 11) == "_BNDebugger")
+ name = name.substr(3);
+ else if (name.size() > 15 && name.substr(0, 10) == "BNDebugger")
+ name = name.substr(2);
+ else if (name.size() > 15 && name.substr(0, 7) == "BNDebug")
+ name = name.substr(2);
+ else if (name.size() > 2 && name.substr(0, 2) == "BN")
+ name = name.substr(2);
+ fprintf(out, "%sEnum", name.c_str());
+ }
+ else
+ {
+ fprintf(out, "%s", type->GetNamedTypeReference()->GetName().GetString().c_str());
+ }
+ break;
+ case PointerTypeClass:
+ if (type->GetChildType()->GetClass() == VoidTypeClass)
+ {
+ fprintf(out, "Optional[ctypes.c_void_p]");
+ break;
+ }
+ else if ((type->GetChildType()->GetClass() == IntegerTypeClass) &&
+ (type->GetChildType()->GetWidth() == 1) && (type->GetChildType()->IsSigned()))
+ {
+ fprintf(out, "Optional[str]");
+ break;
+ }
+ else if (type->GetChildType()->GetClass() == FunctionTypeClass)
+ {
+ fprintf(out, "ctypes.CFUNCTYPE(");
+ OutputType(out, type->GetChildType()->GetChildType(), true, true);
+ for (auto& i : type->GetChildType()->GetParameters())
+ {
+ fprintf(out, ", ");
+ OutputType(out, i.type);
+ }
+ fprintf(out, ")");
+ break;
+ }
+ fprintf(out, "ctypes.POINTER(");
+ OutputType(out, type->GetChildType());
+ fprintf(out, ")");
+ break;
+ case ArrayTypeClass:
+ OutputType(out, type->GetChildType());
+ fprintf(out, " * %" PRId64, type->GetElementCount());
+ break;
+ default:
+ fprintf(out, "None");
+ break;
+ }
+}
+
+
+int main(int argc, char* argv[])
+{
+ if (argc < 5)
+ {
+ fprintf(stderr, "Usage: generator <header> <output> <output_template> <output_enum>\n");
+ return 1;
+ }
+
+ // Parse API header to get type and function information
+ map<QualifiedName, Ref<Type>> types, vars, funcs;
+ string errors;
+ auto arch = new CoreArchitecture(BNGetNativeTypeParserArchitecture());
+
+ // Enable ephemeral settings
+ Settings::Instance()->LoadSettingsFile("");
+ Settings::Instance()->Set("analysis.types.parserName", "ClangTypeParser");
+ bool ok = arch->GetStandalonePlatform()->ParseTypesFromSourceFile(argv[1], types, vars, funcs, errors);
+
+ if (!ok)
+ {
+ fprintf(stderr, "Errors: %s\n", errors.c_str());
+ return 1;
+ }
+
+ FILE* out = fopen(argv[2], "w");
+ FILE* out_template = fopen(argv[3], "r");
+ FILE* enums = fopen(argv[4], "w");
+
+ fprintf(enums, "import enum\n");
+
+ // Copy the content of the template to the output file
+ int c;
+ while((c = fgetc(out_template)) != EOF)
+ fputc(c, out);
+
+ // Create type objects
+ fprintf(out, "# Type definitions\n");
+ for (auto& i : types)
+ {
+ string name;
+ if (i.first.size() != 1)
+ continue;
+ name = i.first[0];
+ if (name == "BNBinaryView")
+ {
+ fprintf(out, "from binaryninja._binaryninjacore import BNBinaryView, BNBinaryViewHandle\n");
+ continue;
+ }
+ if (i.second->GetClass() == StructureTypeClass)
+ {
+ fprintf(out, "class %s(ctypes.Structure):\n", name.c_str());
+
+ // python uses str's, C uses byte-arrays
+ bool stringField = false;
+ for (auto& arg : i.second->GetStructure()->GetMembers())
+ {
+ if ((arg.type->GetClass() == PointerTypeClass) &&
+ (arg.type->GetChildType()->GetWidth() == 1) &&
+ (arg.type->GetChildType()->IsSigned()))
+ {
+ fprintf(out, "\t@property\n\tdef %s(self):\n\t\treturn pyNativeStr(self._%s)\n", arg.name.c_str(), arg.name.c_str());
+ fprintf(out, "\t@%s.setter\n\tdef %s(self, value):\n\t\tself._%s = cstr(value)\n", arg.name.c_str(), arg.name.c_str(), arg.name.c_str());
+ stringField = true;
+ }
+ }
+
+ if (!stringField)
+ fprintf(out, "\tpass\n");
+
+ fprintf(out, "%sHandle = ctypes.POINTER(%s)\n", name.c_str(), name.c_str());
+ }
+ else if (i.second->GetClass() == EnumerationTypeClass)
+ {
+ bool isBNAPIEnum = false;
+ if (name.size() > 16 && name.substr(0, 11) == "_BNDebugger")
+ name = name.substr(3);
+ else if (name.size() > 15 && name.substr(0, 10) == "BNDebugger")
+ name = name.substr(2);
+ else if (name.size() > 15 && name.substr(0, 7) == "BNDebug")
+ name = name.substr(2);
+ else if (name.size() > 2 && name.substr(0, 2) == "BN")
+ {
+ name = name.substr(2);
+ isBNAPIEnum = false;
+ }
+ else
+ continue;
+
+ if (isBNAPIEnum)
+ {
+ fprintf(out, "from binaryninja._binaryninjacore import %sEnum\n", name.c_str());
+ continue;
+ }
+
+ fprintf(out, "%sEnum = ctypes.c_int\n", name.c_str());
+
+ fprintf(enums, "\n\nclass %s(enum.IntEnum):\n", name.c_str());
+ for (auto& j : i.second->GetEnumeration()->GetMembers())
+ {
+ fprintf(enums, "\t%s = %" PRId64 "\n", j.name.c_str(), j.value);
+ }
+ }
+ else if ((i.second->GetClass() == BoolTypeClass) || (i.second->GetClass() == IntegerTypeClass) ||
+ (i.second->GetClass() == FloatTypeClass) || (i.second->GetClass() == ArrayTypeClass))
+ {
+ fprintf(out, "%s = ", name.c_str());
+ OutputType(out, i.second);
+ fprintf(out, "\n");
+ }
+ }
+
+
+ fprintf(out, "\n# Structure definitions\n");
+ set<QualifiedName> structsToProcess;
+ set<QualifiedName> finishedStructs;
+ for (auto& i : types)
+ structsToProcess.insert(i.first);
+ while (structsToProcess.size() != 0)
+ {
+ set<QualifiedName> currentStructList = structsToProcess;
+ structsToProcess.clear();
+ bool processedSome = false;
+ for (auto& i : currentStructList)
+ {
+ string name;
+ if (i.size() != 1)
+ continue;
+ Ref<Type> type = types[i];
+ name = i[0];
+ if ((type->GetClass() == StructureTypeClass) && (type->GetStructure()->GetMembers().size() != 0))
+ {
+ bool requiresDependency = false;
+ for (auto& j : type->GetStructure()->GetMembers())
+ {
+ if ((j.type->GetClass() == NamedTypeReferenceClass) &&
+ (types[j.type->GetNamedTypeReference()->GetName()]->GetClass() == StructureTypeClass) &&
+ (finishedStructs.count(j.type->GetNamedTypeReference()->GetName()) == 0))
+ {
+ // This structure needs another structure that isn't fully defined yet, need to wait
+ // for the dependencies to be defined
+ structsToProcess.insert(i);
+ requiresDependency = true;
+ break;
+ }
+ }
+
+ if (requiresDependency)
+ continue;
+ fprintf(out, "%s._fields_ = [\n", name.c_str());
+ for (auto& j : type->GetStructure()->GetMembers())
+ {
+ // To help the python->C wrappers
+ if ((j.type->GetClass() == PointerTypeClass) &&
+ (j.type->GetChildType()->GetWidth() == 1) &&
+ (j.type->GetChildType()->IsSigned()))
+ {
+ fprintf(out, "\t\t(\"_%s\", ", j.name.c_str());
+ }
+ else
+ fprintf(out, "\t\t(\"%s\", ", j.name.c_str());
+ OutputType(out, j.type);
+ fprintf(out, "),\n");
+ }
+ fprintf(out, "\t]\n");
+ finishedStructs.insert(i);
+ processedSome = true;
+ }
+ else if (type->GetClass() == NamedTypeReferenceClass)
+ {
+ if (type->GetNamedTypeReference()->GetTypeReferenceClass() == StructNamedTypeClass)
+ {
+ fprintf(out, "%s = %s\n", name.c_str(), type->GetNamedTypeReference()->GetName().GetString().c_str());
+ fprintf(out, "%sHandle = %sHandle\n", name.c_str(), type->GetNamedTypeReference()->GetName().GetString().c_str());
+ }
+ else if (type->GetNamedTypeReference()->GetTypeReferenceClass() == EnumNamedTypeClass)
+ {
+ fprintf(out, "%s = ctypes.c_int\n", name.c_str());
+ }
+ finishedStructs.insert(i);
+ processedSome = true;
+ }
+ }
+
+ if (!processedSome && structsToProcess.size() != 0)
+ {
+ fprintf(stderr, "Detected dependency cycle in structures\n");
+ for (auto& i : structsToProcess)
+ fprintf(stderr, "%s\n", i.GetString().c_str());
+ return 1;
+ }
+ }
+
+ fprintf(out, "\n# Function definitions\n");
+ for (auto& i : funcs)
+ {
+ string name;
+ if (i.first.size() != 1)
+ continue;
+ name = i.first[0];
+
+ // Check for a string result, these will be automatically wrapped to free the string
+ // memory and return a Python string
+ bool stringResult = (i.second->GetChildType()->GetClass() == PointerTypeClass) &&
+ (i.second->GetChildType()->GetChildType()->GetWidth() == 1) &&
+ (i.second->GetChildType()->GetChildType()->IsSigned());
+ // Pointer returns will be automatically wrapped to return None on null pointer
+ bool pointerResult = (i.second->GetChildType()->GetClass() == PointerTypeClass);
+
+ // From python -> C python3 requires str -> str.encode('charmap')
+ bool swizzleArgs = true;
+ if (name == "BNFreeString")
+ swizzleArgs = false;
+
+ bool callbackConvention = false;
+ if (name == "BNAllocString")
+ {
+ // Don't perform automatic wrapping of string allocation, and return a void
+ // pointer so that callback functions (which is the only valid use of BNDebuggerAllocString)
+ // can properly return the result
+ stringResult = false;
+ callbackConvention = true;
+ swizzleArgs = false;
+ }
+
+ string funcName = string("_") + name;
+
+ fprintf(out, "# -------------------------------------------------------\n");
+ fprintf(out, "# %s\n\n", funcName.c_str());
+ fprintf(out, "%s = core.%s\n", funcName.c_str(), name.c_str());
+ fprintf(out, "%s.restype = ", funcName.c_str());
+ OutputType(out, i.second->GetChildType(), true, callbackConvention);
+ fprintf(out, "\n");
+ if (!i.second->HasVariableArguments())
+ {
+ fprintf(out, "%s.argtypes = [\n", funcName.c_str());
+ for (auto& j : i.second->GetParameters())
+ {
+ fprintf(out, "\t\t");
+ if (name == "BNFreeString")
+ {
+ // BNDebuggerFreeString expects a pointer to a string allocated by the core, so do not use
+ // a c_char_p here, as that would be allocated by the Python runtime. This can
+ // be enforced by outputting like a return value.
+ OutputType(out, j.type, true);
+ }
+ else
+ {
+ OutputType(out, j.type);
+ }
+ fprintf(out, ",\n");
+ }
+ fprintf(out, "\t]");
+ }
+ else
+ {
+ // As of writing this, only BNLog's have variable instruction lengths, but in an attempt not to break in the future:
+ if (funcName.compare(0, 6, "_BNLog") == 0)
+ {
+ if (funcName != "_BNLog")
+ {
+ fprintf(out, "def %s(*args):\n", name.c_str());
+ fprintf(out, "\treturn %s(*[cstr(arg) for arg in args])\n\n", funcName.c_str());
+ continue;
+ }
+ else
+ {
+ fprintf(out, "def %s(level, *args):\n", name.c_str());
+ fprintf(out, "\treturn %s(level, *[cstr(arg) for arg in args])\n\n", funcName.c_str());
+ continue;
+ }
+ }
+ }
+ fprintf(out, "\n\n\n# noinspection PyPep8Naming\n");
+ fprintf(out, "def %s(", name.c_str());
+ if (!i.second->HasVariableArguments())
+ {
+ size_t argN = 0;
+ for (auto& arg: i.second->GetParameters())
+ {
+ string argName = arg.name;
+ if (g_pythonKeywordReplacements.find(argName) != g_pythonKeywordReplacements.end())
+ argName = g_pythonKeywordReplacements[argName];
+
+ if (argName.empty())
+ argName = "arg" + to_string(argN);
+
+ if (argN > 0)
+ fprintf(out, ", ");
+ fprintf(out, "\n\t\t");
+ fprintf(out, "%s: ", argName.c_str());
+ if (swizzleArgs)
+ OutputSwizzledType(out, arg.type);
+ else
+ OutputType(out, arg.type);
+ argN ++;
+ }
+ }
+ fprintf(out, "\n\t\t) -> ");
+ if (swizzleArgs)
+ {
+ if (stringResult || pointerResult)
+ fprintf(out, "Optional[");
+ OutputSwizzledType(out, i.second->GetChildType());
+ if (stringResult || pointerResult)
+ fprintf(out, "]");
+ }
+ else
+ {
+ OutputType(out, i.second->GetChildType());
+ }
+ fprintf(out, ":\n");
+
+ string stringArgFuncCall = funcName + "(";
+ size_t argN = 0;
+ for (auto& arg : i.second->GetParameters())
+ {
+ string argName = arg.name;
+ if (g_pythonKeywordReplacements.find(argName) != g_pythonKeywordReplacements.end())
+ argName = g_pythonKeywordReplacements[argName];
+
+ if (argName.empty())
+ argName = "arg" + to_string(argN);
+
+ if (swizzleArgs && (arg.type->GetClass() == PointerTypeClass) &&
+ (arg.type->GetChildType()->GetClass() == IntegerTypeClass) &&
+ (arg.type->GetChildType()->GetWidth() == 1) &&
+ (arg.type->GetChildType()->IsSigned()))
+ {
+ stringArgFuncCall += string("cstr(") + argName + "), ";
+ }
+ else
+ {
+ stringArgFuncCall += argName + ", ";
+ }
+ argN++;
+ }
+ if (argN > 0)
+ stringArgFuncCall = stringArgFuncCall.substr(0, stringArgFuncCall.size()-2);
+ stringArgFuncCall += ")";
+
+ if (stringResult)
+ {
+ // Emit wrapper to get Python string and free native memory
+ fprintf(out, "\tresult = ");
+ fprintf(out, "%s\n", stringArgFuncCall.c_str());
+ fprintf(out, "\tif not result:\n");
+ fprintf(out, "\t\treturn None\n");
+ fprintf(out, "\tstring = str(pyNativeStr(ctypes.cast(result, ctypes.c_char_p).value))\n");
+ fprintf(out, "\tBNFreeString(result)\n");
+ fprintf(out, "\treturn string\n");
+ }
+ else if (pointerResult)
+ {
+ // Emit wrapper to return None on null pointer
+ fprintf(out, "\tresult = ");
+ fprintf(out, "%s\n", stringArgFuncCall.c_str());
+ fprintf(out, "\tif not result:\n");
+ fprintf(out, "\t\treturn None\n");
+ fprintf(out, "\treturn result\n");
+ }
+ else
+ {
+ fprintf(out, "\treturn ");
+ fprintf(out, "%s\n", stringArgFuncCall.c_str());
+ }
+ fprintf(out, "\n\n");
+ }
+
+ fprintf(out, "\n# Helper functions\n");
+ fprintf(out, "def handle_of_type(value, handle_type):\n");
+ fprintf(out, "\tif isinstance(value, ctypes.POINTER(handle_type)) or isinstance(value, ctypes.c_void_p):\n");
+ fprintf(out, "\t\treturn ctypes.cast(value, ctypes.POINTER(handle_type))\n");
+ fprintf(out, "\traise ValueError('expected pointer to %%s' %% str(handle_type))\n");
+
+ fclose(out);
+ fclose(enums);
+ return 0;
+}
diff --git a/view/sharedcache/api/python/sharedcache.py b/view/sharedcache/api/python/sharedcache.py
new file mode 100644
index 00000000..ca031e03
--- /dev/null
+++ b/view/sharedcache/api/python/sharedcache.py
@@ -0,0 +1,260 @@
+import os
+import ctypes
+import dataclasses
+import traceback
+
+import binaryninja
+from binaryninja._binaryninjacore import BNFreeStringList, BNAllocString, BNFreeString
+
+from . import _sharedcachecore as sccore
+from .sharedcache_enums import *
+
+
+@dataclasses.dataclass
+class DSCMemoryMapping:
+ filePath: str
+ name: str
+ vmAddress: int
+ rawViewOffset: int
+ size: int
+
+ def __str__(self):
+ return repr(self)
+
+ def __repr__(self):
+ return f"<DSCMemoryMapping '{self.name}' {os.path.basename(self.filePath)} raw<{self.rawViewOffset:x}>: {self.vmAddress:x}+{self.size:x}>"
+
+
+@dataclasses.dataclass
+class LoadedRegion:
+ name: str
+ headerAddress: int
+ mappings: list[DSCMemoryMapping]
+
+ def __str__(self):
+ return repr(self)
+
+ def __repr__(self):
+ return f"<LoadedRegion {self.name} @ {self.headerAddress:x}>"
+
+
+@dataclasses.dataclass
+class DSCBackingCacheMapping:
+ vmAddress: int
+ size: int
+ fileOffset: int
+
+ def __str__(self):
+ return repr(self)
+
+ def __repr__(self):
+ return f"<DSCBackingCacheMapping {self.vmAddress:x}+{self.size:x} @ {self.fileOffset:x}"
+
+
+@dataclasses.dataclass
+class DSCBackingCache:
+ path: str
+ isPrimary: bool
+ mappings: list[DSCBackingCacheMapping]
+
+ def __str__(self):
+ return repr(self)
+
+ def __repr__(self):
+ return f"<DSCBackingCache {self.path} {'Primary' if self.isPrimary else 'Secondary'} | {len(self.mappings)} mappings>"
+
+
+@dataclasses.dataclass
+class DSCImageMemoryMapping:
+ filePath: str
+ name: str
+ vmAddress: int
+ size: int
+ loaded: bool
+ rawViewOffset: int
+
+ def __str__(self):
+ return repr(self)
+
+ def __repr__(self):
+ return f"<DSCImageMemoryMapping '{self.name}' {os.path.basename(self.filePath)} raw<{self.rawViewOffset:x}>: {self.vmAddress:x}+{self.size:x}>"
+
+
+@dataclasses.dataclass
+class DSCImage:
+ name: str
+ headerAddress: int
+ mappings: list[DSCImageMemoryMapping]
+
+ def __str__(self):
+ return repr(self)
+
+ def __repr__(self):
+ return f"<DSCImage {self.name} @ {self.headerAddress:x}>"
+
+
+@dataclasses.dataclass
+class DSCSymbol:
+ name: str
+ image: str
+ address: int
+
+ def __str__(self):
+ return repr(self)
+
+ def __repr__(self):
+ return f"<DSCSymbol {self.name} @ {self.address:x} ({self.image}>"
+
+
+class SharedCache:
+ def __init__(self, view):
+ self.handle = sccore.BNGetSharedCache(view.handle)
+
+ def load_image_with_install_name(self, installName):
+ str = BNAllocString(installName.encode('utf-8'))
+ return sccore.BNDSCViewLoadImageWithInstallName(self.handle, str)
+
+ def load_section_at_address(self, addr):
+ return sccore.BNDSCViewLoadSectionAtAddress(self.handle, addr)
+
+ def load_image_containing_address(self, addr):
+ return sccore.BNDSCViewLoadImageContainingAddress(self.handle, addr)
+
+ @property
+ def caches(self):
+ count = ctypes.c_ulonglong()
+ value = sccore.BNDSCViewGetBackingCaches(self.handle, count)
+ if value is None:
+ return []
+
+ result = []
+ for i in range(count.value):
+ mappings = []
+ for j in range(value[i].mappingCount):
+ mapping = DSCBackingCacheMapping(
+ value[i].mappings[j].vmAddress,
+ value[i].mappings[j].size,
+ value[i].mappings[j].fileOffset
+ )
+ mappings.append(mapping)
+ result.append(DSCBackingCache(
+ value[i].path,
+ value[i].isPrimary,
+ mappings
+ ))
+
+ sccore.BNDSCViewFreeBackingCaches(value, count)
+ return result
+
+ @property
+ def images(self):
+ count = ctypes.c_ulonglong()
+ value = sccore.BNDSCViewGetAllImages(self.handle, count)
+ if value is None:
+ return []
+
+ result = []
+ for i in range(count.value):
+ mappings = []
+ for j in range(value[i].mappingCount):
+ mapping = DSCImageMemoryMapping(
+ value[i].mappings[j].filePath,
+ value[i].mappings[j].name,
+ value[i].mappings[j].vmAddress,
+ value[i].mappings[j].size,
+ value[i].mappings[j].loaded,
+ value[i].mappings[j].rawViewOffset
+ )
+ mappings.append(mapping)
+ result.append(DSCImage(
+ value[i].name,
+ value[i].headerAddress,
+ mappings
+ ))
+
+ sccore.BNDSCViewFreeAllImages(value, count)
+ return result
+
+ @property
+ def loaded_images(self):
+ count = ctypes.c_ulonglong()
+ value = sccore.BNDSCViewGetLoadedImages(self.handle, count)
+ if value is None:
+ return []
+
+ result = []
+ for i in range(count.value):
+ mappings = []
+ for j in range(value[i].mappingCount):
+ mapping = DSCMemoryMapping(
+ value[i].mappings[j].filePath,
+ value[i].mappings[j].name,
+ value[i].mappings[j].vmAddress,
+ value[i].mappings[j].rawViewOffset,
+ value[i].mappings[j].size
+ )
+ mappings.append(mapping)
+ result.append(LoadedRegion(
+ value[i].name,
+ value[i].headerAddress,
+ mappings
+ ))
+
+ sccore.BNDSCViewFreeLoadedImages(value, count)
+ return result
+
+ def load_all_symbols_and_wait(self):
+ count = ctypes.c_ulonglong()
+ value = sccore.BNDSCViewLoadAllSymbolsAndWait(self.handle, count)
+ if value is None:
+ return []
+ result = []
+ for i in range(count.value):
+ sym = DSCSymbol(
+ value[i].name,
+ value[i].image,
+ value[i].address
+ )
+ result.append(sym)
+
+ sccore.BNDSCViewFreeSymbols(value, count)
+ return result
+
+ @property
+ def image_names(self):
+ count = ctypes.c_ulonglong()
+ value = sccore.BNDSCViewGetInstallNames(self.handle, count)
+ if value is None:
+ return []
+
+ result = []
+ for i in range(count.value):
+ result.append(value[i].decode('utf-8'))
+
+ BNFreeStringList(value, count)
+ return result
+
+ @property
+ def image_count(self):
+ return sccore.BNDSCViewLoadedImageCount(self.handle)
+
+ @property
+ def state(self):
+ return DSCViewState(sccore.BNDSCViewGetState(self.handle))
+
+ def get_name_for_address(self, address):
+ name = sccore.BNDSCViewGetNameForAddress(self.handle, address)
+ if name is None:
+ return ""
+ result = name
+ return result
+
+ def get_image_name_for_address(self, address):
+ name = sccore.BNDSCViewGetImageNameForAddress(self.handle, address)
+ if name is None:
+ return ""
+ result = name
+ return result
+
+ def find_symbol_at_addr_and_apply_to_addr(self, symbolAddress, targetAddress, triggerReanalysis) -> None:
+ sccore.BNDSCFindSymbolAtAddressAndApplyToAddress(self.handle, symbolAddress, targetAddress, triggerReanalysis)
diff --git a/view/sharedcache/api/python/sharedcache_enums.py b/view/sharedcache/api/python/sharedcache_enums.py
new file mode 100644
index 00000000..34668424
--- /dev/null
+++ b/view/sharedcache/api/python/sharedcache_enums.py
@@ -0,0 +1,14 @@
+import enum
+
+
+class DSCViewLoadProgress(enum.IntEnum):
+ LoadProgressNotStarted = 0
+ LoadProgressLoadingCaches = 1
+ LoadProgressLoadingImages = 2
+ LoadProgressFinished = 3
+
+
+class DSCViewState(enum.IntEnum):
+ Unloaded = 0
+ Loaded = 1
+ LoadedWithImages = 2