summaryrefslogtreecommitdiff
path: root/python
diff options
context:
space:
mode:
authorbambu <bambu@revengsec.com>2016-07-02 14:34:31 -0400
committerbambu <bambu@revengsec.com>2016-07-02 14:34:31 -0400
commit46fe170c40dd0de44b83ebbce673462b5de8760c (patch)
tree104943992de2f194c7e2c6cf46f3153b98fbb133 /python
parent5abb8218e5cd2be9b4cefb16eebdee6caf8108c6 (diff)
parenta7eaa72caa4a5e3e7cd7ea7c32236c93b06d1442 (diff)
Merge remote-tracking branch 'upstream/dev' into dev
Diffstat (limited to 'python')
-rw-r--r--python/__init__.py116
-rw-r--r--python/examples/arm-syscall.py5
-rw-r--r--python/examples/bin-info.py5
-rwxr-xr-xpython/examples/export-svg.py172
-rw-r--r--python/examples/instruction-iterator.py5
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 = {
+ "'": "&#39;",
+ ">": "&#62;",
+ "<": "&#60;",
+ '"': "&#34;",
+ ' ': "&#160;"
+}
+
+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