summaryrefslogtreecommitdiff
path: root/python
diff options
context:
space:
mode:
Diffstat (limited to 'python')
-rw-r--r--python/__init__.py1
-rw-r--r--python/basicblock.py12
-rw-r--r--python/binaryview.py7
-rw-r--r--python/flowgraph.py465
-rw-r--r--python/function.py400
-rw-r--r--python/highlight.py10
-rw-r--r--python/interaction.py172
7 files changed, 685 insertions, 382 deletions
diff --git a/python/__init__.py b/python/__init__.py
index 729e4f8a..05ba4c2b 100644
--- a/python/__init__.py
+++ b/python/__init__.py
@@ -53,6 +53,7 @@ from .scriptingprovider import *
from .pluginmanager import *
from .setting import *
from .metadata import *
+from .flowgraph import *
def shutdown():
diff --git a/python/basicblock.py b/python/basicblock.py
index c55e15f0..3f615bba 100644
--- a/python/basicblock.py
+++ b/python/basicblock.py
@@ -243,14 +243,7 @@ class BasicBlock(object):
>>> current_basic_block.highlight
<color: blue>
"""
- color = core.BNGetBasicBlockHighlight(self.handle)
- if color.style == HighlightColorStyle.StandardHighlightColor:
- return highlight.HighlightColor(color=color.color, alpha=color.alpha)
- elif color.style == HighlightColorStyle.MixedHighlightColor:
- return highlight.HighlightColor(color=color.color, mix_color=color.mixColor, mix=color.mix, alpha=color.alpha)
- elif color.style == HighlightColorStyle.CustomHighlightColor:
- return highlight.HighlightColor(red=color.r, green=color.g, blue=color.b, alpha=color.alpha)
- return highlight.HighlightColor(color=HighlightStandardColor.NoHighlightColor)
+ return highlight.HighlightColor._from_core_struct(core.BNGetBasicBlockHighlight(self.handle))
@highlight.setter
def highlight(self, value):
@@ -341,6 +334,7 @@ class BasicBlock(object):
il_instr = self.il_function[lines[i].instrIndex]
else:
il_instr = None
+ color = highlight.HighlightColor._from_core_struct(lines[i].highlight)
tokens = []
for j in xrange(0, lines[i].count):
token_type = InstructionTextTokenType(lines[i].tokens[j].type)
@@ -352,7 +346,7 @@ class BasicBlock(object):
confidence = lines[i].tokens[j].confidence
address = lines[i].tokens[j].address
tokens.append(function.InstructionTextToken(token_type, text, value, size, operand, context, address, confidence))
- result.append(function.DisassemblyTextLine(addr, tokens, il_instr))
+ result.append(function.DisassemblyTextLine(tokens, addr, il_instr, color))
core.BNFreeDisassemblyTextLines(lines, count.value)
return result
diff --git a/python/binaryview.py b/python/binaryview.py
index 0cf25e70..ede279ad 100644
--- a/python/binaryview.py
+++ b/python/binaryview.py
@@ -41,6 +41,7 @@ import basicblock
import types
import lineardisassembly
import metadata
+import highlight
class BinaryDataNotification(object):
@@ -2961,6 +2962,7 @@ class BinaryView(object):
func = function.Function(self, core.BNNewFunctionReference(lines[i].function))
if lines[i].block:
block = basicblock.BasicBlock(self, core.BNNewBasicBlockReference(lines[i].block))
+ color = highlight.HighlightColor._from_core_struct(lines[i].contents.highlight)
addr = lines[i].contents.addr
tokens = []
for j in xrange(0, lines[i].contents.count):
@@ -2973,7 +2975,7 @@ class BinaryView(object):
confidence = lines[i].contents.tokens[j].confidence
address = lines[i].contents.tokens[j].address
tokens.append(function.InstructionTextToken(token_type, text, value, size, operand, context, address, confidence))
- contents = function.DisassemblyTextLine(addr, tokens)
+ contents = function.DisassemblyTextLine(tokens, addr, color = color)
result.append(lineardisassembly.LinearDisassemblyLine(lines[i].type, func, block, lines[i].lineOffset, contents))
func = None
@@ -3352,6 +3354,9 @@ class BinaryView(object):
def show_html_report(self, title, contents, plaintext = ""):
core.BNShowHTMLReport(self.handle, title, contents, plaintext)
+ def show_graph_report(self, title, graph):
+ core.BNShowHTMLReport(self.handle, title, graph.handle)
+
def get_address_input(self, prompt, title, current_address = None):
if current_address is None:
current_address = self.file.offset
diff --git a/python/flowgraph.py b/python/flowgraph.py
new file mode 100644
index 00000000..1568bc77
--- /dev/null
+++ b/python/flowgraph.py
@@ -0,0 +1,465 @@
+# Copyright (c) 2018 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 ctypes
+import threading
+import traceback
+
+# Binary Ninja components
+import _binaryninjacore as core
+from enums import (BranchType, InstructionTextTokenType, HighlightColorStyle, HighlightStandardColor)
+import function
+import binaryview
+import lowlevelil
+import mediumlevelil
+import basicblock
+import architecture
+import log
+import interaction
+import highlight
+
+
+class FlowGraphEdge(object):
+ def __init__(self, branch_type, source, target, points, back_edge):
+ self.type = BranchType(branch_type)
+ self.source = source
+ self.target = target
+ self.points = points
+ self.back_edge = back_edge
+
+ def __repr__(self):
+ return "<%s: %s>" % (self.type.name, repr(self.target))
+
+
+class FlowGraphNode(object):
+ def __init__(self, graph, handle = None):
+ if handle is None:
+ handle = core.BNCreateFlowGraphNode(graph.handle)
+ self.handle = handle
+ self.graph = graph
+
+ def __del__(self):
+ core.BNFreeFlowGraphNode(self.handle)
+
+ def __eq__(self, value):
+ if not isinstance(value, FlowGraphNode):
+ return False
+ return ctypes.addressof(self.handle.contents) == ctypes.addressof(value.handle.contents)
+
+ def __ne__(self, value):
+ if not isinstance(value, FlowGraphNode):
+ return True
+ return ctypes.addressof(self.handle.contents) != ctypes.addressof(value.handle.contents)
+
+ @property
+ def basic_block(self):
+ """Basic block associated with this part of the flow graph (read-only)"""
+ block = core.BNGetFlowGraphBasicBlock(self.handle)
+ if not block:
+ return None
+ func_handle = core.BNGetBasicBlockFunction(block)
+ if not func_handle:
+ core.BNFreeBasicBlock(block)
+ return None
+
+ view = binaryview.BinaryView(handle = core.BNGetFunctionData(func_handle))
+ func = function.Function(view, func_handle)
+
+ if core.BNIsLowLevelILBasicBlock(block):
+ block = lowlevelil.LowLevelILBasicBlock(view, block,
+ lowlevelil.LowLevelILFunction(func.arch, core.BNGetBasicBlockLowLevelILFunction(block), func))
+ elif core.BNIsMediumLevelILBasicBlock(block):
+ block = mediumlevelil.MediumLevelILBasicBlock(view, block,
+ mediumlevelil.MediumLevelILFunction(func.arch, core.BNGetBasicBlockMediumLevelILFunction(block), func))
+ else:
+ block = basicblock.BasicBlock(view, block)
+ return block
+
+ @property
+ def x(self):
+ """Flow graph block X (read-only)"""
+ return core.BNGetFlowGraphNodeX(self.handle)
+
+ @property
+ def y(self):
+ """Flow graph block Y (read-only)"""
+ return core.BNGetFlowGraphNodeY(self.handle)
+
+ @property
+ def width(self):
+ """Flow graph block width (read-only)"""
+ return core.BNGetFlowGraphNodeWidth(self.handle)
+
+ @property
+ def height(self):
+ """Flow graph block height (read-only)"""
+ return core.BNGetFlowGraphNodeHeight(self.handle)
+
+ @property
+ def lines(self):
+ """Flow graph block list of lines"""
+ count = ctypes.c_ulonglong()
+ lines = core.BNGetFlowGraphNodeLines(self.handle, count)
+ block = self.basic_block
+ result = []
+ for i in xrange(0, count.value):
+ addr = lines[i].addr
+ if (lines[i].instrIndex != 0xffffffffffffffff) and (block is not None) and hasattr(block, 'il_function'):
+ il_instr = block.il_function[lines[i].instrIndex]
+ else:
+ il_instr = None
+ color = highlight.HighlightColor._from_core_struct(lines[i].highlight)
+ tokens = []
+ for j in xrange(0, lines[i].count):
+ token_type = InstructionTextTokenType(lines[i].tokens[j].type)
+ text = lines[i].tokens[j].text
+ value = lines[i].tokens[j].value
+ size = lines[i].tokens[j].size
+ operand = lines[i].tokens[j].operand
+ context = lines[i].tokens[j].context
+ confidence = lines[i].tokens[j].confidence
+ address = lines[i].tokens[j].address
+ tokens.append(function.InstructionTextToken(token_type, text, value, size, operand, context, address, confidence))
+ result.append(function.DisassemblyTextLine(tokens, addr, il_instr, color))
+ core.BNFreeDisassemblyTextLines(lines, count.value)
+ return result
+
+ @lines.setter
+ def lines(self, lines):
+ if isinstance(lines, str):
+ lines = lines.split('\n')
+ line_buf = (core.BNDisassemblyTextLine * len(lines))()
+ for i in xrange(0, len(lines)):
+ line = lines[i]
+ if isinstance(line, str):
+ line = function.DisassemblyTextLine([function.InstructionTextToken(InstructionTextTokenType.TextToken, line)])
+ if not isinstance(line, function.DisassemblyTextLine):
+ line = function.DisassemblyTextLine(line)
+ 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
+ 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))()
+ for j in xrange(0, 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
+ core.BNSetFlowGraphNodeLines(self.handle, line_buf, len(lines))
+
+ @property
+ def outgoing_edges(self):
+ """Flow graph block list of outgoing edges (read-only)"""
+ count = ctypes.c_ulonglong()
+ edges = core.BNGetFlowGraphNodeOutgoingEdges(self.handle, count)
+ result = []
+ for i in xrange(0, count.value):
+ branch_type = BranchType(edges[i].type)
+ target = edges[i].target
+ if target:
+ target = FlowGraphNode(self.graph, core.BNNewFlowGraphNodeReference(target))
+ points = []
+ for j in xrange(0, edges[i].pointCount):
+ points.append((edges[i].points[j].x, edges[i].points[j].y))
+ result.append(FlowGraphEdge(branch_type, self, target, points, edges[i].backEdge))
+ core.BNFreeFlowGraphNodeOutgoingEdgeList(edges, count.value)
+ return result
+
+ @property
+ def highlight(self):
+ """Gets or sets the highlight color for the node
+
+ :Example:
+ >>> g = FlowGraph()
+ >>> node = FlowGraphNode(g)
+ >>> node.highlight = HighlightStandardColor.BlueHighlightColor
+ >>> node.highlight
+ <color: blue>
+ """
+ return highlight.HighlightColor._from_core_struct(core.BNGetFlowGraphNodeHighlight(self.handle))
+
+ @highlight.setter
+ def highlight(self, color):
+ 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)
+ core.BNSetFlowGraphNodeHighlight(self.handle, color._get_core_struct())
+
+ def __repr__(self):
+ block = self.basic_block
+ if block:
+ arch = block.arch
+ if arch:
+ return "<graph node: %s@%#x-%#x>" % (arch.name, block.start, block.end)
+ else:
+ return "<graph node: %#x-%#x>" % (block.start, block.end)
+ return "<graph node>"
+
+ def __iter__(self):
+ count = ctypes.c_ulonglong()
+ lines = core.BNGetFlowGraphNodeLines(self.handle, count)
+ block = self.basic_block
+ try:
+ for i in xrange(0, count.value):
+ addr = lines[i].addr
+ if (lines[i].instrIndex != 0xffffffffffffffff) and (block is not None) and hasattr(block, 'il_function'):
+ il_instr = block.il_function[lines[i].instrIndex]
+ else:
+ il_instr = None
+ tokens = []
+ for j in xrange(0, lines[i].count):
+ token_type = InstructionTextTokenType(lines[i].tokens[j].type)
+ text = lines[i].tokens[j].text
+ value = lines[i].tokens[j].value
+ size = lines[i].tokens[j].size
+ operand = lines[i].tokens[j].operand
+ context = lines[i].tokens[j].context
+ confidence = lines[i].tokens[j].confidence
+ address = lines[i].tokens[j].address
+ tokens.append(function.InstructionTextToken(token_type, text, value, size, operand, context, address, confidence))
+ yield function.DisassemblyTextLine(tokens, addr, il_instr)
+ finally:
+ core.BNFreeDisassemblyTextLines(lines, count.value)
+
+ def add_outgoing_edge(self, edge_type, target):
+ core.BNAddFlowGraphNodeOutgoingEdge(self.handle, edge_type, target.handle)
+
+
+class FlowGraph(object):
+ def __init__(self, handle = None):
+ if handle is None:
+ handle = core.BNCreateFlowGraph()
+ self.handle = handle
+ self._on_complete = None
+ self._cb = ctypes.CFUNCTYPE(None, ctypes.c_void_p)(self._complete)
+
+ def __del__(self):
+ self.abort()
+ core.BNFreeFlowGraph(self.handle)
+
+ def __eq__(self, value):
+ if not isinstance(value, FlowGraph):
+ return False
+ return ctypes.addressof(self.handle.contents) == ctypes.addressof(value.handle.contents)
+
+ def __ne__(self, value):
+ if not isinstance(value, FlowGraph):
+ return True
+ return ctypes.addressof(self.handle.contents) != ctypes.addressof(value.handle.contents)
+
+ @property
+ def function(self):
+ """Function for a flow graph"""
+ func = core.BNGetFunctionForFlowGraph(self.handle)
+ if func is None:
+ return None
+ return function.Function(binaryview.BinaryView(handle = core.BNGetFunctionData(func)), func)
+
+ @function.setter
+ def function(self, func):
+ if func is not None:
+ func = func.handle
+ core.BNSetFunctionForFlowGraph(self.handle, func)
+
+ @property
+ def complete(self):
+ """Whether flow graph layout is complete (read-only)"""
+ return core.BNIsFlowGraphLayoutComplete(self.handle)
+
+ @property
+ def nodes(self):
+ """List of nodes in graph (read-only)"""
+ count = ctypes.c_ulonglong()
+ blocks = core.BNGetFlowGraphNodes(self.handle, count)
+ result = []
+ for i in xrange(0, count.value):
+ result.append(FlowGraphNode(self, core.BNNewFlowGraphNodeReference(blocks[i])))
+ core.BNFreeFlowGraphNodeList(blocks, count.value)
+ return result
+
+ @property
+ def has_nodes(self):
+ """Whether the flow graph has at least one node (read-only)"""
+ return core.BNFlowGraphHasNodes(self.handle)
+
+ @property
+ def width(self):
+ """Flow graph width (read-only)"""
+ return core.BNGetFlowGraphWidth(self.handle)
+
+ @property
+ def height(self):
+ """Flow graph height (read-only)"""
+ return core.BNGetFlowGraphHeight(self.handle)
+
+ @property
+ def horizontal_block_margin(self):
+ return core.BNGetHorizontalFlowGraphBlockMargin(self.handle)
+
+ @horizontal_block_margin.setter
+ def horizontal_block_margin(self, value):
+ core.BNSetFlowGraphBlockMargins(self.handle, value, self.vertical_block_margin)
+
+ @property
+ def vertical_block_margin(self):
+ return core.BNGetVerticalFlowGraphBlockMargin(self.handle)
+
+ @vertical_block_margin.setter
+ def vertical_block_margin(self, value):
+ core.BNSetFlowGraphBlockMargins(self.handle, self.horizontal_block_margin, value)
+
+ @property
+ def is_il(self):
+ return core.BNIsILFlowGraph(self.handle)
+
+ @property
+ def is_low_level_il(self):
+ return core.BNIsLowLevelILFlowGraph(self.handle)
+
+ @property
+ def is_medium_level_il(self):
+ return core.BNIsMediumLevelILFlowGraph(self.handle)
+
+ @property
+ def il_function(self):
+ if self.is_low_level_il:
+ il_func = core.BNGetFlowGraphLowLevelILFunction(self.handle)
+ if not il_func:
+ return None
+ function = self.function
+ if function is None:
+ return None
+ return lowlevelil.LowLevelILFunction(function.arch, il_func, function)
+ if self.is_medium_level_il:
+ il_func = core.BNGetFlowGraphMediumLevelILFunction(self.handle)
+ if not il_func:
+ return None
+ function = self.function
+ if function is None:
+ return None
+ return mediumlevelil.MediumLevelILFunction(function.arch, il_func, function)
+ return None
+
+ @il_function.setter
+ def il_function(self, func):
+ if isinstance(func, lowlevelil.LowLevelILFunction):
+ core.BNSetFlowGraphLowLevelILFunction(self.handle, func.handle)
+ core.BNSetFlowGraphMediumLevelILFunction(self.handle, None)
+ elif isinstance(func, mediumlevelil.MediumLevelILFunction):
+ core.BNSetFlowGraphLowLevelILFunction(self.handle, None)
+ core.BNSetFlowGraphMediumLevelILFunction(self.handle, func.handle)
+ elif func is None:
+ core.BNSetFlowGraphLowLevelILFunction(self.handle, None)
+ core.BNSetFlowGraphMediumLevelILFunction(self.handle, None)
+ else:
+ raise TypeError("expected IL function for setting il_function property")
+
+ def __setattr__(self, name, value):
+ try:
+ object.__setattr__(self, name, value)
+ except AttributeError:
+ raise AttributeError("attribute '%s' is read only" % name)
+
+ def __repr__(self):
+ function = self.function
+ if function is None:
+ return "<flow graph>"
+ return "<graph of %s>" % repr(function)
+
+ def __iter__(self):
+ count = ctypes.c_ulonglong()
+ nodes = core.BNGetFlowGraphNodes(self.handle, count)
+ try:
+ for i in xrange(0, count.value):
+ yield FlowGraphNode(self, core.BNNewFlowGraphNodeReference(nodes[i]))
+ finally:
+ core.BNFreeFlowGraphNodeList(nodes, count.value)
+
+ def _complete(self, ctxt):
+ try:
+ if self._on_complete is not None:
+ self._on_complete()
+ except:
+ log.log_error(traceback.format_exc())
+
+ def layout(self):
+ core.BNStartFlowGraphLayout(self.handle)
+
+ def _wait_complete(self):
+ self._wait_cond.acquire()
+ self._wait_cond.notify()
+ self._wait_cond.release()
+
+ def layout_and_wait(self):
+ self._wait_cond = threading.Condition()
+ self.on_complete(self._wait_complete)
+ self.layout()
+
+ self._wait_cond.acquire()
+ while not self.complete:
+ self._wait_cond.wait()
+ self._wait_cond.release()
+
+ def on_complete(self, callback):
+ self._on_complete = callback
+ core.BNSetFlowGraphCompleteCallback(self.handle, None, self._cb)
+
+ def abort(self):
+ core.BNAbortFlowGraph(self.handle)
+
+ def get_nodes_in_region(self, left, top, right, bottom):
+ count = ctypes.c_ulonglong()
+ nodes = core.BNGetFlowGraphNodesInRegion(self.handle, left, top, right, bottom, count)
+ result = []
+ for i in xrange(0, count.value):
+ result.append(FlowGraphNode(self, core.BNNewFlowGraphNodeReference(nodes[i])))
+ core.BNFreeFlowGraphNodeList(nodes, count.value)
+ return result
+
+ def append(self, node):
+ return core.BNAddFlowGraphNode(self.handle, node.handle)
+
+ def __getitem__(self, i):
+ node = core.BNGetFlowGraphNode(self.handle, i)
+ if node is None:
+ return None
+ return FlowGraphNode(self, node)
+
+ def show(self, title):
+ interaction.show_graph_report(title, self)
diff --git a/python/function.py b/python/function.py
index 6db44e6d..dca9dda6 100644
--- a/python/function.py
+++ b/python/function.py
@@ -39,6 +39,7 @@ import mediumlevelil
import binaryview
import log
import callingconvention
+import flowgraph
class LookupTableEntry(object):
@@ -844,6 +845,14 @@ class Function(object):
def analysis_skip_override(self, override):
core.BNSetFunctionAnalysisSkipOverride(self.handle, override)
+ @property
+ def unresolved_stack_adjustment_graph(self):
+ """Flow graph of unresolved stack adjustments (read-only)"""
+ graph = core.BNGetUnresolvedStackAdjustmentGraph(self.handle)
+ if not graph:
+ return None
+ return flowgraph.FlowGraph(graph)
+
def __iter__(self):
count = ctypes.c_ulonglong()
blocks = core.BNGetFunctionBasicBlockList(self.handle, count)
@@ -1108,8 +1117,12 @@ class Function(object):
core.BNFreeRegisterList(flags)
return result
- def create_graph(self):
- return FunctionGraph(self._view, core.BNCreateFunctionGraph(self.handle))
+ def create_graph(self, graph_type = FunctionGraphType.NormalFunctionGraph, settings = None):
+ if settings is not None:
+ settings_obj = settings.handle
+ else:
+ settings_obj = None
+ return flowgraph.FlowGraph(core.BNCreateFunctionGraph(self.handle, graph_type, settings_obj))
def apply_imported_types(self, sym):
core.BNApplyImportedTypes(self.handle, sym.handle)
@@ -1461,6 +1474,7 @@ class Function(object):
result = []
for i in xrange(0, count.value):
addr = lines[i].addr
+ color = highlight.HighlightColor._from_core_struct(lines[i].highlight)
tokens = []
for j in xrange(0, lines[i].count):
token_type = InstructionTextTokenType(lines[i].tokens[j].type)
@@ -1472,7 +1486,7 @@ class Function(object):
confidence = lines[i].tokens[j].confidence
address = lines[i].tokens[j].address
tokens.append(InstructionTextToken(token_type, text, value, size, operand, context, address, confidence))
- result.append(DisassemblyTextLine(addr, tokens))
+ result.append(DisassemblyTextLine(tokens, addr, color = color))
core.BNFreeDisassemblyTextLines(lines, count.value)
return result
@@ -1598,10 +1612,18 @@ class AdvancedFunctionAnalysisDataRequestor(object):
class DisassemblyTextLine(object):
- def __init__(self, addr, tokens, il_instr = None):
- self.address = addr
+ def __init__(self, tokens, address = None, il_instr = None, color = None):
+ self.address = address
self.tokens = tokens
self.il_instruction = il_instr
+ if color is None:
+ self.highlight = highlight.HighlightColor()
+ else:
+ 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)
+ self.highlight = color
def __str__(self):
result = ""
@@ -1610,192 +1632,11 @@ class DisassemblyTextLine(object):
return result
def __repr__(self):
+ if self.address is None:
+ return str(self)
return "<%#x: %s>" % (self.address, str(self))
-class FunctionGraphEdge(object):
- def __init__(self, branch_type, source, target, points, back_edge):
- self.type = BranchType(branch_type)
- self.source = source
- self.target = target
- self.points = points
- self.back_edge = back_edge
-
- def __repr__(self):
- return "<%s: %s>" % (self.type.name, repr(self.target))
-
-
-class FunctionGraphBlock(object):
- def __init__(self, handle, graph):
- self.handle = handle
- self.graph = graph
-
- def __del__(self):
- core.BNFreeFunctionGraphBlock(self.handle)
-
- def __eq__(self, value):
- if not isinstance(value, FunctionGraphBlock):
- return False
- return ctypes.addressof(self.handle.contents) == ctypes.addressof(value.handle.contents)
-
- def __ne__(self, value):
- if not isinstance(value, FunctionGraphBlock):
- return True
- return ctypes.addressof(self.handle.contents) != ctypes.addressof(value.handle.contents)
-
- @property
- def basic_block(self):
- """Basic block associated with this part of the function graph (read-only)"""
- block = core.BNGetFunctionGraphBasicBlock(self.handle)
- func_handle = core.BNGetBasicBlockFunction(block)
- if func_handle is None:
- core.BNFreeBasicBlock(block)
- return None
-
- view = binaryview.BinaryView(handle = core.BNGetFunctionData(func_handle))
- func = Function(view, func_handle)
-
- if core.BNIsLowLevelILBasicBlock(block):
- block = lowlevelil.LowLevelILBasicBlock(view, block,
- lowlevelil.LowLevelILFunction(func.arch, core.BNGetBasicBlockLowLevelILFunction(block), func))
- elif core.BNIsMediumLevelILBasicBlock(block):
- block = mediumlevelil.MediumLevelILBasicBlock(view, block,
- mediumlevelil.MediumLevelILFunction(func.arch, core.BNGetBasicBlockMediumLevelILFunction(block), func))
- else:
- block = basicblock.BasicBlock(view, block)
- return block
-
- @property
- def arch(self):
- """Function graph block architecture (read-only)"""
- arch = core.BNGetFunctionGraphBlockArchitecture(self.handle)
- if arch is None:
- return None
- return architecture.CoreArchitecture._from_cache(arch)
-
- @property
- def start(self):
- """Function graph block start (read-only)"""
- return core.BNGetFunctionGraphBlockStart(self.handle)
-
- @property
- def end(self):
- """Function graph block end (read-only)"""
- return core.BNGetFunctionGraphBlockEnd(self.handle)
-
- @property
- def x(self):
- """Function graph block X (read-only)"""
- return core.BNGetFunctionGraphBlockX(self.handle)
-
- @property
- def y(self):
- """Function graph block Y (read-only)"""
- return core.BNGetFunctionGraphBlockY(self.handle)
-
- @property
- def width(self):
- """Function graph block width (read-only)"""
- return core.BNGetFunctionGraphBlockWidth(self.handle)
-
- @property
- def height(self):
- """Function graph block height (read-only)"""
- return core.BNGetFunctionGraphBlockHeight(self.handle)
-
- @property
- def lines(self):
- """Function graph block list of lines (read-only)"""
- count = ctypes.c_ulonglong()
- lines = core.BNGetFunctionGraphBlockLines(self.handle, count)
- block = self.basic_block
- result = []
- for i in xrange(0, count.value):
- addr = lines[i].addr
- if (lines[i].instrIndex != 0xffffffffffffffff) and hasattr(block, 'il_function'):
- il_instr = block.il_function[lines[i].instrIndex]
- else:
- il_instr = None
- tokens = []
- for j in xrange(0, lines[i].count):
- token_type = InstructionTextTokenType(lines[i].tokens[j].type)
- text = lines[i].tokens[j].text
- value = lines[i].tokens[j].value
- size = lines[i].tokens[j].size
- operand = lines[i].tokens[j].operand
- context = lines[i].tokens[j].context
- confidence = lines[i].tokens[j].confidence
- address = lines[i].tokens[j].address
- tokens.append(InstructionTextToken(token_type, text, value, size, operand, context, address, confidence))
- result.append(DisassemblyTextLine(addr, tokens, il_instr))
- core.BNFreeDisassemblyTextLines(lines, count.value)
- return result
-
- @property
- def outgoing_edges(self):
- """Function graph block list of outgoing edges (read-only)"""
- count = ctypes.c_ulonglong()
- edges = core.BNGetFunctionGraphBlockOutgoingEdges(self.handle, count)
- result = []
- for i in xrange(0, count.value):
- branch_type = BranchType(edges[i].type)
- target = edges[i].target
- if target:
- func = core.BNGetBasicBlockFunction(target)
- if func is None:
- core.BNFreeBasicBlock(target)
- target = None
- else:
- target = basicblock.BasicBlock(binaryview.BinaryView(handle = core.BNGetFunctionData(func)),
- core.BNNewBasicBlockReference(target))
- core.BNFreeFunction(func)
- points = []
- for j in xrange(0, edges[i].pointCount):
- points.append((edges[i].points[j].x, edges[i].points[j].y))
- result.append(FunctionGraphEdge(branch_type, self, target, points, edges[i].backEdge))
- core.BNFreeFunctionGraphBlockOutgoingEdgeList(edges, count.value)
- return result
-
- def __setattr__(self, name, value):
- try:
- object.__setattr__(self, name, value)
- except AttributeError:
- raise AttributeError("attribute '%s' is read only" % name)
-
- def __repr__(self):
- arch = self.arch
- if arch:
- return "<graph block: %s@%#x-%#x>" % (arch.name, self.start, self.end)
- else:
- return "<graph block: %#x-%#x>" % (self.start, self.end)
-
- def __iter__(self):
- count = ctypes.c_ulonglong()
- lines = core.BNGetFunctionGraphBlockLines(self.handle, count)
- block = self.basic_block
- try:
- for i in xrange(0, count.value):
- addr = lines[i].addr
- if (lines[i].instrIndex != 0xffffffffffffffff) and hasattr(block, 'il_function'):
- il_instr = block.il_function[lines[i].instrIndex]
- else:
- il_instr = None
- tokens = []
- for j in xrange(0, lines[i].count):
- token_type = InstructionTextTokenType(lines[i].tokens[j].type)
- text = lines[i].tokens[j].text
- value = lines[i].tokens[j].value
- size = lines[i].tokens[j].size
- operand = lines[i].tokens[j].operand
- context = lines[i].tokens[j].context
- confidence = lines[i].tokens[j].confidence
- address = lines[i].tokens[j].address
- tokens.append(InstructionTextToken(token_type, text, value, size, operand, context, address, confidence))
- yield DisassemblyTextLine(addr, tokens, il_instr)
- finally:
- core.BNFreeDisassemblyTextLines(lines, count.value)
-
-
class DisassemblySettings(object):
def __init__(self, handle = None):
if handle is None:
@@ -1833,189 +1674,6 @@ class DisassemblySettings(object):
core.BNSetDisassemblySettingsOption(self.handle, option, state)
-class FunctionGraph(object):
- def __init__(self, view, handle):
- self.view = view
- self.handle = handle
- self._on_complete = None
- self._cb = ctypes.CFUNCTYPE(None, ctypes.c_void_p)(self._complete)
-
- def __del__(self):
- self.abort()
- core.BNFreeFunctionGraph(self.handle)
-
- def __eq__(self, value):
- if not isinstance(value, FunctionGraph):
- return False
- return ctypes.addressof(self.handle.contents) == ctypes.addressof(value.handle.contents)
-
- def __ne__(self, value):
- if not isinstance(value, FunctionGraph):
- return True
- return ctypes.addressof(self.handle.contents) != ctypes.addressof(value.handle.contents)
-
- @property
- def function(self):
- """Function for a function graph (read-only)"""
- func = core.BNGetFunctionForFunctionGraph(self.handle)
- if func is None:
- return None
- return Function(self.view, func)
-
- @property
- def complete(self):
- """Whether function graph layout is complete (read-only)"""
- return core.BNIsFunctionGraphLayoutComplete(self.handle)
-
- @property
- def type(self):
- """Function graph type (read-only)"""
- return FunctionGraphType(core.BNGetFunctionGraphType(self.handle))
-
- @property
- def blocks(self):
- """List of basic blocks in function (read-only)"""
- count = ctypes.c_ulonglong()
- blocks = core.BNGetFunctionGraphBlocks(self.handle, count)
- result = []
- for i in xrange(0, count.value):
- result.append(FunctionGraphBlock(core.BNNewFunctionGraphBlockReference(blocks[i]), self))
- core.BNFreeFunctionGraphBlockList(blocks, count.value)
- return result
-
- @property
- def has_blocks(self):
- """Whether the function graph has at least one block (read-only)"""
- return core.BNFunctionGraphHasBlocks(self.handle)
-
- @property
- def width(self):
- """Function graph width (read-only)"""
- return core.BNGetFunctionGraphWidth(self.handle)
-
- @property
- def height(self):
- """Function graph height (read-only)"""
- return core.BNGetFunctionGraphHeight(self.handle)
-
- @property
- def horizontal_block_margin(self):
- return core.BNGetHorizontalFunctionGraphBlockMargin(self.handle)
-
- @horizontal_block_margin.setter
- def horizontal_block_margin(self, value):
- core.BNSetFunctionGraphBlockMargins(self.handle, value, self.vertical_block_margin)
-
- @property
- def vertical_block_margin(self):
- return core.BNGetVerticalFunctionGraphBlockMargin(self.handle)
-
- @vertical_block_margin.setter
- def vertical_block_margin(self, value):
- core.BNSetFunctionGraphBlockMargins(self.handle, self.horizontal_block_margin, value)
-
- @property
- def settings(self):
- return DisassemblySettings(core.BNGetFunctionGraphSettings(self.handle))
-
- @property
- def is_il(self):
- return core.BNIsILFunctionGraph(self.handle)
-
- @property
- def is_low_level_il(self):
- return core.BNIsLowLevelILFunctionGraph(self.handle)
-
- @property
- def is_medium_level_il(self):
- return core.BNIsMediumLevelILFunctionGraph(self.handle)
-
- @property
- def il_function(self):
- if self.is_low_level_il:
- il_func = core.BNGetFunctionGraphLowLevelILFunction(self.handle)
- if not il_func:
- return None
- return lowlevelil.LowLevelILFunction(self.function.arch, il_func, self.function)
- if self.is_medium_level_il:
- il_func = core.BNGetFunctionGraphMediumLevelILFunction(self.handle)
- if not il_func:
- return None
- return mediumlevelil.MediumLevelILFunction(self.function.arch, il_func, self.function)
- return None
-
- def __setattr__(self, name, value):
- try:
- object.__setattr__(self, name, value)
- except AttributeError:
- raise AttributeError("attribute '%s' is read only" % name)
-
- def __repr__(self):
- return "<graph of %s>" % repr(self.function)
-
- def __iter__(self):
- count = ctypes.c_ulonglong()
- blocks = core.BNGetFunctionGraphBlocks(self.handle, count)
- try:
- for i in xrange(0, count.value):
- yield FunctionGraphBlock(core.BNNewFunctionGraphBlockReference(blocks[i]), self)
- finally:
- core.BNFreeFunctionGraphBlockList(blocks, count.value)
-
- def _complete(self, ctxt):
- try:
- if self._on_complete is not None:
- self._on_complete()
- except:
- log.log_error(traceback.format_exc())
-
- def layout(self, graph_type = FunctionGraphType.NormalFunctionGraph):
- if isinstance(graph_type, str):
- graph_type = FunctionGraphType[graph_type]
- core.BNStartFunctionGraphLayout(self.handle, graph_type)
-
- def _wait_complete(self):
- self._wait_cond.acquire()
- self._wait_cond.notify()
- self._wait_cond.release()
-
- def layout_and_wait(self, graph_type=FunctionGraphType.NormalFunctionGraph):
- self._wait_cond = threading.Condition()
- self.on_complete(self._wait_complete)
- self.layout(graph_type)
-
- self._wait_cond.acquire()
- while not self.complete:
- self._wait_cond.wait()
- self._wait_cond.release()
-
- def on_complete(self, callback):
- self._on_complete = callback
- core.BNSetFunctionGraphCompleteCallback(self.handle, None, self._cb)
-
- def abort(self):
- core.BNAbortFunctionGraph(self.handle)
-
- def get_blocks_in_region(self, left, top, right, bottom):
- count = ctypes.c_ulonglong()
- blocks = core.BNGetFunctionGraphBlocksInRegion(self.handle, left, top, right, bottom, count)
- result = []
- for i in xrange(0, count.value):
- result.append(FunctionGraphBlock(core.BNNewFunctionGraphBlockReference(blocks[i]), self))
- core.BNFreeFunctionGraphBlockList(blocks, count.value)
- return result
-
- def is_option_set(self, option):
- if isinstance(option, str):
- option = DisassemblyOption[option]
- return core.BNIsFunctionGraphOptionSet(self.handle, option)
-
- def set_option(self, option, state = True):
- if isinstance(option, str):
- option = DisassemblyOption[option]
- core.BNSetFunctionGraphOption(self.handle, option, state)
-
-
class RegisterInfo(object):
def __init__(self, full_width_reg, size, offset=0, extend=ImplicitRegisterExtend.NoExtend, index=None):
self.full_width_reg = full_width_reg
diff --git a/python/highlight.py b/python/highlight.py
index 96bc543d..87329202 100644
--- a/python/highlight.py
+++ b/python/highlight.py
@@ -110,3 +110,13 @@ class HighlightColor(object):
result.b = self.blue
return result
+
+ @staticmethod
+ def _from_core_struct(color):
+ if color.style == HighlightColorStyle.StandardHighlightColor:
+ return HighlightColor(color=color.color, alpha=color.alpha)
+ elif color.style == HighlightColorStyle.MixedHighlightColor:
+ return HighlightColor(color=color.color, mix_color=color.mixColor, mix=color.mix, alpha=color.alpha)
+ elif color.style == HighlightColorStyle.CustomHighlightColor:
+ return HighlightColor(red=color.r, green=color.g, blue=color.b, alpha=color.alpha)
+ return HighlightColor(color=HighlightStandardColor.NoHighlightColor)
diff --git a/python/interaction.py b/python/interaction.py
index 4f6ed67d..81aeb04f 100644
--- a/python/interaction.py
+++ b/python/interaction.py
@@ -23,9 +23,10 @@ import traceback
# Binary Ninja components
import _binaryninjacore as core
-from enums import FormInputFieldType, MessageBoxIcon, MessageBoxButtonSet, MessageBoxButtonResult
+from enums import FormInputFieldType, MessageBoxIcon, MessageBoxButtonSet, MessageBoxButtonResult, ReportType
import binaryview
import log
+import flowgraph
class LabelField(object):
@@ -249,6 +250,8 @@ class InteractionHandler(object):
self._cb.showPlainTextReport = self._cb.showPlainTextReport.__class__(self._show_plain_text_report)
self._cb.showMarkdownReport = self._cb.showMarkdownReport.__class__(self._show_markdown_report)
self._cb.showHTMLReport = self._cb.showHTMLReport.__class__(self._show_html_report)
+ self._cb.showGraphReport = self._cb.showGraphReport.__class__(self._show_graph_report)
+ self._cb.showReportCollection = self._cb.showReportCollection.__class__(self._show_report_collection)
self._cb.getTextLineInput = self._cb.getTextLineInput.__class__(self._get_text_line_input)
self._cb.getIntegerInput = self._cb.getIntegerInput.__class__(self._get_int_input)
self._cb.getAddressInput = self._cb.getAddressInput.__class__(self._get_address_input)
@@ -293,6 +296,22 @@ class InteractionHandler(object):
except:
log.log_error(traceback.format_exc())
+ def _show_graph_report(self, ctxt, view, title, graph):
+ try:
+ if view:
+ view = binaryview.BinaryView(handle = core.BNNewViewReference(view))
+ else:
+ view = None
+ self.show_graph_report(view, title, flowgraph.FlowGraph(core.BNNewFlowGraphReference(graph)))
+ except:
+ log.log_error(traceback.format_exc())
+
+ def _show_report_collection(self, ctxt, title, reports):
+ try:
+ self.show_report_collection(title, ReportCollection(core.BNNewReportCollectionReference(reports)))
+ except:
+ log.log_error(traceback.format_exc())
+
def _get_text_line_input(self, ctxt, result, prompt, title):
try:
value = self.get_text_line_input(prompt, title)
@@ -426,6 +445,12 @@ class InteractionHandler(object):
if len(plaintext) != 0:
self.show_plain_text_report(view, title, plaintext)
+ def show_graph_report(self, view, title, graph):
+ pass
+
+ def show_report_collection(self, title, reports):
+ pass
+
def get_text_line_input(self, prompt, title):
return None
@@ -461,6 +486,123 @@ class InteractionHandler(object):
return MessageBoxButtonResult.CancelButton
+class PlainTextReport(object):
+ def __init__(self, title, contents, view = None):
+ self.view = view
+ self.title = title
+ self.contents = contents
+
+ def __repr__(self):
+ return "<plain text report: %s>" % self.title
+
+ def __str__(self):
+ return self.contents
+
+
+class MarkdownReport(object):
+ def __init__(self, title, contents, plaintext = "", view = None):
+ self.view = view
+ self.title = title
+ self.contents = contents
+ self.plaintext = plaintext
+
+ def __repr__(self):
+ return "<markdown report: %s>" % self.title
+
+ def __str__(self):
+ return self.contents
+
+
+class HTMLReport(object):
+ def __init__(self, title, contents, plaintext = "", view = None):
+ self.view = view
+ self.title = title
+ self.contents = contents
+ self.plaintext = plaintext
+
+ def __repr__(self):
+ return "<html report: %s>" % self.title
+
+ def __str__(self):
+ return self.contents
+
+
+class FlowGraphReport(object):
+ def __init__(self, title, graph, view = None):
+ self.view = view
+ self.title = title
+ self.graph = graph
+
+ def __repr__(self):
+ return "<graph report: %s>" % self.title
+
+
+class ReportCollection(object):
+ def __init__(self, handle = None):
+ if handle is None:
+ self.handle = core.BNCreateReportCollection()
+ else:
+ self.handle = handle
+
+ def __len__(self):
+ return core.BNGetReportCollectionCount(self.handle)
+
+ def _report_from_index(self, i):
+ report_type = core.BNGetReportType(self.handle, i)
+ title = core.BNGetReportTitle(self.handle, i)
+ view = core.BNGetReportView(self.handle, i)
+ if view:
+ view = binaryview.BinaryView(handle = view)
+ else:
+ view = None
+ if report_type == ReportType.PlainTextReportType:
+ contents = core.BNGetReportContents(self.handle, i)
+ return PlainTextReport(title, contents, view)
+ elif report_type == ReportType.MarkdownReportType:
+ contents = core.BNGetReportContents(self.handle, i)
+ plaintext = core.BNGetReportPlainText(self.handle, i)
+ return MarkdownReport(title, contents, plaintext, view)
+ elif report_type == ReportType.HTMLReportType:
+ contents = core.BNGetReportContents(self.handle, i)
+ plaintext = core.BNGetReportPlainText(self.handle, i)
+ return HTMLReport(title, contents, plaintext, view)
+ elif report_type == ReportType.FlowGraphReportType:
+ graph = flowgraph.FlowGraph(core.BNGetReportFlowGraph(self.handle, i))
+ return FlowGraphReport(title, graph, view)
+ raise TypeError("invalid report type %s" % repr(report_type))
+
+ def __getitem__(self, i):
+ if isinstance(i, slice) or isinstance(i, tuple):
+ raise IndexError("expected integer report index")
+ if (i < 0) or (i >= len(self)):
+ raise IndexError("index out of range")
+ return self._report_from_index(i)
+
+ def __iter__(self):
+ count = len(self)
+ for i in xrange(0, count):
+ yield self._report_from_index(i)
+
+ def __repr__(self):
+ return "<reports: %s>" % repr(list(self))
+
+ def append(self, report):
+ if report.view is None:
+ view = None
+ else:
+ view = report.view.handle
+ if isinstance(report, PlainTextReport):
+ core.BNAddPlainTextReportToCollection(self.handle, view, report.title, report.contents)
+ elif isinstance(report, MarkdownReport):
+ core.BNAddMarkdownReportToCollection(self.handle, view, report.title, report.contents, report.plaintext)
+ elif isinstance(report, HTMLReport):
+ core.BNAddHTMLReportToCollection(self.handle, view, report.title, report.contents, report.plaintext)
+ elif isinstance(report, FlowGraphReport):
+ core.BNAddGraphReportToCollection(self.handle, view, report.title, report.graph.handle)
+ else:
+ raise TypeError("expected report object")
+
+
def markdown_to_html(contents):
"""
``markdown_to_html`` converts the provided markdown to HTML.
@@ -527,6 +669,34 @@ def show_html_report(title, contents, plaintext=""):
core.BNShowHTMLReport(None, title, contents, plaintext)
+def show_graph_report(title, graph):
+ """
+ ``show_graph_report`` displays a flow graph in UI applications.
+
+ Note: This API function will have no effect outside the UI.
+
+ :param FlowGraph graph: Flow graph to display
+ :rtype: None
+ """
+ func = graph.function
+ if func is None:
+ core.BNShowGraphReport(None, title, graph.handle)
+ else:
+ core.BNShowGraphReport(func.view.handle, title, graph.handle)
+
+
+def show_report_collection(title, reports):
+ """
+ ``show_report_collection`` displays mulitple reports in UI applications.
+
+ Note: This API function will have no effect outside the UI.
+
+ :param ReportCollection reports: Reports to display
+ :rtype: None
+ """
+ core.BNShowReportCollection(title, reports.handle)
+
+
def get_text_line_input(prompt, title):
"""
``get_text_line_input`` prompts the user to input a string with the given prompt and title.