# Copyright (c) 2015-2016 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. from binaryninja import * import os import webbrowser try: from urllib import pathname2url # Python 2.x except: from urllib.request import pathname2url # Python 3.x 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]} escape_table = { "'": "'", ">": ">", "<": "<", '"': """, ' ': " " } def escape(string): string = string.decode('utf-8').encode('ascii', 'xmlcharrefreplace') # handle extended unicode return ''.join(escape_table.get(i, i) for i in string) # still escape the basics def save_svg(bv, function): address = hex(function.start).replace('L', '') path = os.path.dirname(bv.file.filename) origname = os.path.basename(bv.file.filename) filename = os.path.join(path, 'binaryninja-{filename}-{function}.html'.format(filename=origname, function=address)) outputfile = get_save_filename_input('File name for export_svg', 'HTML files (*.html)', filename) if outputfile is None: return content = render_svg(function) output = open(outputfile, 'w') output.write(content) output.close() if show_message_box("Open SVG", "Would you like to view the exported SVG?", buttons=core.BNMessageBoxButtonSet.YesNoButtonSet, icon = core.BNMessageBoxIcon.QuestionIcon) == core.BNMessageBoxButtonResult.YesButton: url = 'file:{}'.format(pathname2url(outputfile)) webbrowser.open(url) def instruction_data_flow(function, address): ''' TODO: Extract data flow information ''' length = function.view.get_instruction_length(function.arch, address) bytes = function.view.read(address, length) hex = bytes.encode('hex') padded = ' '.join([hex[i:i + 2] for i in range(0, len(hex), 2)]) return 'Opcode: {bytes}'.format(bytes=padded) def render_svg(function): graph = function.create_graph() graph.layout_and_wait() heightconst = 15 ratio = 0.48 widthconst = heightconst * ratio output = ''' ''' output += ''' '''.format(width=graph.width * widthconst, height=graph.height * heightconst) output += ''' Function Graph 0 ''' edges = '' for i, block in enumerate(graph.blocks): # Calculate basic block location and coordinates x = ((block.x) * widthconst) y = ((block.y) * heightconst) width = ((block.width) * widthconst) height = ((block.height) * heightconst) # Render block output += ' \n'.format(i=i) output += ' Basic Block {i}\n'.format(i=i) rgb = colors['none'] try: bb = block.basic_block color_code = bb.highlight.color color_str = bb.highlight._standard_color_to_str(color_code) if color_str in colors: rgb = colors[color_str] except: pass output += ' \n'.format(x=x, y=y, width=width, height=height, r=rgb[0], g=rgb[1], b=rgb[2]) # Render instructions, unfortunately tspans don't allow copying/pasting more # than one line at a time, need SVG 1.2 textarea tags for that it looks like output += ' \n'.format(x=x, y=y + (i + 1) * heightconst) for i, line in enumerate(block.lines): output += ' '.format(x=x, y=y + (i + 0.7) * heightconst, address=hex(line.address)[:-1]) hover = instruction_data_flow(function, line.address) output += '{hover}'.format(hover=hover) for token in line.tokens: # TODO: add hover for hex, function, and reg tokens output += '{text}'.format(text=escape(token.text), tokentype=token.type.name) output += '\n' output += ' \n' output += ' \n' # Edges are rendered in a seperate chunk so they have priority over the # basic blocks or else they'd render below them for edge in block.outgoing_edges: points = "" for x, y in edge.points: points += str(x * widthconst) + "," + str(y * heightconst) + " " edges += ' \n'.format(type=edge.type.name, points=points) output += ' ' + edges + '\n' output += ' \n' output += '' return output PluginCommand.register_for_function("Export to SVG", "Exports an SVG of the current function", save_svg)