diff options
| author | Peter LaFosse <peter@vector35.com> | 2018-10-17 17:37:15 -0400 |
|---|---|---|
| committer | Peter LaFosse <peter@vector35.com> | 2018-10-19 10:05:09 -0400 |
| commit | 4bb4a2016d90176938bc8737fd9f29e2d6ea2737 (patch) | |
| tree | c3a4f4573fbceafd1eb07cf0a7beaea528c79d83 /python/datarender.py | |
| parent | 8de8bad6f2f5517d858568a9871dd99f7e8d5d4b (diff) | |
Expose DataRender APIs, allow setting const on types, and allow type comparison
Diffstat (limited to 'python/datarender.py')
| -rw-r--r-- | python/datarender.py | 181 |
1 files changed, 181 insertions, 0 deletions
diff --git a/python/datarender.py b/python/datarender.py new file mode 100644 index 00000000..db60f42e --- /dev/null +++ b/python/datarender.py @@ -0,0 +1,181 @@ +# Copyright (c) 2015-2017 Vector 35 LLC +# +# 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 + +class DataRenderer(object): + """ + DataRender objects tell the Linear View how to render specific types. + + The `perform_is_valid_for_data` method returns a boolean to indicate if your derrived 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 `DataRender`'s. + + After defining the `DataRenderer` subclass you must then register it with the core. This is done by calling + either `register_type_sepcific` 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_sepcific() + """ + 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) + + @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) + + def register_type_sepcific(self): + core.BNRegisterTypeSpecificDataRenderer(core.BNGetDataRendererContainer(), self.handle) + + def register_generic(self): + core.BNRegisterGenericDataRenderer(core.BNGetDataRendererContainer(), self.handle) + + 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 = [] + for i in range(prefixCount): + token_type = InstructionTextTokenType(prefix[i].type) + text = prefix[i].text + value = prefix[i].value + size = prefix[i].size + operand = prefix[i].operand + context = prefix[i].context + address = prefix[i].address + confidence = prefix[i].confidence + prefixTokens.append(InstructionTextToken(token_type, text, value, size, operand, context, address, confidence)) + + 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() + line_buf[i].count = len(line.tokens) + line_buf[i].tokens = (core.BNInstructionTextToken * len(line.tokens))() + 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 + for j in range(len(line.tokens)): + line_buf[i].tokens[j].type = line.tokens[j].type + line_buf[i].tokens[j].text = line.tokens[j].text + line_buf[i].tokens[j].value = line.tokens[j].value + line_buf[i].tokens[j].size = line.tokens[j].size + line_buf[i].tokens[j].operand = line.tokens[j].operand + line_buf[i].tokens[j].context = line.tokens[j].context + line_buf[i].tokens[j].confidence = line.tokens[j].confidence + line_buf[i].tokens[j].address = line.tokens[j].address + + return ctypes.cast(line_buf, ctypes.c_void_p).value + except: + log_error(traceback.format_exc()) + return None + + def perform_free_object(self, ctxt): + pass + + def perform_is_valid_for_data(self, ctxt, view, addr, type, context): + return False + + def perform_get_lines_for_data(self, ctxt, view, addr, type, prefix, width, context): + return [] + + def __del__(self): + pass |
