summaryrefslogtreecommitdiff
path: root/python
diff options
context:
space:
mode:
Diffstat (limited to 'python')
-rw-r--r--python/__init__.py1
-rw-r--r--python/architecture.py5
-rw-r--r--python/basicblock.py25
-rw-r--r--python/binaryview.py52
-rw-r--r--python/downloadprovider.py269
-rw-r--r--python/function.py78
-rw-r--r--python/interaction.py6
-rw-r--r--python/log.py3
-rw-r--r--python/lowlevelil.py19
-rw-r--r--python/mediumlevelil.py33
-rw-r--r--python/platform.py5
-rw-r--r--python/plugin.py237
-rw-r--r--python/pluginmanager.py3
-rw-r--r--python/scriptingprovider.py13
-rw-r--r--python/transform.py5
15 files changed, 717 insertions, 37 deletions
diff --git a/python/__init__.py b/python/__init__.py
index 729e4f8a..fda5f297 100644
--- a/python/__init__.py
+++ b/python/__init__.py
@@ -50,6 +50,7 @@ from .lineardisassembly import *
from .undoaction import *
from .highlight import *
from .scriptingprovider import *
+from .downloadprovider import *
from .pluginmanager import *
from .setting import *
from .metadata import *
diff --git a/python/architecture.py b/python/architecture.py
index c5f87ec6..df22da7f 100644
--- a/python/architecture.py
+++ b/python/architecture.py
@@ -392,6 +392,11 @@ class Architecture(object):
return ctypes.addressof(self.handle.contents) != ctypes.addressof(value.handle.contents)
@property
+ def list(self):
+ """Allow tab completion to discover metaclass list property"""
+ pass
+
+ @property
def full_width_regs(self):
"""List of full width register strings (read-only)"""
count = ctypes.c_ulonglong()
diff --git a/python/basicblock.py b/python/basicblock.py
index 1d932c5e..a79b53ad 100644
--- a/python/basicblock.py
+++ b/python/basicblock.py
@@ -256,6 +256,21 @@ class BasicBlock(object):
def highlight(self, value):
self.set_user_highlight(value)
+ @property
+ def is_il(self):
+ """Whether the basic block contains IL"""
+ return core.BNIsILBasicBlock(self.handle)
+
+ @property
+ def is_low_level_il(self):
+ """Whether the basic block contains Low Level IL"""
+ return core.BNIsLowLevelILBasicBlock(self.handle)
+
+ @property
+ def is_medium_level_il(self):
+ """Whether the basic block contains Medium Level IL"""
+ return core.BNIsMediumLevelILBasicBlock(self.handle)
+
@classmethod
def get_iterated_dominance_frontier(self, blocks):
if len(blocks) == 0:
@@ -293,10 +308,12 @@ class BasicBlock(object):
idx = start
while idx < end:
- data = self.view.read(idx, 16)
+ data = self.view.read(idx, self.arch.max_instr_length)
inst_info = self.arch.get_instruction_info(data, idx)
inst_text = self.arch.get_instruction_text(data, idx)
+ if inst_info is None:
+ break
yield inst_text
idx += inst_info.length
@@ -322,6 +339,10 @@ class BasicBlock(object):
result = []
for i in xrange(0, count.value):
addr = lines[i].addr
+ if (lines[i].instrIndex != 0xffffffffffffffff) and hasattr(self, 'il_function'):
+ il_instr = self.il_function[lines[i].instrIndex]
+ else:
+ il_instr = None
tokens = []
for j in xrange(0, lines[i].count):
token_type = InstructionTextTokenType(lines[i].tokens[j].type)
@@ -333,7 +354,7 @@ class BasicBlock(object):
confidence = lines[i].tokens[j].confidence
address = lines[i].tokens[j].address
tokens.append(function.InstructionTextToken(token_type, text, value, size, operand, context, address, confidence))
- result.append(function.DisassemblyTextLine(addr, tokens))
+ result.append(function.DisassemblyTextLine(addr, tokens, il_instr))
core.BNFreeDisassemblyTextLines(lines, count.value)
return result
diff --git a/python/binaryview.py b/python/binaryview.py
index 0cf25e70..1e078523 100644
--- a/python/binaryview.py
+++ b/python/binaryview.py
@@ -145,6 +145,27 @@ class AnalysisCompletionEvent(object):
core.BNCancelAnalysisCompletionEvent(self.handle)
+class ActiveAnalysisInfo(object):
+ def __init__(self, func, analysis_time, update_count, submit_count):
+ self.func = func
+ self.analysis_time = analysis_time
+ self.update_count = update_count
+ self.submit_count = submit_count
+
+ def __repr__(self):
+ return "<ActiveAnalysisInfo %s, analysis_time %d, update_count %d, submit_count %d>" % (self.func, self.analysis_time, self.update_count, self.submit_count)
+
+
+class AnalysisInfo(object):
+ def __init__(self, state, analysis_time, active_info):
+ self.state = AnalysisState(state)
+ self.analysis_time = analysis_time
+ self.active_info = active_info
+
+ def __repr__(self):
+ return "<AnalysisInfo %s, analysis_time %d, active_info %s>" % (self.state, self.analysis_time, self.active_info)
+
+
class AnalysisProgress(object):
def __init__(self, state, count, total):
self.state = state
@@ -156,6 +177,8 @@ class AnalysisProgress(object):
return "Disassembling (%d/%d)" % (self.count, self.total)
if self.state == AnalysisState.AnalyzeState:
return "Analyzing (%d/%d)" % (self.count, self.total)
+ if self.state == AnalysisState.ExtendedAnalyzeState:
+ return "Extended Analysis"
return "Idle"
def __repr__(self):
@@ -343,6 +366,11 @@ class BinaryViewType(object):
return ctypes.addressof(self.handle.contents) != ctypes.addressof(value.handle.contents)
@property
+ def list(self):
+ """Allow tab completion to discover metaclass list property"""
+ pass
+
+ @property
def name(self):
"""BinaryView name (read-only)"""
return core.BNGetBinaryViewTypeName(self.handle)
@@ -934,6 +962,20 @@ class BinaryView(object):
self.file.saved = value
@property
+ def analysis_info(self):
+ """Relevant analysis information with list of functions under active analysis (read-only)"""
+ info_ref = core.BNGetAnalysisInfo(self.handle)
+ info = info_ref[0]
+ active_info_list = []
+ for i in xrange(0, info.count):
+ func = function.Function(self, core.BNNewFunctionReference(info.activeInfo[i].func))
+ active_info = ActiveAnalysisInfo(func, info.activeInfo[i].analysisTime, info.activeInfo[i].updateCount, info.activeInfo[i].submitCount)
+ active_info_list.append(active_info)
+ result = AnalysisInfo(info.state, info.analysisTime, active_info_list)
+ core.BNFreeAnalysisInfo(info_ref)
+ return result
+
+ @property
def analysis_progress(self):
"""Status of current analysis (read-only)"""
result = core.BNGetAnalysisProgress(self.handle)
@@ -1025,6 +1067,14 @@ class BinaryView(object):
return function.RegisterValue(self.arch, result.value, confidence = result.confidence)
@property
+ def parameters_for_analysis(self):
+ return core.BNGetParametersForAnalysis(self.handle)
+
+ @parameters_for_analysis.setter
+ def parameters_for_analysis(self, params):
+ core.BNSetParametersForAnalysis(self.handle, params)
+
+ @property
def max_function_size_for_analysis(self):
"""Maximum size of function (sum of basic block sizes in bytes) for auto analysis"""
return core.BNGetMaxFunctionSizeForAnalysis(self.handle)
@@ -2720,6 +2770,8 @@ class BinaryView(object):
if start is None:
strings = core.BNGetStrings(self.handle, count)
else:
+ if length is None:
+ length = self.end - start
strings = core.BNGetStringsInRange(self.handle, start, length, count)
result = []
for i in xrange(0, count.value):
diff --git a/python/downloadprovider.py b/python/downloadprovider.py
new file mode 100644
index 00000000..d34ca358
--- /dev/null
+++ b/python/downloadprovider.py
@@ -0,0 +1,269 @@
+# Copyright (c) 2015-2018 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.
+
+
+import abc
+import code
+import ctypes
+import sys
+import traceback
+
+# Binary Ninja Components
+import binaryninja._binaryninjacore as core
+from binaryninja.setting import Setting
+from binaryninja import startup
+from binaryninja import log
+
+
+class DownloadInstance(object):
+ def __init__(self, provider, handle = None):
+ if handle is None:
+ self._cb = core.BNDownloadInstanceCallbacks()
+ self._cb.context = 0
+ self._cb.destroyInstance = self._cb.destroyInstance.__class__(self._destroy_instance)
+ self._cb.performRequest = self._cb.performRequest.__class__(self._perform_request)
+ self.handle = core.BNInitDownloadInstance(provider.handle, self._cb)
+ else:
+ self.handle = core.handle_of_type(handle, core.BNDownloadInstance)
+ self._outputCallbacks = None
+
+ def __del__(self):
+ core.BNFreeDownloadInstance(self.handle)
+
+ def _destroy_instance(self, ctxt):
+ try:
+ self.perform_destroy_instance()
+ except:
+ log.log_error(traceback.format_exc())
+
+ def _perform_request(self, ctxt, url):
+ try:
+ return self.perform_request(url)
+ except:
+ log.log_error(traceback.format_exc())
+ return -1
+
+ @abc.abstractmethod
+ def perform_destroy_instance(self):
+ raise NotImplementedError
+
+ @abc.abstractmethod
+ def perform_request(self, ctxt, url):
+ raise NotImplementedError
+
+ def perform_request(self, url, callbacks):
+ return core.BNPerformDownloadRequest(self.handle, url, callbacks)
+
+
+class _DownloadProviderMetaclass(type):
+ @property
+ def list(self):
+ """List all DownloadProvider types (read-only)"""
+ startup._init_plugins()
+ count = ctypes.c_ulonglong()
+ types = core.BNGetDownloadProviderList(count)
+ result = []
+ for i in xrange(0, count.value):
+ result.append(DownloadProvider(types[i]))
+ core.BNFreeDownloadProviderList(types)
+ return result
+
+ def __iter__(self):
+ startup._init_plugins()
+ count = ctypes.c_ulonglong()
+ types = core.BNGetDownloadProviderList(count)
+ try:
+ for i in xrange(0, count.value):
+ yield DownloadProvider(types[i])
+ finally:
+ core.BNFreeDownloadProviderList(types)
+
+ def __getitem__(self, value):
+ startup._init_plugins()
+ provider = core.BNGetDownloadProviderByName(str(value))
+ if provider is None:
+ raise KeyError("'%s' is not a valid download provider" % str(value))
+ return DownloadProvider(provider)
+
+ def __setattr__(self, name, value):
+ try:
+ type.__setattr__(self, name, value)
+ except AttributeError:
+ raise AttributeError("attribute '%s' is read only" % name)
+
+
+class DownloadProvider(object):
+ __metaclass__ = _DownloadProviderMetaclass
+ name = None
+ instance_class = None
+ _registered_providers = []
+
+ def __init__(self, handle = None):
+ if handle is not None:
+ self.handle = core.handle_of_type(handle, core.BNDownloadProvider)
+ self.__dict__["name"] = core.BNGetDownloadProviderName(handle)
+
+ def register(self):
+ self._cb = core.BNDownloadProviderCallbacks()
+ self._cb.context = 0
+ self._cb.createInstance = self._cb.createInstance.__class__(self._create_instance)
+ self.handle = core.BNRegisterDownloadProvider(self.__class__.name, self._cb)
+ self.__class__._registered_providers.append(self)
+
+ def _create_instance(self, ctxt):
+ try:
+ result = self.__class__.instance_class(self)
+ if result is None:
+ return None
+ return ctypes.cast(core.BNNewDownloadInstanceReference(result.handle), ctypes.c_void_p).value
+ except:
+ log.log_error(traceback.format_exc())
+ return None
+
+ def create_instance(self):
+ result = core.BNCreateDownloadProviderInstance(self.handle)
+ if result is None:
+ return None
+ return DownloadInstance(self, handle = result)
+
+
+if sys.version_info >= (2, 7, 9):
+ try:
+ from urllib.request import urlopen, build_opener, install_opener, ProxyHandler
+ from urllib.error import URLError
+ except ImportError:
+ from urllib2 import urlopen, build_opener, install_opener, ProxyHandler, URLError
+
+ class PythonDownloadInstance(DownloadInstance):
+ def __init__(self, provider):
+ super(PythonDownloadInstance, self).__init__(provider)
+
+ @abc.abstractmethod
+ def perform_destroy_instance(self):
+ pass
+
+ @abc.abstractmethod
+ def perform_request(self, url):
+ try:
+ proxy_setting = Setting('download-client').get_string('https-proxy')
+ if proxy_setting:
+ opener = build_opener(ProxyHandler({'https': proxy_setting}))
+ install_opener(opener)
+
+ r = urlopen(url.decode("charmap"))
+ total_size = int(r.headers.get('content-length', 0))
+ bytes_sent = 0
+ while True:
+ data = r.read(4096)
+ if not data:
+ break
+ raw_bytes = (ctypes.c_ubyte * len(data)).from_buffer_copy(data)
+ bytes_wrote = core.BNWriteDataForDownloadInstance(self.handle, raw_bytes, len(raw_bytes))
+ if bytes_wrote != len(raw_bytes):
+ core.BNSetErrorForDownloadInstance(self.handle, "Bytes written mismatch!")
+ return -1
+ bytes_sent = bytes_sent + bytes_wrote
+ continue_download = core.BNNotifyProgressForDownloadInstance(self.handle, bytes_sent, total_size)
+ if continue_download is False:
+ core.BNSetErrorForDownloadInstance(self.handle, "Download aborted!")
+ return -1
+
+ if not bytes_sent:
+ core.BNSetErrorForDownloadInstance(self.handle, "Received no data!")
+ return -1
+
+ except URLError as e:
+ core.BNSetErrorForDownloadInstance(self.handle, e.__class__.__name__)
+ return -1
+ except:
+ core.BNSetErrorForDownloadInstance(self.handle, "Unknown Exception!")
+ log.log_error(traceback.format_exc())
+ return -1
+
+ return 0
+
+ class PythonDownloadProvider(DownloadProvider):
+ name = "DefaultDownloadProvider"
+ instance_class = PythonDownloadInstance
+
+ PythonDownloadProvider().register()
+else:
+ try:
+ import requests
+ from requests import pyopenssl
+ class PythonDownloadInstance(DownloadInstance):
+ def __init__(self, provider):
+ super(PythonDownloadInstance, self).__init__(provider)
+
+ @abc.abstractmethod
+ def perform_destroy_instance(self):
+ pass
+
+ @abc.abstractmethod
+ def perform_request(self, url):
+ try:
+ proxy_setting = Setting('download-client').get_string('https-proxy')
+ if proxy_setting:
+ proxies = {"https": proxy_setting}
+ else:
+ proxies = None
+
+ r = requests.get(url.decode("charmap"), proxies=proxies)
+ if not r.ok:
+ core.BNSetErrorForDownloadInstance(self.handle, "Received error from server")
+ return -1
+ data = r.content
+ if len(data) == 0:
+ core.BNSetErrorForDownloadInstance(self.handle, "No data received from server!")
+ return -1
+ raw_bytes = (ctypes.c_ubyte * len(data)).from_buffer_copy(data)
+ bytes_wrote = core.BNWriteDataForDownloadInstance(self.handle, raw_bytes, len(raw_bytes))
+ if bytes_wrote != len(raw_bytes):
+ core.BNSetErrorForDownloadInstance(self.handle, "Bytes written mismatch!")
+ return -1
+ continue_download = core.BNNotifyProgressForDownloadInstance(self.handle, bytes_wrote, bytes_wrote)
+ if continue_download is False:
+ core.BNSetErrorForDownloadInstance(self.handle, "Download aborted!")
+ return -1
+ except requests.RequestException as e:
+ core.BNSetErrorForDownloadInstance(self.handle, e.__class__.__name__)
+ return -1
+ except:
+ core.BNSetErrorForDownloadInstance(self.handle, "Unknown Exception!")
+ log.log_error(traceback.format_exc())
+ return -1
+
+ return 0
+
+ class PythonDownloadProvider(DownloadProvider):
+ name = "DefaultDownloadProvider"
+ instance_class = PythonDownloadInstance
+
+ PythonDownloadProvider().register()
+ except ImportError:
+ if sys.platform == "win32":
+ log.log_error("Provided Python version is too old! Only Python 2.7.10 and above are known to work on Windows!")
+ else:
+ log.log_error("On Python versions below 2.7.9, the pip requests[security] package is required for network connectivity!")
+ log.log_error("On an Ubuntu 14.04 install, the following three commands are sufficient to enable networking for the current user:")
+ log.log_error(" sudo apt install python-pip")
+ log.log_error(" python -m pip install pip --upgrade --user")
+ log.log_error(" python -m pip install requests[security] --upgrade --user")
+
diff --git a/python/function.py b/python/function.py
index 1cc4dcac..2f794f47 100644
--- a/python/function.py
+++ b/python/function.py
@@ -24,7 +24,7 @@ import ctypes
# Binary Ninja components
import _binaryninjacore as core
-from enums import (FunctionGraphType, BranchType, SymbolType, InstructionTextTokenType,
+from enums import (AnalysisSkipReason, FunctionGraphType, BranchType, SymbolType, InstructionTextTokenType,
HighlightStandardColor, HighlightColorStyle, RegisterValueType, ImplicitRegisterExtend,
DisassemblyOption, IntegerDisplayType, InstructionTextTokenContext, VariableSourceType,
FunctionAnalysisSkipOverride)
@@ -828,6 +828,11 @@ class Function(object):
"""Whether automatic analysis was skipped for this function"""
return core.BNIsFunctionAnalysisSkipped(self.handle)
+ @property
+ def analysis_skip_reason(self):
+ """Function analysis skip reason"""
+ return AnalysisSkipReason(core.BNGetAnalysisSkipReason(self.handle))
+
@analysis_skipped.setter
def analysis_skipped(self, skip):
if skip:
@@ -1598,9 +1603,10 @@ class AdvancedFunctionAnalysisDataRequestor(object):
class DisassemblyTextLine(object):
- def __init__(self, addr, tokens):
+ def __init__(self, addr, tokens, il_instr = None):
self.address = addr
self.tokens = tokens
+ self.il_instruction = il_instr
def __str__(self):
result = ""
@@ -1625,8 +1631,9 @@ class FunctionGraphEdge(object):
class FunctionGraphBlock(object):
- def __init__(self, handle):
+ def __init__(self, handle, graph):
self.handle = handle
+ self.graph = graph
def __del__(self):
core.BNFreeFunctionGraphBlock(self.handle)
@@ -1645,13 +1652,22 @@ class FunctionGraphBlock(object):
def basic_block(self):
"""Basic block associated with this part of the function graph (read-only)"""
block = core.BNGetFunctionGraphBasicBlock(self.handle)
- func = core.BNGetBasicBlockFunction(block)
- if func is None:
+ func_handle = core.BNGetBasicBlockFunction(block)
+ if func_handle is None:
core.BNFreeBasicBlock(block)
- block = None
+ return None
+
+ view = binaryview.BinaryView(handle = core.BNGetFunctionData(func_handle))
+ func = Function(view, func_handle)
+
+ if core.BNIsLowLevelILBasicBlock(block):
+ block = lowlevelil.LowLevelILBasicBlock(view, block,
+ lowlevelil.LowLevelILFunction(func.arch, core.BNGetBasicBlockLowLevelILFunction(block), func))
+ elif core.BNIsMediumLevelILBasicBlock(block):
+ block = mediumlevelil.MediumLevelILBasicBlock(view, block,
+ mediumlevelil.MediumLevelILFunction(func.arch, core.BNGetBasicBlockMediumLevelILFunction(block), func))
else:
- block = basicblock.BasicBlock(binaryview.BinaryView(handle = core.BNGetFunctionData(func)), block)
- core.BNFreeFunction(func)
+ block = basicblock.BasicBlock(view, block)
return block
@property
@@ -1697,9 +1713,14 @@ class FunctionGraphBlock(object):
"""Function graph block list of lines (read-only)"""
count = ctypes.c_ulonglong()
lines = core.BNGetFunctionGraphBlockLines(self.handle, count)
+ block = self.basic_block
result = []
for i in xrange(0, count.value):
addr = lines[i].addr
+ if (lines[i].instrIndex != 0xffffffffffffffff) and hasattr(block, 'il_function'):
+ il_instr = block.il_function[lines[i].instrIndex]
+ else:
+ il_instr = None
tokens = []
for j in xrange(0, lines[i].count):
token_type = InstructionTextTokenType(lines[i].tokens[j].type)
@@ -1711,7 +1732,7 @@ class FunctionGraphBlock(object):
confidence = lines[i].tokens[j].confidence
address = lines[i].tokens[j].address
tokens.append(InstructionTextToken(token_type, text, value, size, operand, context, address, confidence))
- result.append(DisassemblyTextLine(addr, tokens))
+ result.append(DisassemblyTextLine(addr, tokens, il_instr))
core.BNFreeDisassemblyTextLines(lines, count.value)
return result
@@ -1756,9 +1777,14 @@ class FunctionGraphBlock(object):
def __iter__(self):
count = ctypes.c_ulonglong()
lines = core.BNGetFunctionGraphBlockLines(self.handle, count)
+ block = self.basic_block
try:
for i in xrange(0, count.value):
addr = lines[i].addr
+ if (lines[i].instrIndex != 0xffffffffffffffff) and hasattr(block, 'il_function'):
+ il_instr = block.il_function[lines[i].instrIndex]
+ else:
+ il_instr = None
tokens = []
for j in xrange(0, lines[i].count):
token_type = InstructionTextTokenType(lines[i].tokens[j].type)
@@ -1770,7 +1796,7 @@ class FunctionGraphBlock(object):
confidence = lines[i].tokens[j].confidence
address = lines[i].tokens[j].address
tokens.append(InstructionTextToken(token_type, text, value, size, operand, context, address, confidence))
- yield DisassemblyTextLine(addr, tokens)
+ yield DisassemblyTextLine(addr, tokens, il_instr)
finally:
core.BNFreeDisassemblyTextLines(lines, count.value)
@@ -1858,7 +1884,7 @@ class FunctionGraph(object):
blocks = core.BNGetFunctionGraphBlocks(self.handle, count)
result = []
for i in xrange(0, count.value):
- result.append(FunctionGraphBlock(core.BNNewFunctionGraphBlockReference(blocks[i])))
+ result.append(FunctionGraphBlock(core.BNNewFunctionGraphBlockReference(blocks[i]), self))
core.BNFreeFunctionGraphBlockList(blocks, count.value)
return result
@@ -1897,6 +1923,32 @@ class FunctionGraph(object):
def settings(self):
return DisassemblySettings(core.BNGetFunctionGraphSettings(self.handle))
+ @property
+ def is_il(self):
+ return core.BNIsILFunctionGraph(self.handle)
+
+ @property
+ def is_low_level_il(self):
+ return core.BNIsLowLevelILFunctionGraph(self.handle)
+
+ @property
+ def is_medium_level_il(self):
+ return core.BNIsMediumLevelILFunctionGraph(self.handle)
+
+ @property
+ def il_function(self):
+ if self.is_low_level_il:
+ il_func = core.BNGetFunctionGraphLowLevelILFunction(self.handle)
+ if not il_func:
+ return None
+ return lowlevelil.LowLevelILFunction(self.function.arch, il_func, self.function)
+ if self.is_medium_level_il:
+ il_func = core.BNGetFunctionGraphMediumLevelILFunction(self.handle)
+ if not il_func:
+ return None
+ return mediumlevelil.MediumLevelILFunction(self.function.arch, il_func, self.function)
+ return None
+
def __setattr__(self, name, value):
try:
object.__setattr__(self, name, value)
@@ -1911,7 +1963,7 @@ class FunctionGraph(object):
blocks = core.BNGetFunctionGraphBlocks(self.handle, count)
try:
for i in xrange(0, count.value):
- yield FunctionGraphBlock(core.BNNewFunctionGraphBlockReference(blocks[i]))
+ yield FunctionGraphBlock(core.BNNewFunctionGraphBlockReference(blocks[i]), self)
finally:
core.BNFreeFunctionGraphBlockList(blocks, count.value)
@@ -1954,7 +2006,7 @@ class FunctionGraph(object):
blocks = core.BNGetFunctionGraphBlocksInRegion(self.handle, left, top, right, bottom, count)
result = []
for i in xrange(0, count.value):
- result.append(FunctionGraphBlock(core.BNNewFunctionGraphBlockReference(blocks[i])))
+ result.append(FunctionGraphBlock(core.BNNewFunctionGraphBlockReference(blocks[i]), self))
core.BNFreeFunctionGraphBlockList(blocks, count.value)
return result
diff --git a/python/interaction.py b/python/interaction.py
index 4f6ed67d..5b7ec497 100644
--- a/python/interaction.py
+++ b/python/interaction.py
@@ -669,11 +669,9 @@ def get_save_filename_input(prompt, ext="", default_name=""):
def get_directory_name_input(prompt, default_name=""):
"""
- ``get_directory_name_input`` prompts the user for a directory name to save as, optionally providing and
- default_name.
+ ``get_directory_name_input`` prompts the user for a directory name to save as, optionally providing a default_name.
- Note: This API function differently on the command line vs. the UI. In the UI a popup is used. On the commandline
- a simple text prompt is used. The ui uses the native window popup for file selection.
+ Note: This API function differently on the command line vs. the UI. In the UI a popup is used. On the commandline a simple text prompt is used. The ui uses the native window popup for file selection.
:param str prompt: Prompt to display.
:param str default_name: Optional, default directory name.
diff --git a/python/log.py b/python/log.py
index f7144183..b1fd7095 100644
--- a/python/log.py
+++ b/python/log.py
@@ -21,6 +21,7 @@
# Binary Ninja components
import _binaryninjacore as core
+from enums import LogLevel
_output_to_log = False
@@ -135,7 +136,7 @@ def log_alert(text):
core.BNLogAlert("%s", str(text))
-def log_to_stdout(min_level):
+def log_to_stdout(min_level=LogLevel.InfoLog):
"""
``log_to_stdout`` redirects minimum log level to standard out.
diff --git a/python/lowlevelil.py b/python/lowlevelil.py
index ab6170a5..bd1e9349 100644
--- a/python/lowlevelil.py
+++ b/python/lowlevelil.py
@@ -260,6 +260,7 @@ class LowLevelILInstruction(object):
LowLevelILOperation.LLIL_JUMP_TO: [("dest", "expr"), ("targets", "int_list")],
LowLevelILOperation.LLIL_CALL: [("dest", "expr")],
LowLevelILOperation.LLIL_CALL_STACK_ADJUST: [("dest", "expr"), ("stack_adjustment", "int"), ("reg_stack_adjustments", "reg_stack_adjust")],
+ LowLevelILOperation.LLIL_TAILCALL: [("dest", "expr")],
LowLevelILOperation.LLIL_RET: [("dest", "expr")],
LowLevelILOperation.LLIL_NORET: [],
LowLevelILOperation.LLIL_IF: [("condition", "expr"), ("true", "int"), ("false", "int")],
@@ -328,6 +329,7 @@ class LowLevelILInstruction(object):
LowLevelILOperation.LLIL_FLAG_BIT_SSA: [("src", "flag_ssa"), ("bit", "int")],
LowLevelILOperation.LLIL_CALL_SSA: [("output", "expr"), ("dest", "expr"), ("stack", "expr"), ("param", "expr")],
LowLevelILOperation.LLIL_SYSCALL_SSA: [("output", "expr"), ("stack", "expr"), ("param", "expr")],
+ LowLevelILOperation.LLIL_TAILCALL_SSA: [("output", "expr"), ("dest", "expr"), ("stack", "expr"), ("param", "expr")],
LowLevelILOperation.LLIL_CALL_OUTPUT_SSA: [("dest_memory", "int"), ("dest", "reg_ssa_list")],
LowLevelILOperation.LLIL_CALL_STACK_SSA: [("src", "reg_ssa"), ("src_memory", "int")],
LowLevelILOperation.LLIL_CALL_PARAM: [("src", "expr_list")],
@@ -1592,10 +1594,20 @@ class LowLevelILFunction(object):
"""
return self.expr(LowLevelILOperation.LLIL_CALL_STACK_ADJUST, dest.index, stack_adjust)
+ def tailcall(self, dest):
+ """
+ ``tailcall`` returns an expression which jumps (branches) to the expression ``dest``
+
+ :param LowLevelILExpr dest: the expression to jump to
+ :return: The expression ``tailcall(dest)``
+ :rtype: LowLevelILExpr
+ """
+ return self.expr(LowLevelILOperation.LLIL_TAILCALL, dest.index)
+
def ret(self, dest):
"""
``ret`` returns an expression which jumps (branches) to the expression ``dest``. ``ret`` is a special alias for
- jump that makes the disassembler top disassembling.
+ jump that makes the disassembler stop disassembling.
:param LowLevelILExpr dest: the expression to jump to
:return: The expression ``jump(dest)``
@@ -1797,8 +1809,9 @@ class LowLevelILFunction(object):
param_list = []
for param in params:
param_list.append(param.index)
- return self.expr(LowLevelILOperation.LLIL_INTRINSIC, len(outputs), self.add_operand_list(output_list),
- self.arch.get_intrinsic_index(intrinsic), len(params), self.add_operand_list(param_list), flags = flags)
+ call_param = self.expr(LowLevelILOperation.LLIL_CALL_PARAM, len(params), self.add_operand_list(param_list).index)
+ return self.expr(LowLevelILOperation.LLIL_INTRINSIC, len(outputs), self.add_operand_list(output_list).index,
+ self.arch.get_intrinsic_index(intrinsic), call_param.index, flags = flags)
def breakpoint(self):
"""
diff --git a/python/mediumlevelil.py b/python/mediumlevelil.py
index caf19e8e..100795fe 100644
--- a/python/mediumlevelil.py
+++ b/python/mediumlevelil.py
@@ -148,6 +148,8 @@ class MediumLevelILInstruction(object):
MediumLevelILOperation.MLIL_ADD_OVERFLOW: [("left", "expr"), ("right", "expr")],
MediumLevelILOperation.MLIL_SYSCALL: [("output", "var_list"), ("params", "expr_list")],
MediumLevelILOperation.MLIL_SYSCALL_UNTYPED: [("output", "expr"), ("params", "expr"), ("stack", "expr")],
+ MediumLevelILOperation.MLIL_TAILCALL: [("output", "var_list"), ("dest", "expr"), ("params", "expr_list")],
+ MediumLevelILOperation.MLIL_TAILCALL_UNTYPED: [("output", "expr"), ("dest", "expr"), ("params", "expr"), ("stack", "expr")],
MediumLevelILOperation.MLIL_BP: [],
MediumLevelILOperation.MLIL_TRAP: [("vector", "int")],
MediumLevelILOperation.MLIL_INTRINSIC: [("output", "var_list"), ("intrinsic", "intrinsic"), ("params", "expr_list")],
@@ -193,6 +195,8 @@ class MediumLevelILInstruction(object):
MediumLevelILOperation.MLIL_CALL_UNTYPED_SSA: [("output", "expr"), ("dest", "expr"), ("params", "expr"), ("stack", "expr")],
MediumLevelILOperation.MLIL_SYSCALL_SSA: [("output", "expr"), ("params", "expr_list"), ("src_memory", "int")],
MediumLevelILOperation.MLIL_SYSCALL_UNTYPED_SSA: [("output", "expr"), ("params", "expr"), ("stack", "expr")],
+ MediumLevelILOperation.MLIL_TAILCALL_SSA: [("output", "expr"), ("dest", "expr"), ("params", "expr_list"), ("src_memory", "int")],
+ MediumLevelILOperation.MLIL_TAILCALL_UNTYPED_SSA: [("output", "expr"), ("dest", "expr"), ("params", "expr"), ("stack", "expr")],
MediumLevelILOperation.MLIL_CALL_OUTPUT_SSA: [("dest_memory", "int"), ("dest", "var_ssa_list")],
MediumLevelILOperation.MLIL_CALL_PARAM_SSA: [("src_memory", "int"), ("src", "var_ssa_list")],
MediumLevelILOperation.MLIL_LOAD_SSA: [("src", "expr"), ("src_memory", "int")],
@@ -410,11 +414,12 @@ class MediumLevelILInstruction(object):
return [self.dest]
elif self.operation in [MediumLevelILOperation.MLIL_SET_VAR_SPLIT, MediumLevelILOperation.MLIL_SET_VAR_SPLIT_SSA]:
return [self.high, self.low]
- elif self.operation in [MediumLevelILOperation.MLIL_CALL, MediumLevelILOperation.MLIL_SYSCALL]:
+ elif self.operation in [MediumLevelILOperation.MLIL_CALL, MediumLevelILOperation.MLIL_SYSCALL, MediumLevelILOperation.MLIL_TAILCALL]:
return self.output
- elif self.operation in [MediumLevelILOperation.MLIL_CALL_UNTYPED, MediumLevelILOperation.MLIL_SYSCALL_UNTYPED,
+ elif self.operation in [MediumLevelILOperation.MLIL_CALL_UNTYPED, MediumLevelILOperation.MLIL_SYSCALL_UNTYPED, MediumLevelILOperation.MLIL_TAILCALL_UNTYPED,
MediumLevelILOperation.MLIL_CALL_SSA, MediumLevelILOperation.MLIL_CALL_UNTYPED_SSA,
- MediumLevelILOperation.MLIL_SYSCALL_SSA, MediumLevelILOperation.MLIL_SYSCALL_UNTYPED_SSA]:
+ MediumLevelILOperation.MLIL_SYSCALL_SSA, MediumLevelILOperation.MLIL_SYSCALL_UNTYPED_SSA,
+ MediumLevelILOperation.MLIL_TAILCALL_SSA, MediumLevelILOperation.MLIL_TAILCALL_UNTYPED_SSA]:
return self.output.vars_written
elif self.operation in [MediumLevelILOperation.MLIL_CALL_OUTPUT, MediumLevelILOperation.MLIL_CALL_OUTPUT_SSA]:
return self.dest
@@ -430,14 +435,14 @@ class MediumLevelILInstruction(object):
elif self.operation in [MediumLevelILOperation.MLIL_SET_VAR_SSA_FIELD,
MediumLevelILOperation.MLIL_SET_VAR_ALIASED_FIELD]:
return [self.prev] + self.src.vars_read
- elif self.operation in [MediumLevelILOperation.MLIL_CALL, MediumLevelILOperation.MLIL_SYSCALL,
- MediumLevelILOperation.MLIL_CALL_SSA, MediumLevelILOperation.MLIL_SYSCALL_SSA]:
+ elif self.operation in [MediumLevelILOperation.MLIL_CALL, MediumLevelILOperation.MLIL_SYSCALL, MediumLevelILOperation.MLIL_TAILCALL,
+ MediumLevelILOperation.MLIL_CALL_SSA, MediumLevelILOperation.MLIL_SYSCALL_SSA, MediumLevelILOperation.MLIL_TAILCALL_SSA]:
result = []
for param in self.params:
result += param.vars_read
return result
- elif self.operation in [MediumLevelILOperation.MLIL_CALL_UNTYPED, MediumLevelILOperation.MLIL_SYSCALL_UNTYPED,
- MediumLevelILOperation.MLIL_CALL_UNTYPED_SSA, MediumLevelILOperation.MLIL_SYSCALL_UNTYPED_SSA]:
+ elif self.operation in [MediumLevelILOperation.MLIL_CALL_UNTYPED, MediumLevelILOperation.MLIL_SYSCALL_UNTYPED, MediumLevelILOperation.MLIL_TAILCALL_UNTYPED,
+ MediumLevelILOperation.MLIL_CALL_UNTYPED_SSA, MediumLevelILOperation.MLIL_SYSCALL_UNTYPED_SSA, MediumLevelILOperation.MLIL_TAILCALL_UNTYPED_SSA]:
return self.params.vars_read
elif self.operation in [MediumLevelILOperation.MLIL_CALL_PARAM, MediumLevelILOperation.MLIL_CALL_PARAM_SSA,
MediumLevelILOperation.MLIL_VAR_PHI]:
@@ -851,6 +856,20 @@ class MediumLevelILFunction(object):
core.BNFreeILInstructionList(instrs)
return result
+ def is_ssa_var_live(self, ssa_var):
+ """
+ ``is_ssa_var_live`` determines if ``ssa_var`` is live at any point in the function
+
+ :param SSAVariable ssa_var: the SSA variable to query
+ :return: whether the variable is live at any point in the function
+ :rtype: bool
+ """
+ var_data = core.BNVariable()
+ var_data.type = ssa_var.var.source_type
+ var_data.index = ssa_var.var.index
+ var_data.storage = ssa_var.var.storage
+ return core.BNIsMediumLevelILSSAVarLive(self.handle, var_data, ssa_var.version)
+
def get_var_definitions(self, var):
count = ctypes.c_ulonglong()
var_data = core.BNVariable()
diff --git a/python/platform.py b/python/platform.py
index a79e3b9a..dd756178 100644
--- a/python/platform.py
+++ b/python/platform.py
@@ -121,6 +121,11 @@ class Platform(object):
return ctypes.addressof(self.handle.contents) != ctypes.addressof(value.handle.contents)
@property
+ def list(self):
+ """Allow tab completion to discover metaclass list property"""
+ pass
+
+ @property
def default_calling_convention(self):
"""
Default calling convention.
diff --git a/python/plugin.py b/python/plugin.py
index e0054d86..13ca51af 100644
--- a/python/plugin.py
+++ b/python/plugin.py
@@ -30,6 +30,8 @@ import filemetadata
import binaryview
import function
import log
+import lowlevelil
+import mediumlevelil
class PluginCommandContext(object):
@@ -38,6 +40,7 @@ class PluginCommandContext(object):
self.address = 0
self.length = 0
self.function = None
+ self.instruction = None
class _PluginCommandMetaClass(type):
@@ -80,6 +83,11 @@ class PluginCommand(object):
self.description = str(cmd.description)
self.type = PluginCommandType(cmd.type)
+ @property
+ def list(self):
+ """Allow tab completion to discover metaclass list property"""
+ pass
+
@classmethod
def _default_action(cls, view, action):
try:
@@ -118,6 +126,50 @@ class PluginCommand(object):
log.log_error(traceback.format_exc())
@classmethod
+ def _low_level_il_function_action(cls, view, func, action):
+ try:
+ file_metadata = filemetadata.FileMetadata(handle = core.BNGetFileForView(view))
+ view_obj = binaryview.BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view))
+ owner = function.Function(view_obj, core.BNGetLowLevelILOwnerFunction(func))
+ func_obj = lowlevelil.LowLevelILFunction(owner.arch, core.BNNewLowLevelILFunctionReference(func), owner)
+ action(view_obj, func_obj)
+ except:
+ log.log_error(traceback.format_exc())
+
+ @classmethod
+ def _low_level_il_instruction_action(cls, view, func, instr, action):
+ try:
+ file_metadata = filemetadata.FileMetadata(handle = core.BNGetFileForView(view))
+ view_obj = binaryview.BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view))
+ owner = function.Function(view_obj, core.BNGetLowLevelILOwnerFunction(func))
+ func_obj = lowlevelil.LowLevelILFunction(owner.arch, core.BNNewLowLevelILFunctionReference(func), owner)
+ action(view_obj, func_obj[instr])
+ except:
+ log.log_error(traceback.format_exc())
+
+ @classmethod
+ def _medium_level_il_function_action(cls, view, func, action):
+ try:
+ file_metadata = filemetadata.FileMetadata(handle = core.BNGetFileForView(view))
+ view_obj = binaryview.BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view))
+ owner = function.Function(view_obj, core.BNGetMediumLevelILOwnerFunction(func))
+ func_obj = mediumlevelil.MediumLevelILFunction(owner.arch, core.BNNewMediumLevelILFunctionReference(func), owner)
+ action(view_obj, func_obj)
+ except:
+ log.log_error(traceback.format_exc())
+
+ @classmethod
+ def _medium_level_il_instruction_action(cls, view, func, instr, action):
+ try:
+ file_metadata = filemetadata.FileMetadata(handle = core.BNGetFileForView(view))
+ view_obj = binaryview.BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view))
+ owner = function.Function(view_obj, core.BNGetMediumLevelILOwnerFunction(func))
+ func_obj = mediumlevelil.MediumLevelILFunction(owner.arch, core.BNNewMediumLevelILFunctionReference(func), owner)
+ action(view_obj, func_obj[instr])
+ except:
+ log.log_error(traceback.format_exc())
+
+ @classmethod
def _default_is_valid(cls, view, is_valid):
try:
if is_valid is None:
@@ -167,6 +219,62 @@ class PluginCommand(object):
return False
@classmethod
+ def _low_level_il_function_is_valid(cls, view, func, is_valid):
+ try:
+ if is_valid is None:
+ return True
+ file_metadata = filemetadata.FileMetadata(handle = core.BNGetFileForView(view))
+ view_obj = binaryview.BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view))
+ owner = function.Function(view_obj, core.BNGetLowLevelILOwnerFunction(func))
+ func_obj = lowlevelil.LowLevelILFunction(owner.arch, core.BNNewLowLevelILFunctionReference(func), owner)
+ return is_valid(view_obj, func_obj)
+ except:
+ log.log_error(traceback.format_exc())
+ return False
+
+ @classmethod
+ def _low_level_il_instruction_is_valid(cls, view, func, instr, is_valid):
+ try:
+ if is_valid is None:
+ return True
+ file_metadata = filemetadata.FileMetadata(handle = core.BNGetFileForView(view))
+ view_obj = binaryview.BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view))
+ owner = function.Function(view_obj, core.BNGetLowLevelILOwnerFunction(func))
+ func_obj = lowlevelil.LowLevelILFunction(owner.arch, core.BNNewLowLevelILFunctionReference(func), owner)
+ return is_valid(view_obj, func_obj[instr])
+ except:
+ log.log_error(traceback.format_exc())
+ return False
+
+ @classmethod
+ def _medium_level_il_function_is_valid(cls, view, func, is_valid):
+ try:
+ if is_valid is None:
+ return True
+ file_metadata = filemetadata.FileMetadata(handle = core.BNGetFileForView(view))
+ view_obj = binaryview.BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view))
+ owner = function.Function(view_obj, core.BNGetMediumLevelILOwnerFunction(func))
+ func_obj = mediumlevelil.MediumLevelILFunction(owner.arch, core.BNNewMediumLevelILFunctionReference(func), owner)
+ return is_valid(view_obj, func_obj)
+ except:
+ log.log_error(traceback.format_exc())
+ return False
+
+ @classmethod
+ def _medium_level_il_instruction_is_valid(cls, view, func, instr, is_valid):
+ try:
+ if is_valid is None:
+ return True
+ file_metadata = filemetadata.FileMetadata(handle = core.BNGetFileForView(view))
+ view_obj = binaryview.BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view))
+ owner = function.Function(view_obj, core.BNGetMediumLevelILOwnerFunction(func))
+ func_obj = mediumlevelil.MediumLevelILFunction(owner.arch, core.BNNewMediumLevelILFunctionReference(func), owner)
+ return is_valid(view_obj, func_obj[instr])
+ except:
+ log.log_error(traceback.format_exc())
+ return False
+
+ @classmethod
def register(cls, name, description, action, is_valid = None):
"""
``register`` Register a plugin
@@ -243,6 +351,82 @@ class PluginCommand(object):
core.BNRegisterPluginCommandForFunction(name, description, action_obj, is_valid_obj, None)
@classmethod
+ def register_for_low_level_il_function(cls, name, description, action, is_valid = None):
+ """
+ ``register_for_low_level_il_function`` Register a plugin to be called with a low level IL function argument
+
+ :param str name: name of the plugin
+ :param str description: description of the plugin
+ :param action: function to call with the ``BinaryView`` and a ``LowLevelILFunction`` as arguments
+ :param is_valid: optional argument of a function passed a ``BinaryView`` to determine whether the plugin should be enabled for that view
+ :rtype: None
+
+ .. warning:: Calling ``register_for_low_level_il_function`` with the same function name will replace the existing function but will leak the memory of the original plugin.
+ """
+ startup._init_plugins()
+ action_obj = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.POINTER(core.BNBinaryView), ctypes.POINTER(core.BNLowLevelILFunction))(lambda ctxt, view, func: cls._low_level_il_function_action(view, func, action))
+ is_valid_obj = ctypes.CFUNCTYPE(ctypes.c_bool, ctypes.c_void_p, ctypes.POINTER(core.BNBinaryView), ctypes.POINTER(core.BNLowLevelILFunction))(lambda ctxt, view, func: cls._low_level_il_function_is_valid(view, func, is_valid))
+ cls._registered_commands.append((action_obj, is_valid_obj))
+ core.BNRegisterPluginCommandForLowLevelILFunction(name, description, action_obj, is_valid_obj, None)
+
+ @classmethod
+ def register_for_low_level_il_instruction(cls, name, description, action, is_valid = None):
+ """
+ ``register_for_low_level_il_instruction`` Register a plugin to be called with a low level IL instruction argument
+
+ :param str name: name of the plugin
+ :param str description: description of the plugin
+ :param action: function to call with the ``BinaryView`` and a ``LowLevelILInstruction`` as arguments
+ :param is_valid: optional argument of a function passed a ``BinaryView`` to determine whether the plugin should be enabled for that view
+ :rtype: None
+
+ .. warning:: Calling ``register_for_low_level_il_instruction`` with the same function name will replace the existing function but will leak the memory of the original plugin.
+ """
+ startup._init_plugins()
+ action_obj = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.POINTER(core.BNBinaryView), ctypes.POINTER(core.BNLowLevelILFunction), ctypes.c_ulonglong)(lambda ctxt, view, func, instr: cls._low_level_il_instruction_action(view, func, instr, action))
+ is_valid_obj = ctypes.CFUNCTYPE(ctypes.c_bool, ctypes.c_void_p, ctypes.POINTER(core.BNBinaryView), ctypes.POINTER(core.BNLowLevelILFunction), ctypes.c_ulonglong)(lambda ctxt, view, func, instr: cls._low_level_il_instruction_is_valid(view, func, instr, is_valid))
+ cls._registered_commands.append((action_obj, is_valid_obj))
+ core.BNRegisterPluginCommandForLowLevelILInstruction(name, description, action_obj, is_valid_obj, None)
+
+ @classmethod
+ def register_for_medium_level_il_function(cls, name, description, action, is_valid = None):
+ """
+ ``register_for_medium_level_il_function`` Register a plugin to be called with a medium level IL function argument
+
+ :param str name: name of the plugin
+ :param str description: description of the plugin
+ :param action: function to call with the ``BinaryView`` and a ``MediumLevelILFunction`` as arguments
+ :param is_valid: optional argument of a function passed a ``BinaryView`` to determine whether the plugin should be enabled for that view
+ :rtype: None
+
+ .. warning:: Calling ``register_for_medium_level_il_function`` with the same function name will replace the existing function but will leak the memory of the original plugin.
+ """
+ startup._init_plugins()
+ action_obj = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.POINTER(core.BNBinaryView), ctypes.POINTER(core.BNMediumLevelILFunction))(lambda ctxt, view, func: cls._medium_level_il_function_action(view, func, action))
+ is_valid_obj = ctypes.CFUNCTYPE(ctypes.c_bool, ctypes.c_void_p, ctypes.POINTER(core.BNBinaryView), ctypes.POINTER(core.BNMediumLevelILFunction))(lambda ctxt, view, func: cls._medium_level_il_function_is_valid(view, func, is_valid))
+ cls._registered_commands.append((action_obj, is_valid_obj))
+ core.BNRegisterPluginCommandForMediumLevelILFunction(name, description, action_obj, is_valid_obj, None)
+
+ @classmethod
+ def register_for_medium_level_il_instruction(cls, name, description, action, is_valid = None):
+ """
+ ``register_for_medium_level_il_instruction`` Register a plugin to be called with a medium level IL instruction argument
+
+ :param str name: name of the plugin
+ :param str description: description of the plugin
+ :param action: function to call with the ``BinaryView`` and a ``MediumLevelILInstruction`` as arguments
+ :param is_valid: optional argument of a function passed a ``BinaryView`` to determine whether the plugin should be enabled for that view
+ :rtype: None
+
+ .. warning:: Calling ``register_for_medium_level_il_instruction`` with the same function name will replace the existing function but will leak the memory of the original plugin.
+ """
+ startup._init_plugins()
+ action_obj = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.POINTER(core.BNBinaryView), ctypes.POINTER(core.BNMediumLevelILFunction), ctypes.c_ulonglong)(lambda ctxt, view, func, instr: cls._medium_level_il_instruction_action(view, func, instr, action))
+ is_valid_obj = ctypes.CFUNCTYPE(ctypes.c_bool, ctypes.c_void_p, ctypes.POINTER(core.BNBinaryView), ctypes.POINTER(core.BNMediumLevelILFunction), ctypes.c_ulonglong)(lambda ctxt, view, func, instr: cls._medium_level_il_instruction_is_valid(view, func, instr, is_valid))
+ cls._registered_commands.append((action_obj, is_valid_obj))
+ core.BNRegisterPluginCommandForMediumLevelILInstruction(name, description, action_obj, is_valid_obj, None)
+
+ @classmethod
def get_valid_list(cls, context):
"""Dict of registered plugins"""
commands = cls.list
@@ -275,6 +459,36 @@ class PluginCommand(object):
if not self.command.functionIsValid:
return True
return self.command.functionIsValid(self.command.context, context.view.handle, context.function.handle)
+ elif self.command.type == PluginCommandType.LowLevelILFunctionPluginCommand:
+ if context.function is None:
+ return False
+ if not self.command.lowLevelILFunctionIsValid:
+ return True
+ return self.command.lowLevelILFunctionIsValid(self.command.context, context.view.handle, context.function.handle)
+ elif self.command.type == PluginCommandType.LowLevelILInstructionPluginCommand:
+ if context.instruction is None:
+ return False
+ if not isinstance(context.instruction, lowlevelil.LowLevelILInstruction):
+ return False
+ if not self.command.lowLevelILInstructionIsValid:
+ return True
+ return self.command.lowLevelILInstructionIsValid(self.command.context, context.view.handle,
+ context.instruction.function.handle, context.instruction.instr_index)
+ elif self.command.type == PluginCommandType.MediumLevelILFunctionPluginCommand:
+ if context.function is None:
+ return False
+ if not self.command.mediumLevelILFunctionIsValid:
+ return True
+ return self.command.mediumLevelILFunctionIsValid(self.command.context, context.view.handle, context.function.handle)
+ elif self.command.type == PluginCommandType.MediumLevelILInstructionPluginCommand:
+ if context.instruction is None:
+ return False
+ if not isinstance(context.instruction, mediumlevelil.MediumLevelILInstruction):
+ return False
+ if not self.command.mediumLevelILInstructionIsValid:
+ return True
+ return self.command.mediumLevelILInstructionIsValid(self.command.context, context.view.handle,
+ context.instruction.function.handle, context.instruction.instr_index)
return False
def execute(self, context):
@@ -288,6 +502,16 @@ class PluginCommand(object):
self.command.rangeCommand(self.command.context, context.view.handle, context.address, context.length)
elif self.command.type == PluginCommandType.FunctionPluginCommand:
self.command.functionCommand(self.command.context, context.view.handle, context.function.handle)
+ elif self.command.type == PluginCommandType.LowLevelILFunctionPluginCommand:
+ self.command.lowLevelILFunctionCommand(self.command.context, context.view.handle, context.function.handle)
+ elif self.command.type == PluginCommandType.LowLevelILInstructionPluginCommand:
+ self.command.lowLevelILInstructionCommand(self.command.context, context.view.handle,
+ context.instruction.function.handle, context.instruction.instr_index)
+ elif self.command.type == PluginCommandType.MediumLevelILFunctionPluginCommand:
+ self.command.mediumLevelILFunctionCommand(self.command.context, context.view.handle, context.function.handle)
+ elif self.command.type == PluginCommandType.MediumLevelILInstructionPluginCommand:
+ self.command.mediumLevelILInstructionCommand(self.command.context, context.view.handle,
+ context.instruction.function.handle, context.instruction.instr_index)
def __repr__(self):
return "<PluginCommand: %s>" % self.name
@@ -341,8 +565,8 @@ class _BackgroundTaskMetaclass(type):
tasks = core.BNGetRunningBackgroundTasks(count)
result = []
for i in xrange(0, count.value):
- result.append(BackgroundTask(core.BNNewBackgroundTaskReference(tasks[i])))
- core.BNFreeBackgroundTaskList(tasks)
+ result.append(BackgroundTask(handle=core.BNNewBackgroundTaskReference(tasks[i])))
+ core.BNFreeBackgroundTaskList(tasks, count.value)
return result
def __iter__(self):
@@ -351,9 +575,9 @@ class _BackgroundTaskMetaclass(type):
tasks = core.BNGetRunningBackgroundTasks(count)
try:
for i in xrange(0, count.value):
- yield BackgroundTask(core.BNNewBackgroundTaskReference(tasks[i]))
+ yield BackgroundTask(handle=core.BNNewBackgroundTaskReference(tasks[i]))
finally:
- core.BNFreeBackgroundTaskList(tasks)
+ core.BNFreeBackgroundTaskList(tasks, count.value)
class BackgroundTask(object):
@@ -369,6 +593,11 @@ class BackgroundTask(object):
core.BNFreeBackgroundTask(self.handle)
@property
+ def list(self):
+ """Allow tab completion to discover metaclass list property"""
+ pass
+
+ @property
def progress(self):
"""Text description of the progress of the background task (displayed in status bar of the UI)"""
return core.BNGetBackgroundTaskProgressText(self.handle)
diff --git a/python/pluginmanager.py b/python/pluginmanager.py
index 2f293577..0cc43aca 100644
--- a/python/pluginmanager.py
+++ b/python/pluginmanager.py
@@ -32,6 +32,7 @@ class RepoPlugin(object):
created by parsing the plugins.json in a plugin repository.
"""
def __init__(self, handle):
+ raise Exception("RepoPlugin temporarily disabled!")
self.handle = core.handle_of_type(handle, core.BNRepoPlugin)
def __del__(self):
@@ -136,6 +137,7 @@ class Repository(object):
``Repository`` is a read-only class. Use RepositoryManager to Enable/Disable/Install/Uninstall plugins.
"""
def __init__(self, handle):
+ raise Exception("Repository temporarily disabled!")
self.handle = core.handle_of_type(handle, core.BNRepository)
def __del__(self):
@@ -199,6 +201,7 @@ class RepositoryManager(object):
the plugins that are installed/unstalled enabled/disabled
"""
def __init__(self, handle=None):
+ raise Exception("RepositoryManager temporarily disabled!")
self.handle = core.BNGetRepositoryManager()
def __getitem__(self, repo_path):
diff --git a/python/scriptingprovider.py b/python/scriptingprovider.py
index e7838748..9cacc6ad 100644
--- a/python/scriptingprovider.py
+++ b/python/scriptingprovider.py
@@ -304,6 +304,12 @@ class ScriptingProvider(object):
self.handle = core.handle_of_type(handle, core.BNScriptingProvider)
self.__dict__["name"] = core.BNGetScriptingProviderName(handle)
+ @property
+ def list(self):
+ """Allow tab completion to discover metaclass list property"""
+ pass
+
+
def register(self):
self._cb = core.BNScriptingProviderCallbacks()
self._cb.context = 0
@@ -650,6 +656,7 @@ original_stdin = sys.stdin
original_stdout = sys.stdout
original_stderr = sys.stderr
-sys.stdin = _PythonScriptingInstanceInput(sys.stdin)
-sys.stdout = _PythonScriptingInstanceOutput(sys.stdout, False)
-sys.stderr = _PythonScriptingInstanceOutput(sys.stderr, True)
+def redirect_stdio():
+ sys.stdin = _PythonScriptingInstanceInput(sys.stdin)
+ sys.stdout = _PythonScriptingInstanceOutput(sys.stdout, False)
+ sys.stderr = _PythonScriptingInstanceOutput(sys.stderr, True)
diff --git a/python/transform.py b/python/transform.py
index 59d719e7..3284ed74 100644
--- a/python/transform.py
+++ b/python/transform.py
@@ -200,6 +200,11 @@ class Transform(object):
log.log_error(traceback.format_exc())
return False
+ @property
+ def list(self):
+ """Allow tab completion to discover metaclass list property"""
+ pass
+
@abc.abstractmethod
def perform_decode(self, data, params):
if self.type == TransformType.InvertingTransform: