# from binaryninja import * import os import webbrowser import time import sys from pathlib import Path from urllib.request import pathname2url from binaryninja.interaction import get_save_filename_input, show_message_box, TextLineField, ChoiceField, SaveFileNameField, get_form_input from binaryninja.settings import Settings from binaryninja.enums import MessageBoxButtonSet, MessageBoxIcon, MessageBoxButtonResult, InstructionTextTokenType, BranchType, DisassemblyOption, FunctionGraphType, ThemeColor from binaryninja.function import DisassemblySettings from binaryninja.plugin import PluginCommand from binaryninjaui import getThemeColor, getTokenColor, UIContext colors = { 'green': [162, 217, 175], 'red': [222, 143, 151], 'blue': [128, 198, 233], 'cyan': [142, 230, 237], 'lightCyan': [176, 221, 228], 'orange': [237, 189, 129], 'yellow': [237, 223, 179], 'magenta': [218, 196, 209], 'none': [74, 74, 74], 'disabled': [144, 144, 144] } escape_table = {"'": "'", ">": ">", "<": "<", '"': """, ' ': " "} def escape(toescape): # handle extended unicode toescape = toescape.encode('ascii', 'xmlcharrefreplace') # still escape the basics return ''.join(escape_table.get(chr(i), chr(i)) for i in toescape) def save_svg(bv, function): sym = bv.get_symbol_at(function.start) if sym: offset = sym.name else: offset = "%x" % function.start path = Path(os.path.dirname(bv.file.filename)) origname = os.path.basename(bv.file.filename) filename = path / f'binaryninja-{origname}-{offset}.html' functionChoice = TextLineField("Blank to accept default") # TODO: implement linear disassembly settings and output modeChoices = ["Graph"] modeChoiceField = ChoiceField("Mode", modeChoices) if Settings().get_bool('ui.debugMode'): formChoices = [ "Assembly", "Lifted IL", "LLIL", "LLIL SSA", "Mapped Medium", "Mapped Medium SSA", "MLIL", "MLIL SSA", "HLIL", "HLIL SSA" ] formChoiceField = ChoiceField("Form", formChoices) else: formChoices = ["Assembly", "LLIL", "MLIL", "HLIL"] formChoiceField = ChoiceField("Form", formChoices) showOpcodes = ChoiceField("Show Opcodes", ["Yes", "No"]) showAddresses = ChoiceField("Show Addresses", ["Yes", "No"]) saveFileChoices = SaveFileNameField("Output file", 'HTML files (*.html)', str(filename)) if not get_form_input([ f'Current Function: {offset}', functionChoice, formChoiceField, modeChoiceField, showOpcodes, showAddresses, saveFileChoices ], "SVG Export") or saveFileChoices.result is None: return if saveFileChoices.result == '': outputfile = filename else: outputfile = saveFileChoices.result content = render_svg( function, offset, modeChoices[modeChoiceField.result], formChoices[formChoiceField.result], showOpcodes.result == 0, showAddresses.result == 0, origname ) output = open(outputfile, 'w') output.write(content) output.close() result = show_message_box( "Open SVG", "Would you like to view the exported SVG?", buttons=MessageBoxButtonSet.YesNoButtonSet, icon=MessageBoxIcon.QuestionIcon ) if result == MessageBoxButtonResult.YesButton: # might need more testing, latest py3 on windows seems.... broken with these APIs relative to other platforms if sys.platform == 'win32': webbrowser.open(outputfile) else: webbrowser.open('file://' + str(outputfile)) def instruction_data_flow(function, address): # TODO: Extract data flow information length = function.view.get_instruction_length(address) func_bytes = function.view.read(address, length) hex = func_bytes.hex() padded = ' '.join([hex[i:i + 2] for i in range(0, len(hex), 2)]) return f'Opcode: {padded}' def rgbStr(tokenType): '''Given a token string name, look up the theme color for it and return as rbg(x,y,z) str''' try: color = eval(f'getThemeColor(ThemeColor.{tokenType})') except: color = None if (not color): try: ctx = UIContext.activeContext() view_frame = ctx.getCurrentViewFrame() color = eval(f'getTokenColor(view_frame, InstructionTextTokenType.{tokenType})') except: return 'rgb(224, 224, 224)' r = color.getRgb()[0] g = color.getRgb()[1] b = color.getRgb()[2] return f"rgb({r}, {g}, {b})" def render_svg(function, offset, mode, form, showOpcodes, showAddresses, origname): settings = DisassemblySettings() if showOpcodes: settings.set_option(DisassemblyOption.ShowOpcode, True) if showAddresses: settings.set_option(DisassemblyOption.ShowAddress, True) if form == "LLIL": graph_type = FunctionGraphType.LowLevelILFunctionGraph elif form == "LLIL SSA": graph_type = FunctionGraphType.LowLevelILSSAFormFunctionGraph elif form == "Lifted IL": graph_type = FunctionGraphType.LiftedILFunctionGraph elif form == "Mapped Medium": graph_type = FunctionGraphType.MappedMediumLevelILFunctionGraph elif form == "Mapped Medium SSA": graph_type = FunctionGraphType.MappedMediumLevelILSSAFormFunctionGraph elif form == "MLIL": graph_type = FunctionGraphType.MediumLevelILFunctionGraph elif form == "MLIL SSA": graph_type = FunctionGraphType.MediumLevelILSSAFormFunctionGraph elif form == "HLIL": graph_type = FunctionGraphType.HighLevelILFunctionGraph elif form == "HLIL SSA": graph_type = FunctionGraphType.HighLevelILSSAFormFunctionGraph else: graph_type = FunctionGraphType.NormalFunctionGraph graph = function.create_graph(graph_type=graph_type, settings=settings) graph.layout_and_wait() heightconst = 15 ratio = 0.48 widthconst = heightconst * ratio output = f''' \n' timestring=time.strftime("%c") output += f'
This CFG generated by Binary Ninja from {origname} on {timestring} showing {offset} as {form}.
' output += '' return output PluginCommand.register_for_function("Export to SVG", "Exports an SVG of the current function", save_svg)