Source code for binaryninja.firmwareninja

# coding=utf-8
# Copyright (c) 2015-2024 Vector 35 Inc
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
# deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.

import ctypes
from dataclasses import dataclass
from typing import Callable, Union, Optional
from .binaryview import BinaryView, Section, DataVariable
from .variable import RegisterValue
from .enums import (
    FirmwareNinjaMemoryHeuristic,
    FirmwareNinjaMemoryAccessType,
    FirmwareNinjaSectionAnalysisMode,
    FirmwareNinjaSectionType,
)
from .function import Function
from .project import ProjectFile
from . import _binaryninjacore as core


[docs] class FirmwareNinjaRelationship: """ ``class FirmwareNinjaRelationship`` is a class for representing inter-binary and cross-binary relationships. This class is only available in the Ultimate Edition of Binary Ninja. """ def __init__(self, view: BinaryView, handle=None) -> None: if handle is None: self.handle = core.BNCreateFirmwareNinjaRelationship(view.handle) else: self.handle = handle self._view = view def __del__(self): if core is not None: core.BNFreeFirmwareNinjaRelationship(self.handle) @property def _primary_data_variable(self) -> DataVariable: bn_data_var = core.BNDataVariable() if not core.BNFirmwareNinjaRelationshipGetPrimaryDataVariable(self.handle, ctypes.byref(bn_data_var)): return None result = None try: result = DataVariable.from_core_struct(bn_data_var, self._view) finally: core.BNFreeDataVariable(ctypes.byref(bn_data_var)) return result @property def _primary_function(self) -> Function: bn_function = core.BNFirmwareNinjaRelationshipGetPrimaryFunction(self.handle) if not bn_function: return None return Function(handle=bn_function) @property def _primary_address(self) -> int: result = ctypes.c_uint64() if not core.BNFirmwareNinjaRelationshipGetPrimaryAddress(self.handle, ctypes.byref(result)): return None return result.value @property def primary(self) -> Union[DataVariable, Function, int]: """ ``primary`` returns the primary function, data variable, or address of the relationship :return: Primary object of the relationship :rtype: Union[DataVariable, Function, int] """ if core.BNFirmwareNinjaRelationshipPrimaryIsDataVariable(self.handle): return self._primary_data_variable elif core.BNFirmwareNinjaRelationshipPrimaryIsFunction(self.handle): return self._primary_function elif core.BNFirmwareNinjaRelationshipPrimaryIsAddress(self.handle): return self._primary_address else: return None @primary.setter def primary(self, obj: Union[DataVariable, Function, int]) -> None: if isinstance(obj, DataVariable): core.BNFirmwareNinjaRelationshipSetPrimaryDataVariable(self.handle, obj.address) elif isinstance(obj, Function): core.BNFirmwareNinjaRelationshipSetPrimaryFunction(self.handle, obj.handle) elif isinstance(obj, int): core.BNFirmwareNinjaRelationshipSetPrimaryAddress(self.handle, obj) else: raise ValueError("Primary object must be a DataVariable, Function, or integer address") @property def _secondary_data_variable(self) -> DataVariable: bn_data_var = core.BNDataVariable() if not core.BNFirmwareNinjaRelationshipGetSecondaryDataVariable(self.handle, ctypes.byref(bn_data_var)): return None result = None try: result = DataVariable.from_core_struct(bn_data_var, self._view) finally: core.BNFreeDataVariable(ctypes.byref(bn_data_var)) return result @property def _secondary_function(self) -> Function: bn_function = core.BNFirmwareNinjaRelationshipGetSecondaryFunction(self.handle) if not bn_function: return None return Function(handle=bn_function) @property def _secondary_address(self) -> int: result = ctypes.c_uint64() if not core.BNFirmwareNinjaRelationshipGetSecondaryAddress(self.handle, ctypes.byref(result)): return None return result.value @property def _secondary_external_symbol(self) -> str: return core.BNFirmwareNinjaRelationshipGetSecondaryExternalSymbol(self.handle) @property def _secondary_external_project_file(self) -> ProjectFile: bn_project_file = core.BNFirmwareNinjaRelationshipGetSecondaryExternalProjectFile(self.handle) if not bn_project_file: return None return ProjectFile(bn_project_file) @property def secondary(self) -> Union[DataVariable, Function, int, tuple[int, ProjectFile], tuple[str, ProjectFile]]: """ ``secondary`` returns the secondary function, data variable, address, external address, or external symbol of the relationship :return: Secondary object of the relationship :rtype: Union[DataVariable, Function, int, tuple[int, ProjectFile], tuple[str, ProjectFile]] """ if core.BNFirmwareNinjaRelationshipSecondaryIsDataVariable(self.handle): return self._secondary_data_variable elif core.BNFirmwareNinjaRelationshipSecondaryIsFunction(self.handle): return self._secondary_function elif core.BNFirmwareNinjaRelationshipSecondaryIsAddress(self.handle): return self._secondary_address elif core.BNFirmwareNinjaRelationshipSecondaryIsExternalAddress(self.handle): return self._secondary_address, self._secondary_external_project_file elif core.BNFirmwareNinjaRelationshipSecondaryIsExternalSymbol(self.handle): return self._secondary_external_symbol, self._secondary_external_project_file else: return None @secondary.setter def secondary( self, obj: Union[DataVariable, Function, int, tuple[int, ProjectFile], tuple[str, ProjectFile]]) -> None: if isinstance(obj, tuple): if len(obj) != 2: raise ValueError("External object must be a tuple of (address, ProjectFile) or (symbol, ProjectFile)") if not isinstance(obj[0], int) and not isinstance(obj[0], str): raise ValueError("External object must be a tuple of (address, ProjectFile) or (symbol, ProjectFile)") if not isinstance(obj[1], ProjectFile): raise ValueError("External object must be a tuple of (address, ProjectFile) or (symbol, ProjectFile)") if isinstance(obj, DataVariable): core.BNFirmwareNinjaRelationshipSetSecondaryDataVariable(self.handle, obj.address) elif isinstance(obj, Function): core.BNFirmwareNinjaRelationshipSetSecondaryFunction(self.handle, obj.handle) elif isinstance(obj, int): core.BNFirmwareNinjaRelationshipSetSecondaryAddress(self.handle, obj) elif isinstance(obj, tuple): if isinstance(obj[0], int): core.BNFirmwareNinjaRelationshipSetSecondaryExternalAddress(self.handle, obj[1]._handle, obj[0]) elif isinstance(obj[0], str): core.BNFirmwareNinjaRelationshipSetSecondaryExternalSymbol(self.handle, obj[1]._handle, obj[0]) else: raise ValueError("Invalid secondary object type") @property def description(self) -> str: """ ``description`` returns the description of the relationship :return: Description of the relationship :rtype: str """ return core.BNFirmwareNinjaRelationshipGetDescription(self.handle) @description.setter def description(self, description: str) -> None: core.BNFirmwareNinjaRelationshipSetDescription(self.handle, description) @property def provenance(self) -> str: """ ``provenance`` returns the provenance of the relationship :return: Provenance of the relationship :rtype: str """ return core.BNFirmwareNinjaRelationshipGetProvenance(self.handle) @provenance.setter def provenance(self, provenance: str) -> None: core.BNFirmwareNinjaRelationshipSetProvenance(self.handle, provenance) @property def guid(self) -> str: """ ``guid`` returns the GUID of the relationship :return: GUID of the relationship :rtype: str """ return core.BNFirmwareNinjaRelationshipGetGuid(self.handle)
[docs] class FirmwareNinjaReferenceNode: """ ``class FirmwareNinjaReferenceNode`` is a class for building reference trees for functions, data variables, and memory regions. This class is only available in the Ultimate Edition of Binary Ninja. """ def __init__(self, handle=None, view=None): assert handle is not None, "Cannot create reference node directly, run `FirmwareNinja.get_reference_tree`" self._handle = handle self._view = view def __del__(self): if core is not None: core.BNFreeFirmwareNinjaReferenceNode(self._handle) @property def _function(self) -> Function: bn_function = core.BNFirmwareNinjaReferenceNodeGetFunction(self._handle) if not bn_function: return None return Function(handle=bn_function) @property def _data_variable(self) -> DataVariable: bn_data_var = core.BNDataVariable() if not core.BNFirmwareNinjaReferenceNodeGetDataVariable(self._handle, ctypes.byref(bn_data_var)): return None result = None try: result = DataVariable.from_core_struct(bn_data_var, self._view) finally: core.BNFreeDataVariable(ctypes.byref(bn_data_var)) return result; @property def object(self) -> Union[Function, DataVariable]: """ ``object`` returns the function or data variable contained in the reference tree node, or None if the object is a root node and only contains children :return: Object contained in the reference tree node :rtype: Union[Function, DataVariable] """ if core.BNFirmwareNinjaReferenceNodeIsFunction(self._handle): return self._function elif core.BNFirmwareNinjaReferenceNodeIsDataVariable(self._handle): return self._data_variable else: return None @property def children(self) -> list["FirmwareNinjaReferenceNode"]: """ ``children`` returns the child nodes contained in the reference tree node :return: Child nodes contained in the reference tree node :rtype: list[FirmwareNinjaReferenceNode] """ if not core.BNFirmwareNinjaReferenceNodeHasChildren(self._handle): return [] count = ctypes.c_ulonglong(0) nodes = [] try: bn_nodes = core.BNFirmwareNinjaReferenceNodeGetChildren(self._handle, count) for i in range(count.value): nodes.append( FirmwareNinjaReferenceNode(core.BNNewFirmwareNinjaReferenceNodeReference(bn_nodes[i]), self._view) ) finally: core.BNFreeFirmwareNinjaReferenceNodes(bn_nodes, count.value) return nodes
[docs] @dataclass class FirmwareNinjaDevice: """ ``class FirmwareNinjaDevice`` is a class that stores information about a hardware device, including the device name, start address, size, and information about the device. This class is only available in the Ultimate Edition of Binary Ninja. """ name: str start: int size: int info: str
[docs] @dataclass class FirmwareNinjaSection: """ ``class FirmwareNinjaSection`` is a class that stores information about a section identified with Firmware Ninja analysis, including the section type, start address, size, and entropy. This class is only available in the Ultimate Edition of Binary Ninja. """ type: FirmwareNinjaSectionType start: int size: int entropy: float
[docs] @dataclass class FirmwareNinjaMemoryAccess: """ ``class FirmwareNinjaMemoryAccess`` is a class that stores information on instructions that access regions of memory that are not file-backed, such as memory-mapped I/O and RAM. This class is only available in the Ultimate Edition of Binary Ninja. """ instr_address: int mem_address: RegisterValue heuristic: FirmwareNinjaMemoryHeuristic type: FirmwareNinjaMemoryAccessType value: RegisterValue
[docs] @classmethod def from_BNFirmwareNinjaMemoryAccess(cls, access: core.BNFirmwareNinjaMemoryAccess) -> "FirmwareNinjaMemoryAccess": return cls( instr_address=access.instrAddress, mem_address=RegisterValue.from_BNRegisterValue(access.memAddress), heuristic=FirmwareNinjaMemoryHeuristic(access.heuristic), type=FirmwareNinjaMemoryAccessType(access.type), value=RegisterValue.from_BNRegisterValue(access.value), )
[docs] @classmethod def to_BNFirmwareNinjaMemoryAccess(cls, access: "FirmwareNinjaMemoryAccess") -> core.BNFirmwareNinjaMemoryAccess: return core.BNFirmwareNinjaMemoryAccess( instrAddress=access.instr_address, memAddress=RegisterValue.to_BNRegisterValue(access.mem_address), heuristic=access.heuristic, type=access.type, value=RegisterValue.to_BNRegisterValue(access.value), )
[docs] @dataclass class FirmwareNinjaFunctionMemoryAccesses: """ ``class FirmwareNinjaFunctionMemoryAccesses`` is a class that stores information on accesses made by a function to memory regions that are not file-backed, such as memory-mapped I/O and RAM. This class is only available in the Ultimate Edition of Binary Ninja. """ function: Function accesses: list[FirmwareNinjaMemoryAccess]
[docs] @classmethod def from_BNFirmwareNinjaFunctionMemoryAccesses( cls, info: core.BNFirmwareNinjaFunctionMemoryAccesses, view: BinaryView, ) -> "FirmwareNinjaFunctionMemoryAccesses": accesses = [] for i in range(info.count): access = info.accesses[i] accesses.append(FirmwareNinjaMemoryAccess.from_BNFirmwareNinjaMemoryAccess(access.contents)) return cls( function=view.get_function_at(info.start), accesses=accesses, )
[docs] @dataclass class FirmwareNinjaDeviceAccesses: """ ``class FirmwareNinjaDeviceAccesses`` is a class that stores information on the number of accesses to hardware devices for each board that is compatible with the current architecture. This information can be used to identify a board based on the number of accesses to hardware devices. This class is only available in the Ultimate Edition of Binary Ninja. """ board_name: str total: int unique: int
[docs] class FirmwareNinja: """ ``class FirmwareNinja`` is a class that aids in analysis of firmware binaries. This class is only available in the Ultimate Edition of Binary Ninja. :Example: >>> from binaryninja import * >>> view = load("path/to/firmware.bin", options={"loader.imageBase": 0x100000}) >>> fwn = FirmwareNinja(view) >>> fwn.get_function_memory_accesses()[0].accesses[0].mem_address <const ptr 0x40090028> """ def __init__(self, view: BinaryView) -> None: self._view = view self._handle = core.BNCreateFirmwareNinja(view.handle) def __del__(self): if core is not None: core.BNFreeFirmwareNinja(self._handle)
[docs] def store_custom_device(self, name: str, start: int, size: int, info: str) -> bool: """ ``store_custom_device`` stores a user-defined Firmware Ninja device in the binary view metadata :param str name: Name of the device :param int start: Start address of the device :param int size: Size of the device memory region :param str info: Information about the device :return: True on success, False on failure :rtype: bool """ return core.BNFirmwareNinjaStoreCustomDevice(self._handle, name, start, start + size, info)
[docs] def remove_custom_device(self, name: str) -> bool: """ ``remove_custom_device`` removes a user-defined Firmware Ninja device from the binary view metadata by device name :param str name: Name of the device :return: True on success, False on failure :rtype: bool """ return core.BNFirmwareNinjaRemoveCustomDevice(self._handle, name)
@property def user_devices(self) -> list[FirmwareNinjaDevice]: """ ``user_devices`` queries user-defined Firmware Ninja devices from the binary view metadata :return: List of Firmware Ninja devices :rtype: list[FirmwareNinjaDevice] """ devices = ctypes.POINTER(core.BNFirmwareNinjaDevice)() count = core.BNFirmwareNinjaQueryCustomDevices(self._handle, ctypes.byref(devices)) if count == -1: raise RuntimeError("BNFirmwareNinjaQueryCustomDevices") try: device_list = [] for i in range(count): device_list.append( FirmwareNinjaDevice( name=devices[i].name, start=devices[i].start, size=devices[i].end - devices[i].start, info=devices[i].info, ) ) return device_list finally: core.BNFirmwareNinjaFreeDevices(devices, count) @property def boards(self) -> list[str]: """ ``boards`` queries the name of all boards that are compatible with the current architecture :return: List of board names :rtype: list[str] """ boards = ctypes.POINTER(ctypes.c_char_p)() count = core.BNFirmwareNinjaQueryBoardNamesForArchitecture( self._handle, self._view.arch.handle, ctypes.byref(boards) ) if count == -1: raise RuntimeError("BNFirmwareNinjaQueryBoardNamesForArchitecture") try: board_list = [] for i in range(count): board_list.append(boards[i].decode("utf-8")) return board_list finally: core.BNFirmwareNinjaFreeBoardNames(boards, count)
[docs] def get_devices_for_board(self, name: str) -> list[FirmwareNinjaDevice]: """ ``get_devices_for_board`` queries the hardware device information for a specific board :Example: >>> fwn = FirmwareNinja(bv) >>> fwn.get_devices_for_board(fwn.boards[0])[0] FirmwareNinjaDevice(name='nand@12f', start=303, size=1024, info='marvell,orion-nand') :param str name: Name of the board :return: List of Firmware Ninja devices :rtype: list[FirmwareNinjaDevice] """ devices = ctypes.POINTER(core.BNFirmwareNinjaDevice)() count = core.BNFirmwareNinjaQueryBoardDevices(self._handle, self._view.arch.handle, name, ctypes.byref(devices)) if count == -1: raise RuntimeError("BNFirmwareNinjaQueryBoardDevices") try: device_list = [] for i in range(count): device_list.append( FirmwareNinjaDevice( name=devices[i].name, start=devices[i].start, size=devices[i].end - devices[i].start, info=devices[i].info, ) ) return device_list finally: core.BNFirmwareNinjaFreeDevices(devices, count)
[docs] def get_sections_from_entropy( self, high_code_entropy_threshold: float = 0.910, low_code_entropy_threshold: float = 0.500, block_size: int = 4096, mode: FirmwareNinjaSectionAnalysisMode = FirmwareNinjaSectionAnalysisMode.DetectStringsSectionAnalysisMode, ) -> list[FirmwareNinjaSection]: """ ``get_sections_from_entropy`` uses entropy analysis and heuristics to identify code, data, padding, and compressed sections in the file-backed regions of the binary view :Example: >>> fwn = FirmwareNinja(bv) >>> fwn.get_sections_from_entropy(block_size=2048)[0].entropy 0.48716872930526733 >>> fwn.get_sections_from_entropy(block_size=2048)[0].type <FirmwareNinjaSectionType.DataSectionType: 1> :param float high_code_entropy_threshold: High code entropy threshold :param float low_code_entropy_threshold: Low code entropy threshold :param int block_size: Block size :param FirmwareNinjaSectionAnalysisMode mode: Analysis mode :return: List of sections :rtype: list[FirmwareNinjaSection] """ sections = ctypes.POINTER(core.BNFirmwareNinjaSection)() count = core.BNFirmwareNinjaFindSectionsWithEntropy( self._handle, ctypes.byref(sections), high_code_entropy_threshold, low_code_entropy_threshold, block_size, mode, ) if count == -1: raise RuntimeError("BNFirmwareNinjaFindSectionsWithEntropy") try: section_list = [] for i in range(count): section_list.append( FirmwareNinjaSection( type=FirmwareNinjaSectionType(sections[i].type), start=sections[i].start, size=sections[i].end - sections[i].start, entropy=sections[i].entropy, ) ) return section_list finally: core.BNFirmwareNinjaFreeSections(sections, count)
[docs] def get_function_memory_accesses(self, progress_func: Callable = None) -> list[FirmwareNinjaFunctionMemoryAccesses]: """ ``get_function_memory_accesses`` runs analysis to find accesses to memory regions that are not file-backed, such as memory-mapped I/O and RAM :param callback progress_func: optional function to be called with the current progress and total count. :return: List of function memory accesses :rtype: list[FirmwareNinjaFunctionMemoryAccesses] """ fma_info = ctypes.POINTER((ctypes.POINTER(core.BNFirmwareNinjaFunctionMemoryAccesses)))() if progress_func is None: progress_cfunc = ctypes.CFUNCTYPE(ctypes.c_bool, ctypes.c_void_p, ctypes.c_ulonglong, ctypes.c_ulonglong)( lambda ctxt, cur, total: True ) else: progress_cfunc = ctypes.CFUNCTYPE(ctypes.c_bool, ctypes.c_void_p, ctypes.c_ulonglong, ctypes.c_ulonglong)( lambda ctxt, cur, total: progress_func(cur, total) ) count = core.BNFirmwareNinjaGetFunctionMemoryAccesses( self._handle, ctypes.byref(fma_info), progress_cfunc, None ) if count == -1: raise RuntimeError("BNFirmwareNinjaGetFunctionMemoryAccesses") try: fma_info_list = [] for i in range(count): fma_info_list.append( FirmwareNinjaFunctionMemoryAccesses.from_BNFirmwareNinjaFunctionMemoryAccesses( fma_info[i].contents, self._view ) ) return fma_info_list finally: core.BNFirmwareNinjaFreeFunctionMemoryAccesses(fma_info, count)
def _fma_info_list_to_array(self, fma: list[FirmwareNinjaFunctionMemoryAccesses]) -> ctypes.POINTER: fma_info_ptr_array = (ctypes.POINTER(core.BNFirmwareNinjaFunctionMemoryAccesses) * len(fma))() for i, info in enumerate(fma): accesses_ptr_array = (ctypes.POINTER(core.BNFirmwareNinjaMemoryAccess) * len(info.accesses))() for j, access in enumerate(info.accesses): accesses_ptr_array[j] = ctypes.pointer(FirmwareNinjaMemoryAccess.to_BNFirmwareNinjaMemoryAccess(access)) fma_info_struct = core.BNFirmwareNinjaFunctionMemoryAccesses( start=info.function.start, count=len(info.accesses), accesses=accesses_ptr_array, ) fma_info_ptr_array[i] = ctypes.pointer(fma_info_struct) return fma_info_ptr_array
[docs] def store_function_memory_accesses(self, fma: list[FirmwareNinjaFunctionMemoryAccesses]) -> None: """ ``store_function_memory_accesses`` saves information on function memory accesses to binary view metadata :Example: >>> fwn = FirmwareNinja(bv) >>> fma = fwn.get_function_memory_accesses() >>> fwn.store_function_memory_accesses(fma) :param list[FirmwareNinjaFunctionMemoryAccesses] fma: List of function memory accesses """ fma_info_ptr_array = self._fma_info_list_to_array(fma) core.BNFirmwareNinjaStoreFunctionMemoryAccessesToMetadata(self._handle, fma_info_ptr_array, len(fma))
[docs] def query_function_memory_accesses(self) -> list[FirmwareNinjaFunctionMemoryAccesses]: """ ``query_function_memory_accesses`` queries information on function memory accesses from binary view metadata :return: List of function memory accesses :rtype: list[FirmwareNinjaFunctionMemoryAccesses] """ fma = ctypes.POINTER((ctypes.POINTER(core.BNFirmwareNinjaFunctionMemoryAccesses)))() count = core.BNFirmwareNinjaQueryFunctionMemoryAccessesFromMetadata(self._handle, ctypes.byref(fma)) if count == -1: return None try: fma_info_list = [] for i in range(count): fma_info_list.append( FirmwareNinjaFunctionMemoryAccesses.from_BNFirmwareNinjaFunctionMemoryAccesses( fma[i].contents, self._view ) ) return fma_info_list finally: core.BNFirmwareNinjaFreeFunctionMemoryAccesses(fma, count)
[docs] def get_board_device_accesses( self, fma: list[FirmwareNinjaFunctionMemoryAccesses] ) -> list[FirmwareNinjaDeviceAccesses]: """ ``get_board_device_accesses`` counts accesses made to memory-mapped hardware devices for each board that is compatible with the current architecture. This function can be used to help identify a board. :Example: >>> fwn = FirmwareNinja(bv) >>> fma = fwn.get_function_memory_accesses() >>> fwn.get_board_device_accesses(fma)[0] FirmwareNinjaDeviceAccesses(board_name='stm32mp157c-dhcom-picoitx', total=414, unique=2) :param list[FirmwareNinjaFunctionMemoryAccesses] fma: List of function memory accesses :return: List of device accesses :rtype: list[FirmwareNinjaDeviceAccesses] """ fma_info_ptr_array = self._fma_info_list_to_array(fma) device_accesses = ctypes.POINTER(core.BNFirmwareNinjaDeviceAccesses)() count = core.BNFirmwareNinjaGetBoardDeviceAccesses( self._handle, fma_info_ptr_array, len(fma), ctypes.byref(device_accesses), self._view.arch.handle ) if count == -1: raise RuntimeError("BNFirmwareNinjaGetBoardDeviceAccesses") try: device_accesses_list = [] for i in range(count): device_accesses_list.append( FirmwareNinjaDeviceAccesses( board_name=device_accesses[i].name, total=device_accesses[i].total, unique=device_accesses[i].unique, ) ) return device_accesses_list finally: core.BNFirmwareNinjaFreeBoardDeviceAccesses(device_accesses, count)
[docs] def get_reference_tree( self, location: Union[Section, FirmwareNinjaDevice, Function, DataVariable, int], fma: list[FirmwareNinjaFunctionMemoryAccesses], value: Optional[int] = None, ) -> FirmwareNinjaReferenceNode: """ ``get_reference_tree`` returns a tree of reference nodes for a memory region, function, or address :param Union[Section, FirmwareNinjaDevice, DataVariable, Function, int] location: Memory location to build the reference tree for :param list[FirmwareNinjaFunctionMemoryAccesses] fma: List of function memory accesses or None to use cross references. None should only be supplied if location is a Function, DataVariable, or address. :param Optional[int] value: Only include the node in the tree if this value is written to the location :return: Root reference node containing the reference tree :rtype: FirmwareNinjaReferenceNode """ if fma is None and (isinstance(location, Section) or isinstance(location, FirmwareNinjaDevice)): raise ValueError("Function memory accesses cannot be None for location type Section or FirmwareNinjaDevice") value = ctypes.pointer(ctypes.c_uint64(value)) if value is not None else None fma_info_ptr_array = None if fma is not None and len(fma) > 0: fma_info_ptr_array = self._fma_info_list_to_array(fma) if isinstance(location, FirmwareNinjaDevice): bn_node = core.BNFirmwareNinjaGetMemoryRegionReferenceTree( self._handle, location.start, location.start + location.size, fma_info_ptr_array, len(fma), value ) elif isinstance(location, Function): bn_node = core.BNFirmwareNinjaGetAddressReferenceTree( self._handle, location.start, fma_info_ptr_array, len(fma), value ) elif isinstance(location, Section): bn_node = core.BNFirmwareNinjaGetMemoryRegionReferenceTree( self._handle, location.start, location.start + location.length, fma_info_ptr_array, len(fma), value ) elif isinstance(location, DataVariable): bn_node = core.BNFirmwareNinjaGetAddressReferenceTree( self._handle, location.address, fma_info_ptr_array, len(fma), value ) elif isinstance(location, int): bn_node = core.BNFirmwareNinjaGetAddressReferenceTree( self._handle, location, fma_info_ptr_array, len(fma), value ) else: raise ValueError("Invalid location type") if not bn_node: return None return FirmwareNinjaReferenceNode(handle=bn_node, view=self._view)
@property def relationships(self) -> list[FirmwareNinjaRelationship]: """ ``relationships`` queries all Firmware Ninja relationships from the binary view metadata :return: List of relationships :rtype: list[FirmwareNinjaRelationship] """ count = ctypes.c_ulonglong(0) relationships = core.BNFirmwareNinjaQueryRelationships(self._handle, count) relationship_list = [] for i in range(count.value): relationship_list.append(FirmwareNinjaRelationship(self._view, handle=relationships[i])) return relationship_list
[docs] def add_relationship(self, relationship: FirmwareNinjaRelationship) -> None: """ ``add_relationship`` adds a relationship to the binary view metadata :param FirmwareNinjaRelationship relationship: Relationship to add """ if relationship.primary is None: raise ValueError("Primary object must be set") if relationship.secondary is None: raise ValueError("Secondary object must be set") core.BNFirmwareNinjaAddRelationship(self._handle, relationship.handle)
[docs] def get_relationship_by_guid(self, guid: str) -> FirmwareNinjaRelationship: """ ``get_relationship_by_guid`` queries a relationship from the binary view metadata by GUID :param str guid: GUID of the relationship :return: Relationship :rtype: FirmwareNinjaRelationship """ relationship = core.BNFirmwareNinjaGetRelationshipByGuid(self._handle, guid) if not relationship: return None return FirmwareNinjaRelationship(self._view, handle=relationship)
[docs] def remove_relationship_by_guid(self, guid: str) -> None: """ ``remove_relationship_by_guid`` removes a relationship from the binary view metadata by GUID :param str guid: GUID of the relationship """ core.BNFirmwareNinjaRemoveRelationshipByGuid(self._handle, guid)