From dff192e3bd6d5737633a740988c31e4ecba92998 Mon Sep 17 00:00:00 2001 From: Mason Reed Date: Wed, 22 May 2024 20:06:24 -0400 Subject: [PATCH 01/24] Add background task example plugin Clarify BackgroundTask functionality (skip-note, skip-ci) Providing clear documentation so it is understood that background tasks do not manage the execution of said the task. Add background_task plugin to examples section --- README.md | 1 + binaryninjaapi.h | 16 +++- examples/CMakeLists.txt | 1 + examples/background_task/CMakeLists.txt | 36 +++++++++ .../background_task/src/backgroundtask.cpp | 80 +++++++++++++++++++ 5 files changed, 132 insertions(+), 2 deletions(-) create mode 100644 examples/background_task/CMakeLists.txt create mode 100644 examples/background_task/src/backgroundtask.cpp diff --git a/README.md b/README.md index 2d35380a7..b0c0caa1a 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,7 @@ In addition to the default build setup, you may want to: There are many examples available. The [Python examples folder](https://github.com/Vector35/binaryninja-api/tree/dev/python/examples) demonstrates many different applications of the Python API, while C++ examples include: +- [background_task](https://github.com/Vector35/binaryninja-api/tree/dev/examples/background_task) is a plugin that demonstrates managing a background task.\* - [bin-info](https://github.com/Vector35/binaryninja-api/tree/dev/examples/bin-info) is a standalone executable that prints some information about a given binary to the terminal.\* - [breakpoint](https://github.com/Vector35/binaryninja-api/tree/dev/examples/breakpoint) is a plugin that allows you to select a region within an x86 binary and use the context menu to fill it with breakpoint bytes. - [command-line disassm](https://github.com/Vector35/binaryninja-api/tree/dev/examples/cmdline_disasm) demonstrates how to dump disassembly to the command line.\* diff --git a/binaryninjaapi.h b/binaryninjaapi.h index 364eb5642..d2cf74d2b 100644 --- a/binaryninjaapi.h +++ b/binaryninjaapi.h @@ -15513,8 +15513,20 @@ namespace BinaryNinja { public CoreRefCountObject { public: - BackgroundTask(BNBackgroundTask* task); - BackgroundTask(const std::string& initialText, bool canCancel); + BackgroundTask(BNBackgroundTask *task); + + /*! + Provides a mechanism for reporting progress of + an optionally cancelable task to the user via the status bar in the UI. + If canCancel is is `True`, then the task can be cancelled either + programmatically or by the user via the UI. + + \note This API does not provide a means to execute a task. The caller is responsible to execute (and possibly cancel) the task. + + \param initialText Text description of the progress of the background task (displayed in status bar of the UI) + \param canCancel Whether the task can be cancelled + */ + BackgroundTask(const std::string &initialText, bool canCancel); bool CanCancel() const; bool IsCancelled() const; diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 8a831b46d..790815c35 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -1,3 +1,4 @@ +add_subdirectory(background_task) add_subdirectory(bin-info) add_subdirectory(breakpoint) add_subdirectory(cmdline_disasm) diff --git a/examples/background_task/CMakeLists.txt b/examples/background_task/CMakeLists.txt new file mode 100644 index 000000000..36e012b0d --- /dev/null +++ b/examples/background_task/CMakeLists.txt @@ -0,0 +1,36 @@ +cmake_minimum_required(VERSION 3.13 FATAL_ERROR) + +project(background_task CXX C) + +file(GLOB SOURCES + src/*.cpp + src/*.h) + +add_library(background_task SHARED ${SOURCES}) + +if(NOT BN_API_BUILD_EXAMPLES AND NOT BN_INTERNAL_BUILD) + # Out-of-tree build + find_path( + BN_API_PATH + NAMES binaryninjaapi.h + HINTS ../.. binaryninjaapi $ENV{BN_API_PATH} + REQUIRED + ) + add_subdirectory(${BN_API_PATH} api) +endif() + +target_link_libraries(background_task binaryninjaui) + +set_target_properties(background_task PROPERTIES + CXX_STANDARD 17 + CXX_VISIBILITY_PRESET hidden + CXX_STANDARD_REQUIRED ON + VISIBILITY_INLINES_HIDDEN ON + POSITION_INDEPENDENT_CODE ON + LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/out/bin) + +if(BN_INTERNAL_BUILD) + ui_plugin_rpath(background_task) +endif() + +bn_install_plugin(${PROJECT_NAME}) diff --git a/examples/background_task/src/backgroundtask.cpp b/examples/background_task/src/backgroundtask.cpp new file mode 100644 index 000000000..8a3070808 --- /dev/null +++ b/examples/background_task/src/backgroundtask.cpp @@ -0,0 +1,80 @@ +#include +#include + +#include "binaryninjaapi.h" + +using namespace BinaryNinja; + +static Ref inspireBackgroundTask = nullptr; + +uint64_t InspireWriteCallback(uint8_t *data, uint64_t len, void *ctxt) +{ + try + { + Json::Value json; + std::unique_ptr reader(Json::CharReaderBuilder().newCharReader()); + std::string errors; + if (!reader->parse((char *) data, (char *) data + len, &json, &errors)) + { + LogError("Failed to parse! %s", errors.c_str()); + } + else + { + std::random_device rd; + std::map hist; + std::uniform_int_distribution dist(0, json.size()); + auto randQuoteObj = json.get(dist(rd), 0); + if (randQuoteObj.isObject() && randQuoteObj.size() == 2) + { + std::string quote = randQuoteObj.get("text", Json::Value("INVALID")).asString(); + LogInfo("%s", quote.c_str()); + // Display quote in progress text for 3 seconds. + inspireBackgroundTask->SetProgressText(quote); + std::this_thread::sleep_for(std::chrono::seconds(3)); + } + } + } catch (Json::Exception e) + { + LogError("JSON exception! %s", e.m_message.c_str()); + inspireBackgroundTask->Cancel(); + } + return len; +} + +bool InspireProgressCallback(void *ctxt, uint64_t progress, uint64_t total) +{ + // Close connection on cancellation + return !inspireBackgroundTask->IsCancelled(); +} + +void Inspire(BinaryView *bv) +{ + inspireBackgroundTask = new BackgroundTask("Getting inspired!", true); + std::thread inspireThread([]() { + LogInfo("Getting inspired!"); + BNDownloadInstanceOutputCallbacks outputCallbacks; + memset(&outputCallbacks, 0, sizeof(outputCallbacks)); + outputCallbacks.writeCallback = InspireWriteCallback; + outputCallbacks.progressCallback = InspireProgressCallback; + + auto downloadProvider = DownloadProvider::GetByName("CoreDownloadProvider"); + auto downloadInstance = downloadProvider->CreateNewInstance(); + inspireBackgroundTask->SetProgressText("Waiting for inspiration..."); + if (downloadInstance->PerformRequest("https://type.fit/api/quotes", &outputCallbacks)) + LogError("Inspiration failed!"); + + inspireBackgroundTask->Finish(); + }); + inspireThread.detach(); +} + +extern "C" { + BN_DECLARE_CORE_ABI_VERSION + + BINARYNINJAPLUGIN bool CorePluginInit() + { + PluginCommand::Register("Inspire me!", "Print an inspirational quote to the log", Inspire); + + return true; + } +} From e11e177d96736ab665b0cc37c19f184b93690fe2 Mon Sep 17 00:00:00 2001 From: Jordan Wiens Date: Wed, 22 May 2024 21:34:44 -0400 Subject: [PATCH 02/24] actual fix for incorrect settings table --- python/settings.py | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/python/settings.py b/python/settings.py index 6c73c4e6c..fa7cc275f 100644 --- a/python/settings.py +++ b/python/settings.py @@ -59,28 +59,28 @@ class Settings: :func:`register_group` method allows for specifying a UI friendly title for use in the Binary Ninja UI. Defining a new setting requires a \ unique setting key and a JSON string of property, value pairs. The following table describes the available properties and values. - ================== ====================================== ================== ======== ======================================================================= - Property JSON Data Type Prerequisite Optional {Allowed Values} and Notes - ================== ====================================== ================== ======== ======================================================================= - "title" string None No Concise Setting Title - "type" string None No {"array", "boolean", "number", "string"} - "elementType" string "type" is "array" No {"string"} - "enum" array : {string} "type" is "string" Yes Enumeration definitions - "enumDescriptions" array : {string} "type" is "string" Yes Enumeration descriptions that match "enum" array - "minValue" number "type" is "number" Yes Specify 0 to infer unsigned (default is signed) - "maxValue" number "type" is "number" Yes Values less than or equal to INT_MAX result in a QSpinBox UI element - "precision" number "type" is "number" Yes Specify precision for a QDoubleSpinBox - "default" {array, boolean, number, string, null} None Yes Specify optimal default value - "aliases" array : {string} None Yes Array of deprecated setting key(s) - "description" string None No Detailed setting description - "ignore" array : {string} None Yes {"SettingsUserScope", "SettingsProjectScope", "SettingsResourceScope"} - "message" string None Yes An optional message with additional emphasis - "readOnly" boolean None Yes Only enforced by UI elements - "optional" boolean None Yes Indicates setting can be null - "hidden" bool "type" is "string" Yes Indicates the UI should conceal the content - "requiresRestart boolean None Yes Enable restart notification in the UI upon change - "uiSelectionAction" string "type" is "string" Yes {"file", "directory", } Informs the UI to add a button to open a selection dialog or run a registered UIAction - ================== ====================================== ================== ======== ======================================================================= + =================== ====================================== ================== ======== ======================================================================= + Property JSON Data Type Prerequisite Optional {Allowed Values} and Notes + =================== ====================================== ================== ======== ======================================================================= + "title" string None No Concise Setting Title + "type" string None No {"array", "boolean", "number", "string"} + "elementType" string "type" is "array" No {"string"} + "enum" array : {string} "type" is "string" Yes Enumeration definitions + "enumDescriptions" array : {string} "type" is "string" Yes Enumeration descriptions that match "enum" array + "minValue" number "type" is "number" Yes Specify 0 to infer unsigned (default is signed) + "maxValue" number "type" is "number" Yes Values less than or equal to INT_MAX result in a QSpinBox UI element + "precision" number "type" is "number" Yes Specify precision for a QDoubleSpinBox + "default" {array, boolean, number, string, null} None Yes Specify optimal default value + "aliases" array : {string} None Yes Array of deprecated setting key(s) + "description" string None No Detailed setting description + "ignore" array : {string} None Yes {"SettingsUserScope", "SettingsProjectScope", "SettingsResourceScope"} + "message" string None Yes An optional message with additional emphasis + "readOnly" boolean None Yes Only enforced by UI elements + "optional" boolean None Yes Indicates setting can be null + "hidden" bool "type" is "string" Yes Indicates the UI should conceal the content + "requiresRestart boolean None Yes Enable restart notification in the UI upon change + "uiSelectionAction" string "type" is "string" Yes {"file", "directory", } Informs the UI to add a button to open a selection dialog or run a registered UIAction + =================== ====================================== ================== ======== ======================================================================= .. note:: In order to facilitate deterministic analysis results, settings from the *'default'* schema that impact analysis are serialized \ from Default, User, and Project scope into Resource scope during initial BinaryView analysis. This allows an analysis database to be opened \ From 304969ef22b4c58e3e890e77b36adcafbad25175 Mon Sep 17 00:00:00 2001 From: Jordan Wiens Date: Wed, 22 May 2024 23:30:11 -0400 Subject: [PATCH 03/24] cleanup all outstanding sphinx RST warnings --- python/binaryview.py | 98 +++++++++++++++++++++++------------------ python/highlevelil.py | 18 ++++---- python/mediumlevelil.py | 18 ++++---- 3 files changed, 74 insertions(+), 60 deletions(-) diff --git a/python/binaryview.py b/python/binaryview.py index 6b18ecc81..39cd344d3 100644 --- a/python/binaryview.py +++ b/python/binaryview.py @@ -1441,6 +1441,7 @@ def __len__(self): def serialize(cls, image_base: int, start: int, length: int, data_offset: int=0, data_length: int=0, flags: 'SegmentFlag'=SegmentFlag.SegmentReadable, auto_defined=True, segments: str="[]"): """ Serialize segment parameters into a JSON string. This is useful for generating a properly formatted segment description as options when using `load`. + :param int image_base: The base address of the image. :param int start: The start address of the segment. :param int length: The length of the segment. @@ -1452,14 +1453,12 @@ def serialize(cls, image_base: int, start: int, length: int, data_offset: int=0, :return: A JSON string representing the segment. :rtype: str - Example usage: - ``` - >>> base = 0x400000 - >>> rom_base = 0xffff0000 - >>> segments = Segment.serialize(image_base=base, start=base, length=0x1000, data_offset=0, data_length=0x1000, flags=SegmentFlag.SegmentReadable|SegmentFlag.SegmentExecutable) - >>> segments = Segment.serialize(image_base=base, start=rom_base, length=0x1000, flags=SegmentFlag.SegmentReadable, segments=segments) - >>> view = load(bytes.fromhex('5054ebfe'), options={'loader.imageBase': base, 'loader.platform': 'x86', 'loader.segments': segments}) - ``` + :Example:: + >>> base = 0x400000 + >>> rom_base = 0xffff0000 + >>> segments = Segment.serialize(image_base=base, start=base, length=0x1000, data_offset=0, data_length=0x1000, flags=SegmentFlag.SegmentReadable|SegmentFlag.SegmentExecutable) + >>> segments = Segment.serialize(image_base=base, start=rom_base, length=0x1000, flags=SegmentFlag.SegmentReadable, segments=segments) + >>> view = load(bytes.fromhex('5054ebfe'), options={'loader.imageBase': base, 'loader.platform': 'x86', 'loader.segments': segments}) """ segments_list = json.loads(segments) segment_info = { @@ -1602,6 +1601,7 @@ def __contains__(self, i: int): def serialize(cls, image_base: int, name: str, start: int, length: int, semantics: SectionSemantics=SectionSemantics.DefaultSectionSemantics, type: str="", align: int=1, entry_size: int=0, link: str="", info_section: str="", info_data: int=0, auto_defined: bool=True, sections: str="[]"): """ Serialize section parameters into a JSON string. This is useful for generating a properly formatted section description as options when using `load`. + :param int image_base: The base address of the image. :param str name: The name of the section. :param int start: The start address of the section. @@ -2114,16 +2114,18 @@ def __iter__(self): class MemoryMap: - """ + r""" The MemoryMap object is used to describe a system level MemoryMap for which a BinaryView is loaded into. A loaded BinaryView has a view into the MemoryMap which is described by the Segments defined in that BinaryView. The MemoryMap object allows for the addition of multiple, arbitrary overlapping regions of memory. Segmenting of the address space is automatically handled when the MemoryMap is modified and in the case where a portion of the system address space has - multilple defined regions, the default ordering gives priority to the most recently added region. This feature is + multiple defined regions, the default ordering gives priority to the most recently added region. This feature is experimental and under active development. :Example: +.. code-block:: python + >>> base = 0x10000 >>> rom_base = 0xc0000000 >>> segments = Segment.serialize(image_base=base, start=base, length=0x1000, data_offset=0, data_length=0x1000, flags=SegmentFlag.SegmentReadable|SegmentFlag.SegmentExecutable) @@ -2134,12 +2136,12 @@ class MemoryMap: size: 0x4 objects: 'origin' | Mapped - + size: 0x1000 objects: 'origin' | Unmapped | FILL<0x0> - + size: 0x14 objects: @@ -2151,13 +2153,13 @@ class MemoryMap: size: 0x4 objects: 'origin' | Mapped - + size: 0x1000 objects: 'rom' | Mapped 'origin' | Unmapped | FILL<0x0> - + size: 0x14 objects: @@ -2173,20 +2175,20 @@ class MemoryMap: size: 0x4 objects: 'origin' | Mapped - + size: 0x8 objects: 'pad' | Mapped 'rom' | Mapped 'origin' | Unmapped | FILL<0x0> - + size: 0xff8 objects: 'rom' | Mapped 'origin' | Unmapped | FILL<0x0> - + size: 0x14 objects: @@ -2671,8 +2673,10 @@ def new(data: Optional[Union[bytes, bytearray, 'databuffer.DataBuffer']] = None, """ ``new`` creates a new, Raw :py:class:`BinaryView` for the provided data. - :param Union[str, bytes, bytearray, 'databuffer.DataBuffer', 'os.PathLike', 'BinaryView'] data: path to file/bndb, raw bytes, or raw view to load - :param :py:class:`~binaryninja.filemetadata.FileMetadata` file_metadata: Optional FileMetadata object for this new view + :param data: path to file/bndb, raw bytes, or raw view to load + :type data: Union[:py:class:`str`, :py:class:`bytes`, :py:class:`bytearray`, :py:class:`~binaryninja.databuffer.DataBuffer`, :py:class:`os.PathLike`, :py:class:`BinaryView`] + :param file_metadata: Optional FileMetadata object for this new view + :type file_metadata: :py:class:`~binaryninja.filemetadata.FileMetadata` :return: returns a :py:class:`BinaryView` object for the given filename or ``None`` :rtype: :py:class:`BinaryView` or ``None`` @@ -4066,12 +4070,9 @@ def navigate(self, view_name: str, offset: int) -> bool: return self._file.navigate(view_name, offset) def read(self, addr: int, length: int) -> bytes: - """ + r""" ``read`` returns the data reads at most ``length`` bytes from virtual address ``addr``. - .. note:: Python2 returns a str, but Python3 returns a bytes object. str(DataBufferObject) will \ - still get you a str in either case. - :param int addr: virtual address to read from. :param int length: number of bytes to read. :return: at most ``length`` bytes from the virtual address ``addr``, empty string on error or no data @@ -4081,7 +4082,7 @@ def read(self, addr: int, length: int) -> bytes: >>> #Opening a x86_64 Mach-O binary >>> bv = BinaryView.new("/bin/ls") # note that we are using `new` instead of `load` to get the raw view >>> bv.read(0,4) - b\'\\xcf\\xfa\\xed\\xfe\' + b'\xcf\xfa\xed\xfe' """ if (addr < 0) or (length < 0): raise ValueError("length and address must both be positive") @@ -4897,7 +4898,7 @@ def get_data_refs(self, addr: int, length: Optional[int] = None) -> Generator[in .. warning:: If you're looking at this API, please double check that you don't mean to use :py:func:`get_code_refs` instead. \ `get_code_refs` returns references from code to the specified address while this API returns references from data \ - (pointers in global variables for example). Also, note there exists :py:func:`get_data_refs_from`. + (pointers in global variables for example). Also, note there exists :py:func:`get_data_refs_from`. :param int addr: virtual address to query for references :param int length: optional length of query @@ -4929,7 +4930,7 @@ def get_data_refs_from(self, addr: int, length: Optional[int] = None) -> Generat ``get_data_refs_from`` returns a list of virtual addresses referenced by the address ``addr``. Optionally specifying a length. When ``length`` is set ``get_data_refs_from`` returns the data referenced in the range ``addr``-``addr``+``length``. This function returns both autoanalysis ("auto") and user-specified ("user") xrefs. To add a user-specified - reference, see :py:func:`add_user_data_ref`. Also, note there exists :py:func:`get_data_refs`. + reference, see :py:func:`add_user_data_ref`. Also, note there exists :py:func:`get_data_refs`. :param int addr: virtual address to query for references :param int length: optional length of query @@ -6093,7 +6094,7 @@ def tags(self) -> List[Tuple[int, 'Tag']]: ``tags`` gets a list of all data :py:class:`Tag` objects in the view. Tags are returned as a list of (address, :py:class:`Tag`) pairs. - :type: list(int, Tag) + :rtype: list(int, Tag) """ return self.get_tags() @@ -6102,7 +6103,7 @@ def get_tags(self, auto: Optional[bool] = None) -> List[Tuple[int, 'Tag']]: ``tags`` gets a list of all data :py:class:`Tag` objects in the view. Tags are returned as a list of (address, :py:class:`Tag`) pairs. - :type: list(int, Tag) + :rtype: list(int, Tag) """ count = ctypes.c_ulonglong() @@ -8014,22 +8015,30 @@ def set_manual_type_source_override(self, entries: Mapping['_types.QualifiedName a new dependency. This is useful if a BinaryView was automatically marked up with a lot of debug information but you - want to export only a subset of that information into a new TypeLibrary. By creating a describing + want to export only a subset of that information into a new TypeLibrary. By creating a description of which local types correspond to types in other already extant libraries, those types will be avoided during the recursive export. This data is not persisted and does not impact analysis. - BinaryView contains the following types: + For example, if a BinaryView contains the following types: + +.. code-block:: c + struct RECT { ... }; // omitted struct ContrivedExample { RECT rect; }; - overrides = {"RECT": ("tagRECT", "winX64common")} - bv.set_manual_type_source_override(overrides) - bv.export_type_to_library(dest_new_typelib, "ContrivedExample", bv.get_type_by_name("ContrivedExample")) +Then the following python: + +.. code-block:: python + + overrides = {"RECT": ("tagRECT", "winX64common")} + bv.set_manual_type_source_override(overrides) + bv.export_type_to_library(dest_new_typelib, "ContrivedExample", bv.get_type_by_name("ContrivedExample")) + +Results in dest_new_typelib only having ContrivedExample added, and "RECT" being inserted as a dependency +to a the type "tagRECT" found in the typelibrary "winX64common" - Results in dest_new_typelib only having ContrivedExample added, and "RECT" being inserted as a dependency - to a the type "tagRECT" found in the typelibrary "winX64common" """ count = len(entries) src_names = (core.BNQualifiedName * count)() @@ -8907,16 +8916,20 @@ def find_all_constant( return self.QueueGenerator(t, results) def search(self, pattern: str, start: int = None, end: int = None, raw: bool = False, ignore_case: bool = False, overlap: bool = False, align: int = 1) -> QueueGenerator: - """ + r""" Searches for matches of the specified `pattern` within this BinaryView with an optionally provided address range specified by `start` and `end`. The search pattern can be interpreted in various ways: + - specified as a string of hexadecimal digits where whitespace is ignored, and the '?' character acts as a wildcard - a regular expression suitable for working with bytes - or if the `raw` option is enabled, the pattern is interpreted as a raw string, and any special characters are escaped and interpreted literally - :param str pattern: The pattern to search for. - :param int start: The address to start the search from. (default: None) - :param int end: The address to end the search (inclusive). (default: None) + :param pattern: The pattern to search for. + :type pattern: :py:class:`str` + :param start: The address to start the search from. (default: None) + :type start: :py:class:`int` + :param end: The address to end the search (inclusive). (default: None) + :type end: :py:class:`int` :param bool raw: Whether to interpret the pattern as a raw string (default: False). :param bool ignore_case: Whether to perform case-insensitive matching (default: False). :param bool overlap: Whether to allow matches to overlap (default: False). @@ -8931,7 +8944,7 @@ def search(self, pattern: str, start: int = None, end: int = None, raw: bool = F >>> bytes(list(bv.search("50 ?4"))[0][1]).hex() '5004' - >>> bytes(list(bv.search("[\\x20-\\x25][\\x60-\\x67]"))[0][1]).hex() + >>> bytes(list(bv.search("[\x20-\x25][\x60-\x67]"))[0][1]).hex() '2062' """ if start is None: @@ -9345,6 +9358,7 @@ def store_metadata(self, key: str, md: metadata.MetadataValueType, isAuto: bool to True. Auto metadata is not saved into the database and is presumably re-generated \ when re-opening the database. :rtype: None + :Example: >>> bv.store_metadata("integer", 1337) @@ -9762,7 +9776,7 @@ def eof(self) -> bool: return core.BNIsEndOfFile(self._handle) def read(self, length: int, address: Optional[int] = None) -> Optional[bytes]: - """ + r""" ``read`` returns ``length`` bytes read from the current offset, adding ``length`` to offset. :param int length: number of bytes to read. @@ -9772,7 +9786,7 @@ def read(self, length: int, address: Optional[int] = None) -> Optional[bytes]: :Example: >>> br.read(8) - '\\xcf\\xfa\\xed\\xfe\\x07\\x00\\x00\\x01' + '\xcf\xfa\xed\xfe\x07\x00\x00\x01' >>> """ if address is not None: diff --git a/python/highlevelil.py b/python/highlevelil.py index 93e3c3b90..34bfef910 100644 --- a/python/highlevelil.py +++ b/python/highlevelil.py @@ -870,15 +870,15 @@ def visit(self, cb: HighLevelILVisitorCallback, :return: True if all instructions were visited, False if the callback returned False :Example: - >>> def visitor(_a, inst, _c, _d) -> bool: - >>> if isinstance(inst, Constant): - >>> print(f"Found constant: {inst.constant}") - >>> return False # Stop recursion (once we find a constant, don't recurse in to any sub-instructions (which there won't actually be any...)) - >>> # Otherwise, keep recursing the subexpressions of this instruction; if no return value is provided, it'll keep descending - >>> - >>> # Finds all constants used in the program - >>> for inst in bv.hlil_instructions: - >>> inst.visit(visitor) + >>> def visitor(_a, inst, _c, _d) -> bool: + >>> if isinstance(inst, Constant): + >>> print(f"Found constant: {inst.constant}") + >>> return False # Stop recursion (once we find a constant, don't recurse in to any sub-instructions (which there won't actually be any...)) + >>> # Otherwise, keep recursing the subexpressions of this instruction; if no return value is provided, it'll keep descending + >>> + >>> # Finds all constants used in the program + >>> for inst in bv.hlil_instructions: + >>> inst.visit(visitor) """ if cb(name, self, "HighLevelILInstruction", parent) == False: return False diff --git a/python/mediumlevelil.py b/python/mediumlevelil.py index bd13afa25..b6f4a664d 100644 --- a/python/mediumlevelil.py +++ b/python/mediumlevelil.py @@ -546,15 +546,15 @@ def visit(self, cb: MediumLevelILVisitorCallback, :return: True if all instructions were visited, False if the callback returned False :Example: - >>> def visitor(_a, inst, _c, _d) -> bool: - >>> if isinstance(inst, Constant): - >>> print(f"Found constant: {inst.constant}") - >>> return False # Stop recursion (once we find a constant, don't recurse in to any sub-instructions (which there won't actually be any...)) - >>> # Otherwise, keep recursing the subexpressions of this instruction; if no return value is provided, it'll keep descending - >>> - >>> # Finds all constants used in the program - >>> for inst in current_mlil.instructions: - >>> inst.visit(visitor) + >>> def visitor(_a, inst, _c, _d) -> bool: + >>> if isinstance(inst, Constant): + >>> print(f"Found constant: {inst.constant}") + >>> return False # Stop recursion (once we find a constant, don't recurse in to any sub-instructions (which there won't actually be any...)) + >>> # Otherwise, keep recursing the subexpressions of this instruction; if no return value is provided, it'll keep descending + >>> + >>> # Finds all constants used in the program + >>> for inst in current_mlil.instructions: + >>> inst.visit(visitor) """ if cb(name, self, "MediumLevelILInstruction", parent) == False: return False From 7e1e001b7b07285913d05a0dea31fba99395ca02 Mon Sep 17 00:00:00 2001 From: Mason Reed Date: Thu, 23 May 2024 08:16:01 -0400 Subject: [PATCH 04/24] Fix building background_task example plugin on headless --- examples/background_task/CMakeLists.txt | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/examples/background_task/CMakeLists.txt b/examples/background_task/CMakeLists.txt index 36e012b0d..dde3b2c8c 100644 --- a/examples/background_task/CMakeLists.txt +++ b/examples/background_task/CMakeLists.txt @@ -2,11 +2,8 @@ cmake_minimum_required(VERSION 3.13 FATAL_ERROR) project(background_task CXX C) -file(GLOB SOURCES - src/*.cpp - src/*.h) - -add_library(background_task SHARED ${SOURCES}) +add_library(background_task SHARED + src/backgroundtask.cpp) if(NOT BN_API_BUILD_EXAMPLES AND NOT BN_INTERNAL_BUILD) # Out-of-tree build @@ -19,7 +16,8 @@ if(NOT BN_API_BUILD_EXAMPLES AND NOT BN_INTERNAL_BUILD) add_subdirectory(${BN_API_PATH} api) endif() -target_link_libraries(background_task binaryninjaui) +target_link_libraries(${PROJECT_NAME} + binaryninjaapi) set_target_properties(background_task PROPERTIES CXX_STANDARD 17 @@ -29,8 +27,4 @@ set_target_properties(background_task PROPERTIES POSITION_INDEPENDENT_CODE ON LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/out/bin) -if(BN_INTERNAL_BUILD) - ui_plugin_rpath(background_task) -endif() - bn_install_plugin(${PROJECT_NAME}) From 3375478963786425457931027834955525009658 Mon Sep 17 00:00:00 2001 From: Glenn Smith Date: Mon, 20 May 2024 20:18:52 -0400 Subject: [PATCH 05/24] Add TypeBuilder::SetWidth and TypeBuilder::SetAlignment --- binaryninjaapi.h | 2 ++ binaryninjacore.h | 2 ++ python/types.py | 8 ++++++++ type.cpp | 22 ++++++++++++++++++++++ 4 files changed, 34 insertions(+) diff --git a/binaryninjaapi.h b/binaryninjaapi.h index d2cf74d2b..d8db649b6 100644 --- a/binaryninjaapi.h +++ b/binaryninjaapi.h @@ -8963,6 +8963,8 @@ namespace BinaryNinja { Ref GetEnumeration() const; Ref GetNamedTypeReference() const; Confidence GetScope() const; + TypeBuilder& SetWidth(size_t width); + TypeBuilder& SetAlignment(size_t alignment); TypeBuilder& SetNamedTypeReference(NamedTypeReference* ntr); TypeBuilder& SetScope(const Confidence& scope); TypeBuilder& SetConst(const Confidence& cnst); diff --git a/binaryninjacore.h b/binaryninjacore.h index 9e0b6986f..b865fadc0 100644 --- a/binaryninjacore.h +++ b/binaryninjacore.h @@ -5920,6 +5920,8 @@ extern "C" BINARYNINJACOREAPI void BNSetTypeBuilderPure(BNTypeBuilder* type, BNBoolWithConfidence* pure); BINARYNINJACOREAPI void BNSetFunctionTypeBuilderParameters( BNTypeBuilder* type, BNFunctionParameter* params, size_t paramCount); + BINARYNINJACOREAPI void BNTypeBuilderSetWidth(BNTypeBuilder* type, size_t width); + BINARYNINJACOREAPI void BNTypeBuilderSetAlignment(BNTypeBuilder* type, size_t alignment); BINARYNINJACOREAPI void BNTypeBuilderSetConst(BNTypeBuilder* type, BNBoolWithConfidence* cnst); BINARYNINJACOREAPI void BNTypeBuilderSetVolatile(BNTypeBuilder* type, BNBoolWithConfidence* vltl); BINARYNINJACOREAPI void BNTypeBuilderSetSigned(BNTypeBuilder* type, BNBoolWithConfidence* sign); diff --git a/python/types.py b/python/types.py index 945b88bed..aea8f02c8 100644 --- a/python/types.py +++ b/python/types.py @@ -749,6 +749,10 @@ def named_type_reference( def width(self) -> _int: return core.BNGetTypeBuilderWidth(self._handle) + @width.setter + def width(self, value: _int): + core.BNTypeBuilderSetWidth(self._handle, value) + def __len__(self): return self.width @@ -788,6 +792,10 @@ def volatile( def alignment(self) -> _int: return core.BNGetTypeBuilderAlignment(self._handle) + @alignment.setter + def alignment(self, alignment: _int): + core.BNTypeBuilderSetAlignment(self._handle, alignment) + @property def child(self) -> 'Type': type_conf = core.BNGetTypeBuilderChildType(self._handle) diff --git a/type.cpp b/type.cpp index 6324238be..3cfa26f9a 100644 --- a/type.cpp +++ b/type.cpp @@ -1349,12 +1349,34 @@ Confidence TypeBuilder::IsConst() const return Confidence(result.value, result.confidence); } + +Confidence TypeBuilder::IsVolatile() const +{ + BNBoolWithConfidence result = BNIsTypeBuilderVolatile(m_object); + return Confidence(result.value, result.confidence); +} + + void TypeBuilder::SetIntegerTypeDisplayType(BNIntegerDisplayType displayType) { BNSetIntegerTypeDisplayType(m_object, displayType); } +TypeBuilder& TypeBuilder::SetWidth(size_t width) +{ + BNTypeBuilderSetWidth(m_object, width); + return *this; +} + + +TypeBuilder& TypeBuilder::SetAlignment(size_t alignment) +{ + BNTypeBuilderSetAlignment(m_object, alignment); + return *this; +} + + TypeBuilder& TypeBuilder::SetConst(const Confidence& cnst) { BNBoolWithConfidence bc; From 0824604746724984472975400443871294158813 Mon Sep 17 00:00:00 2001 From: Glenn Smith Date: Fri, 10 May 2024 20:01:13 -0400 Subject: [PATCH 06/24] Expose Type::PointerSuffix --- binaryninjaapi.h | 11 +++++++ binaryninjacore.h | 11 ++++++- python/types.py | 65 ++++++++++++++++++++++++++++++++++++++- type.cpp | 78 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 163 insertions(+), 2 deletions(-) diff --git a/binaryninjaapi.h b/binaryninjaapi.h index d8db649b6..ffc4fd868 100644 --- a/binaryninjaapi.h +++ b/binaryninjaapi.h @@ -8595,6 +8595,10 @@ namespace BinaryNinja { uint64_t GetElementCount() const; uint64_t GetOffset() const; + std::set GetPointerSuffix() const; + std::string GetPointerSuffixString() const; + std::vector GetPointerSuffixTokens(uint8_t baseConfidence = BN_FULL_CONFIDENCE) const; + std::string GetString(Platform* platform = nullptr, BNTokenEscapingType escaping = NoTokenEscapingType) const; std::string GetTypeAndName(const QualifiedName& name, BNTokenEscapingType escaping = NoTokenEscapingType) const; std::string GetStringBeforeName(Platform* platform = nullptr, BNTokenEscapingType escaping = NoTokenEscapingType) const; @@ -8986,6 +8990,13 @@ namespace BinaryNinja { TypeBuilder& SetPure(const Confidence& pure); TypeBuilder& SetParameters(const std::vector& params); + std::set GetPointerSuffix() const; + std::string GetPointerSuffixString() const; + std::vector GetPointerSuffixTokens(uint8_t baseConfidence = BN_FULL_CONFIDENCE) const; + + TypeBuilder& AddPointerSuffix(BNPointerSuffix ps); + TypeBuilder& SetPointerSuffix(const std::set& suffix); + std::string GetString(Platform* platform = nullptr) const; std::string GetTypeAndName(const QualifiedName& name) const; std::string GetStringBeforeName(Platform* platform = nullptr) const; diff --git a/binaryninjacore.h b/binaryninjacore.h index b865fadc0..361f86df9 100644 --- a/binaryninjacore.h +++ b/binaryninjacore.h @@ -802,7 +802,7 @@ extern "C" UnalignedSuffix, RestrictSuffix, ReferenceSuffix, - LvalueSuffix + LvalueSuffix, } BNPointerSuffix; // Caution: these enumeration values are used a lookups into the static NameTypeStrings in the core @@ -5871,6 +5871,10 @@ extern "C" BINARYNINJACOREAPI char* BNGetTypeAlternateName(BNType* type); BINARYNINJACOREAPI uint32_t BNTypeGetSystemCallNumber(BNType* type); BINARYNINJACOREAPI bool BNTypeIsSystemCall(BNType* type); + BINARYNINJACOREAPI BNPointerSuffix* BNGetTypePointerSuffix(BNType* type, size_t* count); + BINARYNINJACOREAPI char* BNGetTypePointerSuffixString(BNType* type); + BINARYNINJACOREAPI BNInstructionTextToken* BNGetTypePointerSuffixTokens(BNType* type, uint8_t baseConfidence, size_t* count); + BINARYNINJACOREAPI void BNFreePointerSuffixList(BNPointerSuffix* suffix, size_t count); BINARYNINJACOREAPI char* BNGetTypeString(BNType* type, BNPlatform* platform, BNTokenEscapingType escaping); BINARYNINJACOREAPI char* BNGetTypeStringBeforeName(BNType* type, BNPlatform* platform, BNTokenEscapingType escaping); @@ -5933,6 +5937,11 @@ extern "C" BINARYNINJACOREAPI bool BNTypeBuilderIsSystemCall(BNTypeBuilder* type); BINARYNINJACOREAPI uint32_t BNTypeBuilderGetSystemCallNumber(BNTypeBuilder* type); BINARYNINJACOREAPI void BNTypeBuilderSetStackAdjustment(BNTypeBuilder* type, BNOffsetWithConfidence* adjust); + BINARYNINJACOREAPI BNPointerSuffix* BNGetTypeBuilderPointerSuffix(BNTypeBuilder* type, size_t* count); + BINARYNINJACOREAPI char* BNGetTypeBuilderPointerSuffixString(BNTypeBuilder* type); + BINARYNINJACOREAPI BNInstructionTextToken* BNGetTypeBuilderPointerSuffixTokens(BNTypeBuilder* type, uint8_t baseConfidence, size_t* count); + BINARYNINJACOREAPI void BNAddTypeBuilderPointerSuffix(BNTypeBuilder* type, BNPointerSuffix ps); + BINARYNINJACOREAPI void BNSetTypeBuilderPointerSuffix(BNTypeBuilder* type, BNPointerSuffix* suffix, size_t count); BINARYNINJACOREAPI char* BNGetTypeBuilderString(BNTypeBuilder* type, BNPlatform* platform); BINARYNINJACOREAPI char* BNGetTypeBuilderStringBeforeName(BNTypeBuilder* type, BNPlatform* platform); diff --git a/python/types.py b/python/types.py index aea8f02c8..579186123 100644 --- a/python/types.py +++ b/python/types.py @@ -29,7 +29,7 @@ from .enums import ( StructureVariant, SymbolType, SymbolBinding, TypeClass, NamedTypeReferenceClass, ReferenceType, VariableSourceType, TypeReferenceType, MemberAccess, MemberScope, TypeDefinitionLineType, TokenEscapingType, - NameType + NameType, PointerSuffix ) from . import callingconvention from . import function as _function @@ -963,6 +963,41 @@ def origin(self) -> Optional[Tuple['QualifiedName', int]]: def origin(self, origin: 'NamedTypeReferenceType'): core.BNSetTypeBuilderNamedTypeReference(self._handle, origin.ntr_handle) + @property + def pointer_suffix(self) -> List[PointerSuffix]: + count = ctypes.c_size_t(0) + suffix = core.BNGetTypeBuilderPointerSuffix(self._handle, count) + assert suffix is not None, "core.BNGetTypeBuilderPointerSuffix returned None" + try: + result = [] + for i in range(count.value): + result.append(PointerSuffix(suffix[i])) + return result + finally: + core.BNFreePointerSuffixList(suffix, count) + + @pointer_suffix.setter + def pointer_suffix(self, value: List[PointerSuffix]): + suffix = (core.PointerSuffixEnum * len(value))() + for i, s in enumerate(value): + suffix[i] = core.PointerSuffixEnum(s) + core.BNSetTypeBuilderPointerSuffix(self._handle, suffix, len(value)) + + def add_pointer_suffix(self, suffix: PointerSuffix): + core.BNAddTypeBuilderPointerSuffix(self._handle, suffix) + + @property + def pointer_suffix_string(self) -> str: + return core.BNGetTypeBuilderPointerSuffixString(self._handle) + + def get_pointer_suffix_tokens(self, base_confidence: int = core.max_confidence) -> List['_function.InstructionTextToken']: + count = ctypes.c_ulonglong() + tokens = core.BNGetTypeBuilderPointerSuffixTokens(self._handle, base_confidence, count) + assert tokens is not None, "core.BNGetTypeBuilderPointerSuffixTokens returned None" + result = _function.InstructionTextToken._from_core_struct(tokens, count.value) + core.BNFreeInstructionText(tokens, count.value) + return result + class ArrayBuilder(TypeBuilder): @classmethod @@ -2794,6 +2829,34 @@ def target(self) -> Type: def children(self) -> List[Type]: return [self.target] + @property + def pointer_suffix(self) -> List[PointerSuffix]: + count = ctypes.c_size_t(0) + suffix = core.BNGetTypePointerSuffix(self.handle, count) + assert suffix is not None, "core.BNGetTypePointerSuffix returned None" + try: + result = [] + for i in range(count.value): + result.append(suffix[i]) + return result + finally: + core.BNFreePointerSuffixList(suffix, count) + + @property + def pointer_suffix_string(self) -> str: + return core.BNGetTypePointerSuffixString(self.handle) + + def get_pointer_suffix_tokens(self, base_confidence: int = core.max_confidence) -> List['_function.InstructionTextToken']: + count = ctypes.c_ulonglong() + platform = None + if self._platform is not None: + platform = self._platform.handle + tokens = core.BNGetTypePointerSuffixTokens(self._handle, base_confidence, count) + assert tokens is not None, "core.BNGetTypePointerSuffixTokens returned None" + result = _function.InstructionTextToken._from_core_struct(tokens, count.value) + core.BNFreeInstructionText(tokens, count.value) + return result + class ArrayType(Type): @classmethod diff --git a/type.cpp b/type.cpp index 3cfa26f9a..8731e8aec 100644 --- a/type.cpp +++ b/type.cpp @@ -693,6 +693,36 @@ Confidence Type::GetStackAdjustment() const } +std::set Type::GetPointerSuffix() const +{ + size_t count = 0; + BNPointerSuffix* suffix = BNGetTypePointerSuffix(m_object, &count); + std::set result(suffix, suffix + count); + BNFreePointerSuffixList(suffix, count); + return result; + +} + + +std::string Type::GetPointerSuffixString() const +{ + char* string = BNGetTypePointerSuffixString(m_object); + std::string result(string); + BNFreeString(string); + return result; +} + + +std::vector Type::GetPointerSuffixTokens(uint8_t baseConfidence) const +{ + size_t count = 0; + BNInstructionTextToken* tokens = BNGetTypePointerSuffixTokens(m_object, baseConfidence, &count); + std::vector result = InstructionTextToken::ConvertInstructionTextTokenList(tokens, count); + BNFreeInstructionText(tokens, count); + return result; +} + + string Type::GetString(Platform* platform, BNTokenEscapingType escaping) const { char* str = BNGetTypeString(m_object, platform ? platform->GetObject() : nullptr, escaping); @@ -1936,6 +1966,54 @@ TypeBuilder& TypeBuilder::SetParameters(const std::vector& pa } +std::set TypeBuilder::GetPointerSuffix() const +{ + size_t count = 0; + BNPointerSuffix* suffix = BNGetTypeBuilderPointerSuffix(m_object, &count); + std::set result(suffix, suffix + count); + BNFreePointerSuffixList(suffix, count); + return result; +} + + +std::string TypeBuilder::GetPointerSuffixString() const +{ + char* string = BNGetTypeBuilderPointerSuffixString(m_object); + std::string result(string); + BNFreeString(string); + return result; +} + + +std::vector TypeBuilder::GetPointerSuffixTokens(uint8_t baseConfidence) const +{ + size_t count = 0; + BNInstructionTextToken* tokens = BNGetTypeBuilderPointerSuffixTokens(m_object, baseConfidence, &count); + std::vector result = InstructionTextToken::ConvertInstructionTextTokenList(tokens, count); + BNFreeInstructionText(tokens, count); + return result; +} + + +TypeBuilder& TypeBuilder::AddPointerSuffix(BNPointerSuffix ps) +{ + BNAddTypeBuilderPointerSuffix(m_object, ps); + return *this; +} + + +TypeBuilder& TypeBuilder::SetPointerSuffix(const std::set& suffix) +{ + std::vector apiSuffix; + for (auto& s: suffix) + { + apiSuffix.push_back(s); + } + BNSetTypeBuilderPointerSuffix(m_object, apiSuffix.data(), apiSuffix.size()); + return *this; +} + + QualifiedName TypeBuilder::GetTypeName() const { BNQualifiedName name = BNTypeBuilderGetTypeName(m_object); From 7038897105235644749844401411645589822198 Mon Sep 17 00:00:00 2001 From: Glenn Smith Date: Sat, 11 May 2024 17:15:53 -0400 Subject: [PATCH 07/24] Based pointers --- binaryninjaapi.h | 5 +++++ binaryninjacore.h | 13 +++++++++++++ python/types.py | 35 ++++++++++++++++++++++++++++++++--- type.cpp | 31 +++++++++++++++++++++++++++++++ 4 files changed, 81 insertions(+), 3 deletions(-) diff --git a/binaryninjaapi.h b/binaryninjaapi.h index ffc4fd868..477d1b600 100644 --- a/binaryninjaapi.h +++ b/binaryninjaapi.h @@ -8594,6 +8594,8 @@ namespace BinaryNinja { uint64_t GetElementCount() const; uint64_t GetOffset() const; + BNPointerBaseType GetPointerBaseType() const; + int64_t GetPointerBaseOffset() const; std::set GetPointerSuffix() const; std::string GetPointerSuffixString() const; @@ -8984,11 +8986,14 @@ namespace BinaryNinja { uint64_t GetElementCount() const; uint64_t GetOffset() const; uint32_t GetSystemCallNumber() const; + BNPointerBaseType GetPointerBaseType() const; + int64_t GetPointerBaseOffset() const; TypeBuilder& SetOffset(uint64_t offset); TypeBuilder& SetFunctionCanReturn(const Confidence& canReturn); TypeBuilder& SetPure(const Confidence& pure); TypeBuilder& SetParameters(const std::vector& params); + TypeBuilder& SetPointerBase(BNPointerBaseType baseType, int64_t baseOffset); std::set GetPointerSuffix() const; std::string GetPointerSuffixString() const; diff --git a/binaryninjacore.h b/binaryninjacore.h index 361f86df9..3cc746e19 100644 --- a/binaryninjacore.h +++ b/binaryninjacore.h @@ -805,6 +805,14 @@ extern "C" LvalueSuffix, } BNPointerSuffix; + typedef enum BNPointerBaseType + { + AbsolutePointerBaseType, + RelativeToConstantPointerBaseType, + RelativeToBinaryStartPointerBaseType, + RelativeToVariableAddressPointerBaseType, + } BNPointerBaseType; + // Caution: these enumeration values are used a lookups into the static NameTypeStrings in the core // if you modify this you must also modify the string lookups as well typedef enum BNNameType @@ -5868,6 +5876,8 @@ extern "C" BINARYNINJACOREAPI BNQualifiedName BNTypeGetStructureName(BNType* type); BINARYNINJACOREAPI BNNamedTypeReference* BNGetRegisteredTypeName(BNType* type); BINARYNINJACOREAPI BNReferenceType BNTypeGetReferenceType(BNType* type); + BINARYNINJACOREAPI BNPointerBaseType BNTypeGetPointerBaseType(BNType* type); + BINARYNINJACOREAPI int64_t BNTypeGetPointerBaseOffset(BNType* type); BINARYNINJACOREAPI char* BNGetTypeAlternateName(BNType* type); BINARYNINJACOREAPI uint32_t BNTypeGetSystemCallNumber(BNType* type); BINARYNINJACOREAPI bool BNTypeIsSystemCall(BNType* type); @@ -5920,6 +5930,7 @@ extern "C" BINARYNINJACOREAPI uint64_t BNGetTypeBuilderElementCount(BNTypeBuilder* type); BINARYNINJACOREAPI uint64_t BNGetTypeBuilderOffset(BNTypeBuilder* type); BINARYNINJACOREAPI void BNSetTypeBuilderOffset(BNTypeBuilder* type, uint64_t offset); + BINARYNINJACOREAPI void BNSetTypeBuilderPointerBase(BNTypeBuilder* type, BNPointerBaseType baseType, int64_t baseOffset); BINARYNINJACOREAPI void BNSetFunctionTypeBuilderCanReturn(BNTypeBuilder* type, BNBoolWithConfidence* canReturn); BINARYNINJACOREAPI void BNSetTypeBuilderPure(BNTypeBuilder* type, BNBoolWithConfidence* pure); BINARYNINJACOREAPI void BNSetFunctionTypeBuilderParameters( @@ -5933,6 +5944,8 @@ extern "C" BINARYNINJACOREAPI BNOffsetWithConfidence BNGetTypeBuilderStackAdjustment(BNTypeBuilder* type); BINARYNINJACOREAPI BNQualifiedName BNTypeBuilderGetStructureName(BNTypeBuilder* type); BINARYNINJACOREAPI BNReferenceType BNTypeBuilderGetReferenceType(BNTypeBuilder* type); + BINARYNINJACOREAPI BNPointerBaseType BNTypeBuilderGetPointerBaseType(BNTypeBuilder* type); + BINARYNINJACOREAPI int64_t BNTypeBuilderGetPointerBaseOffset(BNTypeBuilder* type); BINARYNINJACOREAPI char* BNGetTypeBuilderAlternateName(BNTypeBuilder* type); BINARYNINJACOREAPI bool BNTypeBuilderIsSystemCall(BNTypeBuilder* type); BINARYNINJACOREAPI uint32_t BNTypeBuilderGetSystemCallNumber(BNTypeBuilder* type); diff --git a/python/types.py b/python/types.py index 579186123..d240dce99 100644 --- a/python/types.py +++ b/python/types.py @@ -27,9 +27,11 @@ # Binary Ninja components from . import _binaryninjacore as core from .enums import ( - StructureVariant, SymbolType, SymbolBinding, TypeClass, NamedTypeReferenceClass, ReferenceType, VariableSourceType, - TypeReferenceType, MemberAccess, MemberScope, TypeDefinitionLineType, TokenEscapingType, - NameType, PointerSuffix + StructureVariant, SymbolType, SymbolBinding, TypeClass, NamedTypeReferenceClass, + ReferenceType, VariableSourceType, + TypeReferenceType, MemberAccess, MemberScope, TypeDefinitionLineType, + TokenEscapingType, + NameType, PointerSuffix, PointerBaseType ) from . import callingconvention from . import function as _function @@ -998,6 +1000,25 @@ def get_pointer_suffix_tokens(self, base_confidence: int = core.max_confidence) core.BNFreeInstructionText(tokens, count.value) return result + def set_pointer_base(self, base_type: PointerBaseType, base_offset: int): + core.BNSetTypeBuilderPointerBase(self._handle, base_type, base_offset) + + @property + def pointer_base_type(self) -> PointerBaseType: + return PointerBaseType(core.BNTypeBuilderGetPointerBaseType(self._handle)) + + @pointer_base_type.setter + def pointer_base_type(self, value: PointerBaseType): + self.set_pointer_base(value, self.pointer_base_offset) + + @property + def pointer_base_offset(self) -> int: + return core.BNTypeBuilderGetPointerBaseOffset(self._handle) + + @pointer_base_offset.setter + def pointer_base_offset(self, value: int): + self.set_pointer_base(self.pointer_base_type, value) + class ArrayBuilder(TypeBuilder): @classmethod @@ -2857,6 +2878,14 @@ def get_pointer_suffix_tokens(self, base_confidence: int = core.max_confidence) core.BNFreeInstructionText(tokens, count.value) return result + @property + def pointer_base_type(self) -> PointerBaseType: + return PointerBaseType(core.BNTypeGetPointerBaseType(self._handle)) + + @property + def pointer_base_offset(self) -> int: + return core.BNTypeGetPointerBaseOffset(self._handle) + class ArrayType(Type): @classmethod diff --git a/type.cpp b/type.cpp index 8731e8aec..9ebdc156e 100644 --- a/type.cpp +++ b/type.cpp @@ -686,6 +686,18 @@ uint64_t Type::GetOffset() const } +BNPointerBaseType Type::GetPointerBaseType() const +{ + return BNTypeGetPointerBaseType(m_object); +} + + +int64_t Type::GetPointerBaseOffset() const +{ + return BNTypeGetPointerBaseOffset(m_object); +} + + Confidence Type::GetStackAdjustment() const { BNOffsetWithConfidence result = BNGetTypeStackAdjustment(m_object); @@ -1966,6 +1978,13 @@ TypeBuilder& TypeBuilder::SetParameters(const std::vector& pa } +TypeBuilder& TypeBuilder::SetPointerBase(BNPointerBaseType baseType, int64_t baseOffset) +{ + BNSetTypeBuilderPointerBase(m_object, baseType, baseOffset); + return *this; +} + + std::set TypeBuilder::GetPointerSuffix() const { size_t count = 0; @@ -2058,6 +2077,18 @@ uint32_t TypeBuilder::GetSystemCallNumber() const } +BNPointerBaseType TypeBuilder::GetPointerBaseType() const +{ + return BNTypeBuilderGetPointerBaseType(m_object); +} + + +int64_t TypeBuilder::GetPointerBaseOffset() const +{ + return BNTypeBuilderGetPointerBaseOffset(m_object); +} + + QualifiedName TypeBuilder::GetStructureName() const { BNQualifiedName name = BNTypeBuilderGetStructureName(m_object); From 4838227769697870958437596131371780384967 Mon Sep 17 00:00:00 2001 From: Glenn Smith Date: Wed, 22 May 2024 12:42:40 -0400 Subject: [PATCH 08/24] Docs for type attributes --- docs/guide/types/attributes.md | 276 +++++++++++++++++++++++++++++++++ docs/guide/types/basictypes.md | 47 +----- docs/guide/types/index.md | 1 + python/types.py | 27 ++++ 4 files changed, 307 insertions(+), 44 deletions(-) create mode 100644 docs/guide/types/attributes.md diff --git a/docs/guide/types/attributes.md b/docs/guide/types/attributes.md new file mode 100644 index 000000000..49839e232 --- /dev/null +++ b/docs/guide/types/attributes.md @@ -0,0 +1,276 @@ +# Type Attributes and Annotations + +There are a number of custom attributes and annotations you can add to types in Binary Ninja. They can modify type details, analysis, and presentation. + +## Structure Packing + +Use the attribute `__packed` in a structure definition to indicate that structure fields should be packed without padding. This is similar to `#pragma pack(1)` in MSVC and `__attribute__((packed))` in GCC/Clang. + +### Examples + +``` C +/* Normally, fields are padded to their type's alignment */ +struct UnpackedHeader +{ + uint16_t size; /* Offset: 0x0 */ + char *name; /* Offset: 0x8 */ + uint32_t version; /* Offset: 0x10 */ + void (* callback)(); /* Offset: 0x18 */ +}; + +/* Fields in a packed structure will never be padded, regardless of pointer or integer alignment preference */ +struct PackedHeader __packed +{ + uint16_t size; /* Offset: 0x0 */ + char *name; /* Offset: 0x2 */ + uint32_t version; /* Offset: 0xA */ + void (* callback)(); /* Offset: 0xE */ +}; + +/* These also work, thanks to Clang's broad feature support across targets */ +struct __attribute__((packed)) Header +{ + uint16_t size; /* Offset: 0x0 */ + char *name; /* Offset: 0x2 */ + uint32_t version; /* Offset: 0xA */ + void (* callback)(); /* Offset: 0xE */ +}; +/* Or with the MSVC pragma */ +#pragma pack(1) +struct Header +{ + uint16_t size; /* Offset: 0x0 */ + char *name; /* Offset: 0x2 */ + uint32_t version; /* Offset: 0xA */ + void (* callback)(); /* Offset: 0xE */ +}; +``` + +## Structure Padding + +You can manually specify padding members to fill empty space in structure definitions. This is commonly used when turning structures into text for use by C/C++ type parsers like GCC/Clang/MSVC. The `__padding` attribute informs Binary Ninja that the member is present simply to fill space, and it will be discarded during parsing. + +### Examples + +When inputting this type to the type parser... +``` C +struct Foo +{ + uint32_t field_0; /* Offset: 0x0 */ + __padding char _4[0xc]; /* Will be empty space when type is parsed */ + char* field_10; /* Offset: 0x10 */ +}; +``` +...the following type is produced: +``` C +struct Foo +{ + uint32_t field_0; /* Offset: 0x0 */ + ?? ?? ?? ?? /* Empty padding 0x4 -> 0x7 */ +?? ?? ?? ?? ?? ?? ?? ?? /* Empty padding 0x8 -> 0xF */ + char* field_10; /* Offset: 0x10 */ +}; +``` + +## Structures with Base Classes and Inheritence + +See [Working with C++ Types and Virtual Function Tables](cpp.md). + +## Functions That Don't Return + +If you know that a function does not return (either via infinite loop, or terminating the process), you can annotate their definition with `__noreturn` to inform the analysis of this. Any calls to these functions will cause disassembly in the caller to stop, assuming execution does not continue. + +### Examples + +``` C +/* Function definitions put the attribute at the end */ +void exit(int code) __noreturn; + +/* Function pointers put the attribute atfer the definition */ +void (* func_ptr)() __noreturn; +typedef void (* func_ptr_t)() __noreturn; +void takes_callback(int (* param_func_ptr)() __noreturn); + +/* It also works in other places */ +__noreturn void (* func_ptr)(); +void (__noreturn * func_ptr)(); +void (* __noreturn func_ptr)(); +``` + +## Function Calling Conventions + +Function prototypes support various keywords to indicate their calling convention: + +``` text +__cdecl +__stdcall +__fastcall +__convention("convention_name") +``` + +Due to the nature of parsing with Clang, most dedicated convention keywords are only available on their relevant targets. For example, `__stdcall` and `__fastcall` only apply to X86-based targets. + +If you have a custom calling convention, or one with no dedicated keyword, you can specify the convention name with the `__convention("name")` attribute. + +### Examples + +``` C +/* Functions put the attribute between the return type and name */ +void __fastcall func(); + +/* Function pointers put the attribute before the pointer */ +void (__stdcall* func_ptr)(); +typedef void (__stdcall* func_ptr_t)(); +void takes_callback(int (__stdcall* param_func_ptr)()); + +/* Other calling conventions can be specified by name */ +void __convention("regparm") func(); +``` + +## System Call Functions for Type Libraries + +[Type Libraries](typelibraries.md) can annotate system calls by adding functions with the special `__syscall()` attribute, specifying names and arguments for each syscall number. This attribute has no effect outside of [Type Libraries](typelibraries.md) and [Platform Types](platformtypes.md). + +### Examples + +``` C +/* From linux-x86_64's SYSCALLS Type Library */ +int64_t sys_read(int32_t fd, void* buf, uint64_t count) __syscall(0); +int64_t sys_write(int32_t fd, void const* buf, uint64_t count) __syscall(1); + +/* From linux-x86_64.c (Platform Types) */ +void sys_exit(int status) __noreturn __syscall(60); +void sys_exit_group(int status) __noreturn __syscall(231); +``` + +## Pure Functions + +Functions whose result depends entirely on their input parameters can be marked as "pure." If they are called and their result value is not used, they are eliminated as dead code (as their only effect comes from their return value). Generally speaking, auto analysis will only mark functions as pure if the following conditions are met: + +* Function has no instructions that access memory +* Function has no unresolved indirect branches +* Function has no unimplemented or intrinsic instructions +* Function does not call any other functions or syscalls +* Function can return + +These functions are annotated in the type system with the `__pure` attribute, which you can apply like the other function attributes. + +### Examples + +``` C +int get_twice(int arg) __pure +{ + return arg * 2; +} +int main() +{ + (void)get_twice(1); /* result is unused, this will be dead code eliminated */ +} +``` + +## Offset Pointers + +Offset pointers, often called shifted pointers or relative pointers, are used to provide extra information about the "origin" of a pointer. They can include offset information about a struct and member, for example. These are used by analysis to keep track of shifted references to a structure and resolve member field accesses to be relative to the base. + +More discussion about Offset Pointers can be found [on our blog](https://binary.ninja/2022/10/28/3.2-released.html#offset-pointers). + +### Examples + +``` C +/* rbx_1 is a void* located 0x2b0 bytes from the start of a _KSDEVICE */ +void* __offset(_KSDEVICE, 0x2b0) rbx_1 = &device->__offset(0x2b0).q; + +/* Further relative accesses through rbx_1 are annotated and displayed relative to the structure */ +(rbx_1 - 0x2b0)->field18 = 0; /* mov qword [rbx_1-0x298], 0x0 */ +``` + +## Based Pointers + +Many binary formats contain pointers that reference addresses based on the start of memory or the address of the variable itself. You can annotate the base of these pointers using the `__based()` attribute. Binary Ninja supports these formats of based pointers: + +* `__based(start)` Pointer based on the image start (BinaryView start address) + * `__based(start, 0x100)` You can specify a constant offset to add to the pointer + * `__based(start, -0x100)` Offsets can be negative too +* `__based(var)` Pointer based relative to a Data Variable typed with the pointer + * `__based(var, 0x100)` You can specify a constant offset to add to the pointer + * `__based(var, -0x100)` Offsets can be negative too +* `__based(const, 0x100)` Pointer based relative to some constant value + +### Examples + +These are used by MSVC RTTI on x86_64 binaries: +``` C +/* This structure definition... */ +struct BaseClassDescriptor +{ + TypeDescriptor* __ptr32 __based(start) pTypeDescriptor; + uint32_t numContainedBases; + int32_t mdisp; + int32_t pdisp; + int32_t vdisp; + uint32_t attributes; + ClassHierarchyDescriptor* __ptr32 __based(start) pClassDescriptor; +}; + +/* ...results in the following presentation in Linear View */ +struct BaseClassDescriptor type_info::`RTTI Base Class Descriptor at (0,32,4,82)' = +{ + struct TypeDescriptor* __ptr32 __based(start) pTypeDescriptor = class type_info `RTTI Type Descriptor' { 0x180000000 + 0x11180 } + uint32_t numContainedBases = 0x0 + int32_t mdisp = 0x0 + int32_t pdisp = 0x20 + int32_t vdisp = 0x4 + uint32_t attributes = 0x52 + struct ClassHierarchyDescriptor* __ptr32 __based(start) pClassDescriptor = type_info::`RTTI Class Hierarchy Descriptor' { 0x180000000 + 0xdfd0 } +} +``` + +You can define structures who reference other structures relative to their variable address in memory. Address references are _relative to the pointer, not the base of the structure._ + +``` C +/* This structure definition... */ +struct Texture +{ + uint32_t width; + uint32_t height; + char* __based(var) texNameOffset; + uint32_t mask; + uint32_t flags; +}; + +/* ...results in the following presentation in Linear View */ +struct Texture tile_red +{ + uint32_t width = 128 + uint32_t height = 128 + char* __based(var, 0x10) texNameOffset = string_tile_red { &tile_red->texNameOffset + 0x10 } + uint32_t mask = 0 + uint32_t flags = 0 +} +char string_tile_red[9] = "tile_red", 0; +``` + +## Pointers with Custom Sizes + +Some structures store pointers with a size different than the platform's address width. For example, a 32-bit image base-relative pointer used on an 64-bit architecture. These sized pointers can be annotated with the `__ptr8`, `__ptr16`, `__ptr32`, `__ptr64`, or `__ptr_width()` attributes. + +These are often combined with [Based Pointers](#based-pointers), since pointers smaller than the address width cannot point to parts of memory without being shifted first. + +### Examples + +These are seen in places like MSVC RTTI on x86_64 binaries: + +``` C +struct BaseClassDescriptor +{ + TypeDescriptor* __ptr32 __based(start) pTypeDescriptor; + uint32_t numContainedBases; + int32_t mdisp; + int32_t pdisp; + int32_t vdisp; + uint32_t attributes; + ClassHierarchyDescriptor* __ptr32 __based(start) pClassDescriptor; +}; + +struct BaseClassDescriptor* __ptr32 __based(start) `type_info::\`RTTI Base Class Array'`[0x1]; +``` diff --git a/docs/guide/types/basictypes.md b/docs/guide/types/basictypes.md index b9530ddb8..7366b47fa 100644 --- a/docs/guide/types/basictypes.md +++ b/docs/guide/types/basictypes.md @@ -127,47 +127,6 @@ the name of a type will take you to its definition. * **Show Inherited Members** - When working with [structures with inheritance](cpp.md#derived-classes), show members from base classes in child classes * **Wrap Lines** - Soft-wrap long text lines -### Type Annotations - -The Types view now annotates code references to structure offsets. It uses the same convention as in the graph/linear view. For example, the `__offset(0x8).q` token means the code references the offset 0x8 bytes into this structure, and the size of the access is a qword. This will make it easier to see which offsets of a structure are being used, and aid in the process of creating structure members. - -## Attributes - -Structs support the attribute `__packed` to indicate that there is no padding. Additionally, function prototypes support the following keywords to indicate their calling convention or other features: - -``` text -__cdecl -__stdcall -__fastcall -__convention -__noreturn -``` - -To use the `__convention` keyword, pass in the convention name as a parameter argument: - -``` -__convention("customconvention") -``` - - -## Examples - -``` C -enum _flags -{ - F_X = 0x1, - F_W = 0x2, - F_R = 0x4 -}; -``` - -``` C -struct Header __packed -{ - char *name; - uint32_t version; - void (* callback)(); - uint16_t size; - enum _flags flags; -}; -``` +### Structure Offset Annotations + +The Types view annotates code references to structure offsets if a structure member is not present. These annotations use the same convention as in the graph/linear view, showing the offset position and width. For example, the `__offset(0x8).q` token means the code references the offset 0x8 bytes into this structure, and the size of the access is a qword (8 bytes). You can press S on an offset annotation and a structure member of the appropriate type will be created there. Additionally, if you right click the structure name, you can choose to create all of these references. diff --git a/docs/guide/types/index.md b/docs/guide/types/index.md index 49ad8a65e..ea1df4f80 100644 --- a/docs/guide/types/index.md +++ b/docs/guide/types/index.md @@ -5,6 +5,7 @@ There's so many things to learn about working with Types in Binary Ninja that we - [Basic Type Editing](basictypes.md): Brief overview of the basics - [Working with Types](type.md): Interacting with types in disassembly and decompilation - [Importing/Exporting Types](typeimportexport.md): How to import or export types from header files, archives, or other BNDBs +- [Attributes and Annotations](attributes.md): Annotations you can apply to types to influence analysis and presentation Additionally, several types of containers for type information are documented here: diff --git a/python/types.py b/python/types.py index d240dce99..a47a10435 100644 --- a/python/types.py +++ b/python/types.py @@ -967,6 +967,7 @@ def origin(self, origin: 'NamedTypeReferenceType'): @property def pointer_suffix(self) -> List[PointerSuffix]: + """Pointer suffix, e.g. __unaligned is [UnalignedSuffix] (read-only)""" count = ctypes.c_size_t(0) suffix = core.BNGetTypeBuilderPointerSuffix(self._handle, count) assert suffix is not None, "core.BNGetTypeBuilderPointerSuffix returned None" @@ -986,13 +987,23 @@ def pointer_suffix(self, value: List[PointerSuffix]): core.BNSetTypeBuilderPointerSuffix(self._handle, suffix, len(value)) def add_pointer_suffix(self, suffix: PointerSuffix): + """ + Append a suffix to the pointer, must be one defined in :py:class:`PointerSuffix`. + :param suffix: New suffix + """ core.BNAddTypeBuilderPointerSuffix(self._handle, suffix) @property def pointer_suffix_string(self) -> str: + """Pointer suffix, but as a string, e.g. "__unaligned" (read-only)""" return core.BNGetTypeBuilderPointerSuffixString(self._handle) def get_pointer_suffix_tokens(self, base_confidence: int = core.max_confidence) -> List['_function.InstructionTextToken']: + """ + Get the pointer suffix, as a list of tokens + :param base_confidence: (optional) Confidence value to combine with the pointer's confidence + :return: Token list + """ count = ctypes.c_ulonglong() tokens = core.BNGetTypeBuilderPointerSuffixTokens(self._handle, base_confidence, count) assert tokens is not None, "core.BNGetTypeBuilderPointerSuffixTokens returned None" @@ -1001,10 +1012,16 @@ def get_pointer_suffix_tokens(self, base_confidence: int = core.max_confidence) return result def set_pointer_base(self, base_type: PointerBaseType, base_offset: int): + """ + Set the pointer base type and offset + :param base_type: Base type, e.g. __based(start) is RelativeToBinaryStartPointerBaseType + :param base_offset: Base offset, e.g. __based(start, 0x1000) is 0x1000 + """ core.BNSetTypeBuilderPointerBase(self._handle, base_type, base_offset) @property def pointer_base_type(self) -> PointerBaseType: + """Pointer base type, e.g. __based(start) is RelativeToBinaryStartPointerBaseType""" return PointerBaseType(core.BNTypeBuilderGetPointerBaseType(self._handle)) @pointer_base_type.setter @@ -1013,6 +1030,7 @@ def pointer_base_type(self, value: PointerBaseType): @property def pointer_base_offset(self) -> int: + """Pointer base offset, e.g. __based(start, 0x1000) is 0x1000""" return core.BNTypeBuilderGetPointerBaseOffset(self._handle) @pointer_base_offset.setter @@ -2852,6 +2870,7 @@ def children(self) -> List[Type]: @property def pointer_suffix(self) -> List[PointerSuffix]: + """Pointer suffix, e.g. __unaligned is [UnalignedSuffix] (read-only)""" count = ctypes.c_size_t(0) suffix = core.BNGetTypePointerSuffix(self.handle, count) assert suffix is not None, "core.BNGetTypePointerSuffix returned None" @@ -2865,9 +2884,15 @@ def pointer_suffix(self) -> List[PointerSuffix]: @property def pointer_suffix_string(self) -> str: + """Pointer suffix, but as a string, e.g. "__unaligned" (read-only)""" return core.BNGetTypePointerSuffixString(self.handle) def get_pointer_suffix_tokens(self, base_confidence: int = core.max_confidence) -> List['_function.InstructionTextToken']: + """ + Get the pointer suffix, as a list of tokens + :param base_confidence: (optional) Confidence value to combine with the pointer's confidence + :return: Token list + """ count = ctypes.c_ulonglong() platform = None if self._platform is not None: @@ -2880,10 +2905,12 @@ def get_pointer_suffix_tokens(self, base_confidence: int = core.max_confidence) @property def pointer_base_type(self) -> PointerBaseType: + """Pointer base type, e.g. __based(start) is RelativeToBinaryStartPointerBaseType (read-only)""" return PointerBaseType(core.BNTypeGetPointerBaseType(self._handle)) @property def pointer_base_offset(self) -> int: + """Pointer base offset, e.g. __based(start, 0x1000) is 0x1000 (read-only)""" return core.BNTypeGetPointerBaseOffset(self._handle) From 9f6774bc41896e48f55c48b32f8d67cd6dd0f8aa Mon Sep 17 00:00:00 2001 From: Xusheng Date: Fri, 24 May 2024 13:28:16 +0800 Subject: [PATCH 09/24] Remove duplicate definition of core API functions --- binaryninjacore.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/binaryninjacore.h b/binaryninjacore.h index 3cc746e19..d7d6244a7 100644 --- a/binaryninjacore.h +++ b/binaryninjacore.h @@ -7175,8 +7175,6 @@ extern "C" BINARYNINJACOREAPI BNRemote* BNCollaborationCreateRemote(const char* name, const char* address); BINARYNINJACOREAPI void BNCollaborationRemoveRemote(BNRemote* remote); BINARYNINJACOREAPI void BNCollaborationSaveRemotes(); - BINARYNINJACOREAPI bool BNCollaborationIsCollaborationDatabase(BNDatabase* database); - BINARYNINJACOREAPI bool BNCollaborationIsSnapshotIgnored(BNDatabase* database, BNSnapshot* snapshot); BINARYNINJACOREAPI bool BNCollaborationSyncDatabase(BNDatabase* database, BNRemoteFile* file, BNCollaborationAnalysisConflictHandler conflictHandler, void* conflictHandlerCtxt, BNProgressFunction progress, void* progressCtxt, BNCollaborationNameChangesetFunction nameChangeset, void* nameChangesetCtxt); BINARYNINJACOREAPI bool BNCollaborationSyncTypeArchive(BNTypeArchive* archive, BNRemoteFile* file, bool(*conflictHandler)(void*, BNTypeArchiveMergeConflict** conflicts, size_t conflictCount), void* conflictHandlerCtxt, BNProgressFunction progress, void* progressCtxt); BINARYNINJACOREAPI bool BNCollaborationPushTypeArchive(BNTypeArchive* archive, BNRemoteFile* file, size_t* count, BNProgressFunction progress, void* progressCtxt); From 5ec6a03709395cd0cfd5193ebc3ed4abbfc5f597 Mon Sep 17 00:00:00 2001 From: deadc0de6 Date: Fri, 1 Mar 2024 10:59:00 +0100 Subject: [PATCH 10/24] ensures python site package dir exists --- scripts/install_api.py | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/scripts/install_api.py b/scripts/install_api.py index 5e93064ba..975840000 100755 --- a/scripts/install_api.py +++ b/scripts/install_api.py @@ -40,6 +40,21 @@ def check_virtual_environment() -> bool: return False +def getsitepackage() -> str: + """ + gets the site package and creates it if needed + returns empty if fails + """ + install_path = getsitepackages()[0] + if not os.path.exists(install_path): + try: + os.makedirs(install_path, exist_ok=True) + except OSError: + print_error(f"Root install specified but cannot create {install_path}") + return '' + return install_path + + def binaryninja_installed() -> bool: try: binaryninja = importlib.util.find_spec("binaryninja") @@ -121,20 +136,20 @@ def install(interactive=False, on_root=False, on_pyenv=False) -> bool: api_path = new_path if on_root: - install_path = getsitepackages()[0] - if not os.access(install_path, os.W_OK): - print_error(f"Root install specified but cannot write to {install_path}") + install_path = getsitepackage() + if not install_path or not os.access(install_path, os.W_OK): + print_error(f"Root install specified but cannot write to \"{install_path}\"") return False else: - print(f"Installing on root site: {install_path}") + print(f"Installing on root site: \"{install_path}\"") elif on_pyenv: - install_path = getsitepackages()[0] - print(f"Installing on pyenv site: {install_path}") + install_path = getsitepackage() + print(f"Installing on pyenv site: \"{install_path}") elif check_virtual_environment(): - install_path = getsitepackages()[0] - print(f"Installing on virtual environment site: {install_path}") + install_path = getsitepackage() + print(f"Installing on virtual environment site: \"{install_path}\"") else: if not check_enableusersite(): @@ -146,6 +161,9 @@ def install(interactive=False, on_root=False, on_pyenv=False) -> bool: os.makedirs(install_path) print(f"Installing on user site: {install_path}") + if not install_path: + print_error("empty site packages path") + return False binaryninja_pth_path = os.path.join(install_path, "binaryninja.pth") with open(binaryninja_pth_path, 'wb') as pth_file: pth_file.write((api_path + '\n').encode("charmap")) From 99a857393b90457366f55e39e42364f02e3e10b4 Mon Sep 17 00:00:00 2001 From: Jordan Wiens Date: Fri, 24 May 2024 15:21:48 -0400 Subject: [PATCH 11/24] fix missing params in ILOperands and mark as deprecated in LLIL, HLIL --- python/highlevelil.py | 2 ++ python/lowlevelil.py | 2 ++ python/mediumlevelil.py | 13 +++++++------ 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/python/highlevelil.py b/python/highlevelil.py index 34bfef910..8e3f96966 100644 --- a/python/highlevelil.py +++ b/python/highlevelil.py @@ -146,6 +146,8 @@ class HighLevelILInstruction(BaseILInstruction): core_instr: CoreHighLevelILInstruction as_ast: bool instr_index: InstructionIndex + # ILOperations is deprecated and will be removed in a future version once BNIL Graph no longer uses it + # Use the visit methods visit, visit_all, and visit_operands ILOperations: ClassVar[Mapping[HighLevelILOperation, List[Tuple[str, str]]]] = { HighLevelILOperation.HLIL_NOP: [], HighLevelILOperation.HLIL_BLOCK: [("body", "expr_list")], HighLevelILOperation.HLIL_IF: [("condition", "expr"), ("true", "expr"), diff --git a/python/lowlevelil.py b/python/lowlevelil.py index e3c3ccab8..3cd4e7cc6 100644 --- a/python/lowlevelil.py +++ b/python/lowlevelil.py @@ -322,6 +322,8 @@ class LowLevelILInstruction(BaseILInstruction): expr_index: ExpressionIndex instr: CoreLowLevelILInstruction instr_index: Optional[InstructionIndex] + # ILOperations is deprecated and will be removed in a future version once BNIL Graph no longer uses it + # Use the visit methods visit, visit_all, and visit_operands ILOperations: ClassVar[Dict[LowLevelILOperation, List[Tuple[str, str]]]] = { LowLevelILOperation.LLIL_NOP: [], LowLevelILOperation.LLIL_SET_REG: [("dest", "reg"), ("src", "expr")], LowLevelILOperation.LLIL_SET_REG_SPLIT: [("hi", "reg"), ("lo", "reg"), diff --git a/python/mediumlevelil.py b/python/mediumlevelil.py index b6f4a664d..59f212b11 100644 --- a/python/mediumlevelil.py +++ b/python/mediumlevelil.py @@ -176,7 +176,7 @@ class MediumLevelILInstruction(BaseILInstruction): instr: CoreMediumLevelILInstruction instr_index: InstructionIndex - # ILOperations is deprecated and will be removed in a future version + # ILOperations is deprecated and will be removed in a future version once BNIL Graph no longer uses it # Use the visit methods visit, visit_all, and visit_operands ILOperations: ClassVar[Mapping[MediumLevelILOperation, List[Tuple[str, str]]]] = { MediumLevelILOperation.MLIL_NOP: [], MediumLevelILOperation.MLIL_SET_VAR: [("dest", "var"), ("src", "expr")], @@ -332,13 +332,13 @@ class MediumLevelILInstruction(BaseILInstruction): ], MediumLevelILOperation.MLIL_SET_VAR_SSA: [ ("dest", "var_ssa"), ("src", "expr") ], MediumLevelILOperation.MLIL_SET_VAR_SSA_FIELD: [ - ("prev", "var_ssa_dest_and_src"), ("offset", "int"), ("src", "expr") + ("dest", "var_ssa_dest_and_src"), ("prev", "var_ssa_dest_and_src"), ("offset", "int"), ("src", "expr") ], MediumLevelILOperation.MLIL_SET_VAR_SPLIT_SSA: [ ("high", "var_ssa"), ("low", "var_ssa"), ("src", "expr") ], MediumLevelILOperation.MLIL_SET_VAR_ALIASED: [ - ("prev", "var_ssa_dest_and_src"), ("src", "expr") + ("dest", "var_ssa_dest_and_src"), ("prev", "var_ssa_dest_and_src"), ("src", "expr") ], MediumLevelILOperation.MLIL_SET_VAR_ALIASED_FIELD: [ - ("prev", "var_ssa_dest_and_src"), ("offset", "int"), ("src", "expr") + ("dest", "var_ssa_dest_and_src"), ("prev", "var_ssa_dest_and_src"), ("offset", "int"), ("src", "expr") ], MediumLevelILOperation.MLIL_VAR_SSA: [("src", "var_ssa")], MediumLevelILOperation.MLIL_VAR_SSA_FIELD: [ ("src", "var_ssa"), ("offset", "int") ], MediumLevelILOperation.MLIL_VAR_ALIASED: [ @@ -348,7 +348,7 @@ class MediumLevelILInstruction(BaseILInstruction): ], MediumLevelILOperation.MLIL_VAR_SPLIT_SSA: [ ("high", "var_ssa"), ("low", "var_ssa") ], MediumLevelILOperation.MLIL_CALL_SSA: [ - ("output", "expr"), ("dest", "expr"), + ("output", "expr"), ("output_dest_memory", "int"), ("dest", "expr"), ("params", "expr_list"), ("src_memory", "int") ], MediumLevelILOperation.MLIL_CALL_UNTYPED_SSA: [ ("output", "expr"), ("dest", "expr"), ("params", "expr"), ("stack", "expr") @@ -358,7 +358,8 @@ class MediumLevelILInstruction(BaseILInstruction): ], MediumLevelILOperation.MLIL_SYSCALL_UNTYPED_SSA: [ ("output", "expr"), ("params", "expr"), ("stack", "expr") ], MediumLevelILOperation.MLIL_TAILCALL_SSA: [ - ("output", "expr"), ("dest", "expr"), ("params", "expr_list"), ("src_memory", "int") + ("output", "expr"), ("output_dest_memory", "int"), + ("dest", "expr"), ("params", "expr_list"), ("src_memory", "int") ], MediumLevelILOperation.MLIL_TAILCALL_UNTYPED_SSA: [ ("output", "expr"), ("dest", "expr"), ("params", "expr"), ("stack", "expr") ], MediumLevelILOperation.MLIL_CALL_OUTPUT_SSA: [ From 74920c190c5c6230833be6d50536119ce5e44c98 Mon Sep 17 00:00:00 2001 From: Zichuan Li <34680029+river-li@users.noreply.github.com> Date: Fri, 24 May 2024 11:53:02 -0400 Subject: [PATCH 12/24] Fix the method name in doc The parameter name is not correct --- docs/dev/annotation.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/dev/annotation.md b/docs/dev/annotation.md index abdf91c14..c75ca7754 100644 --- a/docs/dev/annotation.md +++ b/docs/dev/annotation.md @@ -142,18 +142,18 @@ Though convenient it is many orders of magnitude slower than simply calling the ```python Type.int(4) # Creates a 4 byte signed integer Type.int(8, False) # Creates an 8 bytes unsigned integer -Type.int(2, altName="short") # Creates a 2 byte signed integer named 'short' +Type.int(2, alternate_name="short") # Creates a 2 byte signed integer named 'short' # Similarly through their classes directly IntegerType.create(4) IntegerType.create(8, False) -IntegerType.create(2, altName="short") +IntegerType.create(2, alternate_name="short") ``` #### Character Types ```python Type.char() # This is just a 1 byte signed integer and can be used as such -Type.wideChar(2, altName="wchar_t") # Creates a wide character with the name 'wchar_t' +Type.wide_char(2, alternate_name="wchar_t") # Creates a wide character with the name 'wchar_t' # Similarly through their classes directly CharType.create() WideCharType.create(2) From 56115aecf186bc720dae9a20cc4c6aef248ba07f Mon Sep 17 00:00:00 2001 From: Ryan Snyder Date: Fri, 24 May 2024 14:47:15 -0400 Subject: [PATCH 13/24] platform: initial BNCustomPlatform support --- architecture.cpp | 2 +- binaryninjaapi.h | 40 ++++++++ binaryninjacore.h | 19 ++++ binaryview.cpp | 4 +- debuginfo.cpp | 2 +- function.cpp | 2 +- platform.cpp | 130 ++++++++++++++++++++++++-- platform/windows/platform_windows.cpp | 42 +++++++++ python/platform.py | 90 +++++++++++++++++- typearchive.cpp | 4 +- typecontainer.cpp | 2 +- typeparser.cpp | 6 +- 12 files changed, 319 insertions(+), 24 deletions(-) diff --git a/architecture.cpp b/architecture.cpp index 979ba3e2d..90d18d061 100644 --- a/architecture.cpp +++ b/architecture.cpp @@ -1372,7 +1372,7 @@ Ref Architecture::GetFastcallCallingConvention() Ref Architecture::GetStandalonePlatform() { - return new Platform(BNGetArchitectureStandalonePlatform(m_object)); + return new CorePlatform(BNGetArchitectureStandalonePlatform(m_object)); } diff --git a/binaryninjaapi.h b/binaryninjaapi.h index 477d1b600..8eb3cdaf5 100644 --- a/binaryninjaapi.h +++ b/binaryninjaapi.h @@ -14495,6 +14495,12 @@ namespace BinaryNinja { Platform(Architecture* arch, const std::string& name, const std::string& typeFile, const std::vector& includeDirs = std::vector()); + static void InitCallback(void *ctxt, BNPlatform*); + static void InitViewCallback(void* ctxt, BNBinaryView* view); + static uint32_t* GetGlobalRegistersCallback(void* ctxt, size_t* count); + static void FreeRegisterListCallback(void* ctxt, uint32_t* regs, size_t count); + static BNType* GetGlobalRegisterTypeCallback(void* ctxt, uint32_t reg); + public: Platform(BNPlatform* platform); @@ -14630,6 +14636,30 @@ namespace BinaryNinja { */ void SetSystemCallConvention(CallingConvention* cc); + /*! Callback that will be called when the platform of a binaryview + * is set. Allows for the Platform to to do platform-specific + * processing of views just after finalization. + * + * \param view BinaryView that was just set to this Platform + */ + virtual void BinaryViewInit(BinaryView* view); + + /*! Get the global register list for this Platform + * + * Allows the Platform to override the global register list + * used by analysis. + */ + virtual std::vector GetGlobalRegisters(); + + /*! Get the type of a global register + * + * Called by analysis when the incoming register value of a + * global register is observed. + * + * \param reg The register being queried for type information. + */ + virtual Ref GetGlobalRegisterType(uint32_t reg); + Ref GetRelatedPlatform(Architecture* arch); void AddRelatedPlatform(Architecture* arch, Platform* platform); Ref GetAssociatedPlatformByAddress(uint64_t& addr); @@ -14720,6 +14750,16 @@ namespace BinaryNinja { const std::string& autoTypeSource = ""); }; + + class CorePlatform : public Platform + { + public: + CorePlatform(BNPlatform* plat); + + virtual std::vector GetGlobalRegisters() override; + virtual Ref GetGlobalRegisterType(uint32_t reg) override; + }; + /*! \ingroup typeparser */ diff --git a/binaryninjacore.h b/binaryninjacore.h index d7d6244a7..68dde4673 100644 --- a/binaryninjacore.h +++ b/binaryninjacore.h @@ -1872,6 +1872,18 @@ extern "C" bool (*skipAndReturnValue)(void* ctxt, uint8_t* data, uint64_t addr, size_t len, uint64_t value); } BNCustomArchitecture; + typedef struct BNCustomPlatform + { + void* context; + void (*init)(void* ctxt, BNPlatform* obj); + void (*viewInit)(void* ctxt, BNBinaryView* view); + + uint32_t* (*getGlobalRegisters)(void* ctxt, size_t* count); + void (*freeRegisterList)(void* ctxt, uint32_t* regs, size_t len); + + BNType* (*getGlobalRegisterType)(void* ctxt, uint32_t reg); + } BNCustomPlatform; + typedef struct BNBasicBlockEdge { BNBranchType type; @@ -6360,6 +6372,10 @@ extern "C" BINARYNINJACOREAPI BNPlatform* BNCreatePlatform(BNArchitecture* arch, const char* name); BINARYNINJACOREAPI BNPlatform* BNCreatePlatformWithTypes( BNArchitecture* arch, const char* name, const char* typeFile, const char** includeDirs, size_t includeDirCount); + BINARYNINJACOREAPI BNPlatform* BNCreateCustomPlatform(BNArchitecture* arch, const char* name, BNCustomPlatform* impl); + BINARYNINJACOREAPI BNPlatform* BNCreateCustomPlatformWithTypes( + BNArchitecture* arch, const char* name, BNCustomPlatform* impl, + const char* typeFile, const char** includeDirs, size_t includeDirCount); BINARYNINJACOREAPI void BNRegisterPlatform(const char* os, BNPlatform* platform); BINARYNINJACOREAPI BNPlatform* BNNewPlatformReference(BNPlatform* platform); BINARYNINJACOREAPI void BNFreePlatform(BNPlatform* platform); @@ -6391,6 +6407,9 @@ extern "C" BINARYNINJACOREAPI void BNRegisterPlatformFastcallCallingConvention(BNPlatform* platform, BNCallingConvention* cc); BINARYNINJACOREAPI void BNSetPlatformSystemCallConvention(BNPlatform* platform, BNCallingConvention* cc); + BINARYNINJACOREAPI uint32_t* BNGetPlatformGlobalRegisters(BNPlatform* platform, size_t* count); + BINARYNINJACOREAPI BNType* BNGetPlatformGlobalRegisterType(BNPlatform* platform, uint32_t reg); + BINARYNINJACOREAPI BNPlatform* BNGetArchitectureStandalonePlatform(BNArchitecture* arch); BINARYNINJACOREAPI BNPlatform* BNGetRelatedPlatform(BNPlatform* platform, BNArchitecture* arch); diff --git a/binaryview.cpp b/binaryview.cpp index 98124fe5b..83ed3f3ba 100644 --- a/binaryview.cpp +++ b/binaryview.cpp @@ -1920,7 +1920,7 @@ Ref BinaryView::GetDefaultPlatform() const BNPlatform* platform = BNGetDefaultPlatform(m_object); if (!platform) return nullptr; - return new Platform(platform); + return new CorePlatform(platform); } @@ -4177,7 +4177,7 @@ std::optional, QualifiedName>> BinaryView::LookupImporte return std::nullopt; QualifiedName targetName = QualifiedName::FromAPIObject(&resultName); BNFreeQualifiedName(&resultName); - return std::make_pair(new Platform(resultLib), targetName); + return std::make_pair(new CorePlatform(resultLib), targetName); } diff --git a/debuginfo.cpp b/debuginfo.cpp index 2af165250..412d44bb7 100644 --- a/debuginfo.cpp +++ b/debuginfo.cpp @@ -100,7 +100,7 @@ vector DebugInfo::GetFunctions(const string& parserName) cons functions[i].fullName ? functions[i].fullName : "", functions[i].rawName ? functions[i].rawName : "", functions[i].address, functions[i].type ? new Type(BNNewTypeReference(functions[i].type)) : nullptr, - functions[i].platform ? new Platform(BNNewPlatformReference(functions[i].platform)) : nullptr, components); + functions[i].platform ? new CorePlatform(BNNewPlatformReference(functions[i].platform)) : nullptr, components); } BNFreeDebugFunctions(functions, count); diff --git a/function.cpp b/function.cpp index 27a91952a..2838f0796 100644 --- a/function.cpp +++ b/function.cpp @@ -192,7 +192,7 @@ Ref Function::GetView() const Ref Function::GetPlatform() const { - return new Platform(BNGetFunctionPlatform(m_object)); + return new CorePlatform(BNGetFunctionPlatform(m_object)); } diff --git a/platform.cpp b/platform.cpp index 7a6e21299..f123b76da 100644 --- a/platform.cpp +++ b/platform.cpp @@ -30,20 +30,89 @@ Platform::Platform(BNPlatform* platform) } +CorePlatform::CorePlatform(BNPlatform* platform) : Platform(platform) {} + + Platform::Platform(Architecture* arch, const string& name) { - m_object = BNCreatePlatform(arch->GetObject(), name.c_str()); + BNCustomPlatform plat; + plat.context = this; + plat.init = InitCallback; + plat.viewInit = InitViewCallback; + plat.getGlobalRegisters = GetGlobalRegistersCallback; + plat.freeRegisterList = FreeRegisterListCallback; + plat.getGlobalRegisterType = GetGlobalRegisterTypeCallback; + m_object = BNCreateCustomPlatform(arch->GetObject(), name.c_str(), &plat); + AddRefForRegistration(); } Platform::Platform(Architecture* arch, const string& name, const string& typeFile, const vector& includeDirs) { + BNCustomPlatform plat; + plat.context = this; + plat.init = InitCallback; + plat.viewInit = InitViewCallback; + plat.getGlobalRegisters = GetGlobalRegistersCallback; + plat.freeRegisterList = FreeRegisterListCallback; + plat.getGlobalRegisterType = GetGlobalRegisterTypeCallback; const char** includeDirList = new const char*[includeDirs.size()]; for (size_t i = 0; i < includeDirs.size(); i++) includeDirList[i] = includeDirs[i].c_str(); - m_object = BNCreatePlatformWithTypes( - arch->GetObject(), name.c_str(), typeFile.c_str(), includeDirList, includeDirs.size()); + m_object = BNCreateCustomPlatformWithTypes( + arch->GetObject(), name.c_str(), &plat, + typeFile.c_str(), includeDirList, includeDirs.size()); delete[] includeDirList; + AddRefForRegistration(); +} + + + + +void Platform::InitCallback(void* ctxt, BNPlatform* plat) +{ +} + + +void Platform::InitViewCallback(void* ctxt, BNBinaryView* view) +{ + CallbackRef plat(ctxt); + Ref viewObj = new BinaryView(BNNewViewReference(view)); + plat->BinaryViewInit(viewObj); +} + + +uint32_t* Platform::GetGlobalRegistersCallback(void* ctxt, size_t* count) +{ + CallbackRef plat(ctxt); + + std::vector regs = plat->GetGlobalRegisters(); + *count = regs.size(); + + uint32_t* result = new uint32_t[regs.size()]; + for (size_t i = 0; i < regs.size(); i++) + result[i] = regs[i]; + + return result; +} + + +void Platform::FreeRegisterListCallback(void*, uint32_t* regs, size_t) +{ + delete[] regs; +} + + +BNType* Platform::GetGlobalRegisterTypeCallback(void* ctxt, uint32_t reg) +{ + CallbackRef plat(ctxt); + + Ref result = plat->GetGlobalRegisterType(reg); + + if (!result) + return nullptr; + + return BNNewTypeReference(result->GetObject()); } @@ -73,7 +142,7 @@ Ref Platform::GetByName(const string& name) BNPlatform* platform = BNGetPlatformByName(name.c_str()); if (!platform) return nullptr; - return new Platform(platform); + return new CorePlatform(platform); } @@ -85,7 +154,7 @@ vector> Platform::GetList() vector> result; result.reserve(count); for (size_t i = 0; i < count; i++) - result.push_back(new Platform(BNNewPlatformReference(list[i]))); + result.push_back(new CorePlatform(BNNewPlatformReference(list[i]))); BNFreePlatformList(list, count); return result; @@ -100,7 +169,7 @@ vector> Platform::GetList(Architecture* arch) vector> result; result.reserve(count); for (size_t i = 0; i < count; i++) - result.push_back(new Platform(BNNewPlatformReference(list[i]))); + result.push_back(new CorePlatform(BNNewPlatformReference(list[i]))); BNFreePlatformList(list, count); return result; @@ -115,7 +184,7 @@ vector> Platform::GetList(const string& os) vector> result; result.reserve(count); for (size_t i = 0; i < count; i++) - result.push_back(new Platform(BNNewPlatformReference(list[i]))); + result.push_back(new CorePlatform(BNNewPlatformReference(list[i]))); BNFreePlatformList(list, count); return result; @@ -130,7 +199,7 @@ vector> Platform::GetList(const string& os, Architecture* arch) vector> result; result.reserve(count); for (size_t i = 0; i < count; i++) - result.push_back(new Platform(BNNewPlatformReference(list[i]))); + result.push_back(new CorePlatform(BNNewPlatformReference(list[i]))); BNFreePlatformList(list, count); return result; @@ -248,12 +317,53 @@ void Platform::SetSystemCallConvention(CallingConvention* cc) } +void Platform::BinaryViewInit(BinaryView*) +{ +} + + +std::vector Platform::GetGlobalRegisters() +{ + return GetArchitecture()->GetGlobalRegisters(); +} + + +Ref Platform::GetGlobalRegisterType(uint32_t reg) +{ + return nullptr; +} + + +std::vector CorePlatform::GetGlobalRegisters() +{ + size_t count; + uint32_t* regs = BNGetPlatformGlobalRegisters(m_object, &count); + + std::vector result; + for (size_t i = 0; i < count; i++) + result.push_back(regs[i]); + + BNFreeRegisterList(regs); + + return result; +} + + +Ref CorePlatform::GetGlobalRegisterType(uint32_t reg) +{ + BNType* res = BNGetPlatformGlobalRegisterType(m_object, reg); + if (!res) + return nullptr; + return new Type(res); +} + + Ref Platform::GetRelatedPlatform(Architecture* arch) { BNPlatform* platform = BNGetRelatedPlatform(m_object, arch->GetObject()); if (!platform) return nullptr; - return new Platform(platform); + return new CorePlatform(platform); } @@ -268,7 +378,7 @@ Ref Platform::GetAssociatedPlatformByAddress(uint64_t& addr) BNPlatform* platform = BNGetAssociatedPlatformByAddress(m_object, &addr); if (!platform) return nullptr; - return new Platform(platform); + return new CorePlatform(platform); } diff --git a/platform/windows/platform_windows.cpp b/platform/windows/platform_windows.cpp index 2b0a427f2..cb256660f 100644 --- a/platform/windows/platform_windows.cpp +++ b/platform/windows/platform_windows.cpp @@ -7,11 +7,16 @@ using namespace std; class WindowsX86Platform: public Platform { + uint32_t m_fsbase; + Ref m_teb; + public: WindowsX86Platform(Architecture* arch): Platform(arch, "windows-x86") { Ref cc; + m_fsbase = arch->GetRegisterByName("fsbase"); + cc = arch->GetCallingConventionByName("cdecl"); if (cc) { @@ -36,14 +41,35 @@ class WindowsX86Platform: public Platform if (cc) RegisterCallingConvention(cc); } + + + virtual void BinaryViewInit(BinaryView* view) override + { + if (!m_teb) + m_teb = Type::PointerType(GetArchitecture()->GetAddressSize(), Type::NamedType(QualifiedName("TEB"), GetTypeByName(QualifiedName("TEB")))); + } + + + virtual Ref GetGlobalRegisterType(uint32_t reg) override + { + if (reg == m_fsbase) + return m_teb; + + return nullptr; + } }; class WindowsX64Platform: public Platform { + uint32_t m_gsbase; + Ref m_teb; + public: WindowsX64Platform(Architecture* arch): Platform(arch, "windows-x86_64") { + m_gsbase = arch->GetRegisterByName("gsbase"); + Ref cc; cc = arch->GetCallingConventionByName("win64"); @@ -55,6 +81,22 @@ class WindowsX64Platform: public Platform RegisterStdcallCallingConvention(cc); } } + + + virtual void BinaryViewInit(BinaryView* view) override + { + if (!m_teb) + m_teb = Type::PointerType(GetArchitecture()->GetAddressSize(), Type::NamedType(QualifiedName("TEB"), GetTypeByName(QualifiedName("TEB")))); + } + + + virtual Ref GetGlobalRegisterType(uint32_t reg) override + { + if (reg == m_gsbase) + return m_teb; + + return nullptr; + } }; diff --git a/python/platform.py b/python/platform.py index d67f49014..0fd54e529 100644 --- a/python/platform.py +++ b/python/platform.py @@ -20,6 +20,7 @@ import os import ctypes +import traceback from typing import List, Dict, Optional # Binary Ninja components @@ -31,6 +32,8 @@ from . import typelibrary from . import architecture from . import typecontainer +from . import binaryview +from .log import log_error class _PlatformMetaClass(type): @@ -61,6 +64,10 @@ class Platform(metaclass=_PlatformMetaClass): name = None type_file_path = None # path to platform types file type_include_dirs = [] # list of directories available to #include from type_file_path + global_regs = [] # list of global registers. if empty, it populated with the arch global reg list + global_reg_types = {} # opportunity for plugin to provide default types for the entry value of global registers + + _registered_platforms = [] def __init__(self, arch: Optional['architecture.Architecture'] = None, handle=None): if handle is None: @@ -68,27 +75,92 @@ def __init__(self, arch: Optional['architecture.Architecture'] = None, handle=No raise ValueError("platform must have an associated architecture") assert self.__class__.name is not None, "Can not instantiate Platform directly, you probably want arch.standalone_platform" _arch = arch + if len(self.global_regs) == 0: + self.__dict__["global_regs"] = arch.global_regs + self._cb = core.BNCustomPlatform() + self._cb.context = 0 + self._cb.init = self._cb.init.__class__(self._init) + self._cb.viewInit = self._cb.viewInit.__class__(self._view_init) + self._cb.getGlobalRegisters = self._cb.getGlobalRegisters.__class__(self._get_global_regs) + self._cb.freeRegisterList = self._cb.freeRegisterList.__class__(self._free_register_list) + self._cb.getGlobalRegisterType = self._cb.getGlobalRegisterType.__class__(self._get_global_reg_type) + self._pending_reg_lists = {} if self.__class__.type_file_path is None: - _handle = core.BNCreatePlatform(arch.handle, self.__class__.name) + _handle = core.BNCreateCustomPlatform(arch.handle, self.__class__.name, self._cb) assert _handle is not None else: dir_buf = (ctypes.c_char_p * len(self.__class__.type_include_dirs))() for (i, dir) in enumerate(self.__class__.type_include_dirs): dir_buf[i] = dir.encode('charmap') - _handle = core.BNCreatePlatformWithTypes( - arch.handle, self.__class__.name, self.__class__.type_file_path, dir_buf, + _handle = core.BNCreateCustomPlatformWithTypes( + arch.handle, self.__class__.name, self._cb, self.__class__.type_file_path, dir_buf, len(self.__class__.type_include_dirs) ) assert _handle is not None + self.__class__._registered_platforms.append(self) else: _handle = handle _arch = architecture.CoreArchitecture._from_cache(core.BNGetPlatformArchitecture(_handle)) + count = ctypes.c_ulonglong() + regs = core.BNGetPlatformGlobalRegisters(handle, count) + result = [] + for i in range(0, count.value): + result.append(_arch.get_reg_name(regs[i])) + core.BNFreeRegisterList(regs) + self.__dict__["global_regs"] = result assert _handle is not None assert _arch is not None self.handle: ctypes.POINTER(core.BNPlatform) = _handle self._arch = _arch self._name = None + def _init(self, ctxt): + pass + + def _view_init(self, ctxt, view): + try: + view_obj = binaryview.BinaryView(handle=core.BNNewViewReference(view)) + self.view_init(view) + except: + log_error(traceback.format_exc()) + + def _get_global_regs(self, ctxt, count): + try: + regs = self.global_regs + count[0] = len(regs) + reg_buf = (ctypes.c_uint * len(regs))() + for i in range(0, len(regs)): + reg_buf[i] = self.arch.regs[regs[i]].index + result = ctypes.cast(reg_buf, ctypes.c_void_p) + self._pending_reg_lists[result.value] = (result, reg_buf) + return result.value + except: + log_error(traceback.format_exc()) + count[0] = 0 + return None + + def _free_register_list(self, ctxt, regs, size): + try: + buf = ctypes.cast(regs, ctypes.c_void_p) + if buf.value not in self._pending_reg_lists: + raise ValueError("freeing register list that wasn't allocated") + del self._pending_reg_lists[buf.value] + except: + log_error(traceback.format_exc()) + + def _get_global_reg_type(self, ctxt, reg): + try: + reg_name = self.arch.get_reg_name(reg) + if reg_name in self.global_reg_types: + type_obj = self.global_reg_types[reg_name] + log_error(f"aaaa {type_obj}") + handle = core.BNNewTypeReference(type_obj.handle) + return ctypes.cast(handle, ctypes.c_void_p).value + return None + except: + log_error(traceback.format_exc()) + return None + def __del__(self): if core is not None: core.BNFreePlatform(self.handle) @@ -253,6 +325,18 @@ def calling_conventions(self): core.BNFreeCallingConventionList(cc, count.value) return result + def get_global_register_type(self, reg: 'architecture.RegisterType'): + reg = self.arch.get_reg_index(reg) + type_obj = core.BNGetPlatformGlobalRegisterType(self.handle, reg) + if type_obj is None: + return None + return types.Type.create(type_obj, platform=self) + + def view_init(self, view): + pass + #raise NotImplementedError + + @property def types(self): """List of platform-specific types (read-only)""" diff --git a/typearchive.cpp b/typearchive.cpp index 29dd0d6ae..151564db5 100644 --- a/typearchive.cpp +++ b/typearchive.cpp @@ -148,7 +148,7 @@ Ref TypeArchive::GetPlatform() const BNPlatform* platform = BNGetTypeArchivePlatform(m_object); if (!platform) return nullptr; - return new Platform(platform); + return new CorePlatform(platform); } @@ -632,4 +632,4 @@ std::optional TypeArchive::MergeSnapshots( } return resultCpp; -} \ No newline at end of file +} diff --git a/typecontainer.cpp b/typecontainer.cpp index 6050fde3e..b6fd54f35 100644 --- a/typecontainer.cpp +++ b/typecontainer.cpp @@ -128,7 +128,7 @@ Ref TypeContainer::GetPlatform() const BNPlatform* platform = BNTypeContainerGetPlatform(m_object); if (!platform) return nullptr; - return new Platform(platform); + return new CorePlatform(platform); } diff --git a/typeparser.cpp b/typeparser.cpp index 8aaf02c47..67dadd956 100644 --- a/typeparser.cpp +++ b/typeparser.cpp @@ -147,7 +147,7 @@ bool TypeParser::PreprocessSourceCallback(void* ctxt, bool success = parser->PreprocessSource( source, fileName, - new Platform(platform), + new CorePlatform(platform), TypeContainer{BNDuplicateTypeContainer(existingTypes)}, optionsCpp, includeDirsCpp, @@ -207,7 +207,7 @@ bool TypeParser::ParseTypesFromSourceCallback(void* ctxt, bool success = parser->ParseTypesFromSource( source, fileName, - new Platform(platform), + new CorePlatform(platform), TypeContainer{BNDuplicateTypeContainer(existingTypes)}, optionsCpp, includeDirsCpp, @@ -278,7 +278,7 @@ bool TypeParser::ParseTypeStringCallback(void* ctxt, vector errorsCpp; bool success = parser->ParseTypeString( source, - new Platform(platform), + new CorePlatform(platform), TypeContainer{BNDuplicateTypeContainer(existingTypes)}, resultCpp, errorsCpp From 08a1e68f7dbf4671ec53538e874162998b249f0c Mon Sep 17 00:00:00 2001 From: Ryan Snyder Date: Fri, 24 May 2024 16:09:43 -0400 Subject: [PATCH 14/24] arch: multiple delay slot support, suppress spurious mips warning --- arch/mips/arch_mips.cpp | 4 ++-- arch/riscv/src/lib.rs | 2 +- architecture.cpp | 7 ++++--- binaryninjaapi.h | 2 +- binaryninjacore.h | 6 +++--- python/architecture.py | 6 +++--- rust/src/architecture.rs | 8 ++++---- 7 files changed, 18 insertions(+), 17 deletions(-) diff --git a/arch/mips/arch_mips.cpp b/arch/mips/arch_mips.cpp index 6a3d80e4d..50ad0f75e 100644 --- a/arch/mips/arch_mips.cpp +++ b/arch/mips/arch_mips.cpp @@ -301,7 +301,7 @@ class MipsArchitecture: public Architecture if (instr.operands[0].immediate != addr + 8) result.AddBranch(CallDestination, instr.operands[0].immediate, nullptr, hasBranchDelay); else - result.branchDelay = 1; // We have a "get pc" mnemonic; do nothing + result.delaySlots = 1; // We have a "get pc" mnemonic; do nothing break; case MIPS_JAL: @@ -311,7 +311,7 @@ class MipsArchitecture: public Architecture //Jmp to register register value is unknown case MIPS_JALR: case MIPS_JALR_HB: - result.branchDelay = 1; + result.delaySlots = 1; break; case MIPS_BGEZAL: diff --git a/arch/riscv/src/lib.rs b/arch/riscv/src/lib.rs index 91a418f7e..bc67e4655 100644 --- a/arch/riscv/src/lib.rs +++ b/arch/riscv/src/lib.rs @@ -687,7 +687,7 @@ impl architecture::Architecture fo _ => return None, }; - let mut res = InstructionInfo::new(inst_len, false); + let mut res = InstructionInfo::new(inst_len, 0); match op { Op::Jal(ref j) => { diff --git a/architecture.cpp b/architecture.cpp index 90d18d061..aa229b4e5 100644 --- a/architecture.cpp +++ b/architecture.cpp @@ -34,15 +34,15 @@ InstructionInfo::InstructionInfo() length = 0; archTransitionByTargetAddr = false; branchCount = 0; - branchDelay = false; + delaySlots = 0; } -void InstructionInfo::AddBranch(BNBranchType type, uint64_t target, Architecture* arch, bool hasDelaySlot) +void InstructionInfo::AddBranch(BNBranchType type, uint64_t target, Architecture* arch, uint8_t delaySlot) { if (branchCount >= BN_MAX_INSTRUCTION_BRANCHES) return; - branchDelay = hasDelaySlot; + delaySlots = delaySlot; branchType[branchCount] = type; branchTarget[branchCount] = target; branchArch[branchCount++] = arch ? arch->GetObject() : nullptr; @@ -242,6 +242,7 @@ bool Architecture::GetInstructionInfoCallback( CallbackRef arch(ctxt); InstructionInfo info; + info.delaySlots = result->delaySlots; bool ok = arch->GetInstructionInfo(data, addr, maxLen, info); *result = info; return ok; diff --git a/binaryninjaapi.h b/binaryninjaapi.h index 8eb3cdaf5..0e7f2db25 100644 --- a/binaryninjaapi.h +++ b/binaryninjaapi.h @@ -7569,7 +7569,7 @@ namespace BinaryNinja { struct InstructionInfo : public BNInstructionInfo { InstructionInfo(); - void AddBranch(BNBranchType type, uint64_t target = 0, Architecture* arch = nullptr, bool hasDelaySlot = false); + void AddBranch(BNBranchType type, uint64_t target = 0, Architecture* arch = nullptr, uint8_t delaySlots = 0); }; struct NameAndType diff --git a/binaryninjacore.h b/binaryninjacore.h index 68dde4673..af05cb002 100644 --- a/binaryninjacore.h +++ b/binaryninjacore.h @@ -37,14 +37,14 @@ // Current ABI version for linking to the core. This is incremented any time // there are changes to the API that affect linking, including new functions, // new types, or modifications to existing functions or types. -#define BN_CURRENT_CORE_ABI_VERSION 63 +#define BN_CURRENT_CORE_ABI_VERSION 64 // Minimum ABI version that is supported for loading of plugins. Plugins that // are linked to an ABI version less than this will not be able to load and // will require rebuilding. The minimum version is increased when there are // incompatible changes that break binary compatibility, such as changes to // existing types or functions. -#define BN_MINIMUM_CORE_ABI_VERSION 62 +#define BN_MINIMUM_CORE_ABI_VERSION 64 #ifdef __GNUC__ #ifdef BINARYNINJACORE_LIBRARY @@ -1685,7 +1685,7 @@ extern "C" size_t length; size_t branchCount; bool archTransitionByTargetAddr; - bool branchDelay; + uint8_t delaySlots; BNBranchType branchType[BN_MAX_INSTRUCTION_BRANCHES]; uint64_t branchTarget[BN_MAX_INSTRUCTION_BRANCHES]; BNArchitecture* branchArch[BN_MAX_INSTRUCTION_BRANCHES]; // If null, same architecture as instruction diff --git a/python/architecture.py b/python/architecture.py index ce69af864..3e4a010e2 100644 --- a/python/architecture.py +++ b/python/architecture.py @@ -131,7 +131,7 @@ def __repr__(self): class InstructionInfo: length: int = 0 arch_transition_by_target_addr: bool = False - branch_delay: bool = False + branch_delay: int = 0 branches: List[InstructionBranch] = field(default_factory=list) def add_branch(self, branch_type: BranchType, target: int = 0, arch: Optional['Architecture'] = None) -> None: @@ -648,7 +648,7 @@ def _get_instruction_info(self, ctxt, data, addr, max_len, result): return False result[0].length = info.length result[0].archTransitionByTargetAddr = info.arch_transition_by_target_addr - result[0].branchDelay = info.branch_delay + result[0].delaySlots = info.branch_delay result[0].branchCount = len(info.branches) for i in range(0, len(info.branches)): if isinstance(info.branches[i].type, str): @@ -2346,7 +2346,7 @@ def get_instruction_info(self, data: bytes, addr: int) -> Optional[InstructionIn result = InstructionInfo() result.length = info.length result.arch_transition_by_target_addr = info.archTransitionByTargetAddr - result.branch_delay = info.branchDelay + result.branch_delay = info.delaySlots for i in range(0, info.branchCount): target = info.branchTarget[i] if info.branchArch[i]: diff --git a/rust/src/architecture.rs b/rust/src/architecture.rs index 6831844ef..11daa71c6 100644 --- a/rust/src/architecture.rs +++ b/rust/src/architecture.rs @@ -97,11 +97,11 @@ impl<'a> Iterator for BranchIter<'a> { #[repr(C)] pub struct InstructionInfo(BNInstructionInfo); impl InstructionInfo { - pub fn new(len: usize, branch_delay: bool) -> Self { + pub fn new(len: usize, delay_slots: u8) -> Self { InstructionInfo(BNInstructionInfo { length: len, archTransitionByTargetAddr: false, - branchDelay: branch_delay, + delaySlots: delay_slots, branchCount: 0usize, branchType: [BranchType::UnresolvedBranch; 3], branchTarget: [0u64; 3], @@ -121,8 +121,8 @@ impl InstructionInfo { self.0.branchCount } - pub fn branch_delay(&self) -> bool { - self.0.branchDelay + pub fn delay_slots(&self) -> u8 { + self.0.delaySlots } pub fn branches(&self) -> BranchIter { From 7146494fbe0035317eefa2aba75312ba78e87bc4 Mon Sep 17 00:00:00 2001 From: Rusty Wagner Date: Fri, 24 May 2024 19:47:19 -0400 Subject: [PATCH 15/24] Fix do while condition and body being flipped in Rust HLIL bindings --- rust/src/hlil/instruction.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rust/src/hlil/instruction.rs b/rust/src/hlil/instruction.rs index f98b18847..1ab9ce4d0 100644 --- a/rust/src/hlil/instruction.rs +++ b/rust/src/hlil/instruction.rs @@ -611,8 +611,8 @@ impl HighLevelILInstruction { body: op.operands[1] as usize, }), HLIL_DO_WHILE => Op::DoWhile(While { - condition: op.operands[0] as usize, - body: op.operands[1] as usize, + body: op.operands[0] as usize, + condition: op.operands[1] as usize, }), HLIL_WHILE_SSA => Op::WhileSsa(WhileSsa { condition_phi: op.operands[0] as usize, From 309d606f1c51168c0ef30ccf7850d483429fa35b Mon Sep 17 00:00:00 2001 From: Brian Potchik Date: Mon, 27 May 2024 15:25:45 -0400 Subject: [PATCH 16/24] Add additional JSON schema validation for registered settings. --- binaryninjaapi.h | 2 +- python/settings.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/binaryninjaapi.h b/binaryninjaapi.h index 0e7f2db25..55bb3b44b 100644 --- a/binaryninjaapi.h +++ b/binaryninjaapi.h @@ -15840,7 +15840,7 @@ namespace BinaryNinja { "message" string None Yes An optional message with additional emphasis "readOnly" bool None Yes Only enforced by UI elements "optional" bool None Yes Indicates setting can be null - "hidden" bool "type" is "string" Yes Indicates the UI should conceal the content + "hidden" bool "type" is "string" Yes Indicates the UI should conceal the content. The "ignore" property is required to specify the applicable storage scopes "requiresRestart bool None Yes Enable restart notification in the UI upon change "uiSelectionAction" string "type" is "string" Yes {"file", "directory", } Informs the UI to add a button to open a selection dialog or run a registered UIAction ================== ====================================== ================== ======== ======================================================================= diff --git a/python/settings.py b/python/settings.py index fa7cc275f..2f23bde8e 100644 --- a/python/settings.py +++ b/python/settings.py @@ -77,7 +77,7 @@ class Settings: "message" string None Yes An optional message with additional emphasis "readOnly" boolean None Yes Only enforced by UI elements "optional" boolean None Yes Indicates setting can be null - "hidden" bool "type" is "string" Yes Indicates the UI should conceal the content + "hidden" bool "type" is "string" Yes Indicates the UI should conceal the content. The "ignore" property is required to specify the applicable storage scopes "requiresRestart boolean None Yes Enable restart notification in the UI upon change "uiSelectionAction" string "type" is "string" Yes {"file", "directory", } Informs the UI to add a button to open a selection dialog or run a registered UIAction =================== ====================================== ================== ======== ======================================================================= From a348dab8b14b8ddd36c400ae39a7a48b6a28e829 Mon Sep 17 00:00:00 2001 From: Brian Potchik Date: Mon, 27 May 2024 15:29:48 -0400 Subject: [PATCH 17/24] Update load APIs to take a JSON string for options. --- binaryninjaapi.h | 37 ++++++++++++++++++++++--------------- binaryninjacore.h | 14 ++++++-------- binaryview.cpp | 32 +++++++++++++++++++++++++------- metadata.cpp | 22 +++++++++++++++------- python/binaryview.py | 22 +++++++++++----------- python/metadata.py | 3 +++ rust/src/lib.rs | 5 ++--- rust/src/metadata.rs | 13 +++++++++++++ 8 files changed, 97 insertions(+), 51 deletions(-) diff --git a/binaryninjaapi.h b/binaryninjaapi.h index 55bb3b44b..55b8ef555 100644 --- a/binaryninjaapi.h +++ b/binaryninjaapi.h @@ -1366,6 +1366,7 @@ namespace BinaryNinja { std::vector GetRaw() const; std::vector> GetArray() const; std::map> GetKeyValueStore() const; + std::string GetJsonString() const; // For key-value data only /*! Get a Metadata object by key. Only for if IsKeyValueStore == true @@ -1483,15 +1484,12 @@ namespace BinaryNinja { \param filename Path to filename or BNDB to open. \param updateAnalysis If true, UpdateAnalysisAndWait() will be called after opening a BinaryView. + \param options A Json string whose keys are setting identifiers and whose values are the desired settings. \param progress Optional function to be called with progress updates as the view is being loaded. If the function returns false, it will cancel Load. - \param options A Json object whose keys are setting identifiers and whose values are - the desired settings. \return Constructed view, or a nullptr Ref */ - Ref Load(const std::string& filename, bool updateAnalysis = true, - std::function progress = {}, Ref options = new Metadata(MetadataType::KeyValueDataType)); - + Ref Load(const std::string& filename, bool updateAnalysis = true, const std::string& options = "{}", std::function progress = {}); /*! Open a BinaryView from a raw data buffer, initializing data views and loading settings. @threadmainonly @@ -1502,14 +1500,12 @@ namespace BinaryNinja { \param rawData Buffer with raw binary data to load (cannot load from bndb) \param updateAnalysis If true, UpdateAnalysisAndWait() will be called after opening a BinaryView. + \param options A Json string whose keys are setting identifiers and whose values are the desired settings. \param progress Optional function to be called with progress updates as the view is being loaded. If the function returns false, it will cancel Load. - \param options A Json object whose keys are setting identifiers and whose values are - the desired settings. \return Constructed view, or a nullptr Ref */ - Ref Load(const DataBuffer& rawData, bool updateAnalysis = true, - std::function progress = {}, Ref options = new Metadata(MetadataType::KeyValueDataType)); + Ref Load(const DataBuffer& rawData, bool updateAnalysis = true, const std::string& options = "{}", std::function progress = {}); /*! Open a BinaryView from a raw BinaryView, initializing data views and loading settings. @@ -1522,16 +1518,27 @@ namespace BinaryNinja { \param rawData BinaryView with raw binary data to load \param updateAnalysis If true, UpdateAnalysisAndWait() will be called after opening a BinaryView. + \param options A Json string whose keys are setting identifiers and whose values are the desired settings. \param progress Optional function to be called with progress updates as the view is being loaded. If the function returns false, it will cancel Load. - \param options A Json object whose keys are setting identifiers and whose values are - the desired settings. - \param isDatabase True if the view being loaded is the raw view of an already opened database. \return Constructed view, or a nullptr Ref */ - Ref Load(Ref rawData, bool updateAnalysis = true, - std::function progress = {}, Ref options = new Metadata(MetadataType::KeyValueDataType), - bool isDatabase = false); + Ref Load(Ref rawData, bool updateAnalysis = true, const std::string& options = "{}", std::function progress = {}); + + /*! + Deprecated. Use non-metadata version. + */ + Ref Load(const std::string& filename, bool updateAnalysis, std::function progress, Ref options = new Metadata(MetadataType::KeyValueDataType)); + + /*! + Deprecated. Use non-metadata version. + */ + Ref Load(const DataBuffer& rawData, bool updateAnalysis, std::function progress, Ref options = new Metadata(MetadataType::KeyValueDataType)); + + /*! + Deprecated. Use non-metadata version. + */ + Ref Load(Ref rawData, bool updateAnalysis, std::function progress, Ref options = new Metadata(MetadataType::KeyValueDataType), bool isDatabase = false); /*! Demangles using LLVM's demangler diff --git a/binaryninjacore.h b/binaryninjacore.h index af05cb002..35e695416 100644 --- a/binaryninjacore.h +++ b/binaryninjacore.h @@ -37,14 +37,14 @@ // Current ABI version for linking to the core. This is incremented any time // there are changes to the API that affect linking, including new functions, // new types, or modifications to existing functions or types. -#define BN_CURRENT_CORE_ABI_VERSION 64 +#define BN_CURRENT_CORE_ABI_VERSION 65 // Minimum ABI version that is supported for loading of plugins. Plugins that // are linked to an ABI version less than this will not be able to load and // will require rebuilding. The minimum version is increased when there are // incompatible changes that break binary compatibility, such as changes to // existing types or functions. -#define BN_MINIMUM_CORE_ABI_VERSION 64 +#define BN_MINIMUM_CORE_ABI_VERSION 65 #ifdef __GNUC__ #ifdef BINARYNINJACORE_LIBRARY @@ -6119,12 +6119,9 @@ extern "C" BINARYNINJACOREAPI bool BNCheckForStringAnnotationType(BNBinaryView* view, uint64_t addr, char** value, BNStringType* strType, bool allowShortStrings, bool allowLargeStrings, size_t childWidth); - BINARYNINJACOREAPI BNBinaryView* BNLoadFilename(const char* const filename, const bool updateAnalysis, - bool (*progress)(size_t, size_t), const BNMetadata* const options); - BINARYNINJACOREAPI BNBinaryView* BNLoadProjectFile(BNProjectFile* projectFile, const bool updateAnalysis, - bool (*progress)(size_t, size_t), const BNMetadata* const options); - BINARYNINJACOREAPI BNBinaryView* BNLoadBinaryView(BNBinaryView* view, const bool updateAnalysis, - bool (*progress)(size_t, size_t), const BNMetadata* const options, const bool isDatabase); + BINARYNINJACOREAPI BNBinaryView* BNLoadFilename(const char* const filename, const bool updateAnalysis, const char* options, bool (*progress)(size_t, size_t)); + BINARYNINJACOREAPI BNBinaryView* BNLoadProjectFile(BNProjectFile* projectFile, const bool updateAnalysis, const char* options, bool (*progress)(size_t, size_t)); + BINARYNINJACOREAPI BNBinaryView* BNLoadBinaryView(BNBinaryView* view, const bool updateAnalysis, const char* options, bool (*progress)(size_t, size_t)); BINARYNINJACOREAPI BNExternalLibrary* BNBinaryViewAddExternalLibrary(BNBinaryView* view, const char* name, BNProjectFile* backingFile, bool isAuto); BINARYNINJACOREAPI void BNBinaryViewRemoveExternalLibrary(BNBinaryView* view, const char* name); @@ -6902,6 +6899,7 @@ extern "C" BINARYNINJACOREAPI uint8_t* BNMetadataGetRaw(BNMetadata* data, size_t* size); BINARYNINJACOREAPI BNMetadata** BNMetadataGetArray(BNMetadata* data, size_t* size); BINARYNINJACOREAPI BNMetadataValueStore* BNMetadataGetValueStore(BNMetadata* data); + BINARYNINJACOREAPI char* BNMetadataGetJsonString(BNMetadata* data); // Query type of Metadata BINARYNINJACOREAPI BNMetadataType BNMetadataGetType(BNMetadata* data); diff --git a/binaryview.cpp b/binaryview.cpp index 83ed3f3ba..b5c341777 100644 --- a/binaryview.cpp +++ b/binaryview.cpp @@ -5316,11 +5316,7 @@ Ref BinaryData::CreateFromFile(FileMetadata* file, FileAccessor* acc Ref BinaryNinja::Load(const std::string& filename, bool updateAnalysis, std::function progress, Ref options) { - BNBinaryView* handle = BNLoadFilename(filename.c_str(), updateAnalysis, - (bool (*)(size_t, size_t))progress.target(), options->m_object); - if (!handle) - return nullptr; - return new BinaryView(handle); + return Load(filename, updateAnalysis, options->GetJsonString(), progress); } @@ -5336,8 +5332,30 @@ Ref BinaryNinja::Load( Ref BinaryNinja::Load(Ref view, bool updateAnalysis, std::function progress, Ref options, bool isDatabase) { - BNBinaryView* handle = BNLoadBinaryView(view->GetObject(), updateAnalysis, - (bool (*)(size_t, size_t))progress.target(), options->m_object, isDatabase); + return Load(view, updateAnalysis, options->GetJsonString(), progress); +} + + +Ref BinaryNinja::Load(const std::string& filename, bool updateAnalysis, const std::string& options, std::function progress) +{ + BNBinaryView* handle = BNLoadFilename(filename.c_str(), updateAnalysis, options.c_str(), (bool (*)(size_t, size_t))progress.target()); + if (!handle) + return nullptr; + return new BinaryView(handle); +} + + +Ref BinaryNinja::Load(const DataBuffer& rawData, bool updateAnalysis, const std::string& options, std::function progress) +{ + Ref file = new FileMetadata(); + Ref view = new BinaryData(file, rawData); + return Load(view, updateAnalysis, options, progress); +} + + +Ref BinaryNinja::Load(Ref view, bool updateAnalysis, const std::string& options, std::function progress) +{ + BNBinaryView* handle = BNLoadBinaryView(view->GetObject(), updateAnalysis, options.c_str(), (bool (*)(size_t, size_t))progress.target()); if (!handle) return nullptr; return new BinaryView(handle); diff --git a/metadata.cpp b/metadata.cpp index 409ea47b9..492957550 100644 --- a/metadata.cpp +++ b/metadata.cpp @@ -13,7 +13,7 @@ Metadata::Metadata(bool data) m_object = BNCreateMetadataBooleanData(data); } -Metadata::Metadata(const string& data) +Metadata::Metadata(const std::string& data) { m_object = BNCreateMetadataStringData(data.c_str()); } @@ -163,12 +163,12 @@ Ref Metadata::Get(size_t index) return new Metadata(result); } -bool Metadata::SetValueForKey(const string& key, Ref data) +bool Metadata::SetValueForKey(const std::string& key, Ref data) { return BNMetadataSetValueForKey(m_object, key.c_str(), data->m_object); } -void Metadata::RemoveKey(const string& key) +void Metadata::RemoveKey(const std::string& key) { return BNMetadataRemoveKey(m_object, key.c_str()); } @@ -183,10 +183,10 @@ bool Metadata::GetBoolean() const return BNMetadataGetBoolean(m_object); } -string Metadata::GetString() const +std::string Metadata::GetString() const { char* str = BNMetadataGetString(m_object); - string result = string(str); + std::string result = std::string(str); BNFreeString(str); return result; } @@ -312,10 +312,10 @@ vector> Metadata::GetArray() const return result; } -map> Metadata::GetKeyValueStore() const +map> Metadata::GetKeyValueStore() const { BNMetadataValueStore* data = BNMetadataGetValueStore(m_object); - map> result; + map> result; for (size_t i = 0; i < data->size; i++) { result[data->keys[i]] = new Metadata(data->values[i]); @@ -323,6 +323,14 @@ map> Metadata::GetKeyValueStore() const return result; } +std::string Metadata::GetJsonString() const +{ + char* str = BNMetadataGetJsonString(m_object); + std::string result = std::string(str); + BNFreeString(str); + return result; +} + bool Metadata::Append(Ref data) { return BNMetadataArrayAppend(m_object, data->m_object); diff --git a/python/binaryview.py b/python/binaryview.py index 39cd344d3..0df5a2b16 100644 --- a/python/binaryview.py +++ b/python/binaryview.py @@ -2136,12 +2136,12 @@ class MemoryMap: size: 0x4 objects: 'origin' | Mapped - + size: 0x1000 objects: 'origin' | Unmapped | FILL<0x0> - + size: 0x14 objects: @@ -2153,13 +2153,13 @@ class MemoryMap: size: 0x4 objects: 'origin' | Mapped - + size: 0x1000 objects: 'rom' | Mapped 'origin' | Unmapped | FILL<0x0> - + size: 0x14 objects: @@ -2175,20 +2175,20 @@ class MemoryMap: size: 0x4 objects: 'origin' | Mapped - + size: 0x8 objects: 'pad' | Mapped 'rom' | Mapped 'origin' | Unmapped | FILL<0x0> - + size: 0xff8 objects: 'rom' | Mapped 'origin' | Unmapped | FILL<0x0> - + size: 0x14 objects: @@ -2751,14 +2751,14 @@ def load(source: Union[str, bytes, bytearray, 'databuffer.DataBuffer', 'os.PathL if isinstance(source, os.PathLike): source = str(source) if isinstance(source, BinaryView): - handle = core.BNLoadBinaryView(source.handle, update_analysis, progress_cfunc, metadata.Metadata(options).handle, source.file.has_database) + handle = core.BNLoadBinaryView(source.handle, update_analysis, json.dumps(options), progress_cfunc) elif isinstance(source, project.ProjectFile): - handle = core.BNLoadProjectFile(source._handle, update_analysis, progress_cfunc, metadata.Metadata(options).handle) + handle = core.BNLoadProjectFile(source._handle, update_analysis, json.dumps(options), progress_cfunc) elif isinstance(source, str): - handle = core.BNLoadFilename(source, update_analysis, progress_cfunc, metadata.Metadata(options).handle) + handle = core.BNLoadFilename(source, update_analysis, json.dumps(options), progress_cfunc) elif isinstance(source, bytes) or isinstance(source, bytearray) or isinstance(source, databuffer.DataBuffer): raw_view = BinaryView.new(source) - handle = core.BNLoadBinaryView(raw_view.handle, update_analysis, progress_cfunc, metadata.Metadata(options).handle, False) + handle = core.BNLoadBinaryView(raw_view.handle, update_analysis, json.dumps(options), progress_cfunc) else: raise NotImplementedError return BinaryView(handle=handle) if handle else None diff --git a/python/metadata.py b/python/metadata.py index e87c5cbbf..77e3e98c0 100644 --- a/python/metadata.py +++ b/python/metadata.py @@ -216,6 +216,9 @@ def get_dict(self): result[key] = self[key] return result + def get_json_string(self): + return str(core.BNMetadataGetJsonString(self.handle)) + @property def type(self): return MetadataType(core.BNMetadataGetType(self.handle)) diff --git a/rust/src/lib.rs b/rust/src/lib.rs index d2894b201..de38be4db 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -197,14 +197,13 @@ const BN_INVALID_EXPR: usize = usize::MAX; /// The main way to open and load files into Binary Ninja. Make sure you've properly initialized the core before calling this function. See [`crate::headless::init()`] pub fn load(filename: S) -> Option> { let filename = filename.into_bytes_with_nul(); - let metadata = Metadata::new_of_type(MetadataType::KeyValueDataType); let handle = unsafe { binaryninjacore_sys::BNLoadFilename( filename.as_ref().as_ptr() as *mut _, true, + std::ptr::null(), None, - metadata.handle, ) }; @@ -245,8 +244,8 @@ pub fn load_with_options( binaryninjacore_sys::BNLoadFilename( filename.as_ref().as_ptr() as *mut _, update_analysis_and_wait, + options_or_default.get_json_string().unwrap().as_ref().as_ptr() as *mut _, None, - options_or_default.as_ref().handle, ) }; diff --git a/rust/src/metadata.rs b/rust/src/metadata.rs index c8c532f39..f635700f2 100644 --- a/rust/src/metadata.rs +++ b/rust/src/metadata.rs @@ -166,6 +166,19 @@ impl Metadata { } } + pub fn get_json_string(&self) -> Result { + match self.get_type() { + MetadataType::StringDataType => { + let ptr: *mut c_char = unsafe { BNMetadataGetJsonString(self.handle) }; + if ptr.is_null() { + return Err(()); + } + Ok(unsafe { BnString::from_raw(ptr) }) + } + _ => Err(()), + } + } + pub fn get_raw(&self) -> Result, ()> { match self.get_type() { MetadataType::RawDataType => { From 2a6cef7eac64dc3df4a17ba3dc73fb5e1046fc29 Mon Sep 17 00:00:00 2001 From: Xusheng Date: Tue, 28 May 2024 11:45:31 +0800 Subject: [PATCH 18/24] Fix Python DownloadProvider. Fix https://github.com/Vector35/binaryninja-api/issues/5467 --- python/downloadprovider.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/downloadprovider.py b/python/downloadprovider.py index 9ac5faaeb..169f08fcc 100644 --- a/python/downloadprovider.py +++ b/python/downloadprovider.py @@ -128,8 +128,8 @@ def data_generator(): self.bn_response.headerKeys = (ctypes.c_char_p * len(py_response.headers))() self.bn_response.headerValues = (ctypes.c_char_p * len(py_response.headers))() for i, (key, value) in enumerate(py_response.headers.items()): - self.bn_response.headerKeys[i] = core.BNAllocString(key.decode('utf8')) - self.bn_response.headerValues[i] = core.BNAllocString(value.decode('utf8')) + self.bn_response.headerKeys[i] = core.BNAllocString(key) + self.bn_response.headerValues[i] = core.BNAllocString(value) out_response[0] = ctypes.pointer(self.bn_response) else: From 9537f33da6b8de883a45c4b7758f0dc5c1438ed7 Mon Sep 17 00:00:00 2001 From: Zichuan Li Date: Tue, 21 May 2024 17:56:03 -0400 Subject: [PATCH 19/24] Solved issue #1180 by adding new APIs 1. Add two new APIs for multiple entry functions `GetAllAnalysisEntryFunctions` and `AddToEntryFunctions` 2. Add Python APIs `entry_functions` and `add_to_entry_functions`. `entry_functions` resturns a list of functions, which supports parsing functions in `init_array`, `fini_array` and TLS callbacks. 3. Modify bin-info, it now prints all entry functions --- binaryninjaapi.h | 11 +++++++ binaryninjacore.h | 2 ++ binaryview.cpp | 21 +++++++++++++ examples/bin-info/src/bin-info.cpp | 7 +++++ python/binaryview.py | 48 ++++++++++++++++++++++++++++++ rust/src/binaryview.rs | 9 ++++++ view/elf/elfview.cpp | 10 +++++-- view/pe/peview.cpp | 2 ++ 8 files changed, 108 insertions(+), 2 deletions(-) diff --git a/binaryninjaapi.h b/binaryninjaapi.h index 55b8ef555..114b6e853 100644 --- a/binaryninjaapi.h +++ b/binaryninjaapi.h @@ -4946,6 +4946,12 @@ namespace BinaryNinja { */ void AddEntryPointForAnalysis(Platform* platform, uint64_t start); + /*! adds an function to all entry function list + + \param func Function to add + */ + void AddToEntryFunctions(Function* func); + /*! removes a function from the list of functions \param func Function to be removed @@ -5089,6 +5095,11 @@ namespace BinaryNinja { */ Ref GetAnalysisEntryPoint(); + /*! Get all entry functions (including user-defined ones) + + \return vector of Functions + */ + std::vector> GetAllEntryFunctions(); /*! Get most recently used Basic Block containing a virtual address diff --git a/binaryninjacore.h b/binaryninjacore.h index 35e695416..a7b968e68 100644 --- a/binaryninjacore.h +++ b/binaryninjacore.h @@ -4222,6 +4222,8 @@ extern "C" BINARYNINJACOREAPI BNFunction** BNGetAnalysisFunctionsContainingAddress( BNBinaryView* view, uint64_t addr, size_t* count); BINARYNINJACOREAPI BNFunction* BNGetAnalysisEntryPoint(BNBinaryView* view); + BINARYNINJACOREAPI BNFunction** BNGetAllEntryFunctions(BNBinaryView* view, size_t* count); + BINARYNINJACOREAPI void BNAddToEntryFunctions(BNBinaryView* view, BNFunction* func); BINARYNINJACOREAPI char* BNGetGlobalCommentForAddress(BNBinaryView* view, uint64_t addr); BINARYNINJACOREAPI uint64_t* BNGetGlobalCommentedAddresses(BNBinaryView* view, size_t* count); diff --git a/binaryview.cpp b/binaryview.cpp index b5c341777..929d0fe9c 100644 --- a/binaryview.cpp +++ b/binaryview.cpp @@ -1985,6 +1985,12 @@ void BinaryView::AddEntryPointForAnalysis(Platform* platform, uint64_t addr) } +void BinaryView::AddToEntryFunctions(Function* func) +{ + BNAddToEntryFunctions(m_object, func->GetObject()); +} + + void BinaryView::RemoveAnalysisFunction(Function* func, bool updateRefs) { BNRemoveAnalysisFunction(m_object, func->GetObject(), updateRefs); @@ -2208,6 +2214,21 @@ Ref BinaryView::GetAnalysisEntryPoint() } +vector> BinaryView::GetAllEntryFunctions() +{ + size_t count; + BNFunction** funcs = BNGetAllEntryFunctions(m_object, &count); + if (count == 0) + return {}; + + vector> result; + for (size_t i = 0; i < count; i++) + result.push_back(new Function(BNNewFunctionReference(funcs[i]))); + BNFreeFunctionList(funcs, count); + return result; +} + + Ref BinaryView::GetRecentBasicBlockForAddress(uint64_t addr) { BNBasicBlock* block = BNGetRecentBasicBlockForAddress(m_object, addr); diff --git a/examples/bin-info/src/bin-info.cpp b/examples/bin-info/src/bin-info.cpp index b952694df..8e920156b 100644 --- a/examples/bin-info/src/bin-info.cpp +++ b/examples/bin-info/src/bin-info.cpp @@ -58,6 +58,13 @@ int main(int argc, char* argv[]) cout << "PLATFORM: " << bv->GetDefaultPlatform()->GetName() << endl; cout << endl; + cout << "------ALL ENTRY FUNCTIONS---------" << endl; + for (auto func : bv->GetAllEntryFunctions()) + { + cout << hex << func->GetStart() << " " << func->GetSymbol()->GetFullName() << endl; + } + cout << endl; + cout << "---------- 10 Functions ----------" << endl; int x = 0; for (auto func : bv->GetAnalysisFunctionList()) diff --git a/python/binaryview.py b/python/binaryview.py index 0df5a2b16..154b4f8ed 100644 --- a/python/binaryview.py +++ b/python/binaryview.py @@ -3034,6 +3034,39 @@ def entry_function(self) -> Optional['_function.Function']: return None return _function.Function(self, func) + @property + def entry_functions(self) -> FunctionList: + """A List of entry functions (read-only) + This list contains vanilla entry function, and functions like init_array, fini_arry, and TLS callbacks etc. + User-added entry functions(via `add_entry_point`) are also included. + + We see `entry_functions` as good starting points for analysis, these functions normally don't have internal references. + However, note that exported functions in a dll/so file are not included. + + Note the difference with `entry_function` + + :Example: + + >>> bv.entry_function + + >>> bv.entry_functions + [, ] + + :return: a list of functions, containing the vanilla entry and other platform-specific entry functions + :rtype: list(Function) + """ + count = ctypes.c_ulonglong(0) + funcs = core.BNGetAllEntryFunctions(self.handle, count) + + assert funcs is not None, "core.BNGetAllEntryFunctions returned None" + result = [] + try: + for i in range(0, count.value): + result.append(_function.Function(self, core.BNNewFunctionReference(funcs[i]))) + return result + finally: + core.BNFreeFunctionList(funcs, count.value) + @property def symbols(self) -> SymbolMapping: """ @@ -4406,6 +4439,21 @@ def add_entry_point(self, addr: int, plat: Optional['_platform.Platform'] = None raise ValueError("Provided platform is not of type `Platform`") core.BNAddEntryPointForAnalysis(self.handle, plat.handle, addr) + def add_to_entry_functions(self, func: '_function.Function') -> None: + """ + ``add_to_entry_functions`` adds a function to the `entry_functions` list. + + :param Function func: a Function object + :rtype: None + :Example: + >>> bv.entry_functions + [, ] + >>> bv.add_to_entry_functions(bv.get_function_at(0x4014da)) + >>> bv.entry_functions + [, , ] + """ + core.BNAddToEntryFunctions(self.handle, func.handle) + def remove_function(self, func: '_function.Function', update_refs = False) -> None: """ ``remove_function`` removes the function ``func`` from the list of functions diff --git a/rust/src/binaryview.rs b/rust/src/binaryview.rs index aed2a454a..25b3fbb2f 100644 --- a/rust/src/binaryview.rs +++ b/rust/src/binaryview.rs @@ -942,6 +942,15 @@ pub trait BinaryViewExt: BinaryViewBase { } } + fn entry_point_functions(&self) -> Array { + unsafe { + let mut count = 0; + let functions = BNGetAllEntryFunctions(self.as_ref().handle, &mut count); + + Array::new(functions, count, ()) + } + } + fn functions(&self) -> Array { unsafe { let mut count = 0; diff --git a/view/elf/elfview.cpp b/view/elf/elfview.cpp index f938e978e..a01d043b6 100644 --- a/view/elf/elfview.cpp +++ b/view/elf/elfview.cpp @@ -1539,9 +1539,15 @@ bool ElfView::Init() entry += imageBaseAdjustment; Ref entryArch = entryPointArch->GetAssociatedArchitectureByAddress(entry); if (entryArch != entryPointArch) - AddFunctionForAnalysis(platform->GetRelatedPlatform(entryArch), entry); + { + auto func = AddFunctionForAnalysis(platform->GetRelatedPlatform(entryArch), entry); + AddToEntryFunctions(func); + } else - AddFunctionForAnalysis(platform, entry); + { + auto func = AddFunctionForAnalysis(platform, entry); + AddToEntryFunctions(func); + } m_logger->LogDebug("Adding function start: %#" PRIx64 "\n", entry); // name functions in .init_array, .fini_array, .ctors and .dtors diff --git a/view/pe/peview.cpp b/view/pe/peview.cpp index 897404138..bdf8175d5 100644 --- a/view/pe/peview.cpp +++ b/view/pe/peview.cpp @@ -1920,6 +1920,8 @@ bool PEView::Init() m_logger->LogInfo("Found TLS entrypoint %s: 0x%" PRIx64, name, address); Ref assPlatform = platform->GetAssociatedPlatformByAddress(address); AddPESymbol(FunctionSymbol, "", name, address - m_imageBase); + auto func = AddFunctionForAnalysis(platform, address); + AddToEntryFunctions(func); } else m_logger->LogInfo("Found TLS entrypoint %s: 0x%" PRIx64 " however it is not backed by file!", From fbf782fcd84aa77f8765589bdda491646a957044 Mon Sep 17 00:00:00 2001 From: Brandon Miller Date: Tue, 28 May 2024 11:11:34 -0700 Subject: [PATCH 20/24] Use Architecture type for BASE arch param --- python/basedetection.py | 13 +++++++++---- python/examples/raw_binary_base_detection.py | 7 ++++--- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/python/basedetection.py b/python/basedetection.py index f3cc3eb97..73970df51 100644 --- a/python/basedetection.py +++ b/python/basedetection.py @@ -26,6 +26,7 @@ from .enums import BaseAddressDetectionPOIType, BaseAddressDetectionConfidence, BaseAddressDetectionPOISetting from .binaryview import BinaryView from . import _binaryninjacore as core +from . import architecture @dataclass @@ -160,7 +161,7 @@ def aborted(self) -> bool: def detect_base_address( self, - arch: Optional[str] = "", + arch: Optional[Union['architecture.Architecture', str]] = None, analysis: Optional[Literal["basic", "controlFlow", "full"]] = "full", min_strlen: Optional[int] = 10, alignment: Optional[int] = 1024, @@ -175,7 +176,7 @@ def detect_base_address( .. note:: This operation can take a long time to complete depending on the size and complexity of the binary \ and the settings used - :param str arch: CPU architecture of the binary (defaults to using auto-detection) + :param Architecture arch: CPU architecture of the binary (defaults to using auto-detection) :param str analysis: analysis mode (``basic``, ``controlFlow``, or ``full``) :param int min_strlen: minimum length of a string to be considered a point-of-interest :param int alignment: byte boundary to align the base address to while brute-forcing @@ -187,8 +188,11 @@ def detect_base_address( :rtype: bool """ + if isinstance(arch, str): + arch = architecture.Architecture[arch] + if not arch and self._view_arch: - arch = str(self._view_arch) + arch = self._view_arch if analysis not in ["basic", "controlFlow", "full"]: raise ValueError("invalid analysis setting") @@ -202,8 +206,9 @@ def detect_base_address( if high_boundary < low_boundary: raise ValueError("upper boundary must be greater than lower boundary") + archname = arch.name if arch else "" settings = core.BNBaseAddressDetectionSettings( - arch.encode(), + archname.encode(), analysis.encode(), min_strlen, alignment, diff --git a/python/examples/raw_binary_base_detection.py b/python/examples/raw_binary_base_detection.py index b584903c6..1180b6725 100644 --- a/python/examples/raw_binary_base_detection.py +++ b/python/examples/raw_binary_base_detection.py @@ -25,7 +25,7 @@ import argparse import json from os import walk, path -from binaryninja import BaseAddressDetection, log_to_stderr, LogLevel, log_info, log_error +from binaryninja import BaseAddressDetection, log_to_stderr, LogLevel, log_info, log_error, Architecture def _get_directory_listing(_path: str) -> list[str]: @@ -48,7 +48,7 @@ def _parse_args() -> argparse.Namespace: parser.add_argument("--debug", action="store_true", help="enable debug logging") parser.add_argument("--reasons", action="store_true", help="show reasons for base address selection") parser.add_argument("--analysis", type=str, help="analysis level", default="full") - parser.add_argument("--arch", type=str, default="", help="architecture of the binary") + parser.add_argument("--arch", type=str, help="architecture of the binary") return parser.parse_args() @@ -68,7 +68,8 @@ def main() -> None: for _file in files: log_info(f"Running base address detection analysis on '{_file}'...") bad = BaseAddressDetection(_file) - if not bad.detect_base_address(analysis=args.analysis, arch=args.arch): + arch = Architecture[args.arch] if args.arch else None + if not bad.detect_base_address(analysis=args.analysis, arch=arch): log_error("Base address detection analysis failed") continue From 53eed1fedbef8c778ba1ea03d6660a7f097886ca Mon Sep 17 00:00:00 2001 From: Rubens Brandao Date: Mon, 27 May 2024 17:45:25 -0300 Subject: [PATCH 21/24] allow `load_with_option` to accept json options --- rust/src/lib.rs | 18 +++++++++++++----- rust/src/metadata.rs | 25 +++++++++++++++++++++++-- rust/src/string.rs | 12 ++++++++++++ 3 files changed, 48 insertions(+), 7 deletions(-) diff --git a/rust/src/lib.rs b/rust/src/lib.rs index de38be4db..f20a33a92 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -171,8 +171,8 @@ pub use binaryninjacore_sys::BNEndianness as Endianness; use binaryview::BinaryView; use metadata::Metadata; use metadata::MetadataType; -use rc::Ref; use string::BnStrCompatible; +use string::IntoJson; // Commented out to suppress unused warnings // const BN_MAX_INSTRUCTION_LENGTH: u64 = 256; @@ -227,24 +227,32 @@ pub fn load(filename: S) -> Option( +pub fn load_with_options( filename: S, update_analysis_and_wait: bool, - options: Option>, + options: Option, ) -> Option> { let filename = filename.into_bytes_with_nul(); let options_or_default = if let Some(opt) = options { - opt + opt.get_json_string() + .ok()? + .into_bytes_with_nul() + .as_ref() + .to_vec() } else { Metadata::new_of_type(MetadataType::KeyValueDataType) + .get_json_string() + .ok()? + .as_ref() + .to_vec() }; let handle = unsafe { binaryninjacore_sys::BNLoadFilename( filename.as_ref().as_ptr() as *mut _, update_analysis_and_wait, - options_or_default.get_json_string().unwrap().as_ref().as_ptr() as *mut _, + options_or_default.as_ptr() as *mut core::ffi::c_char, None, ) }; diff --git a/rust/src/metadata.rs b/rust/src/metadata.rs index f635700f2..965712078 100644 --- a/rust/src/metadata.rs +++ b/rust/src/metadata.rs @@ -1,5 +1,5 @@ -use crate::rc::{Array, CoreArrayProvider, Guard, CoreArrayProviderInner, Ref, RefCountable}; -use crate::string::{BnStrCompatible, BnString}; +use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable}; +use crate::string::{BnStrCompatible, BnString, IntoJson}; use binaryninjacore_sys::*; use std::collections::HashMap; use std::os::raw::c_char; @@ -702,3 +702,24 @@ impl TryFrom<&Metadata> for HashMap> { .map(|m| m.into_iter().map(|(k, v)| (k.to_string(), v)).collect()) } } + +impl IntoJson for &Metadata { + type Output = BnString; + fn get_json_string(self) -> Result { + Metadata::get_json_string(self) + } +} + +impl IntoJson for Metadata { + type Output = BnString; + fn get_json_string(self) -> Result { + Metadata::get_json_string(&self) + } +} + +impl IntoJson for Ref { + type Output = BnString; + fn get_json_string(self) -> Result { + Metadata::get_json_string(&self) + } +} diff --git a/rust/src/string.rs b/rust/src/string.rs index f4098d66e..936e30334 100644 --- a/rust/src/string.rs +++ b/rust/src/string.rs @@ -246,3 +246,15 @@ unsafe impl BnStrCompatible for &QualifiedName { self.string().into_bytes_with_nul() } } + +pub trait IntoJson { + type Output: BnStrCompatible; + fn get_json_string(self) -> Result; +} + +impl IntoJson for S { + type Output = S; + fn get_json_string(self) -> Result { + Ok(self) + } +} From fce26815b400b5d3c0b67a46085ba8c9a61c1457 Mon Sep 17 00:00:00 2001 From: Rubens Brandao Date: Mon, 27 May 2024 17:53:57 -0300 Subject: [PATCH 22/24] update `Session::load_with_option` to the new API --- rust/src/headless.rs | 9 ++++----- rust/src/lib.rs | 11 +++++------ rust/src/metadata.rs | 7 ------- 3 files changed, 9 insertions(+), 18 deletions(-) diff --git a/rust/src/headless.rs b/rust/src/headless.rs index 15754abaf..d4d8892ab 100644 --- a/rust/src/headless.rs +++ b/rust/src/headless.rs @@ -14,9 +14,8 @@ use crate::{ binaryview, - metadata::Metadata, - rc::{self, Ref}, - string::BnStrCompatible, + rc, + string::{BnStrCompatible, IntoJson}, }; use std::env; @@ -146,11 +145,11 @@ impl Session { /// let bv = headless_session.load_with_options("/bin/cat", true, Some(settings)) /// .expect("Couldn't open `/bin/cat`"); /// ``` - pub fn load_with_options( + pub fn load_with_options( &self, filename: &str, update_analysis_and_wait: bool, - options: Option>, + options: Option, ) -> Option> { crate::load_with_options(filename, update_analysis_and_wait, options) } diff --git a/rust/src/lib.rs b/rust/src/lib.rs index f20a33a92..cb511a7ab 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -197,12 +197,13 @@ const BN_INVALID_EXPR: usize = usize::MAX; /// The main way to open and load files into Binary Ninja. Make sure you've properly initialized the core before calling this function. See [`crate::headless::init()`] pub fn load(filename: S) -> Option> { let filename = filename.into_bytes_with_nul(); + let options = ""; let handle = unsafe { binaryninjacore_sys::BNLoadFilename( filename.as_ref().as_ptr() as *mut _, true, - std::ptr::null(), + options.as_ptr() as *mut core::ffi::c_char, None, ) }; @@ -216,15 +217,13 @@ pub fn load(filename: S) -> OptionYour JSON needs to be compliant with RapidJSON's format, which basically means you just need to escape double quotes (\") and single quotes won't work. +/// /// ```no_run /// use binaryninja::{metadata::Metadata, rc::Ref}; /// use std::collections::HashMap; /// -/// let settings: Ref = HashMap::from([ -/// ("analysis.linearSweep.autorun", false.into()), -/// ]).into(); -/// -/// let bv = binaryninja::load_with_options("/bin/cat", true, Some(settings)) +/// let bv = binaryninja::load_with_options("/bin/cat", true, Some("{\"analysis.linearSweep.autorun\": false}")) /// .expect("Couldn't open `/bin/cat`"); /// ``` pub fn load_with_options( diff --git a/rust/src/metadata.rs b/rust/src/metadata.rs index 965712078..9eeff8fad 100644 --- a/rust/src/metadata.rs +++ b/rust/src/metadata.rs @@ -710,13 +710,6 @@ impl IntoJson for &Metadata { } } -impl IntoJson for Metadata { - type Output = BnString; - fn get_json_string(self) -> Result { - Metadata::get_json_string(&self) - } -} - impl IntoJson for Ref { type Output = BnString; fn get_json_string(self) -> Result { From b3cc03be361d5c6253141e729b096e87e025490f Mon Sep 17 00:00:00 2001 From: Rubens Brandao Date: Mon, 27 May 2024 10:24:28 -0300 Subject: [PATCH 23/24] fix multiple memory leaks related to CStr/BnString --- rust/examples/mlil_visitor/src/main.rs | 1 + rust/src/architecture.rs | 4 ++-- rust/src/hlil/instruction.rs | 9 ++++---- rust/src/hlil/lift.rs | 8 ++++--- rust/src/hlil/operation.rs | 14 ++++++------- rust/src/mlil/instruction.rs | 9 ++++---- rust/src/mlil/lift.rs | 5 +++-- rust/src/mlil/operation.rs | 6 +++--- rust/src/symbol.rs | 20 +++++------------- rust/src/types.rs | 29 ++------------------------ 10 files changed, 36 insertions(+), 69 deletions(-) diff --git a/rust/examples/mlil_visitor/src/main.rs b/rust/examples/mlil_visitor/src/main.rs index a0cb8f0d7..752548c9f 100644 --- a/rust/examples/mlil_visitor/src/main.rs +++ b/rust/examples/mlil_visitor/src/main.rs @@ -1,5 +1,6 @@ use std::env; +use binaryninja::architecture::Intrinsic; use binaryninja::binaryview::BinaryViewExt; use binaryninja::mlil::MediumLevelILLiftedOperand; use binaryninja::mlil::{MediumLevelILFunction, MediumLevelILLiftedInstruction}; diff --git a/rust/src/architecture.rs b/rust/src/architecture.rs index 11daa71c6..cf5d32d64 100644 --- a/rust/src/architecture.rs +++ b/rust/src/architecture.rs @@ -983,8 +983,8 @@ impl FlagGroup for CoreFlagGroup { } } -#[derive(Copy, Clone, Eq, PartialEq)] -pub struct CoreIntrinsic(*mut BNArchitecture, u32); +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub struct CoreIntrinsic(pub(crate) *mut BNArchitecture, pub(crate) u32); impl Intrinsic for crate::architecture::CoreIntrinsic { fn name(&self) -> Cow { diff --git a/rust/src/hlil/instruction.rs b/rust/src/hlil/instruction.rs index 1ab9ce4d0..6b27284d4 100644 --- a/rust/src/hlil/instruction.rs +++ b/rust/src/hlil/instruction.rs @@ -1,11 +1,10 @@ use binaryninjacore_sys::BNGetHighLevelILByIndex; use binaryninjacore_sys::BNHighLevelILOperation; +use crate::architecture::CoreIntrinsic; use crate::operand_iter::OperandIter; use crate::rc::Ref; -use crate::types::{ - ConstantData, ILIntrinsic, RegisterValue, RegisterValueType, SSAVariable, Variable, -}; +use crate::types::{ConstantData, RegisterValue, RegisterValueType, SSAVariable, Variable}; use super::operation::*; use super::{HighLevelILFunction, HighLevelILLiftedInstruction, HighLevelILLiftedInstructionKind}; @@ -812,11 +811,11 @@ impl HighLevelILInstruction { cond_false: self.lift_operand(op.cond_false), }), Intrinsic(op) => Lifted::Intrinsic(LiftedIntrinsic { - intrinsic: ILIntrinsic::new(self.function.get_function().arch(), op.intrinsic), + intrinsic: CoreIntrinsic(self.function.get_function().arch().0, op.intrinsic), params: self.lift_instruction_list(op.first_param, op.num_params), }), IntrinsicSsa(op) => Lifted::IntrinsicSsa(LiftedIntrinsicSsa { - intrinsic: ILIntrinsic::new(self.function.get_function().arch(), op.intrinsic), + intrinsic: CoreIntrinsic(self.function.get_function().arch().0, op.intrinsic), params: self.lift_instruction_list(op.first_param, op.num_params), dest_memory: op.dest_memory, src_memory: op.src_memory, diff --git a/rust/src/hlil/lift.rs b/rust/src/hlil/lift.rs index 731a785c1..300a7f1fb 100644 --- a/rust/src/hlil/lift.rs +++ b/rust/src/hlil/lift.rs @@ -1,7 +1,9 @@ -use super::{operation::*, HighLevelILFunction}; +use super::operation::*; +use super::HighLevelILFunction; +use crate::architecture::CoreIntrinsic; use crate::rc::Ref; -use crate::types::{ConstantData, ILIntrinsic, SSAVariable, Variable}; +use crate::types::{ConstantData, SSAVariable, Variable}; #[derive(Clone)] pub enum HighLevelILLiftedOperand { @@ -11,7 +13,7 @@ pub enum HighLevelILLiftedOperand { Float(f64), Int(u64), IntList(Vec), - Intrinsic(ILIntrinsic), + Intrinsic(CoreIntrinsic), Label(GotoLabel), MemberIndex(Option), Var(Variable), diff --git a/rust/src/hlil/operation.rs b/rust/src/hlil/operation.rs index ee0d437b5..e762a74bc 100644 --- a/rust/src/hlil/operation.rs +++ b/rust/src/hlil/operation.rs @@ -1,8 +1,10 @@ use binaryninjacore_sys::BNGetGotoLabelName; +use crate::architecture::CoreIntrinsic; use crate::function::Function; use crate::rc::Ref; -use crate::types::{ConstantData, ILIntrinsic, SSAVariable, Variable}; +use crate::string::BnString; +use crate::types::{ConstantData, SSAVariable, Variable}; use super::HighLevelILLiftedInstruction; @@ -13,10 +15,8 @@ pub struct GotoLabel { } impl GotoLabel { - pub fn name(&self) -> &str { - let raw_str = unsafe { BNGetGotoLabelName(self.function.handle, self.target) }; - let c_str = unsafe { core::ffi::CStr::from_ptr(raw_str) }; - c_str.to_str().unwrap() + pub fn name(&self) -> BnString { + unsafe { BnString::from_raw(BNGetGotoLabelName(self.function.handle, self.target)) } } } @@ -320,7 +320,7 @@ pub struct Intrinsic { } #[derive(Clone, Debug, PartialEq)] pub struct LiftedIntrinsic { - pub intrinsic: ILIntrinsic, + pub intrinsic: CoreIntrinsic, pub params: Vec, } @@ -335,7 +335,7 @@ pub struct IntrinsicSsa { } #[derive(Clone, Debug, PartialEq)] pub struct LiftedIntrinsicSsa { - pub intrinsic: ILIntrinsic, + pub intrinsic: CoreIntrinsic, pub params: Vec, pub dest_memory: u64, pub src_memory: u64, diff --git a/rust/src/mlil/instruction.rs b/rust/src/mlil/instruction.rs index cb7628853..00c97cb22 100644 --- a/rust/src/mlil/instruction.rs +++ b/rust/src/mlil/instruction.rs @@ -3,11 +3,10 @@ use binaryninjacore_sys::BNGetMediumLevelILByIndex; use binaryninjacore_sys::BNMediumLevelILInstruction; use binaryninjacore_sys::BNMediumLevelILOperation; +use crate::architecture::CoreIntrinsic; use crate::operand_iter::OperandIter; use crate::rc::Ref; -use crate::types::{ - ConstantData, ILIntrinsic, RegisterValue, RegisterValueType, SSAVariable, Variable, -}; +use crate::types::{ConstantData, RegisterValue, RegisterValueType, SSAVariable, Variable}; use super::lift::*; use super::operation::*; @@ -906,7 +905,7 @@ impl MediumLevelILInstruction { output: OperandIter::new(&*self.function, op.first_output, op.num_outputs) .vars() .collect(), - intrinsic: ILIntrinsic::new(self.function.get_function().arch(), op.intrinsic), + intrinsic: CoreIntrinsic(self.function.get_function().arch().0, op.intrinsic), params: OperandIter::new(&*self.function, op.first_param, op.num_params) .exprs() .map(|expr| expr.lift()) @@ -925,7 +924,7 @@ impl MediumLevelILInstruction { output: OperandIter::new(&*self.function, op.first_output, op.num_outputs) .ssa_vars() .collect(), - intrinsic: ILIntrinsic::new(self.function.get_function().arch(), op.intrinsic), + intrinsic: CoreIntrinsic(self.function.get_function().arch().0, op.intrinsic), params: OperandIter::new(&*self.function, op.first_param, op.num_params) .exprs() .map(|expr| expr.lift()) diff --git a/rust/src/mlil/lift.rs b/rust/src/mlil/lift.rs index e8548b064..7a5e159ba 100644 --- a/rust/src/mlil/lift.rs +++ b/rust/src/mlil/lift.rs @@ -1,7 +1,8 @@ use std::collections::BTreeMap; +use crate::architecture::CoreIntrinsic; use crate::rc::Ref; -use crate::types::{ConstantData, ILIntrinsic, SSAVariable, Variable}; +use crate::types::{ConstantData, SSAVariable, Variable}; use super::operation::*; use super::MediumLevelILFunction; @@ -9,7 +10,7 @@ use super::MediumLevelILFunction; #[derive(Clone)] pub enum MediumLevelILLiftedOperand { ConstantData(ConstantData), - Intrinsic(ILIntrinsic), + Intrinsic(CoreIntrinsic), Expr(MediumLevelILLiftedInstruction), ExprList(Vec), Float(f64), diff --git a/rust/src/mlil/operation.rs b/rust/src/mlil/operation.rs index 822688a61..278d1edb7 100644 --- a/rust/src/mlil/operation.rs +++ b/rust/src/mlil/operation.rs @@ -1,6 +1,6 @@ use std::collections::BTreeMap; -use crate::types::{ConstantData, ILIntrinsic, SSAVariable, Variable}; +use crate::{architecture::CoreIntrinsic, types::{ConstantData, SSAVariable, Variable}}; use super::MediumLevelILLiftedInstruction; @@ -355,7 +355,7 @@ pub struct Intrinsic { #[derive(Clone, Debug, PartialEq)] pub struct LiftedIntrinsic { pub output: Vec, - pub intrinsic: ILIntrinsic, + pub intrinsic: CoreIntrinsic, pub params: Vec, } @@ -371,7 +371,7 @@ pub struct IntrinsicSsa { #[derive(Clone, Debug, PartialEq)] pub struct LiftedIntrinsicSsa { pub output: Vec, - pub intrinsic: ILIntrinsic, + pub intrinsic: CoreIntrinsic, pub params: Vec, } diff --git a/rust/src/symbol.rs b/rust/src/symbol.rs index f022fd3f9..d7a73a0ae 100644 --- a/rust/src/symbol.rs +++ b/rust/src/symbol.rs @@ -14,7 +14,6 @@ //! Interfaces for the various kinds of symbols in a binary. -use std::ffi::CStr; use std::fmt; use std::hash::{Hash, Hasher}; use std::ptr; @@ -249,24 +248,15 @@ impl Symbol { } pub fn full_name(&self) -> BnString { - unsafe { - let name = BNGetSymbolFullName(self.handle); - BnString::from_raw(name) - } + unsafe { BnString::from_raw(BNGetSymbolFullName(self.handle)) } } - pub fn short_name(&self) -> &str { - unsafe { - let name = BNGetSymbolShortName(self.handle); - CStr::from_ptr(name).to_str().unwrap() - } + pub fn short_name(&self) -> BnString { + unsafe { BnString::from_raw(BNGetSymbolShortName(self.handle)) } } - pub fn raw_name(&self) -> &str { - unsafe { - let name = BNGetSymbolRawName(self.handle); - CStr::from_ptr(name).to_str().unwrap() - } + pub fn raw_name(&self) -> BnString { + unsafe { BnString::from_raw(BNGetSymbolRawName(self.handle)) } } pub fn address(&self) -> u64 { diff --git a/rust/src/types.rs b/rust/src/types.rs index 1a5918012..7adc449e3 100644 --- a/rust/src/types.rs +++ b/rust/src/types.rs @@ -1194,11 +1194,9 @@ impl Type { } } - pub fn generate_auto_demangled_type_id<'a, S: BnStrCompatible>(name: S) -> &'a str { + pub fn generate_auto_demangled_type_id(name: S) -> BnString { let mut name = QualifiedName::from(name); - unsafe { CStr::from_ptr(BNGenerateAutoDemangledTypeId(&mut name.0)) } - .to_str() - .unwrap() + unsafe { BnString::from_raw(BNGenerateAutoDemangledTypeId(&mut name.0)) } } } @@ -2717,29 +2715,6 @@ impl DataVariableAndName { } } -///////////////////////// -// ILIntrinsic - -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub struct ILIntrinsic { - arch: CoreArchitecture, - index: u32, -} - -impl ILIntrinsic { - pub(crate) fn new(arch: CoreArchitecture, index: u32) -> Self { - Self { arch, index } - } - - pub fn name(&self) -> &str { - let name_ptr = unsafe { BNGetArchitectureIntrinsicName(self.arch.0, self.index) }; - let name_raw = unsafe { core::ffi::CStr::from_ptr(name_ptr) }; - name_raw.to_str().unwrap() - } - - // TODO impl inputs and outputs function -} - ///////////////////////// // RegisterValueType From 2c69eb22efddebde1027ba6cc62b446b052a35b6 Mon Sep 17 00:00:00 2001 From: Rubens Brandao Date: Mon, 27 May 2024 15:12:21 -0300 Subject: [PATCH 24/24] Include CoreAPI documentation into the generate rust bindings --- rust/binaryninjacore-sys/build.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rust/binaryninjacore-sys/build.rs b/rust/binaryninjacore-sys/build.rs index 243178d58..9764662e7 100644 --- a/rust/binaryninjacore-sys/build.rs +++ b/rust/binaryninjacore-sys/build.rs @@ -97,6 +97,8 @@ fn main() { .size_t_is_usize(true) .generate_comments(false) .derive_default(true) + .generate_comments(true) + .clang_arg("-fparse-all-comments") .allowlist_function("BN.*") .allowlist_var("BN_CURRENT_CORE_ABI_VERSION") .allowlist_var("BN_MINIMUM_CORE_ABI_VERSION")