diff options
| author | Rusty Wagner <rusty@vector35.com> | 2016-10-10 18:24:38 -0400 |
|---|---|---|
| committer | Rusty Wagner <rusty@vector35.com> | 2016-10-10 18:24:38 -0400 |
| commit | 98d187d9d86506ea915731266b8faaaa2dc0c9d6 (patch) | |
| tree | fa1ee82d342c12ed5d42e7caf9397312edf7cf63 /python/examples | |
| parent | 4598adb7ae961e69fb6ed3e594c0244c49eef511 (diff) | |
| parent | 55ac7a184b7956c0c2e2ca41d512e7c4a81267d9 (diff) | |
Merge commit '55ac7a184b7956c0c2e2ca41d512e7c4a81267d9'
Diffstat (limited to 'python/examples')
| -rw-r--r-- | python/examples/README.md | 27 | ||||
| -rw-r--r-- | python/examples/angr_plugin.py | 120 | ||||
| -rw-r--r-- | python/examples/arm-syscall.py | 18 | ||||
| -rw-r--r-- | python/examples/bin_info.py (renamed from python/examples/bin-info.py) | 0 | ||||
| -rwxr-xr-x | python/examples/export_svg.py (renamed from python/examples/export-svg.py) | 36 | ||||
| -rw-r--r-- | python/examples/instruction_iterator.py (renamed from python/examples/instruction-iterator.py) | 0 | ||||
| -rw-r--r-- | python/examples/jump_table.py (renamed from python/examples/jump-table.py) | 0 | ||||
| -rw-r--r-- | python/examples/nes.py | 20 | ||||
| -rw-r--r-- | python/examples/print_syscalls.py | 50 | ||||
| -rw-r--r-- | python/examples/version_switcher.py (renamed from python/examples/version-switcher.py) | 8 |
10 files changed, 234 insertions, 45 deletions
diff --git a/python/examples/README.md b/python/examples/README.md index 7fd3ab6b..43af34be 100644 --- a/python/examples/README.md +++ b/python/examples/README.md @@ -4,11 +4,14 @@ The following examples demonstrate some of the Binary Ninja API. They include bo ## Stand-alone -* bin-info.py - general binary information -* arm-syscall.py - extract syscall numbers from IL for arm Mach-O files -* version-switcher.py - uses the update API to see raw version notes and manually downgrade or upgrade +These plugins only operate when run directly outside of the UI -To use the stand-alone Python examples, make sure your `PYTHON_PATH` includes the API, like: +* bin_info.py - general binary information +* print_syscalls.py - extract syscall numbers from IL on specified file. Can be run both headless and in Binary Ninja +* version_switcher.py - uses the update API to see raw version notes and manually downgrade or upgrade +* instruction_iterator.py - very simple plugin that iterates through functions, blocks, and instructions + +To use the stand-alone Python examples, make sure your `PYTHON_PATH` includes the API, as shown below. Please note, this is a feature that requires the "GUI-less processing" capability not available in the personal edition. In the personal edition, all scripts must by run from the integrated python console (follow the directions below under "Loading Plugins" section) ``` PYTHONPATH=$PYTHONPATH:/Applications/Binary\ Ninja.app/Contents/Resources/python @@ -16,9 +19,21 @@ PYTHONPATH=$PYTHONPATH:/Applications/Binary\ Ninja.app/Contents/Resources/python ## GUI Plugins -* nes.py - 6502 CPU architecture including LLIL lifting and `.NES` file format parser +These plugins require the UI to be running + * breakpoint.py - small example showing how to modify a file and register a GUI menu item -* jump-table.py - stop-gap jump table plugin triggered via right-click menu at an indirect jump +* jump_table.py - heuristic based jump table detection for when the data-flow based computation fails, triggered by right-clicking on the location where the jump value is computed +* angr_plugin.py - a plugin to demonstrate both background threads, the simplified plugin UI elements, and highlighting +* export_svg.py - exports the graph view of a function to an SVG file for including in reports + +## Both + +These plugins are able to operate in either the GUI or as stand-alone plugins + +* nes.py - 6502 CPU architecture including LLIL lifting and `.NES` file format parser + + +## Loading Plugins Plugins are meant to be loaded into a running Binary Ninja GUI and should either be copied or symlinked into the appropriate plugin folder. You'll need to then re-start Binary Ninja. diff --git a/python/examples/angr_plugin.py b/python/examples/angr_plugin.py new file mode 100644 index 00000000..5d567511 --- /dev/null +++ b/python/examples/angr_plugin.py @@ -0,0 +1,120 @@ +# This plugin assumes angr is already installed and available on the system. See the angr documentation +# for information about installing angr. It should be installed using the virtualenv method. +# +# This plugin is currently only known to work on Linux using virtualenv. Switch to the virtual environment +# (using a command such as "workon angr"), then run the Binary Ninja UI from the command line. +# +# This method is known to fail on Mac OS X as the virtualenv used by angr does not appear to provide a +# way to automatically link to the correct version of Python, even when running the UI from within the +# virtual environment. A later update may allow for a manual override to link to the required version +# of Python. + +__name__ = "__console__" # angr looks for this, it won't load from within a UI without it +import angr +from binaryninja import * +import tempfile +import threading +import logging +import os + +# Disable warning logs as they show up as errors in the UI +logging.disable(logging.WARNING) + +# Create sets in the BinaryView's data field to store the desired path for each view +BinaryView.set_default_data("angr_find", set()) +BinaryView.set_default_data("angr_avoid", set()) + +def escaped_output(str): + return '\n'.join([s.encode("string_escape") for s in str.split('\n')]) + +# Define a background thread object for solving in the background +class Solver(BackgroundTaskThread): + def __init__(self, find, avoid, view): + BackgroundTaskThread.__init__(self, "Solving with angr...", True) + self.find = tuple(find) + self.avoid = tuple(avoid) + self.view = view + + # Write the binary to disk so that the angr API can read it + self.binary = tempfile.NamedTemporaryFile() + self.binary.write(view.file.raw.read(0, len(view.file.raw))) + self.binary.flush() + + def run(self): + # Create an angr project and an explorer with the user's settings + p = angr.Project(self.binary.name) + e = p.surveyors.Explorer(find = self.find, avoid = self.avoid) + + # Solve loop + while not e.done: + if self.cancelled: + # Solve cancelled, show results if there were any + if len(e.found) > 0: + break + return + + # Perform the next step in the solve + e.step() + + # Update status + active_count = len(e.active) + found_count = len(e.found) + + progress = "Solving with angr (%d active path%s" % (active_count, "s" if active_count != 1 else "") + if found_count > 0: + progress += ", %d path%s found" % (found_count, "s" if found_count != 1 else "") + self.progress = progress + ")..." + + # Solve complete, show report + text_report = "Found %d path%s.\n\n" % (len(e.found), "s" if len(e.found) != 1 else "") + i = 1 + for f in e.found: + text_report += "Path %d\n" % i + "=" * 10 + "\n" + text_report += "stdin:\n" + escaped_output(f.state.posix.dumps(0)) + "\n\n" + text_report += "stdout:\n" + escaped_output(f.state.posix.dumps(1)) + "\n\n" + text_report += "stderr:\n" + escaped_output(f.state.posix.dumps(2)) + "\n\n" + i += 1 + + name = self.view.file.filename + if len(name) > 0: + show_plain_text_report("Results from angr - " + os.path.basename(self.view.file.filename), text_report) + else: + show_plain_text_report("Results from angr", text_report) + +def find_instr(bv, addr): + # Highlight the instruction in green + blocks = bv.get_basic_blocks_at(addr) + for block in blocks: + block.set_auto_highlight(HighlightColor(GreenHighlightColor, alpha = 128)) + block.function.set_auto_instr_highlight(block.arch, addr, GreenHighlightColor) + + # Add the instruction to the list associated with the current view + bv.data.angr_find.add(addr) + +def avoid_instr(bv, addr): + # Highlight the instruction in red + blocks = bv.get_basic_blocks_at(addr) + for block in blocks: + block.set_auto_highlight(HighlightColor(RedHighlightColor, alpha = 128)) + block.function.set_auto_instr_highlight(block.arch, addr, RedHighlightColor) + + # Add the instruction to the list associated with the current view + bv.data.angr_avoid.add(addr) + +def solve(bv): + if len(bv.data.angr_find) == 0: + show_message_box("Angr Solve", "You have not specified a goal instruction.\n\n" + + "Please right click on the goal instruction and select \"Find Path to This Instruction\" to " + + "continue.", OKButtonSet, ErrorIcon) + return + + # Start a solver thread for the path associated with the view + s = Solver(bv.data.angr_find, bv.data.angr_avoid, bv) + s.start() + +# Register commands for the user to interact with the plugin +PluginCommand.register_for_address("Find Path to This Instruction", + "When solving, find a path that gets to this instruction", find_instr) +PluginCommand.register_for_address("Avoid This Instruction", + "When solving, avoid paths that reach this instruction", avoid_instr) +PluginCommand.register("Solve With Angr", "Attempt to solve for a path that satisfies the constraints given", solve) diff --git a/python/examples/arm-syscall.py b/python/examples/arm-syscall.py deleted file mode 100644 index f94b8531..00000000 --- a/python/examples/arm-syscall.py +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/env python -""" - Thanks to @theqlabs from arm.ninja for the nice writeup and idea for this plugin: - http://arm.ninja/2016/03/08/intro-to-binary-ninja-api/ -""" -import sys, binaryninja, time -if len(sys.argv) > 1: - target = sys.argv[1] -else: - raise ValueError("Missing argument to binary.") - -bv = binaryninja.BinaryViewType["Mach-O"].open(target) -bv.update_analysis_and_wait() - -for func in bv.functions: - for il in func.low_level_il: - if il.operation == core.LLIL_SYSCALL: - print "System call address: %x - %d" % (il.address, func.get_reg_value_at_low_level_il_instruction(il.address, bv.platform.system_call_convention.int_arg_regs[0]).value) diff --git a/python/examples/bin-info.py b/python/examples/bin_info.py index 48073894..48073894 100644 --- a/python/examples/bin-info.py +++ b/python/examples/bin_info.py diff --git a/python/examples/export-svg.py b/python/examples/export_svg.py index 67b6072c..a54dc879 100755 --- a/python/examples/export-svg.py +++ b/python/examples/export_svg.py @@ -1,5 +1,13 @@ from binaryninja import * -import os,sys +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 = { "'": "'", @@ -14,14 +22,20 @@ def escape(string): return ''.join(escape_table.get(i,i) for i in string) #still escape the basics def save_svg(bv,function): - filename = os.path.split(bv.file.filename)[1] address = hex(function.start).replace('L','') - outputfile = os.path.join(os.path.expanduser('~'), 'binaryninja-{filename}-{function}.html'.format(filename=filename,function=address)) + 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() - #os.system('open %s' % outputfile) + if show_message_box("Open SVG", "Would you like to view the exported SVG?", buttons = core.YesNoButtonSet, icon = core.QuestionIcon) == core.YesButton: + url = 'file:{}'.format(pathname2url(outputfile)) + webbrowser.open(url) def instruction_data_flow(function,address): ''' TODO: Extract data flow information ''' @@ -46,7 +60,6 @@ def render_svg(function): background-color: rgb(42, 42, 42); } .basicblock { - fill: rgb(74, 74, 74); stroke: rgb(224, 224, 224); } .edge { @@ -133,7 +146,16 @@ def render_svg(function): #Render block output += ' <g id="basicblock{i}">\n'.format(i=i) output += ' <title>Basic Block {i}</title>\n'.format(i=i) - output += ' <rect class="basicblock" x="{x}" y="{y}" height="{height}" width="{width}"/>\n'.format(x=x,y=y,width=width,height=height) + 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 += ' <rect class="basicblock" x="{x}" y="{y}" fill-opacity="0.4" height="{height}" width="{width}" fill="rgb({r},{g},{b})"/>\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 @@ -163,4 +185,4 @@ def render_svg(function): output += '</svg></html>' return output -PluginCommand.register_for_function("Export to SVG", "Exports an SVG of the current function to your home folder.", save_svg) +PluginCommand.register_for_function("Export to SVG", "Exports an SVG of the current function", save_svg) diff --git a/python/examples/instruction-iterator.py b/python/examples/instruction_iterator.py index 43bc000e..43bc000e 100644 --- a/python/examples/instruction-iterator.py +++ b/python/examples/instruction_iterator.py diff --git a/python/examples/jump-table.py b/python/examples/jump_table.py index 39fed1a5..39fed1a5 100644 --- a/python/examples/jump-table.py +++ b/python/examples/jump_table.py diff --git a/python/examples/nes.py b/python/examples/nes.py index 5cb6bc0d..00f9d8eb 100644 --- a/python/examples/nes.py +++ b/python/examples/nes.py @@ -521,9 +521,9 @@ class NESView(BinaryView): def __init__(self, data): BinaryView.__init__(self, data.file) - self.data = data + self.raw = data self.notification = NESViewUpdateNotification(self) - self.data.register_notification(self.notification) + self.raw.register_notification(self.notification) @classmethod def is_valid_for_data(self, data): @@ -539,7 +539,7 @@ class NESView(BinaryView): def init(self): try: - hdr = self.data.read(0, 16) + hdr = self.raw.read(0, 16) self.rom_banks = struct.unpack("B", hdr[4])[0] self.vrom_banks = struct.unpack("B", hdr[5])[0] self.rom_flags = struct.unpack("B", hdr[6])[0] @@ -592,9 +592,9 @@ class NESView(BinaryView): self.define_auto_symbol(Symbol(DataSymbol, 0x4016, "JOY1")) self.define_auto_symbol(Symbol(DataSymbol, 0x4017, "JOY2")) - sym_files = [self.data.file.filename + ".%x.nl" % self.__class__.bank, - self.data.file.filename + ".ram.nl", - self.data.file.filename + ".%x.nl" % (self.rom_banks - 1)] + sym_files = [self.raw.file.filename + ".%x.nl" % self.__class__.bank, + self.raw.file.filename + ".ram.nl", + self.raw.file.filename + ".%x.nl" % (self.rom_banks - 1)] for f in sym_files: if os.path.exists(f): sym_contents = open(f, "r").read() @@ -634,9 +634,9 @@ class NESView(BinaryView): else: to_read = length if addr < 0xc000: - data = self.data.read(self.rom_offset + bank_ofs + (self.__class__.bank * 0x4000), to_read) + data = self.raw.read(self.rom_offset + bank_ofs + (self.__class__.bank * 0x4000), to_read) else: - data = self.data.read(self.rom_offset + bank_ofs + self.rom_length - 0x4000, to_read) + data = self.raw.read(self.rom_offset + bank_ofs + self.rom_length - 0x4000, to_read) result += data if len(data) < to_read: break @@ -663,9 +663,9 @@ class NESView(BinaryView): else: to_write = length if addr < 0xc000: - written = self.data.write(self.rom_offset + bank_ofs + (self.__class__.bank * 0x4000), value[offset : offset + to_write]) + written = self.raw.write(self.rom_offset + bank_ofs + (self.__class__.bank * 0x4000), value[offset : offset + to_write]) else: - written = self.data.write(self.rom_offset + bank_ofs + self.rom_length - 0x4000, value[offset : offset + to_write]) + written = self.raw.write(self.rom_offset + bank_ofs + self.rom_length - 0x4000, value[offset : offset + to_write]) if written < to_write: break length -= to_write diff --git a/python/examples/print_syscalls.py b/python/examples/print_syscalls.py new file mode 100644 index 00000000..7e93356c --- /dev/null +++ b/python/examples/print_syscalls.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python +""" + Thanks to @theqlabs from arm.ninja for the nice writeup and idea for this plugin: + http://arm.ninja/2016/03/08/intro-to-binary-ninja-api/ +""" +import sys +from itertools import chain + +from binaryninja import BinaryView, core + + +def print_syscalls(bv): + """ Print Syscall numbers for a provided binaryview """ + + calling_convention = bv.platform.system_call_convention + if not calling_convention: + print('Error: No syscall convention available for {:s}'.format(bv.platform)) + return + + register = calling_convention.int_arg_regs[0] + + for func in bv.functions: + syscalls = (il for il in chain.from_iterable(func.low_level_il) + if il.operation == core.LLIL_SYSCALL) + for il in syscalls: + value = func.get_reg_value_at(bv.arch, il.address, register).value + print("System call address: {:#x} - {:d}".format(il.address, value)) + + +def main(): + if len(sys.argv) != 2: + print('Usage: {} <file>'.format(sys.argv[0])) + return -1 + + target = sys.argv[1] + + bv = BinaryView.open(target) + view_type = next(bvt for bvt in bv.available_view_types if bvt.name != 'Raw') + if not view_type: + print('Error: Unable to get any other view type besides Raw') + return -1 + + bv = bv.file.get_view_of_type(view_type.name) + bv.update_analysis_and_wait() + + print_syscalls(bv) + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/python/examples/version-switcher.py b/python/examples/version_switcher.py index a1936a52..6199c578 100644 --- a/python/examples/version-switcher.py +++ b/python/examples/version_switcher.py @@ -3,14 +3,14 @@ import sys import binaryninja import datetime -chandefault="private-beta" -channel=0 -versions=0 +chandefault = binaryninja.UpdateChannel.list[0].name +channel = None +versions = [] def load_channel(newchannel): global channel global versions - if (channel != 0 and newchannel == channel.name): + if (channel != None and newchannel == channel.name): print "Same channel, not updating." else: try: |
