summaryrefslogtreecommitdiff
path: root/python/datarender.py
diff options
context:
space:
mode:
authorPeter LaFosse <peter@vector35.com>2018-10-17 17:37:15 -0400
committerPeter LaFosse <peter@vector35.com>2018-10-19 10:05:09 -0400
commit4bb4a2016d90176938bc8737fd9f29e2d6ea2737 (patch)
treec3a4f4573fbceafd1eb07cf0a7beaea528c79d83 /python/datarender.py
parent8de8bad6f2f5517d858568a9871dd99f7e8d5d4b (diff)
Expose DataRender APIs, allow setting const on types, and allow type comparison
Diffstat (limited to 'python/datarender.py')
-rw-r--r--python/datarender.py181
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