Source code for binaryninja.datarender

# Copyright (c) 2015-2019 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 traceback
import ctypes

import binaryninja
from binaryninja import _binaryninjacore as core
from binaryninja.filemetadata import FileMetadata
from binaryninja.binaryview import BinaryView
from binaryninja.function import (DisassemblyTextLine, InstructionTextToken)
from binaryninja.enums import InstructionTextTokenType, TypeClass, HighlightStandardColor
from binaryninja.log import log_error
from binaryninja.types import Type
from binaryninja import highlight

[docs]class DataRenderer(object): _registered_renderers = [] """ DataRenderer objects tell the Linear View how to render specific types. The `perform_is_valid_for_data` method returns a boolean to indicate if your derived class is able to render the type, given the `addr` and `context`. The `context` is a list of Type objects which represents the chain of nested objects that is being displayed. The `perform_get_lines_for_data` method returns a list of `DisassemblyTextLine` objects each one representing a single line of Linear View output. The `prefix` variable is a list of `InstructionTextToken`'s which have already been generated by other `DataRenderer`'s. After defining the `DataRenderer` subclass you must then register it with the core. This is done by calling either `register_type_specific` or `register_generic`. A "generic" type renderer is able to be overridden by a "type specific" renderer. For instance there is a generic struct render which renders any struct that hasn't been explicitly overridden by a "type specific" renderer. In the below example we create a data renderer that overrides the default display for `struct BAR`. class BarDataRenderer(DataRenderer): def __init__(self): DataRenderer.__init__(self) def perform_is_valid_for_data(self, ctxt, view, addr, type, context): return DataRenderer.is_type_of_struct_name(type, "BAR", context) def perform_get_lines_for_data(self, ctxt, view, addr, type, prefix, width, context): prefix.append(InstructionTextToken(InstructionTextTokenType.TextToken, "I'm in ur BAR")) return [DisassemblyTextLine(prefix, addr)] def __del__(self): pass BarDataRenderer().register_type_specific() """
[docs] def __init__(self, context=None): self._cb = core.BNCustomDataRenderer() self._cb.context = context self._cb.freeObject = self._cb.freeObject.__class__(self._free_object) self._cb.isValidForData = self._cb.isValidForData.__class__(self._is_valid_for_data) self._cb.getLinesForData = self._cb.getLinesForData.__class__(self._get_lines_for_data) self.handle = core.BNCreateDataRenderer(self._cb)
[docs] @classmethod def is_type_of_struct_name(cls, type, name, context): return (type.type_class == TypeClass.StructureTypeClass and len(context) > 0 and context[0].type_class == TypeClass.NamedTypeReferenceClass and context[0].named_type_reference.name == name)
[docs] def register_type_specific(self): core.BNRegisterTypeSpecificDataRenderer(core.BNGetDataRendererContainer(), self.handle) self.__class__._registered_renderers.append(self)
[docs] def register_generic(self): core.BNRegisterGenericDataRenderer(core.BNGetDataRendererContainer(), self.handle) self.__class__._registered_renderers.append(self)
def _free_object(self, ctxt): try: self.perform_free_object(ctxt) except: log_error(traceback.format_exc()) def _is_valid_for_data(self, ctxt, view, addr, type, context, ctxCount): try: file_metadata = FileMetadata(handle=core.BNGetFileForView(view)) view = BinaryView(file_metadata=file_metadata, handle=core.BNNewViewReference(view)) type = Type(handle=core.BNNewTypeReference(type)) pycontext = [] for i in range(0, ctxCount): pycontext.append(Type(core.BNNewTypeReference(context[i]))) return self.perform_is_valid_for_data(ctxt, view, addr, type, pycontext) except: log_error(traceback.format_exc()) return False def _get_lines_for_data(self, ctxt, view, addr, type, prefix, prefixCount, width, count, typeCtx, ctxCount): try: file_metadata = FileMetadata(handle=core.BNGetFileForView(view)) view = BinaryView(file_metadata=file_metadata, handle=core.BNNewViewReference(view)) type = Type(handle=core.BNNewTypeReference(type)) prefixTokens = InstructionTextToken.get_instruction_lines(prefix, prefixCount) pycontext = [] for i in range(ctxCount): pycontext.append(Type(core.BNNewTypeReference(typeCtx[i]))) result = self.perform_get_lines_for_data(ctxt, view, addr, type, prefixTokens, width, pycontext) count[0] = len(result) line_buf = (core.BNDisassemblyTextLine * len(result))() for i in range(len(result)): line = result[i] color = line.highlight if not isinstance(color, HighlightStandardColor) and not isinstance(color, highlight.HighlightColor): raise ValueError("Specified color is not one of HighlightStandardColor, highlight.HighlightColor") if isinstance(color, HighlightStandardColor): color = highlight.HighlightColor(color) line_buf[i].highlight = color._get_core_struct() if line.address is None: if len(line.tokens) > 0: line_buf[i].addr = line.tokens[0].address else: line_buf[i].addr = 0 else: line_buf[i].addr = line.address if line.il_instruction is not None: line_buf[i].instrIndex = line.il_instruction.instr_index else: line_buf[i].instrIndex = 0xffffffffffffffff line_buf[i].count = len(line.tokens) line_buf[i].tokens = InstructionTextToken.get_instruction_lines(line.tokens) return ctypes.cast(line_buf, ctypes.c_void_p).value except: log_error(traceback.format_exc()) return None
[docs] def perform_free_object(self, ctxt): pass
[docs] def perform_is_valid_for_data(self, ctxt, view, addr, type, context): return False
[docs] def perform_get_lines_for_data(self, ctxt, view, addr, type, prefix, width, context): return []
def __del__(self): pass