diff options
| author | Mason Reed <mason@vector35.com> | 2025-01-31 12:59:42 -0500 |
|---|---|---|
| committer | Mason Reed <mason@vector35.com> | 2025-07-02 01:58:31 -0400 |
| commit | 110c06851bbbd09f78a3e87979d529d6e09df851 (patch) | |
| tree | 7849015b26a14cd2b7be2d87fc1e0d5c101ef457 /plugins/warp/api/python | |
| parent | 7b1e8bbdb971aed21b6d889aa4a46f9ef54829c1 (diff) | |
WARP 1.0
- Added FFI
- Added a sidebar to the UI
- Added project, directory and archive processing
- Added generic `Container` interface for extensible stores of WARP data
- Fixed type references being constructed and pulled incorrectly
- Added HTML, Markdown and JSON report generation
- Made the WARP information added as an analysis activity
- Flattened the signatures directory, the target information is stored in the file now
- Matched function information is stored as function metadata in the database to reliably persist, alongside the function GUID
- Split the matching out from the application, allowing you to match on a given function without applying it
- Added more/better tests
- Added support for binaries with multiple architectures, the functions are now also queried based off the Target, see WARP spec for more details
- Greatly improved support for RISC architectures, see WARP spec for more details
- Greatly improved UX when loading files after the fact, will now sanely rerun the matcher
- Omitted the function type if not a user type, this greatly reduces file size
- Improved support for functions that reference a page aligned base pointer, see WARP spec for more details
- Removed some extra cache structures that were causing erroneous behavior
- Fixed edge-case in LLIL traversal missing some constant pointers, this was a bug in the Rust bindings
- Added support for function comments
- Made long running tasks, such as generating, matching and loading signatures, cancellable where possible
- Made function constraints more versatile, allowing for easy extensions in the future, see WARP spec for details
- Added options to signature generation, such as what data to store, and whether to compress the data or not
- Made all long running tasks prompt the user for required information before the task starts, allowing users to "set it and forget it" and not have to baby sit the finalization of the task
- Myriad of other changes to the actual WARP format that impact performance, file size and general feature set, see https://github.com/Vector35/warp for more details
Diffstat (limited to 'plugins/warp/api/python')
| -rw-r--r-- | plugins/warp/api/python/CMakeLists.txt | 50 | ||||
| -rw-r--r-- | plugins/warp/api/python/__init__.py | 7 | ||||
| -rw-r--r-- | plugins/warp/api/python/_warpcore.py | 1081 | ||||
| -rw-r--r-- | plugins/warp/api/python/_warpcore_template.py | 58 | ||||
| -rw-r--r-- | plugins/warp/api/python/generator.cpp | 652 | ||||
| -rw-r--r-- | plugins/warp/api/python/warp.py | 384 | ||||
| -rw-r--r-- | plugins/warp/api/python/warp_enums.py | 1 |
7 files changed, 2233 insertions, 0 deletions
diff --git a/plugins/warp/api/python/CMakeLists.txt b/plugins/warp/api/python/CMakeLists.txt new file mode 100644 index 00000000..8e6003c0 --- /dev/null +++ b/plugins/warp/api/python/CMakeLists.txt @@ -0,0 +1,50 @@ +cmake_minimum_required(VERSION 3.9...3.15 FATAL_ERROR) + +project(warp-python-api) + +file(GLOB PYTHON_SOURCES ${PROJECT_SOURCE_DIR}/*.py) +list(REMOVE_ITEM PYTHON_SOURCES ${PROJECT_SOURCE_DIR}/_warpcore.py) +list(REMOVE_ITEM PYTHON_SOURCES ${PROJECT_SOURCE_DIR}/enums.py) + +add_executable(warp_generator + ${PROJECT_SOURCE_DIR}/generator.cpp) +target_link_libraries(warp_generator binaryninjaapi) +target_include_directories(warp_generator PUBLIC {PROJECT_SOURCE_DIR}/../../api) + +set_target_properties(warp_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/warp/) +else() + set(PYTHON_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/out/plugins/warp/) +endif() + +if(WIN32) + if (BN_INTERNAL_BUILD) + add_custom_command(TARGET warp_generator PRE_BUILD + COMMAND ${CMAKE_COMMAND} -E copy ${BN_CORE_OUTPUT_DIR}/binaryninjacore.dll ${PROJECT_BINARY_DIR}/) + else() + add_custom_command(TARGET warp_generator PRE_BUILD + COMMAND ${CMAKE_COMMAND} -E copy ${BN_INSTALL_DIR}/binaryninjacore.dll ${PROJECT_BINARY_DIR}/) + endif() +endif() + +add_custom_target(warp_generator_copy ALL + BYPRODUCTS ${PROJECT_SOURCE_DIR}/_warpcore.py ${PROJECT_SOURCE_DIR}/enums.py + DEPENDS ${PYTHON_SOURCES} ${PROJECT_SOURCE_DIR}/../warpcore.h $<TARGET_FILE:warp_generator> + COMMAND ${CMAKE_COMMAND} -E echo "Copying WARP Python Sources" + COMMAND ${CMAKE_COMMAND} -E make_directory ${PYTHON_OUTPUT_DIRECTORY} + COMMAND ${CMAKE_COMMAND} -E env ASAN_OPTIONS=detect_leaks=0 $<TARGET_FILE:warp_generator> + ${PROJECT_SOURCE_DIR}/../warpcore.h + ${PROJECT_SOURCE_DIR}/_warpcore.py + ${PROJECT_SOURCE_DIR}/_warpcore_template.py + ${PROJECT_SOURCE_DIR}/warp_enums.py + + COMMAND ${CMAKE_COMMAND} -E copy ${PYTHON_SOURCES} ${PYTHON_OUTPUT_DIRECTORY} + COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_SOURCE_DIR}/_warpcore.py ${PYTHON_OUTPUT_DIRECTORY} + COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_SOURCE_DIR}/warp_enums.py ${PYTHON_OUTPUT_DIRECTORY}) + diff --git a/plugins/warp/api/python/__init__.py b/plugins/warp/api/python/__init__.py new file mode 100644 index 00000000..f4bc8e83 --- /dev/null +++ b/plugins/warp/api/python/__init__.py @@ -0,0 +1,7 @@ +import os + +from binaryninja._binaryninjacore import BNGetUserPluginDirectory +user_plugin_dir = os.path.realpath(BNGetUserPluginDirectory()) +current_path = os.path.realpath(__file__) + +from .warp import *
\ No newline at end of file diff --git a/plugins/warp/api/python/_warpcore.py b/plugins/warp/api/python/_warpcore.py new file mode 100644 index 00000000..c53feefa --- /dev/null +++ b/plugins/warp/api/python/_warpcore.py @@ -0,0 +1,1081 @@ +import binaryninja +import ctypes, os + +from typing import Optional +from . import warp_enums +# Load core module +import platform +core = None +core_platform = platform.system() + +from binaryninja import Settings +if Settings().get_bool("corePlugins.warp"): + from binaryninja._binaryninjacore import BNGetBundledPluginDirectory + if core_platform == "Darwin": + _base_path = BNGetBundledPluginDirectory() + core = ctypes.CDLL(os.path.join(_base_path, "libwarp_ninja.dylib")) + + elif core_platform == "Linux": + _base_path = BNGetBundledPluginDirectory() + core = ctypes.CDLL(os.path.join(_base_path, "libwarp_ninja.so")) + + elif (core_platform == "Windows") or (core_platform.find("CYGWIN_NT") == 0): + _base_path = BNGetBundledPluginDirectory() + core = ctypes.CDLL(os.path.join(_base_path, "warp_ninja.dll")) + else: + raise Exception("OS not supported") +else: + from binaryninja._binaryninjacore import BNGetUserPluginDirectory + if core_platform == "Darwin": + _base_path = BNGetUserPluginDirectory() + core = ctypes.CDLL(os.path.join(_base_path, "libwarp_ninja.dylib")) + + elif core_platform == "Linux": + _base_path = BNGetUserPluginDirectory() + core = ctypes.CDLL(os.path.join(_base_path, "libwarp_ninja.so")) + + elif (core_platform == "Windows") or (core_platform.find("CYGWIN_NT") == 0): + _base_path = BNGetUserPluginDirectory() + core = ctypes.CDLL(os.path.join(_base_path, "warp_ninja.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))) + +from binaryninja._binaryninjacore import BNFreeString +# Type definitions +from binaryninja._binaryninjacore import BNArchitecture, BNArchitectureHandle +from binaryninja._binaryninjacore import BNBasicBlock, BNBasicBlockHandle +from binaryninja._binaryninjacore import BNBinaryView, BNBinaryViewHandle +from binaryninja._binaryninjacore import BNFunction, BNFunctionHandle +from binaryninja._binaryninjacore import BNLowLevelILFunction, BNLowLevelILFunctionHandle +from binaryninja._binaryninjacore import BNPlatform, BNPlatformHandle +from binaryninja._binaryninjacore import BNSymbol, BNSymbolHandle +from binaryninja._binaryninjacore import BNType, BNTypeHandle +class BNWARPConstraint(ctypes.Structure): + pass +BNWARPConstraintHandle = ctypes.POINTER(BNWARPConstraint) +class BNWARPContainer(ctypes.Structure): + pass +BNWARPContainerHandle = ctypes.POINTER(BNWARPContainer) +class BNWARPFunction(ctypes.Structure): + pass +BNWARPFunctionHandle = ctypes.POINTER(BNWARPFunction) +class BNWARPFunctionComment(ctypes.Structure): + @property + def text(self): + return pyNativeStr(self._text) + @text.setter + def text(self, value): + self._text = cstr(value) +BNWARPFunctionCommentHandle = ctypes.POINTER(BNWARPFunctionComment) +class BNWARPTarget(ctypes.Structure): + pass +BNWARPTargetHandle = ctypes.POINTER(BNWARPTarget) +class BNWARPUUID(ctypes.Structure): + pass +BNWARPUUIDHandle = ctypes.POINTER(BNWARPUUID) + +# Structure definitions +BNWARPBasicBlockGUID = BNWARPUUID +BNWARPBasicBlockGUIDHandle = BNWARPUUIDHandle +BNWARPConstraintGUID = BNWARPUUID +BNWARPConstraintGUIDHandle = BNWARPUUIDHandle +BNWARPFunctionComment._fields_ = [ + ("_text", ctypes.c_char_p), + ("offset", ctypes.c_longlong), + ] +BNWARPFunctionGUID = BNWARPUUID +BNWARPFunctionGUIDHandle = BNWARPUUIDHandle +BNWARPSource = BNWARPUUID +BNWARPSourceHandle = BNWARPUUIDHandle +BNWARPTypeGUID = BNWARPUUID +BNWARPTypeGUIDHandle = BNWARPUUIDHandle +BNWARPUUID._fields_ = [ + ("uuid", ctypes.c_ubyte * 16), + ] +BNWARPConstraint._fields_ = [ + ("guid", BNWARPConstraintGUID), + ("offset", ctypes.c_longlong), + ] + +# Function definitions +# ------------------------------------------------------- +# _BNWARPContainerAddFunctions + +_BNWARPContainerAddFunctions = core.BNWARPContainerAddFunctions +_BNWARPContainerAddFunctions.restype = ctypes.c_bool +_BNWARPContainerAddFunctions.argtypes = [ + ctypes.POINTER(BNWARPContainer), + ctypes.POINTER(BNWARPTarget), + ctypes.POINTER(BNWARPSource), + ctypes.POINTER(ctypes.POINTER(BNWARPFunction)), + ctypes.c_ulonglong, + ] + + +# noinspection PyPep8Naming +def BNWARPContainerAddFunctions( + container: ctypes.POINTER(BNWARPContainer), + target: ctypes.POINTER(BNWARPTarget), + source: ctypes.POINTER(BNWARPSource), + functions: ctypes.POINTER(ctypes.POINTER(BNWARPFunction)), + count: int + ) -> bool: + return _BNWARPContainerAddFunctions(container, target, source, functions, count) + + +# ------------------------------------------------------- +# _BNWARPContainerAddSource + +_BNWARPContainerAddSource = core.BNWARPContainerAddSource +_BNWARPContainerAddSource.restype = ctypes.c_bool +_BNWARPContainerAddSource.argtypes = [ + ctypes.POINTER(BNWARPContainer), + ctypes.c_char_p, + ctypes.POINTER(BNWARPSource), + ] + + +# noinspection PyPep8Naming +def BNWARPContainerAddSource( + container: ctypes.POINTER(BNWARPContainer), + sourcePath: Optional[str], + result: ctypes.POINTER(BNWARPSource) + ) -> bool: + return _BNWARPContainerAddSource(container, cstr(sourcePath), result) + + +# ------------------------------------------------------- +# _BNWARPContainerAddTypes + +_BNWARPContainerAddTypes = core.BNWARPContainerAddTypes +_BNWARPContainerAddTypes.restype = ctypes.c_bool +_BNWARPContainerAddTypes.argtypes = [ + ctypes.POINTER(BNBinaryView), + ctypes.POINTER(BNWARPContainer), + ctypes.POINTER(BNWARPSource), + ctypes.POINTER(ctypes.POINTER(BNType)), + ctypes.c_ulonglong, + ] + + +# noinspection PyPep8Naming +def BNWARPContainerAddTypes( + view: ctypes.POINTER(BNBinaryView), + container: ctypes.POINTER(BNWARPContainer), + source: ctypes.POINTER(BNWARPSource), + types: ctypes.POINTER(ctypes.POINTER(BNType)), + count: int + ) -> bool: + return _BNWARPContainerAddTypes(view, container, source, types, count) + + +# ------------------------------------------------------- +# _BNWARPContainerCommitSource + +_BNWARPContainerCommitSource = core.BNWARPContainerCommitSource +_BNWARPContainerCommitSource.restype = ctypes.c_bool +_BNWARPContainerCommitSource.argtypes = [ + ctypes.POINTER(BNWARPContainer), + ctypes.POINTER(BNWARPSource), + ] + + +# noinspection PyPep8Naming +def BNWARPContainerCommitSource( + container: ctypes.POINTER(BNWARPContainer), + source: ctypes.POINTER(BNWARPSource) + ) -> bool: + return _BNWARPContainerCommitSource(container, source) + + +# ------------------------------------------------------- +# _BNWARPContainerGetFunctionsWithGUID + +_BNWARPContainerGetFunctionsWithGUID = core.BNWARPContainerGetFunctionsWithGUID +_BNWARPContainerGetFunctionsWithGUID.restype = ctypes.POINTER(ctypes.POINTER(BNWARPFunction)) +_BNWARPContainerGetFunctionsWithGUID.argtypes = [ + ctypes.POINTER(BNWARPContainer), + ctypes.POINTER(BNWARPTarget), + ctypes.POINTER(BNWARPSource), + ctypes.POINTER(BNWARPFunctionGUID), + ctypes.POINTER(ctypes.c_ulonglong), + ] + + +# noinspection PyPep8Naming +def BNWARPContainerGetFunctionsWithGUID( + container: ctypes.POINTER(BNWARPContainer), + target: ctypes.POINTER(BNWARPTarget), + source: ctypes.POINTER(BNWARPSource), + guid: ctypes.POINTER(BNWARPFunctionGUID), + count: ctypes.POINTER(ctypes.c_ulonglong) + ) -> Optional[ctypes.POINTER(ctypes.POINTER(BNWARPFunction))]: + result = _BNWARPContainerGetFunctionsWithGUID(container, target, source, guid, count) + if not result: + return None + return result + + +# ------------------------------------------------------- +# _BNWARPContainerGetName + +_BNWARPContainerGetName = core.BNWARPContainerGetName +_BNWARPContainerGetName.restype = ctypes.POINTER(ctypes.c_byte) +_BNWARPContainerGetName.argtypes = [ + ctypes.POINTER(BNWARPContainer), + ] + + +# noinspection PyPep8Naming +def BNWARPContainerGetName( + container: ctypes.POINTER(BNWARPContainer) + ) -> Optional[Optional[str]]: + result = _BNWARPContainerGetName(container) + if not result: + return None + string = str(pyNativeStr(ctypes.cast(result, ctypes.c_char_p).value)) + BNFreeString(result) + return string + + +# ------------------------------------------------------- +# _BNWARPContainerGetSourcePath + +_BNWARPContainerGetSourcePath = core.BNWARPContainerGetSourcePath +_BNWARPContainerGetSourcePath.restype = ctypes.POINTER(ctypes.c_byte) +_BNWARPContainerGetSourcePath.argtypes = [ + ctypes.POINTER(BNWARPContainer), + ctypes.POINTER(BNWARPSource), + ] + + +# noinspection PyPep8Naming +def BNWARPContainerGetSourcePath( + container: ctypes.POINTER(BNWARPContainer), + source: ctypes.POINTER(BNWARPSource) + ) -> Optional[Optional[str]]: + result = _BNWARPContainerGetSourcePath(container, source) + if not result: + return None + string = str(pyNativeStr(ctypes.cast(result, ctypes.c_char_p).value)) + BNFreeString(result) + return string + + +# ------------------------------------------------------- +# _BNWARPContainerGetSources + +_BNWARPContainerGetSources = core.BNWARPContainerGetSources +_BNWARPContainerGetSources.restype = ctypes.POINTER(BNWARPSource) +_BNWARPContainerGetSources.argtypes = [ + ctypes.POINTER(BNWARPContainer), + ctypes.POINTER(ctypes.c_ulonglong), + ] + + +# noinspection PyPep8Naming +def BNWARPContainerGetSources( + container: ctypes.POINTER(BNWARPContainer), + count: ctypes.POINTER(ctypes.c_ulonglong) + ) -> Optional[ctypes.POINTER(BNWARPSource)]: + result = _BNWARPContainerGetSources(container, count) + if not result: + return None + return result + + +# ------------------------------------------------------- +# _BNWARPContainerGetSourcesWithFunctionGUID + +_BNWARPContainerGetSourcesWithFunctionGUID = core.BNWARPContainerGetSourcesWithFunctionGUID +_BNWARPContainerGetSourcesWithFunctionGUID.restype = ctypes.POINTER(BNWARPSource) +_BNWARPContainerGetSourcesWithFunctionGUID.argtypes = [ + ctypes.POINTER(BNWARPContainer), + ctypes.POINTER(BNWARPTarget), + ctypes.POINTER(BNWARPFunctionGUID), + ctypes.POINTER(ctypes.c_ulonglong), + ] + + +# noinspection PyPep8Naming +def BNWARPContainerGetSourcesWithFunctionGUID( + container: ctypes.POINTER(BNWARPContainer), + target: ctypes.POINTER(BNWARPTarget), + guid: ctypes.POINTER(BNWARPFunctionGUID), + count: ctypes.POINTER(ctypes.c_ulonglong) + ) -> Optional[ctypes.POINTER(BNWARPSource)]: + result = _BNWARPContainerGetSourcesWithFunctionGUID(container, target, guid, count) + if not result: + return None + return result + + +# ------------------------------------------------------- +# _BNWARPContainerGetSourcesWithTypeGUID + +_BNWARPContainerGetSourcesWithTypeGUID = core.BNWARPContainerGetSourcesWithTypeGUID +_BNWARPContainerGetSourcesWithTypeGUID.restype = ctypes.POINTER(BNWARPSource) +_BNWARPContainerGetSourcesWithTypeGUID.argtypes = [ + ctypes.POINTER(BNWARPContainer), + ctypes.POINTER(BNWARPTypeGUID), + ctypes.POINTER(ctypes.c_ulonglong), + ] + + +# noinspection PyPep8Naming +def BNWARPContainerGetSourcesWithTypeGUID( + container: ctypes.POINTER(BNWARPContainer), + guid: ctypes.POINTER(BNWARPTypeGUID), + count: ctypes.POINTER(ctypes.c_ulonglong) + ) -> Optional[ctypes.POINTER(BNWARPSource)]: + result = _BNWARPContainerGetSourcesWithTypeGUID(container, guid, count) + if not result: + return None + return result + + +# ------------------------------------------------------- +# _BNWARPContainerGetTypeGUIDsWithName + +_BNWARPContainerGetTypeGUIDsWithName = core.BNWARPContainerGetTypeGUIDsWithName +_BNWARPContainerGetTypeGUIDsWithName.restype = ctypes.POINTER(BNWARPTypeGUID) +_BNWARPContainerGetTypeGUIDsWithName.argtypes = [ + ctypes.POINTER(BNWARPContainer), + ctypes.POINTER(BNWARPSource), + ctypes.c_char_p, + ctypes.POINTER(ctypes.c_ulonglong), + ] + + +# noinspection PyPep8Naming +def BNWARPContainerGetTypeGUIDsWithName( + container: ctypes.POINTER(BNWARPContainer), + source: ctypes.POINTER(BNWARPSource), + name: Optional[str], + count: ctypes.POINTER(ctypes.c_ulonglong) + ) -> Optional[ctypes.POINTER(BNWARPTypeGUID)]: + result = _BNWARPContainerGetTypeGUIDsWithName(container, source, cstr(name), count) + if not result: + return None + return result + + +# ------------------------------------------------------- +# _BNWARPContainerGetTypeWithGUID + +_BNWARPContainerGetTypeWithGUID = core.BNWARPContainerGetTypeWithGUID +_BNWARPContainerGetTypeWithGUID.restype = ctypes.POINTER(BNType) +_BNWARPContainerGetTypeWithGUID.argtypes = [ + ctypes.POINTER(BNArchitecture), + ctypes.POINTER(BNWARPContainer), + ctypes.POINTER(BNWARPSource), + ctypes.POINTER(BNWARPTypeGUID), + ] + + +# noinspection PyPep8Naming +def BNWARPContainerGetTypeWithGUID( + arch: ctypes.POINTER(BNArchitecture), + container: ctypes.POINTER(BNWARPContainer), + source: ctypes.POINTER(BNWARPSource), + guid: ctypes.POINTER(BNWARPTypeGUID) + ) -> Optional[ctypes.POINTER(BNType)]: + result = _BNWARPContainerGetTypeWithGUID(arch, container, source, guid) + if not result: + return None + return result + + +# ------------------------------------------------------- +# _BNWARPContainerIsSourceUncommitted + +_BNWARPContainerIsSourceUncommitted = core.BNWARPContainerIsSourceUncommitted +_BNWARPContainerIsSourceUncommitted.restype = ctypes.c_bool +_BNWARPContainerIsSourceUncommitted.argtypes = [ + ctypes.POINTER(BNWARPContainer), + ctypes.POINTER(BNWARPSource), + ] + + +# noinspection PyPep8Naming +def BNWARPContainerIsSourceUncommitted( + container: ctypes.POINTER(BNWARPContainer), + source: ctypes.POINTER(BNWARPSource) + ) -> bool: + return _BNWARPContainerIsSourceUncommitted(container, source) + + +# ------------------------------------------------------- +# _BNWARPContainerIsSourceWritable + +_BNWARPContainerIsSourceWritable = core.BNWARPContainerIsSourceWritable +_BNWARPContainerIsSourceWritable.restype = ctypes.c_bool +_BNWARPContainerIsSourceWritable.argtypes = [ + ctypes.POINTER(BNWARPContainer), + ctypes.POINTER(BNWARPSource), + ] + + +# noinspection PyPep8Naming +def BNWARPContainerIsSourceWritable( + container: ctypes.POINTER(BNWARPContainer), + source: ctypes.POINTER(BNWARPSource) + ) -> bool: + return _BNWARPContainerIsSourceWritable(container, source) + + +# ------------------------------------------------------- +# _BNWARPContainerRemoveFunctions + +_BNWARPContainerRemoveFunctions = core.BNWARPContainerRemoveFunctions +_BNWARPContainerRemoveFunctions.restype = ctypes.c_bool +_BNWARPContainerRemoveFunctions.argtypes = [ + ctypes.POINTER(BNWARPContainer), + ctypes.POINTER(BNWARPTarget), + ctypes.POINTER(BNWARPSource), + ctypes.POINTER(ctypes.POINTER(BNWARPFunction)), + ctypes.c_ulonglong, + ] + + +# noinspection PyPep8Naming +def BNWARPContainerRemoveFunctions( + container: ctypes.POINTER(BNWARPContainer), + target: ctypes.POINTER(BNWARPTarget), + source: ctypes.POINTER(BNWARPSource), + functions: ctypes.POINTER(ctypes.POINTER(BNWARPFunction)), + count: int + ) -> bool: + return _BNWARPContainerRemoveFunctions(container, target, source, functions, count) + + +# ------------------------------------------------------- +# _BNWARPContainerRemoveTypes + +_BNWARPContainerRemoveTypes = core.BNWARPContainerRemoveTypes +_BNWARPContainerRemoveTypes.restype = ctypes.c_bool +_BNWARPContainerRemoveTypes.argtypes = [ + ctypes.POINTER(BNWARPContainer), + ctypes.POINTER(BNWARPSource), + ctypes.POINTER(BNWARPTypeGUID), + ctypes.c_ulonglong, + ] + + +# noinspection PyPep8Naming +def BNWARPContainerRemoveTypes( + container: ctypes.POINTER(BNWARPContainer), + source: ctypes.POINTER(BNWARPSource), + types: ctypes.POINTER(BNWARPTypeGUID), + count: int + ) -> bool: + return _BNWARPContainerRemoveTypes(container, source, types, count) + + +# ------------------------------------------------------- +# _BNWARPFreeConstraintList + +_BNWARPFreeConstraintList = core.BNWARPFreeConstraintList +_BNWARPFreeConstraintList.restype = None +_BNWARPFreeConstraintList.argtypes = [ + ctypes.POINTER(BNWARPConstraint), + ctypes.c_ulonglong, + ] + + +# noinspection PyPep8Naming +def BNWARPFreeConstraintList( + constraints: ctypes.POINTER(BNWARPConstraint), + count: int + ) -> None: + return _BNWARPFreeConstraintList(constraints, count) + + +# ------------------------------------------------------- +# _BNWARPFreeContainerList + +_BNWARPFreeContainerList = core.BNWARPFreeContainerList +_BNWARPFreeContainerList.restype = None +_BNWARPFreeContainerList.argtypes = [ + ctypes.POINTER(ctypes.POINTER(BNWARPContainer)), + ctypes.c_ulonglong, + ] + + +# noinspection PyPep8Naming +def BNWARPFreeContainerList( + containers: ctypes.POINTER(ctypes.POINTER(BNWARPContainer)), + count: int + ) -> None: + return _BNWARPFreeContainerList(containers, count) + + +# ------------------------------------------------------- +# _BNWARPFreeContainerReference + +_BNWARPFreeContainerReference = core.BNWARPFreeContainerReference +_BNWARPFreeContainerReference.restype = None +_BNWARPFreeContainerReference.argtypes = [ + ctypes.POINTER(BNWARPContainer), + ] + + +# noinspection PyPep8Naming +def BNWARPFreeContainerReference( + container: ctypes.POINTER(BNWARPContainer) + ) -> None: + return _BNWARPFreeContainerReference(container) + + +# ------------------------------------------------------- +# _BNWARPFreeFunctionCommentList + +_BNWARPFreeFunctionCommentList = core.BNWARPFreeFunctionCommentList +_BNWARPFreeFunctionCommentList.restype = None +_BNWARPFreeFunctionCommentList.argtypes = [ + ctypes.POINTER(BNWARPFunctionComment), + ctypes.c_ulonglong, + ] + + +# noinspection PyPep8Naming +def BNWARPFreeFunctionCommentList( + comments: ctypes.POINTER(BNWARPFunctionComment), + count: int + ) -> None: + return _BNWARPFreeFunctionCommentList(comments, count) + + +# ------------------------------------------------------- +# _BNWARPFreeFunctionList + +_BNWARPFreeFunctionList = core.BNWARPFreeFunctionList +_BNWARPFreeFunctionList.restype = None +_BNWARPFreeFunctionList.argtypes = [ + ctypes.POINTER(ctypes.POINTER(BNWARPFunction)), + ctypes.c_ulonglong, + ] + + +# noinspection PyPep8Naming +def BNWARPFreeFunctionList( + functions: ctypes.POINTER(ctypes.POINTER(BNWARPFunction)), + count: int + ) -> None: + return _BNWARPFreeFunctionList(functions, count) + + +# ------------------------------------------------------- +# _BNWARPFreeFunctionReference + +_BNWARPFreeFunctionReference = core.BNWARPFreeFunctionReference +_BNWARPFreeFunctionReference.restype = None +_BNWARPFreeFunctionReference.argtypes = [ + ctypes.POINTER(BNWARPFunction), + ] + + +# noinspection PyPep8Naming +def BNWARPFreeFunctionReference( + function: ctypes.POINTER(BNWARPFunction) + ) -> None: + return _BNWARPFreeFunctionReference(function) + + +# ------------------------------------------------------- +# _BNWARPFreeTargetReference + +_BNWARPFreeTargetReference = core.BNWARPFreeTargetReference +_BNWARPFreeTargetReference.restype = None +_BNWARPFreeTargetReference.argtypes = [ + ctypes.POINTER(BNWARPTarget), + ] + + +# noinspection PyPep8Naming +def BNWARPFreeTargetReference( + target: ctypes.POINTER(BNWARPTarget) + ) -> None: + return _BNWARPFreeTargetReference(target) + + +# ------------------------------------------------------- +# _BNWARPFreeUUIDList + +_BNWARPFreeUUIDList = core.BNWARPFreeUUIDList +_BNWARPFreeUUIDList.restype = None +_BNWARPFreeUUIDList.argtypes = [ + ctypes.POINTER(BNWARPUUID), + ctypes.c_ulonglong, + ] + + +# noinspection PyPep8Naming +def BNWARPFreeUUIDList( + uuids: ctypes.POINTER(BNWARPUUID), + count: int + ) -> None: + return _BNWARPFreeUUIDList(uuids, count) + + +# ------------------------------------------------------- +# _BNWARPFunctionApply + +_BNWARPFunctionApply = core.BNWARPFunctionApply +_BNWARPFunctionApply.restype = None +_BNWARPFunctionApply.argtypes = [ + ctypes.POINTER(BNWARPFunction), + ctypes.POINTER(BNFunction), + ] + + +# noinspection PyPep8Naming +def BNWARPFunctionApply( + function: ctypes.POINTER(BNWARPFunction), + analysisFunction: ctypes.POINTER(BNFunction) + ) -> None: + return _BNWARPFunctionApply(function, analysisFunction) + + +# ------------------------------------------------------- +# _BNWARPFunctionGetComments + +_BNWARPFunctionGetComments = core.BNWARPFunctionGetComments +_BNWARPFunctionGetComments.restype = ctypes.POINTER(BNWARPFunctionComment) +_BNWARPFunctionGetComments.argtypes = [ + ctypes.POINTER(BNWARPFunction), + ctypes.POINTER(ctypes.c_ulonglong), + ] + + +# noinspection PyPep8Naming +def BNWARPFunctionGetComments( + function: ctypes.POINTER(BNWARPFunction), + count: ctypes.POINTER(ctypes.c_ulonglong) + ) -> Optional[ctypes.POINTER(BNWARPFunctionComment)]: + result = _BNWARPFunctionGetComments(function, count) + if not result: + return None + return result + + +# ------------------------------------------------------- +# _BNWARPFunctionGetConstraints + +_BNWARPFunctionGetConstraints = core.BNWARPFunctionGetConstraints +_BNWARPFunctionGetConstraints.restype = ctypes.POINTER(BNWARPConstraint) +_BNWARPFunctionGetConstraints.argtypes = [ + ctypes.POINTER(BNWARPFunction), + ctypes.POINTER(ctypes.c_ulonglong), + ] + + +# noinspection PyPep8Naming +def BNWARPFunctionGetConstraints( + function: ctypes.POINTER(BNWARPFunction), + count: ctypes.POINTER(ctypes.c_ulonglong) + ) -> Optional[ctypes.POINTER(BNWARPConstraint)]: + result = _BNWARPFunctionGetConstraints(function, count) + if not result: + return None + return result + + +# ------------------------------------------------------- +# _BNWARPFunctionGetGUID + +_BNWARPFunctionGetGUID = core.BNWARPFunctionGetGUID +_BNWARPFunctionGetGUID.restype = BNWARPFunctionGUID +_BNWARPFunctionGetGUID.argtypes = [ + ctypes.POINTER(BNWARPFunction), + ] + + +# noinspection PyPep8Naming +def BNWARPFunctionGetGUID( + function: ctypes.POINTER(BNWARPFunction) + ) -> BNWARPFunctionGUID: + return _BNWARPFunctionGetGUID(function) + + +# ------------------------------------------------------- +# _BNWARPFunctionGetSymbol + +_BNWARPFunctionGetSymbol = core.BNWARPFunctionGetSymbol +_BNWARPFunctionGetSymbol.restype = ctypes.POINTER(BNSymbol) +_BNWARPFunctionGetSymbol.argtypes = [ + ctypes.POINTER(BNWARPFunction), + ctypes.POINTER(BNFunction), + ] + + +# noinspection PyPep8Naming +def BNWARPFunctionGetSymbol( + function: ctypes.POINTER(BNWARPFunction), + analysisFunction: ctypes.POINTER(BNFunction) + ) -> Optional[ctypes.POINTER(BNSymbol)]: + result = _BNWARPFunctionGetSymbol(function, analysisFunction) + if not result: + return None + return result + + +# ------------------------------------------------------- +# _BNWARPFunctionGetSymbolName + +_BNWARPFunctionGetSymbolName = core.BNWARPFunctionGetSymbolName +_BNWARPFunctionGetSymbolName.restype = ctypes.POINTER(ctypes.c_byte) +_BNWARPFunctionGetSymbolName.argtypes = [ + ctypes.POINTER(BNWARPFunction), + ] + + +# noinspection PyPep8Naming +def BNWARPFunctionGetSymbolName( + function: ctypes.POINTER(BNWARPFunction) + ) -> Optional[Optional[str]]: + result = _BNWARPFunctionGetSymbolName(function) + if not result: + return None + string = str(pyNativeStr(ctypes.cast(result, ctypes.c_char_p).value)) + BNFreeString(result) + return string + + +# ------------------------------------------------------- +# _BNWARPFunctionGetType + +_BNWARPFunctionGetType = core.BNWARPFunctionGetType +_BNWARPFunctionGetType.restype = ctypes.POINTER(BNType) +_BNWARPFunctionGetType.argtypes = [ + ctypes.POINTER(BNWARPFunction), + ctypes.POINTER(BNFunction), + ] + + +# noinspection PyPep8Naming +def BNWARPFunctionGetType( + function: ctypes.POINTER(BNWARPFunction), + analysisFunction: ctypes.POINTER(BNFunction) + ) -> Optional[ctypes.POINTER(BNType)]: + result = _BNWARPFunctionGetType(function, analysisFunction) + if not result: + return None + return result + + +# ------------------------------------------------------- +# _BNWARPFunctionsEqual + +_BNWARPFunctionsEqual = core.BNWARPFunctionsEqual +_BNWARPFunctionsEqual.restype = ctypes.c_bool +_BNWARPFunctionsEqual.argtypes = [ + ctypes.POINTER(BNWARPFunction), + ctypes.POINTER(BNWARPFunction), + ] + + +# noinspection PyPep8Naming +def BNWARPFunctionsEqual( + functionA: ctypes.POINTER(BNWARPFunction), + functionB: ctypes.POINTER(BNWARPFunction) + ) -> bool: + return _BNWARPFunctionsEqual(functionA, functionB) + + +# ------------------------------------------------------- +# _BNWARPGetAnalysisFunctionGUID + +_BNWARPGetAnalysisFunctionGUID = core.BNWARPGetAnalysisFunctionGUID +_BNWARPGetAnalysisFunctionGUID.restype = ctypes.c_bool +_BNWARPGetAnalysisFunctionGUID.argtypes = [ + ctypes.POINTER(BNFunction), + ctypes.POINTER(BNWARPFunctionGUID), + ] + + +# noinspection PyPep8Naming +def BNWARPGetAnalysisFunctionGUID( + analysisFunction: ctypes.POINTER(BNFunction), + result: ctypes.POINTER(BNWARPFunctionGUID) + ) -> bool: + return _BNWARPGetAnalysisFunctionGUID(analysisFunction, result) + + +# ------------------------------------------------------- +# _BNWARPGetBasicBlockGUID + +_BNWARPGetBasicBlockGUID = core.BNWARPGetBasicBlockGUID +_BNWARPGetBasicBlockGUID.restype = ctypes.c_bool +_BNWARPGetBasicBlockGUID.argtypes = [ + ctypes.POINTER(BNBasicBlock), + ctypes.POINTER(BNWARPBasicBlockGUID), + ] + + +# noinspection PyPep8Naming +def BNWARPGetBasicBlockGUID( + basicBlock: ctypes.POINTER(BNBasicBlock), + result: ctypes.POINTER(BNWARPBasicBlockGUID) + ) -> bool: + return _BNWARPGetBasicBlockGUID(basicBlock, result) + + +# ------------------------------------------------------- +# _BNWARPGetContainers + +_BNWARPGetContainers = core.BNWARPGetContainers +_BNWARPGetContainers.restype = ctypes.POINTER(ctypes.POINTER(BNWARPContainer)) +_BNWARPGetContainers.argtypes = [ + ctypes.POINTER(ctypes.c_ulonglong), + ] + + +# noinspection PyPep8Naming +def BNWARPGetContainers( + count: ctypes.POINTER(ctypes.c_ulonglong) + ) -> Optional[ctypes.POINTER(ctypes.POINTER(BNWARPContainer))]: + result = _BNWARPGetContainers(count) + if not result: + return None + return result + + +# ------------------------------------------------------- +# _BNWARPGetFunction + +_BNWARPGetFunction = core.BNWARPGetFunction +_BNWARPGetFunction.restype = ctypes.POINTER(BNWARPFunction) +_BNWARPGetFunction.argtypes = [ + ctypes.POINTER(BNFunction), + ] + + +# noinspection PyPep8Naming +def BNWARPGetFunction( + analysisFunction: ctypes.POINTER(BNFunction) + ) -> Optional[ctypes.POINTER(BNWARPFunction)]: + result = _BNWARPGetFunction(analysisFunction) + if not result: + return None + return result + + +# ------------------------------------------------------- +# _BNWARPGetMatchedFunction + +_BNWARPGetMatchedFunction = core.BNWARPGetMatchedFunction +_BNWARPGetMatchedFunction.restype = ctypes.POINTER(BNWARPFunction) +_BNWARPGetMatchedFunction.argtypes = [ + ctypes.POINTER(BNFunction), + ] + + +# noinspection PyPep8Naming +def BNWARPGetMatchedFunction( + analysisFunction: ctypes.POINTER(BNFunction) + ) -> Optional[ctypes.POINTER(BNWARPFunction)]: + result = _BNWARPGetMatchedFunction(analysisFunction) + if not result: + return None + return result + + +# ------------------------------------------------------- +# _BNWARPGetTarget + +_BNWARPGetTarget = core.BNWARPGetTarget +_BNWARPGetTarget.restype = ctypes.POINTER(BNWARPTarget) +_BNWARPGetTarget.argtypes = [ + ctypes.POINTER(BNPlatform), + ] + + +# noinspection PyPep8Naming +def BNWARPGetTarget( + platform: ctypes.POINTER(BNPlatform) + ) -> Optional[ctypes.POINTER(BNWARPTarget)]: + result = _BNWARPGetTarget(platform) + if not result: + return None + return result + + +# ------------------------------------------------------- +# _BNWARPIsLiftedInstructionBlacklisted + +_BNWARPIsLiftedInstructionBlacklisted = core.BNWARPIsLiftedInstructionBlacklisted +_BNWARPIsLiftedInstructionBlacklisted.restype = ctypes.c_bool +_BNWARPIsLiftedInstructionBlacklisted.argtypes = [ + ctypes.POINTER(BNLowLevelILFunction), + ctypes.c_ulonglong, + ] + + +# noinspection PyPep8Naming +def BNWARPIsLiftedInstructionBlacklisted( + liftedFunction: ctypes.POINTER(BNLowLevelILFunction), + idx: int + ) -> bool: + return _BNWARPIsLiftedInstructionBlacklisted(liftedFunction, idx) + + +# ------------------------------------------------------- +# _BNWARPIsLiftedInstructionVariant + +_BNWARPIsLiftedInstructionVariant = core.BNWARPIsLiftedInstructionVariant +_BNWARPIsLiftedInstructionVariant.restype = ctypes.c_bool +_BNWARPIsLiftedInstructionVariant.argtypes = [ + ctypes.POINTER(BNLowLevelILFunction), + ctypes.c_ulonglong, + ] + + +# noinspection PyPep8Naming +def BNWARPIsLiftedInstructionVariant( + liftedFunction: ctypes.POINTER(BNLowLevelILFunction), + idx: int + ) -> bool: + return _BNWARPIsLiftedInstructionVariant(liftedFunction, idx) + + +# ------------------------------------------------------- +# _BNWARPNewContainerReference + +_BNWARPNewContainerReference = core.BNWARPNewContainerReference +_BNWARPNewContainerReference.restype = ctypes.POINTER(BNWARPContainer) +_BNWARPNewContainerReference.argtypes = [ + ctypes.POINTER(BNWARPContainer), + ] + + +# noinspection PyPep8Naming +def BNWARPNewContainerReference( + container: ctypes.POINTER(BNWARPContainer) + ) -> Optional[ctypes.POINTER(BNWARPContainer)]: + result = _BNWARPNewContainerReference(container) + if not result: + return None + return result + + +# ------------------------------------------------------- +# _BNWARPNewFunctionReference + +_BNWARPNewFunctionReference = core.BNWARPNewFunctionReference +_BNWARPNewFunctionReference.restype = ctypes.POINTER(BNWARPFunction) +_BNWARPNewFunctionReference.argtypes = [ + ctypes.POINTER(BNWARPFunction), + ] + + +# noinspection PyPep8Naming +def BNWARPNewFunctionReference( + function: ctypes.POINTER(BNWARPFunction) + ) -> Optional[ctypes.POINTER(BNWARPFunction)]: + result = _BNWARPNewFunctionReference(function) + if not result: + return None + return result + + +# ------------------------------------------------------- +# _BNWARPNewTargetReference + +_BNWARPNewTargetReference = core.BNWARPNewTargetReference +_BNWARPNewTargetReference.restype = ctypes.POINTER(BNWARPTarget) +_BNWARPNewTargetReference.argtypes = [ + ctypes.POINTER(BNWARPTarget), + ] + + +# noinspection PyPep8Naming +def BNWARPNewTargetReference( + target: ctypes.POINTER(BNWARPTarget) + ) -> Optional[ctypes.POINTER(BNWARPTarget)]: + result = _BNWARPNewTargetReference(target) + if not result: + return None + return result + + +# ------------------------------------------------------- +# _BNWARPRunMatcher + +_BNWARPRunMatcher = core.BNWARPRunMatcher +_BNWARPRunMatcher.restype = None +_BNWARPRunMatcher.argtypes = [ + ctypes.POINTER(BNBinaryView), + ] + + +# noinspection PyPep8Naming +def BNWARPRunMatcher( + view: ctypes.POINTER(BNBinaryView) + ) -> None: + return _BNWARPRunMatcher(view) + + +# ------------------------------------------------------- +# _BNWARPUUIDEqual + +_BNWARPUUIDEqual = core.BNWARPUUIDEqual +_BNWARPUUIDEqual.restype = ctypes.c_bool +_BNWARPUUIDEqual.argtypes = [ + ctypes.POINTER(BNWARPUUID), + ctypes.POINTER(BNWARPUUID), + ] + + +# noinspection PyPep8Naming +def BNWARPUUIDEqual( + a: ctypes.POINTER(BNWARPUUID), + b: ctypes.POINTER(BNWARPUUID) + ) -> bool: + return _BNWARPUUIDEqual(a, b) + + +# ------------------------------------------------------- +# _BNWARPUUIDGetString + +_BNWARPUUIDGetString = core.BNWARPUUIDGetString +_BNWARPUUIDGetString.restype = ctypes.POINTER(ctypes.c_byte) +_BNWARPUUIDGetString.argtypes = [ + ctypes.POINTER(BNWARPUUID), + ] + + +# noinspection PyPep8Naming +def BNWARPUUIDGetString( + uuid: ctypes.POINTER(BNWARPUUID) + ) -> Optional[Optional[str]]: + result = _BNWARPUUIDGetString(uuid) + if not result: + return None + string = str(pyNativeStr(ctypes.cast(result, ctypes.c_char_p).value)) + BNFreeString(result) + return string + + + +# 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/plugins/warp/api/python/_warpcore_template.py b/plugins/warp/api/python/_warpcore_template.py new file mode 100644 index 00000000..fd923450 --- /dev/null +++ b/plugins/warp/api/python/_warpcore_template.py @@ -0,0 +1,58 @@ +import binaryninja +import ctypes, os + +from typing import Optional +from . import warp_enums +# Load core module +import platform +core = None +core_platform = platform.system() + +from binaryninja import Settings +if Settings().get_bool("corePlugins.warp"): + from binaryninja._binaryninjacore import BNGetBundledPluginDirectory + if core_platform == "Darwin": + _base_path = BNGetBundledPluginDirectory() + core = ctypes.CDLL(os.path.join(_base_path, "libwarp_ninja.dylib")) + + elif core_platform == "Linux": + _base_path = BNGetBundledPluginDirectory() + core = ctypes.CDLL(os.path.join(_base_path, "libwarp_ninja.so")) + + elif (core_platform == "Windows") or (core_platform.find("CYGWIN_NT") == 0): + _base_path = BNGetBundledPluginDirectory() + core = ctypes.CDLL(os.path.join(_base_path, "warp_ninja.dll")) + else: + raise Exception("OS not supported") +else: + from binaryninja._binaryninjacore import BNGetUserPluginDirectory + if core_platform == "Darwin": + _base_path = BNGetUserPluginDirectory() + core = ctypes.CDLL(os.path.join(_base_path, "libwarp_ninja.dylib")) + + elif core_platform == "Linux": + _base_path = BNGetUserPluginDirectory() + core = ctypes.CDLL(os.path.join(_base_path, "libwarp_ninja.so")) + + elif (core_platform == "Windows") or (core_platform.find("CYGWIN_NT") == 0): + _base_path = BNGetUserPluginDirectory() + core = ctypes.CDLL(os.path.join(_base_path, "warp_ninja.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/plugins/warp/api/python/generator.cpp b/plugins/warp/api/python/generator.cpp new file mode 100644 index 00000000..f40fc01a --- /dev/null +++ b/plugins/warp/api/python/generator.cpp @@ -0,0 +1,652 @@ +/* +Copyright 2020-2025 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().GetValue(), true, true); + for (auto& i : type->GetChildType()->GetParameters()) + { + fprintf(out, ", "); + OutputType(out, i.type.GetValue()); + } + fprintf(out, ")"); + break; + } + fprintf(out, "ctypes.POINTER("); + OutputType(out, type->GetChildType().GetValue()); + fprintf(out, ")"); + break; + case ArrayTypeClass: + OutputType(out, type->GetChildType().GetValue()); + 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().GetValue(), true, true); + for (auto& i : type->GetChildType()->GetParameters()) + { + fprintf(out, ", "); + OutputType(out, i.type.GetValue()); + } + fprintf(out, ")"); + break; + } + fprintf(out, "ctypes.POINTER("); + OutputType(out, type->GetChildType().GetValue()); + fprintf(out, ")"); + break; + case ArrayTypeClass: + OutputType(out, type->GetChildType().GetValue()); + 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, "from binaryninja._binaryninjacore import BNFreeString\n"); + 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 (name == "BNArchitecture") + { + fprintf(out, "from binaryninja._binaryninjacore import BNArchitecture, BNArchitectureHandle\n"); + continue; + } + if (name == "BNBasicBlock") + { + fprintf(out, "from binaryninja._binaryninjacore import BNBasicBlock, BNBasicBlockHandle\n"); + continue; + } + if (name == "BNFunction") + { + fprintf(out, "from binaryninja._binaryninjacore import BNFunction, BNFunctionHandle\n"); + continue; + } + if (name == "BNLowLevelILFunction") + { + fprintf(out, "from binaryninja._binaryninjacore import BNLowLevelILFunction, BNLowLevelILFunctionHandle\n"); + continue; + } + if (name == "BNPlatform") + { + fprintf(out, "from binaryninja._binaryninjacore import BNPlatform, BNPlatformHandle\n"); + continue; + } + if (name == "BNSymbol") + { + fprintf(out, "from binaryninja._binaryninjacore import BNSymbol, BNSymbolHandle\n"); + continue; + } + if (name == "BNType") + { + fprintf(out, "from binaryninja._binaryninjacore import BNType, BNTypeHandle\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) && + (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.GetValue()); + 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().GetValue(), 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.GetValue(), true); + } + else + { + OutputType(out, j.type.GetValue()); + } + 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.GetValue()); + else + OutputType(out, arg.type.GetValue()); + argN ++; + } + } + fprintf(out, "\n\t\t) -> "); + if (swizzleArgs) + { + if (stringResult || pointerResult) + fprintf(out, "Optional["); + OutputSwizzledType(out, i.second->GetChildType().GetValue()); + if (stringResult || pointerResult) + fprintf(out, "]"); + } + else + { + OutputType(out, i.second->GetChildType().GetValue()); + } + 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/plugins/warp/api/python/warp.py b/plugins/warp/api/python/warp.py new file mode 100644 index 00000000..e0500215 --- /dev/null +++ b/plugins/warp/api/python/warp.py @@ -0,0 +1,384 @@ +import ctypes +import dataclasses +import uuid +from typing import List, Optional, Union + +import binaryninja +from binaryninja import BinaryView, Function, BasicBlock, Architecture, Platform, Type, Symbol, LowLevelILInstruction, LowLevelILFunction +from binaryninja._binaryninjacore import BNFreeString, BNAllocString, BNType + +from . import _warpcore as warpcore + + +class WarpUUID: + def __init__(self, _uuid: Union[warpcore.BNWARPUUID, str, uuid.UUID]): + if isinstance(_uuid, str): + _uuid = uuid.UUID(_uuid) + if isinstance(_uuid, uuid.UUID): + uuid_bytes = _uuid.bytes + _uuid = warpcore.BNWARPUUID() + _uuid.uuid = (ctypes.c_ubyte * 16).from_buffer_copy(uuid_bytes) + elif isinstance(_uuid, warpcore.BNWARPUUID): + # We must create a copy! + new_uuid = warpcore.BNWARPUUID() + new_uuid.uuid = (ctypes.c_ubyte * 16).from_buffer_copy(_uuid.uuid) + _uuid = new_uuid + self._uuid = _uuid + + def to_string(self) -> str: + return warpcore.BNWARPUUIDGetString(self._uuid) + + def __str__(self): + return self.to_string() + + def __repr__(self): + return f"<WarpUUID '{str(self)}'>" + + def __hash__(self): + # Hash based on the UUID bytes + return hash(bytes(self._uuid.uuid)) + + def __eq__(self, other): + if not isinstance(other, WarpUUID): + return False + return warpcore.BNWARPUUIDEqual(self._uuid, other._uuid) + + @property + def uuid(self): + return self._uuid + + +class Source(WarpUUID): + def __repr__(self): + return f"<Source '{str(self)}'>" + + +class BasicBlockGUID(WarpUUID): + def __repr__(self): + return f"<BasicBlockGUID '{str(self)}'>" + + +class FunctionGUID(WarpUUID): + def __repr__(self): + return f"<FunctionGUID '{str(self)}'>" + + +class ConstraintGUID(WarpUUID): + def __repr__(self): + return f"<ConstraintGUID '{str(self)}'>" + + +class TypeGUID(WarpUUID): + def __repr__(self): + return f"<TypeGUID '{str(self)}'>" + + +@dataclasses.dataclass +class WarpFunctionComment: + text: str + offset: int + + def __str__(self): + return repr(self) + + def __repr__(self): + return f"<WarpFunctionComment '{self.text}': {self.offset:#x}>" + + @staticmethod + def from_api(comment: warpcore.BNWARPFunctionComment) -> 'WarpFunctionComment': + return WarpFunctionComment( + text=comment.text.decode('utf-8'), + offset=comment.offset + ) + +@dataclasses.dataclass +class WarpConstraint: + guid: ConstraintGUID + offset: Optional[int] + + def __str__(self): + return repr(self) + + def __repr__(self): + if self.offset is None: + return f"<WarpConstraint '{self.guid}'>" + return f"<WarpConstraint '{self.guid}': {self.offset:#x}>" + + @staticmethod + def from_api(constraint: warpcore.BNWARPConstraint) -> 'WarpConstraint': + if constraint.offset == -1: + return WarpConstraint(guid=ConstraintGUID(constraint.guid), offset=None) + return WarpConstraint(guid=ConstraintGUID(constraint.guid), offset=constraint.offset) + +class WarpTarget: + def __init__(self, handle: Union[warpcore.BNWARPTarget, Platform]): + if isinstance(handle, Platform): + self.handle = warpcore.BNWARPGetTarget(handle.handle) + else: + self.handle = handle + + def __del__(self): + if self.handle is not None: + warpcore.BNWARPFreeTargetReference(self.handle) + + @staticmethod + def from_platform(platform: Platform) -> Optional['WarpTarget']: + handle = warpcore.BNWARPGetTarget(platform.handle) + if not handle: + return None + return WarpTarget(handle) + + +class WarpFunction: + def __init__(self, handle: Union[warpcore.BNWARPFunction, Function]): + if isinstance(handle, Function): + self.handle = warpcore.BNWARPGetFunction(handle.handle) + else: + self.handle = handle + def __del__(self): + if self.handle is not None: + warpcore.BNWARPFreeFunctionReference(self.handle) + + def __repr__(self): + return f"<WarpFunction '{self.name}': '{self.guid}'>" + + @property + def guid(self) -> FunctionGUID: + return FunctionGUID(warpcore.BNWARPFunctionGetGUID(self.handle)) + + @property + def name(self) -> str: + return warpcore.BNWARPFunctionGetSymbolName(self.handle) + + def get_symbol(self, function: Function) -> Symbol: + symbol_handle = warpcore.BNWARPFunctionGetSymbol(self.handle, function.handle) + return Symbol(symbol_handle) + + def get_type(self, function: Function) -> Optional[Type]: + type_handle = warpcore.BNWARPFunctionGetType(self.handle, function.handle) + if not type_handle: + return None + return Type(type_handle) + + @property + def constraints(self) -> List[WarpConstraint]: + count = ctypes.c_size_t() + constraints = warpcore.BNWARPFunctionGetConstraints(self.handle, count) + if not constraints: + return [] + result = [] + for i in range(count.value): + result.append(WarpConstraint.from_api(constraints[i])) + warpcore.BNWARPFreeConstraintList(constraints, count.value) + return result + + @property + def comments(self) -> List[WarpFunctionComment]: + count = ctypes.c_size_t() + comments = warpcore.BNWARPFunctionGetComments(self.handle, count) + if not comments: + return [] + result = [] + for i in range(count.value): + result.append(WarpFunctionComment.from_api(comments[i])) + warpcore.BNWARPFreeFunctionCommentList(comments, count.value) + return result + + @staticmethod + def get_matched(function: Function) -> Optional['WarpFunction']: + handle = warpcore.BNWARPGetMatchedFunction(function.handle) + if not handle: + return None + return WarpFunction(handle) + + def apply(self, function: Function): + warpcore.BNWARPFunctionApply(self.handle, function.handle) + + +class _WarpContainerMetaclass(type): + def __iter__(self): + binaryninja._init_plugins() + count = ctypes.c_ulonglong() + containers = warpcore.BNWARPGetContainers(count) + try: + for i in range(0, count.value): + yield WarpContainer(warpcore.BNWARPNewContainerReference(containers[i])) + finally: + warpcore.BNWARPFreeContainerList(containers, count.value) + + def __getitem__(self, value): + binaryninja._init_plugins() + count = ctypes.c_ulonglong() + containers = warpcore.BNWARPGetContainers(count) + try: + for i in range(0, count.value): + container = WarpContainer(warpcore.BNWARPNewContainerReference(containers[i])) + if container.name == str(value): + return container + raise KeyError(f"'{value}' is not a valid container name") + finally: + warpcore.BNWARPFreeContainerList(containers, count.value) + + +class WarpContainer(metaclass=_WarpContainerMetaclass): + def __init__(self, handle: warpcore.BNWARPContainer): + self.handle = handle + + def __del__(self): + if self.handle is not None: + warpcore.BNWARPFreeContainerReference(self.handle) + + def __repr__(self): + return f"<WarpContainer '{self.name}'>" + + @staticmethod + def all() -> List['WarpContainer']: + count = ctypes.c_size_t() + containers = warpcore.BNWARPGetContainers(count) + if not containers: + return [] + result = [] + for i in range(count.value): + result.append(WarpContainer(warpcore.BNWARPNewContainerReference(containers[i]))) + warpcore.BNWARPFreeContainerList(containers, count.value) + return result + + @property + def name(self) -> str: + return warpcore.BNWARPContainerGetName(self.handle) + + @property + def sources(self) -> List[Source]: + count = ctypes.c_size_t() + sources = warpcore.BNWARPContainerGetSources(self.handle, count) + if not sources: + return [] + result = [] + for i in range(count.value): + result.append(Source(sources[i])) + warpcore.BNWARPFreeUUIDList(sources, count.value) + return result + + def add_source(self, source_path: str) -> Optional[Source]: + source = warpcore.BNWARPUUID() + if not warpcore.BNWARPContainerAddSource(self.handle, source_path, source): + return None + return Source(source) + + def commit_source(self, source: Source) -> bool: + return warpcore.BNWARPContainerCommitSource(self.handle, source.uuid) + + def is_source_uncommitted(self, source: Source) -> bool: + return warpcore.BNWARPContainerIsSourceWritable(self.handle, source.uuid) + + def is_source_writable(self, source: Source) -> bool: + return warpcore.BNWARPContainerIsSourceWritable(self.handle, source.uuid) + + def get_source_path(self, source: Source) -> Optional[str]: + return warpcore.BNWARPContainerGetSourcePath(self.handle, source.uuid) + + def add_functions(self, target: WarpTarget, source: Source, functions: List[Function]) -> bool: + count = len(functions) + core_funcs = (ctypes.POINTER(warpcore.BNWARPFunction) * count)() + for i in range(count): + core_funcs[i] = functions[i].handle + return warpcore.BNWARPContainerAddFunctions(self.handle, target.handle, source.uuid, core_funcs, count) + + def add_types(self, view: BinaryView, source: Source, types: List[Type]) -> bool: + count = len(types) + core_types = (ctypes.POINTER(BNType) * count)() + for i in range(count): + core_types[i] = types[i].handle + return warpcore.BNWARPContainerAddTypes(view.handle, self.handle, source.uuid, core_types, count) + + def remove_functions(self, target: WarpTarget, source: Source, functions: List[Function]) -> bool: + count = len(functions) + core_funcs = (ctypes.POINTER(warpcore.BNWARPFunction) * count)() + for i in range(count): + core_funcs[i] = functions[i].handle + return warpcore.BNWARPContainerRemoveFunctions(self.handle, target.handle, source.uuid, core_funcs, count) + + def remove_types(self, source: Source, guids: List[TypeGUID]) -> bool: + count = len(guids) + core_guids = (ctypes.POINTER(warpcore.BNWARPTypeGUID) * count)() + for i in range(count): + core_guids[i] = guids[i].uuid + return warpcore.BNWARPContainerRemoveTypes(self.handle, source.uuid, core_guids, count) + + def get_sources_with_function_guid(self, target: WarpTarget, guid: FunctionGUID) -> List[Source]: + count = ctypes.c_size_t() + sources = warpcore.BNWARPContainerGetSourcesWithFunctionGUID(self.handle, target.handle, guid.uuid, count) + if not sources: + return [] + result = [] + for i in range(count.value): + result.append(Source(sources[i])) + warpcore.BNWARPFreeUUIDList(sources, count.value) + return result + + def get_sources_with_type_guid(self, guid: TypeGUID) -> List[Source]: + count = ctypes.c_size_t() + sources = warpcore.BNWARPContainerGetSourcesWithTypeGUID(self.handle, guid.uuid, count) + if not sources: + return [] + result = [] + for i in range(count.value): + result.append(Source(sources[i])) + warpcore.BNWARPFreeUUIDList(sources, count.value) + return result + + def get_functions_with_guid(self, target: WarpTarget, source: Source, guid: FunctionGUID) -> List[Function]: + count = ctypes.c_size_t() + funcs = warpcore.BNWARPContainerGetFunctionsWithGUID(self.handle, target.handle, source.uuid, guid.uuid, count) + if not funcs: + return [] + result = [] + for i in range(count.value): + result.append(WarpFunction(warpcore.BNWARPNewFunctionReference(funcs[i]))) + warpcore.BNWARPFreeFunctionList(funcs, count.value) + return result + + def get_type_with_guid(self, arch: Architecture, source: Source, guid: TypeGUID) -> Optional[Type]: + ty = warpcore.BNWARPContainerGetTypeWithGUID(self.handle, arch.handle, source.uuid, guid.uuid) + if not ty: + return None + return Type(ty) + + def get_type_guids_with_name(self, source: Source, name: str) -> List[TypeGUID]: + count = ctypes.c_size_t() + guids = warpcore.BNWARPContainerGetTypeGUIDsWithName(self.handle, source.uuid, name, count) + if not guids: + return [] + result = [] + for i in range(count.value): + result.append(TypeGUID(guids[i])) + warpcore.BNWARPFreeUUIDList(guids, count.value) + return result + + +def run_matcher(view: BinaryView): + warpcore.BNWARPRunMatcher(view.handle) + +def is_instruction_variant(function: LowLevelILFunction, variant: LowLevelILInstruction) -> bool: + return warpcore.BNWARPIsLiftedInstructionVariant(function.handle, variant.instr_index) + +def is_instruction_blacklisted(function: LowLevelILFunction, variant: LowLevelILInstruction) -> bool: + return warpcore.BNWARPIsLiftedInstructionBlacklisted(function.handle, variant.instr_index) + +def get_function_guid(function: Function) -> Optional[FunctionGUID]: + guid = warpcore.BNWARPUUID() + if not warpcore.BNWARPGetAnalysisFunctionGUID(function.handle, guid): + return None + return FunctionGUID(guid) + + +def get_basic_block_guid(basic_block: BasicBlock) -> Optional[BasicBlockGUID]: + # TODO: I believe this won't work for HLIL: https://github.com/Vector35/binaryninja-api/issues/6998 + if basic_block.is_il: + basic_block = basic_block.source_block + guid = warpcore.BNWARPUUID() + if not warpcore.BNWARPGetBasicBlockGUID(basic_block.handle, guid): + return None + return BasicBlockGUID(guid) + +# TODO: Magic matched_function, possible_functions
\ No newline at end of file diff --git a/plugins/warp/api/python/warp_enums.py b/plugins/warp/api/python/warp_enums.py new file mode 100644 index 00000000..e47ff1c3 --- /dev/null +++ b/plugins/warp/api/python/warp_enums.py @@ -0,0 +1 @@ +import enum |
