diff options
Diffstat (limited to 'python')
| -rw-r--r-- | python/__init__.py | 116 | ||||
| -rw-r--r-- | python/examples/arm-syscall.py | 5 | ||||
| -rw-r--r-- | python/examples/bin-info.py | 5 | ||||
| -rwxr-xr-x | python/examples/export-svg.py | 172 | ||||
| -rw-r--r-- | python/examples/instruction-iterator.py | 5 |
5 files changed, 233 insertions, 70 deletions
diff --git a/python/__init__.py b/python/__init__.py index 821e3f41..e07bd7c5 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -627,6 +627,45 @@ class BinaryViewType(object): except AttributeError: raise AttributeError, "attribute '%s' is read only" % name +class AnalysisCompletionEvent(object): + def __init__(self, view, callback): + self.view = view + self.callback = callback + self._cb = ctypes.CFUNCTYPE(None, ctypes.c_void_p)(self._notify) + self.handle = core.BNAddAnalysisCompletionEvent(self.view.handle, None, self._cb) + + def __del__(self): + core.BNFreeAnalysisCompletionEvent(self.handle) + + def _notify(self, ctxt): + try: + self.callback() + except: + log_error(traceback.format_exc()) + + def _empty_callback(self): + pass + + def cancel(self): + self.callback = self._empty_callback + core.BNCancelAnalysisCompletionEvent(self.handle) + +class AnalysisProgress(object): + def __init__(self, state, count, total): + self.state = state + self.count = count + self.total = total + + def __str__(self): + if self.state == core.DisassembleState: + return "Disassembling (%d/%d)" % (self.count, self.total) + if self.state == core.AnalyzeState: + return "Analyzing (%d/%d)" % (self.count, self.total) + return "Idle" + + def __repr__(self): + return "<progress: %s>" % str(self) + class BinaryView(object): name = None """Binary View name""" @@ -905,6 +944,12 @@ class BinaryView(object): def saved(self, value): self.file.saved = value + @property + def analysis_progress(self): + """Status of current analysis (read-only)""" + result = core.BNGetAnalysisProgress(self.handle) + return AnalysisProgress(result.state, result.count, result.total) + def __len__(self): return int(core.BNGetViewLength(self.handle)) @@ -1252,6 +1297,29 @@ class BinaryView(object): def update_analysis(self): core.BNUpdateAnalysis(self.handle) + def update_analysis_and_wait(self): + class WaitEvent: + def __init__(self): + self.cond = threading.Condition() + self.done = False + + def complete(self): + self.cond.acquire() + self.done = True + self.cond.notify() + self.cond.release() + + def wait(self): + self.cond.acquire() + while not self.done: + self.cond.wait() + self.cond.release() + + wait = WaitEvent() + event = AnalysisCompletionEvent(self, lambda: wait.complete()) + core.BNUpdateAnalysis(self.handle) + wait.wait() + def abort_analysis(self): core.BNAbortAnalysis(self.handle) @@ -1428,6 +1496,9 @@ class BinaryView(object): core.BNFreeStringList(strings) return result + def add_analysis_completion_event(self, callback): + return AnalysisCompletionEvent(self, callback) + def __setattr__(self, name, value): try: object.__setattr__(self,name,value) @@ -2292,7 +2363,7 @@ class Function(object): def get_reg_value_at_low_level_il_instruction(self, i, reg): if isinstance(reg, str): reg = self.arch.regs[reg].index - value = core.BNGetRegisterValueAtInstruction(self.handle, self.arch.handle, i, reg) + value = core.BNGetRegisterValueAtLowLevelILInstruction(self.handle, i, reg) result = RegisterValue(self.arch, value) core.BNFreeRegisterValue(value) return result @@ -2300,7 +2371,47 @@ class Function(object): def get_reg_value_after_low_level_il_instruction(self, i, reg): if isinstance(reg, str): reg = self.arch.regs[reg].index - value = core.BNGetRegisterValueAfterInstruction(self.handle, self.arch.handle, i, reg) + value = core.BNGetRegisterValueAfterLowLevelILInstruction(self.handle, i, reg) + result = RegisterValue(self.arch, value) + core.BNFreeRegisterValue(value) + return result + + def get_stack_contents_at(self, arch, addr, offset, size): + value = core.BNGetStackContentsAtInstruction(self.handle, arch.handle, addr, offset, size) + result = RegisterValue(arch, value) + core.BNFreeRegisterValue(value) + return result + + def get_stack_contents_after(self, arch, addr, offset, size): + value = core.BNGetStackContentsAfterInstruction(self.handle, arch.handle, addr, offset, size) + result = RegisterValue(arch, value) + core.BNFreeRegisterValue(value) + return result + + def get_stack_contents_at_low_level_il_instruction(self, i, offset, size): + value = core.BNGetStackContentsAtLowLevelILInstruction(self.handle, i, offset, size) + result = RegisterValue(self.arch, value) + core.BNFreeRegisterValue(value) + return result + + def get_stack_contents_after_low_level_il_instruction(self, i, offset, size): + value = core.BNGetStackContentsAfterInstruction(self.handle, i, offset, size) + result = RegisterValue(self.arch, value) + core.BNFreeRegisterValue(value) + return result + + def get_parameter_at(self, arch, addr, func_type, i): + if func_type is not None: + func_type = func_type.handle + value = core.BNGetParameterValueAtInstruction(self.handle, arch.handle, addr, func_type, i) + result = RegisterValue(arch, value) + core.BNFreeRegisterValue(value) + return result + + def get_parameter_at_low_level_il_instruction(self, instr, func_type, i): + if func_type is not None: + func_type = func_type.handle + value = core.BNGetParameterValueAtLowLevelILInstruction(self.handle, instr, func_type, i) result = RegisterValue(self.arch, value) core.BNFreeRegisterValue(value) return result @@ -3939,6 +4050,7 @@ class LowLevelILInstruction(object): core.LLIL_CMP_SGT: [("left", "expr"), ("right", "expr")], core.LLIL_CMP_UGT: [("left", "expr"), ("right", "expr")], core.LLIL_TEST_BIT: [("left", "expr"), ("right", "expr")], + core.LLIL_BOOL_TO_INT: [("src", "expr")], core.LLIL_SYSCALL: [], core.LLIL_BP: [], core.LLIL_TRAP: [("value", "int")], diff --git a/python/examples/arm-syscall.py b/python/examples/arm-syscall.py index cbd8f4bf..f94b8531 100644 --- a/python/examples/arm-syscall.py +++ b/python/examples/arm-syscall.py @@ -10,10 +10,7 @@ else: raise ValueError("Missing argument to binary.") bv = binaryninja.BinaryViewType["Mach-O"].open(target) -bv.update_analysis() - -"""Until update_analysis_and_wait is complete, sleep is necessary as the analysis is multi-threaded.""" -time.sleep(5) +bv.update_analysis_and_wait() for func in bv.functions: for il in func.low_level_il: diff --git a/python/examples/bin-info.py b/python/examples/bin-info.py index 6ec00bce..48073894 100644 --- a/python/examples/bin-info.py +++ b/python/examples/bin-info.py @@ -13,10 +13,7 @@ else: target = "/bin/ls" bv = binaryninja.BinaryViewType[bintype].open(target) -bv.update_analysis() - -"""Until update_analysis_and_wait is complete, sleep is necessary as the analysis is multi-threaded.""" -time.sleep(1) +bv.update_analysis_and_wait() print "-------- %s --------" % target print "START: 0x%x" % bv.start diff --git a/python/examples/export-svg.py b/python/examples/export-svg.py index e00b651f..3800edf7 100755 --- a/python/examples/export-svg.py +++ b/python/examples/export-svg.py @@ -1,62 +1,119 @@ from binaryninja import * -import os +import os,sys + +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): filename = bv.file.filename.split(os.sep)[-1] - outputfile = os.environ['HOME'] + os.sep + 'binaryninja-{filename}-{function}.svg'.format(filename=filename,function=function.symbol.name) - try: - output = open(outputfile,'w') - output.write(render_svg(function)) - output.close() - except: - print "Unexpected error:", sys.exc_info()[0] - raise + address = hex(function.start).replace('L','') + outputfile = os.path.join(os.path.expanduser('~'), 'binaryninja-{filename}-{function}.html'.format(filename=filename,function=address)) + content = render_svg(function) + output = open(outputfile,'w') + output.write(content) + output.close() + #os.system('open %s' % outputfile) + +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.54 - widthconst = int(heightconst*ratio) + ratio = 0.48 + widthconst = heightconst*ratio - output = '''<?xml version="1.0" standalone="no"?> - <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> - <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="{width}" height="{height}" viewBox="0 0 {width} {height}"> + output = '''<html> + <head> + <style type="text/css"> + @import url(https://fonts.googleapis.com/css?family=Source+Code+Pro); + svg { + background-color: rgb(42, 42, 42); + } + .basicblock { + fill: rgb(74, 74, 74); + stroke: rgb(224, 224, 224); + } + .edge { + fill: none; + stroke-width: 1px; + } + .UnconditionalBranch, .IndirectBranch { + stroke: rgb(128, 198, 233); + color: rgb(128, 198, 233); + } + .FalseBranch { + stroke: rgb(222, 143, 151); + color: rgb(222, 143, 151); + } + .TrueBranch { + stroke: rgb(162, 217, 175); + color: rgb(162, 217, 175); + } + .arrow { + stroke-width: 1; + fill: currentColor; + } + text { + font-family: 'Source Code Pro'; + font-size: 9pt; + fill: rgb(224, 224, 224); + } + .CodeSymbolToken { + fill: rgb(128, 198, 223); + } + .DataSymbolToken { + fill: rgb(142, 230, 237); + } + .TextToken, .InstructionToken, .BeginMemoryOperandToken, .EndMemoryOperandToken { + fill: rgb(224, 224, 224); + } + .PossibleAddressToken, .IntegerToken { + fill: rgb(162, 217, 175); + } + .RegisterToken { + fill: rgb(237, 223, 179); + } + .AnnotationToken { + fill: rgb(218, 196, 209); + } + .ImportToken { + fill: rgb(237, 189, 129); + } + .StackVariableToken { + fill: rgb(193, 220, 199); + } + </style> + <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.2/jquery.min.js"></script> + </head> +''' + output += '''<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="{width}" height="{height}"> <defs> - <style type="text/css"><![CDATA[ - @import url(https://fonts.googleapis.com/css?family=Source+Code+Pro); - .edge {{ - fill: none; - stroke-width: 3 - }} - .UnconditionalBranch {{ - stroke: blue; - color: blue; - }} - .FalseBranch {{ - stroke: red; - color: red; - }} - .TrueBranch {{ - stroke: green; - color: green; - }} - .arrow {{ - stroke-width: 3; - fill: currentColor; - }} - text {{ - font-family: 'Source Code Pro'; - font-size: 9pt; - }} - ]]></style> - <marker id="arrow-TrueBranch" class="arrow TrueBranch" viewBox="0 0 10 10" refX="11" refY="5" markerUnits="strokeWidth" markerWidth="4" markerHeight="3" orient="auto"> + <marker id="arrow-TrueBranch" class="arrow TrueBranch" viewBox="0 0 10 10" refX="10" refY="5" markerUnits="strokeWidth" markerWidth="8" markerHeight="6" orient="auto"> + <path d="M 0 0 L 10 5 L 0 10 z" /> + </marker> + <marker id="arrow-FalseBranch" class="arrow FalseBranch" viewBox="0 0 10 10" refX="10" refY="5" markerUnits="strokeWidth" markerWidth="8" markerHeight="6" orient="auto"> <path d="M 0 0 L 10 5 L 0 10 z" /> </marker> - <marker id="arrow-FalseBranch" class="arrow FalseBranch" viewBox="0 0 10 10" refX="11" refY="5" markerUnits="strokeWidth" markerWidth="4" markerHeight="3" orient="auto"> + <marker id="arrow-UnconditionalBranch" class="arrow UnconditionalBranch" viewBox="0 0 10 10" refX="10" refY="5" markerUnits="strokeWidth" markerWidth="8" markerHeight="6" orient="auto"> <path d="M 0 0 L 10 5 L 0 10 z" /> </marker> - <marker id="arrow-UnconditionalBranch" class="arrow UnconditionalBranch" viewBox="0 0 10 10" refX="11" refY="5" markerUnits="strokeWidth" markerWidth="4" markerHeight="3" orient="auto"> + <marker id="arrow-IndirectBranch" class="arrow IndirectBranch" viewBox="0 0 10 10" refX="10" refY="5" markerUnits="strokeWidth" markerWidth="8" markerHeight="6" orient="auto"> <path d="M 0 0 L 10 5 L 0 10 z" /> </marker> </defs> @@ -68,25 +125,28 @@ def render_svg(function): for i,block in enumerate(graph.blocks): #Calculate basic block location and coordinates - x = int((block.x) * widthconst) - y = int((block.y) * heightconst) - width = int((block.width) * widthconst) - height = int((block.height) * heightconst) + x = ((block.x) * widthconst) + y = ((block.y) * heightconst) + width = ((block.width) * widthconst) + height = ((block.height) * heightconst) #Render block - output += ' <g id="basicblock{i}" class="basicblock">\n' - output += ' <title>Basic Block {i}</title>\n' - output += ' <rect style="fill:grey;stroke:black;" x="{x}" y="{y}" height="{height}" width="{width}"/>\n'.format(i=i,x=x,y=y,width=width,height=height) + 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) #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 += ' <text x="{x}" y="{y}">\n'.format(x=x,y=y + (i + 1) * heightconst) for i,line in enumerate(block.lines): - output += ' <tspan id="{address}" x="{x}" y="{y}">'.format(x=x,y=y + (i + 0.7) * heightconst,address=hex(line.address)[:-1]) + output += ' <tspan id="instr-{address}" x="{x}" y="{y}">'.format(x=x,y=y + (i + 0.7) * heightconst,address=hex(line.address)[:-1]) + hover = instruction_data_flow(function, line.address) + output += '<title>{hover}</title>'.format(hover=hover) for token in line.tokens: - output+='<tspan class="{tokentype}">{text}</tspan>'.format(text=token.text,tokentype=token.type) - output += ' </tspan>\n' + # TODO: add hover for hex, function, and reg tokens + output+='<tspan class="{tokentype}">{text}</tspan>'.format(text=escape(token.text),tokentype=token.type) + output += '</tspan>\n' output += ' </text>\n' output += ' </g>\n' @@ -100,7 +160,7 @@ def render_svg(function): edges += ' <polyline class="edge {type}" points="{points}" marker-end="url(#arrow-{type})"/>\n'.format(type=edge.type,points=points) output += ' ' + edges + '\n' output += ' </g>\n' - output += '</svg>' + output += '</svg></html>' return output -PluginCommand.register_for_function("Export to SVG", "Exports an SVG to your home folder for the given function", save_svg) +PluginCommand.register_for_function("Export to SVG", "Exports an SVG of the current function to your home folder.", save_svg) diff --git a/python/examples/instruction-iterator.py b/python/examples/instruction-iterator.py index 7b03b095..43bc000e 100644 --- a/python/examples/instruction-iterator.py +++ b/python/examples/instruction-iterator.py @@ -21,10 +21,7 @@ else: target = "/bin/ls" bv = binaryninja.BinaryViewType[bintype].open(target) -bv.update_analysis() - -"""Until update_analysis_and_wait is complete, sleep is necessary as the analysis is multi-threaded.""" -time.sleep(1) +bv.update_analysis_and_wait() print "-------- %s --------" % target print "START: 0x%x" % bv.start |
