summaryrefslogtreecommitdiff
path: root/python
diff options
context:
space:
mode:
Diffstat (limited to 'python')
-rw-r--r--python/__init__.py1
-rw-r--r--python/architecture.py8
-rw-r--r--python/basicblock.py14
-rw-r--r--python/binaryview.py126
-rw-r--r--python/callingconvention.py6
-rw-r--r--python/examples/angr_plugin.py4
-rw-r--r--python/function.py13
-rw-r--r--python/generator.cpp11
-rw-r--r--python/interaction.py257
-rw-r--r--python/lowlevelil.py5
-rw-r--r--python/mediumlevelil.py11
-rw-r--r--python/metadata.py266
-rw-r--r--python/platform.py12
-rw-r--r--python/types.py4
14 files changed, 676 insertions, 62 deletions
diff --git a/python/__init__.py b/python/__init__.py
index ec25839b..b066e863 100644
--- a/python/__init__.py
+++ b/python/__init__.py
@@ -48,6 +48,7 @@ from .highlight import *
from .scriptingprovider import *
from .pluginmanager import *
from .setting import *
+from .metadata import *
def shutdown():
diff --git a/python/architecture.py b/python/architecture.py
index b9c6fcc9..fa235985 100644
--- a/python/architecture.py
+++ b/python/architecture.py
@@ -361,7 +361,7 @@ class Architecture(object):
cc = core.BNGetArchitectureCallingConventions(self.handle, count)
result = {}
for i in xrange(0, count.value):
- obj = callingconvention.CallingConvention(None, core.BNNewCallingConventionReference(cc[i]))
+ obj = callingconvention.CallingConvention(handle=core.BNNewCallingConventionReference(cc[i]))
result[obj.name] = obj
core.BNFreeCallingConventionList(cc, count)
return result
@@ -898,7 +898,7 @@ class Architecture(object):
:param str data: bytes to be interpreted as low-level IL instructions
:param int addr: virtual address of start of ``data``
:param LowLevelILFunction il: LowLevelILFunction object to append LowLevelILExpr objects to
- :rtype: None
+ :rtype: length of bytes read on success, None on failure
"""
raise NotImplementedError
@@ -1295,7 +1295,7 @@ class Architecture(object):
for i in xrange(len(operands)):
if isinstance(operands[i], str):
operand_list[i].constant = False
- operand_list[i].reg = self.regs[operands[i]]
+ operand_list[i].reg = self.regs[operands[i]].index
elif isinstance(operands[i], lowlevelil.ILRegister):
operand_list[i].constant = False
operand_list[i].reg = operands[i].index
@@ -1319,7 +1319,7 @@ class Architecture(object):
for i in xrange(len(operands)):
if isinstance(operands[i], str):
operand_list[i].constant = False
- operand_list[i].reg = self.regs[operands[i]]
+ operand_list[i].reg = self.regs[operands[i]].index
elif isinstance(operands[i], lowlevelil.ILRegister):
operand_list[i].constant = False
operand_list[i].reg = operands[i].index
diff --git a/python/basicblock.py b/python/basicblock.py
index c92336c5..4a5caa14 100644
--- a/python/basicblock.py
+++ b/python/basicblock.py
@@ -48,6 +48,8 @@ class BasicBlock(object):
def __init__(self, view, handle):
self.view = view
self.handle = core.handle_of_type(handle, core.BNBasicBlock)
+ self._arch = None
+ self._func = None
def __del__(self):
core.BNFreeBasicBlock(self.handle)
@@ -65,18 +67,26 @@ class BasicBlock(object):
@property
def function(self):
"""Basic block function (read-only)"""
+ if self._func is not None:
+ return self._func
func = core.BNGetBasicBlockFunction(self.handle)
if func is None:
return None
- return function.Function(self.view, func)
+ self._func = function.Function(self.view, func)
+ return self._func
@property
def arch(self):
"""Basic block architecture (read-only)"""
+ # The arch for a BasicBlock isn't going to change so just cache
+ # it the first time we need it
+ if self._arch is not None:
+ return self._arch
arch = core.BNGetBasicBlockArchitecture(self.handle)
if arch is None:
return None
- return architecture.Architecture(arch)
+ self._arch = architecture.Architecture(arch)
+ return self._arch
@property
def start(self):
diff --git a/python/binaryview.py b/python/binaryview.py
index 15e5d5da..31fb8d73 100644
--- a/python/binaryview.py
+++ b/python/binaryview.py
@@ -26,7 +26,8 @@ import threading
# Binary Ninja components
import _binaryninjacore as core
-from enums import AnalysisState, SymbolType, InstructionTextTokenType, Endianness, ModificationStatus, StringType, SegmentFlag
+from enums import (AnalysisState, SymbolType, InstructionTextTokenType,
+ Endianness, ModificationStatus, StringType, SegmentFlag)
import function
import startup
import architecture
@@ -39,6 +40,7 @@ import databuffer
import basicblock
import types
import lineardisassembly
+import metadata
class BinaryDataNotification(object):
@@ -115,6 +117,10 @@ class AnalysisCompletionEvent(object):
pass
def cancel(self):
+ """
+ .. warning: This method should only be used when the system is being
+ shut down and no further analysis should be done afterward.
+ """
self.callback = self._empty_callback
core.BNCancelAnalysisCompletionEvent(self.handle)
@@ -1685,11 +1691,25 @@ class BinaryView(object):
return core.BNSaveToFilename(self.handle, str(dest))
def register_notification(self, notify):
+ """
+ `register_notification` provides a mechanism for receiving callbacks for various analysis events. A full
+ list of callbacks can be seen in :py:Class:`BinaryDataNotification`.
+
+ :param BinaryDataNotification notify: notify is a subclassed instance of :py:Class:`BinaryDataNotification`.
+ :rtype: None
+ """
cb = BinaryDataNotificationCallbacks(self, notify)
cb._register()
self.notifications[notify] = cb
def unregister_notification(self, notify):
+ """
+ `unregister_notification` unregisters the :py:Class:`BinaryDataNotification` object passed to
+ `register_notification`
+
+ :param BinaryDataNotification notify: notify is a subclassed instance of :py:Class:`BinaryDataNotification`.
+ :rtype: None
+ """
if notify in self.notifications:
self.notifications[notify]._unregister()
del self.notifications[notify]
@@ -1801,28 +1821,7 @@ class BinaryView(object):
:rtype: None
"""
- class WaitEvent(object):
- 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()
- # TODO: figure out if we actually need this 'event' variable, likely we do
- event = AnalysisCompletionEvent(self, lambda: wait.complete())
- core.BNUpdateAnalysis(self.handle)
- wait.wait()
+ core.BNUpdateAnalysisAndWait(self.handle)
def abort_analysis(self):
"""
@@ -1918,11 +1917,27 @@ class BinaryView(object):
return None
return DataVariable(var.address, types.Type(var.type, confidence = var.typeConfidence), var.autoDiscovered)
+ def get_functions_containing(self, addr):
+ """
+ ``get_functions_containing`` returns a list of functions which contain the given address or None on failure.
+
+ :param int addr: virtual address to query.
+ :rtype: list of Function objects or None
+ """
+ basic_blocks = self.get_basic_blocks_at(addr)
+ if len(basic_blocks) == 0:
+ return None
+
+ result = []
+ for block in basic_blocks:
+ result.append(block.function)
+ return result
+
def get_function_at(self, addr, plat=None):
"""
- ``get_function_at`` gets a binaryninja.Function object for the function at the virtual address ``addr``:
+ ``get_function_at`` gets a Function object for the function that starts at virtual address ``addr``:
- :param int addr: virtual address of the desired function
+ :param int addr: starting virtual address of the desired function
:param Platform plat: plat of the desired function
:return: returns a Function object or None for the function at the virtual address provided
:rtype: Function
@@ -3250,6 +3265,67 @@ class BinaryView(object):
core.BNFreeStringList(outgoing_names, len(name_list))
return result
+ def query_metadata(self, key):
+ """
+ `query_metadata` retrieves a metadata associated with the given key stored in the current BinaryView.
+
+ :param string key: key to query
+ :rtype: metadata associated with the key
+ :Example:
+
+ >>> bv.store_metadata("integer", 1337)
+ >>> bv.query_metadata("integer")
+ 1337L
+ >>> bv.store_metadata("list", [1,2,3])
+ >>> bv.query_metadata("list")
+ [1L, 2L, 3L]
+ >>> bv.store_metadata("string", "my_data")
+ >>> bv.query_metadata("string")
+ 'my_data'
+ """
+ md_handle = core.BNBinaryViewQueryMetadata(self.handle, key)
+ if md_handle is None:
+ raise KeyError(key)
+ return metadata.Metadata(handle=md_handle).value
+
+ def store_metadata(self, key, md):
+ """
+ `store_metadata` stores an object for the given key in the current BinaryView. Objects stored using
+ `store_metadata` can be retrieved when the database is reopend. Objects stored are not arbitrary python
+ objects! The values stored must be able to be held in a Metadata object. See :py:class:`Metadata`
+ for more information. Python objects could obviously be serialized using pickle but this intentionally
+ a task left to the user since there is the potential security issues.
+
+ :param string key: key value to associate the Metadata object with
+ :param Varies md: object to store.
+ :rtype: None
+ :Example:
+
+ >>> bv.store_metadata("integer", 1337)
+ >>> bv.query_metadata("integer")
+ 1337L
+ >>> bv.store_metadata("list", [1,2,3])
+ >>> bv.query_metadata("list")
+ [1L, 2L, 3L]
+ >>> bv.store_metadata("string", "my_data")
+ >>> bv.query_metadata("string")
+ 'my_data'
+ """
+ core.BNBinaryViewStoreMetadata(self.handle, key, metadata.Metadata(md).handle)
+
+ def remove_metadata(self, key):
+ """
+ `remove_metadata` removes the metadata associated with key from the current BinaryView.
+
+ :param string key: key associated with metadata to remove from the BinaryView
+ :rtype: None
+ :Example:
+
+ >>> bv.store_metadata("integer", 1337)
+ >>> bv.remove_metadata("integer")
+ """
+ core.BNBinaryViewRemoveMetadata(self.handle, key)
+
def __setattr__(self, name, value):
try:
object.__setattr__(self, name, value)
diff --git a/python/callingconvention.py b/python/callingconvention.py
index db473c53..21c8c95a 100644
--- a/python/callingconvention.py
+++ b/python/callingconvention.py
@@ -41,8 +41,10 @@ class CallingConvention(object):
_registered_calling_conventions = []
- def __init__(self, arch, handle = None, confidence = types.max_confidence):
+ def __init__(self, arch=None, name=None, handle=None, confidence=types.max_confidence):
if handle is None:
+ if arch is None or name is None:
+ raise ValueError("Must specify either handle or architecture and name")
self.arch = arch
self._pending_reg_lists = {}
self._cb = core.BNCustomCallingConvention()
@@ -56,7 +58,7 @@ class CallingConvention(object):
self._cb.getIntegerReturnValueRegister = self._cb.getIntegerReturnValueRegister.__class__(self._get_int_return_reg)
self._cb.getHighIntegerReturnValueRegister = self._cb.getHighIntegerReturnValueRegister.__class__(self._get_high_int_return_reg)
self._cb.getFloatReturnValueRegister = self._cb.getFloatReturnValueRegister.__class__(self._get_float_return_reg)
- self.handle = core.BNCreateCallingConvention(arch.handle, self.__class__.name, self._cb)
+ self.handle = core.BNCreateCallingConvention(arch.handle, name, self._cb)
self.__class__._registered_calling_conventions.append(self)
else:
self.handle = handle
diff --git a/python/examples/angr_plugin.py b/python/examples/angr_plugin.py
index c84373be..26f8040c 100644
--- a/python/examples/angr_plugin.py
+++ b/python/examples/angr_plugin.py
@@ -42,7 +42,7 @@ from binaryninja.binaryview import BinaryView
from binaryninja.plugin import BackgroundTaskThread, PluginCommand
from binaryninja.interaction import show_plain_text_report, show_message_box
from binaryninja.highlight import HighlightColor
-from binaryninja.enums import HighlightStandardColor, MessageBoxButtonSet
+from binaryninja.enums import HighlightStandardColor, MessageBoxButtonSet, MessageBoxIcon
# Disable warning logs as they show up as errors in the UI
logging.disable(logging.WARNING)
@@ -137,7 +137,7 @@ def solve(bv):
if len(bv.session_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.", MessageBoxButtonSet.OKButtonSet, MessageBoxButtonSet.ErrorIcon)
+ "continue.", MessageBoxButtonSet.OKButtonSet, MessageBoxIcon.ErrorIcon)
return
# Start a solver thread for the path associated with the view
diff --git a/python/function.py b/python/function.py
index c3310b6c..f0b6faee 100644
--- a/python/function.py
+++ b/python/function.py
@@ -206,6 +206,12 @@ class Variable(object):
def __str__(self):
return self.name
+ def __eq__(self, other):
+ return self.identifier == other.identifier
+
+ def __hash__(self):
+ return hash(self.identifier)
+
class ConstantReference(object):
def __init__(self, val, size, ptr, intermediate):
@@ -261,6 +267,9 @@ class Function(object):
return True
return ctypes.addressof(self.handle.contents) != ctypes.addressof(value.handle.contents)
+ def __hash__(self):
+ return hash((self.start, self.arch.name, self.platform.name))
+
@classmethod
def _unregister(cls, func):
handle = ctypes.cast(func, ctypes.c_void_p)
@@ -471,7 +480,11 @@ class Function(object):
def get_comment_at(self, addr):
return core.BNGetCommentForAddress(self.handle, addr)
+ def set_comment_at(self, addr, comment):
+ core.BNSetCommentForAddress(self.handle, addr, comment)
+
def set_comment(self, addr, comment):
+ """Deprecated"""
core.BNSetCommentForAddress(self.handle, addr, comment)
def get_low_level_il_at(self, addr, arch=None):
diff --git a/python/generator.cpp b/python/generator.cpp
index 6f19db66..554f82cd 100644
--- a/python/generator.cpp
+++ b/python/generator.cpp
@@ -165,8 +165,15 @@ int main(int argc, char* argv[])
// Parse API header to get type and function information
map<QualifiedName, Ref<Type>> types, vars, funcs;
string errors;
- bool ok = Architecture::GetByName("generator")->ParseTypesFromSourceFile(argv[1], types, vars, funcs, errors);
- fprintf(stderr, "%s", errors.c_str());
+ auto arch = Architecture::GetByName("generator");
+ if (!arch)
+ {
+ printf("ERROR: License file validation failed (most likely)\n");
+ return 1;
+ }
+
+ bool ok = arch->ParseTypesFromSourceFile(argv[1], types, vars, funcs, errors);
+ fprintf(stderr, "Errors: %s", errors.c_str());
if (!ok)
return 1;
diff --git a/python/interaction.py b/python/interaction.py
index 60607692..979549f5 100644
--- a/python/interaction.py
+++ b/python/interaction.py
@@ -29,6 +29,9 @@ import log
class LabelField(object):
+ """
+ ``LabelField`` adds a text label to the display.
+ """
def __init__(self, text):
self.text = text
@@ -44,6 +47,9 @@ class LabelField(object):
class SeparatorField(object):
+ """
+ ``SeparatorField`` adds vertical separation to the display.
+ """
def _fill_core_struct(self, value):
value.type = FormInputFieldType.SeparatorFormField
@@ -55,6 +61,9 @@ class SeparatorField(object):
class TextLineField(object):
+ """
+ ``TextLineField`` Adds prompt for text string input. Result is stored in self.result as a string on completion.
+ """
def __init__(self, prompt):
self.prompt = prompt
self.result = None
@@ -71,6 +80,10 @@ class TextLineField(object):
class MultilineTextField(object):
+ """
+ ``MultilineTextField`` add multi-line text string input field. Result is stored in self.result
+ as a string. This option is not supported on the command line.
+ """
def __init__(self, prompt):
self.prompt = prompt
self.result = None
@@ -87,6 +100,9 @@ class MultilineTextField(object):
class IntegerField(object):
+ """
+ ``IntegerField`` add prompt for integer. Result is stored in self.result as an int.
+ """
def __init__(self, prompt):
self.prompt = prompt
self.result = None
@@ -103,7 +119,15 @@ class IntegerField(object):
class AddressField(object):
- def __init__(self, prompt, view = None, current_address = 0):
+ """
+ ``AddressField`` prompts the user for an address. By passing the optional view and current_address parameters
+ offsets can be used instead of just an address. Th reslut is stored as in int in self.result.
+
+ Note: This API currenlty functions differently on the command line, as the view and current_address are
+ disregarded. Additionally where as in the ui the result defaults to hexidecimal on the command line 0x must be
+ specified.
+ """
+ def __init__(self, prompt, view=None, current_address=0):
self.prompt = prompt
self.view = view
self.current_address = current_address
@@ -125,6 +149,10 @@ class AddressField(object):
class ChoiceField(object):
+ """
+ ``ChoiceField`` prompts the user to choose from the list of strings provided in ``choices``. Result is stored
+ in self.result as an index in to the coices array.
+ """
def __init__(self, prompt, choices):
self.prompt = prompt
self.choices = choices
@@ -147,7 +175,10 @@ class ChoiceField(object):
class OpenFileNameField(object):
- def __init__(self, prompt, ext = ""):
+ """
+ ``OpenFileNameField`` prompts the user to specify a file name to open. Result is stored in self.result as a string.
+ """
+ def __init__(self, prompt, ext=""):
self.prompt = prompt
self.ext = ext
self.result = None
@@ -165,7 +196,10 @@ class OpenFileNameField(object):
class SaveFileNameField(object):
- def __init__(self, prompt, ext = "", default_name = ""):
+ """
+ ``SaveFileNameField`` prompts the user to specify a file name to save. Result is stored in self.result as a string.
+ """
+ def __init__(self, prompt, ext="", default_name=""):
self.prompt = prompt
self.ext = ext
self.default_name = default_name
@@ -185,13 +219,17 @@ class SaveFileNameField(object):
class DirectoryNameField(object):
- def __init__(self, prompt, default_name = ""):
+ """
+ ``DirectoryNameField`` prompts the user to specify a directory name to open. Result is stored in self.result as
+ a string.
+ """
+ def __init__(self, prompt, default_name=""):
self.prompt = prompt
self.default_name = default_name
self.result = None
def _fill_core_struct(self, value):
- value.type = DirectoryNameField
+ value.type = FormInputFieldType.DirectoryNameFormField
value.prompt = self.prompt
value.defaultName = self.default_name
@@ -353,14 +391,14 @@ class InteractionHandler(object):
field_objs.append(AddressField(fields[i].prompt, view, fields[i].currentAddress))
elif fields[i].type == FormInputFieldType.ChoiceFormField:
choices = []
- for i in xrange(0, fields[i].count):
- choices.append(fields[i].choices[i])
+ for j in xrange(0, fields[i].count):
+ choices.append(fields[i].choices[j])
field_objs.append(ChoiceField(fields[i].prompt, choices))
elif fields[i].type == FormInputFieldType.OpenFileNameFormField:
field_objs.append(OpenFileNameField(fields[i].prompt, fields[i].ext))
elif fields[i].type == FormInputFieldType.SaveFileNameFormField:
field_objs.append(SaveFileNameField(fields[i].prompt, fields[i].ext, fields[i].defaultName))
- elif fields[i].type == DirectoryNameField:
+ elif fields[i].type == FormInputFieldType.DirectoryNameFormField:
field_objs.append(DirectoryNameField(fields[i].prompt, fields[i].defaultName))
else:
field_objs.append(LabelField(fields[i].prompt))
@@ -424,22 +462,86 @@ class InteractionHandler(object):
def markdown_to_html(contents):
+ """
+ ``markdown_to_html`` converts the provided markdown to HTML.
+
+ :param string contents: Markdown contents to convert to HTML.
+ :rtype: string
+ :Example:
+ >>> markdown_to_html("##Yay")
+ '<h2>Yay</h2>'
+ """
return core.BNMarkdownToHTML(contents)
def show_plain_text_report(title, contents):
+ """
+ ``show_plain_text_report`` displays contents to the user in the UI or on the command line.
+
+ 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.
+
+ :param str title: title to display in the UI popup.
+ :param str contents: plain text contents to display
+ :rtype: None
+ :Example:
+ >>> show_plain_text_report("title", "contents")
+ contents
+ """
core.BNShowPlainTextReport(None, title, contents)
-def show_markdown_report(title, contents, plaintext = ""):
+def show_markdown_report(title, contents, plaintext=""):
+ """
+ ``show_markdown_report`` displays the markdown contents in UI applications and plaintext in command line
+ applications.
+
+ 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.
+
+ :param str contents: markdown contents to display
+ :param str plaintext: Plain text version to display (used on the command line)
+ :rtype: None
+ :Example:
+ >>> show_markdown_report("title", "##Contents", "Plain text contents")
+ Plain text contents
+ """
core.BNShowMarkdownReport(None, title, contents, plaintext)
-def show_html_report(title, contents, plaintext = ""):
+def show_html_report(title, contents, plaintext=""):
+ """
+ ``show_html_report`` displays the html contents in UI applications and plaintext in command line
+ applications.
+
+ 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.
+
+ :param str contents: HTML contents to display
+ :param str plaintext: Plain text version to display (used on the command line)
+ :rtype: None
+ :Example"
+ >>> show_html_report("title", "<h1>Contents</h1>", "Plain text contents")
+ Plain text contents
+ """
core.BNShowHTMLReport(None, title, contents, plaintext)
def get_text_line_input(prompt, title):
+ """
+ ``get_text_line_input`` prompts the user to input a string with the given prompt and title.
+
+ 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.
+
+ :param str prompt: String to prompt with.
+ :param str title: Title of the window when executed in the UI.
+ :rtype: string containing the input without trailing newline character.
+ :Example:
+ >>> get_text_line_input("PROMPT>", "getinfo")
+ PROMPT> Input!
+ 'Input!'
+ """
value = ctypes.c_char_p()
if not core.BNGetTextLineInput(value, prompt, title):
return None
@@ -449,6 +551,20 @@ def get_text_line_input(prompt, title):
def get_int_input(prompt, title):
+ """
+ ``get_int_input`` prompts the user to input a integer with the given prompt and title.
+
+ 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.
+
+ :param str prompt: String to prompt with.
+ :param str title: Title of the window when executed in the UI.
+ :rtype: integer value input by the user.
+ :Example:
+ >>> get_int_input("PROMPT>", "getinfo")
+ PROMPT> 10
+ 10
+ """
value = ctypes.c_longlong()
if not core.BNGetIntegerInput(value, prompt, title):
return None
@@ -456,6 +572,20 @@ def get_int_input(prompt, title):
def get_address_input(prompt, title):
+ """
+ ``get_address_input`` prompts the user for an address with the given prompt and title.
+
+ 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.
+
+ :param str prompt: String to prompt with.
+ :param str title: Title of the window when executed in the UI.
+ :rtype: integer value input by the user.
+ :Example:
+ >>> get_address_input("PROMPT>", "getinfo")
+ PROMPT> 10
+ 10L
+ """
value = ctypes.c_ulonglong()
if not core.BNGetAddressInput(value, prompt, title, None, 0):
return None
@@ -463,6 +593,25 @@ def get_address_input(prompt, title):
def get_choice_input(prompt, title, choices):
+ """
+ ``get_choice_input`` prompts the user to select the one of the provided choices.
+
+ 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 a combo box.
+
+ :param str prompt: String to prompt with.
+ :param str title: Title of the window when executed in the UI.
+ :param list choices: A list of strings for the user to choose from.
+ :rtype: integer array index of the selected option
+ :Example:
+ >>> get_choice_input("PROMPT>", "choices", ["Yes", "No", "Maybe"])
+ choices
+ 1) Yes
+ 2) No
+ 3) Maybe
+ PROMPT> 1
+ 0L
+ """
choice_buf = (ctypes.c_char_p * len(choices))()
for i in xrange(0, len(choices)):
choice_buf[i] = str(choices[i])
@@ -472,7 +621,20 @@ def get_choice_input(prompt, title, choices):
return value.value
-def get_open_filename_input(prompt, ext = ""):
+def get_open_filename_input(prompt, ext=""):
+ """
+ ``get_open_filename_input`` prompts the user for a file name to open.
+
+ 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 ext: Optional, file extension
+ :Example:
+ >>> get_open_filename_input("filename:", "exe")
+ filename: foo.exe
+ 'foo.exe'
+ """
value = ctypes.c_char_p()
if not core.BNGetOpenFileNameInput(value, prompt, ext):
return None
@@ -481,7 +643,22 @@ def get_open_filename_input(prompt, ext = ""):
return result
-def get_save_filename_input(prompt, ext = "", default_name = ""):
+def get_save_filename_input(prompt, ext="", default_name=""):
+ """
+ ``get_save_filename_input`` prompts the user for a file name to save as, optionally providing a file extension and
+ 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.
+
+ :param str prompt: Prompt to display.
+ :param str ext: Optional, file extension
+ :param str default_name: Optional, default file name.
+ :Example:
+ >>> get_save_filename_input("filename:", "exe", "foo.exe")
+ filename: foo.exe
+ 'foo.exe'
+ """
value = ctypes.c_char_p()
if not core.BNGetSaveFileNameInput(value, prompt, ext, default_name):
return None
@@ -490,7 +667,22 @@ def get_save_filename_input(prompt, ext = "", default_name = ""):
return result
-def get_directory_name_input(prompt, 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.
+
+ 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.
+ :rtype: str
+ :Example:
+ >>> get_directory_name_input("prompt")
+ prompt dirname
+ 'dirname'
+ """
value = ctypes.c_char_p()
if not core.BNGetDirectoryNameInput(value, prompt, default_name):
return None
@@ -500,6 +692,43 @@ def get_directory_name_input(prompt, default_name = ""):
def get_form_input(fields, title):
+ """
+ ``get_from_input`` Prompts the user for a set of inputs specified in ``fields`` with given title.
+ The fields parameter is a list which can contain the following types:
+ - str - an alias for LabelField
+ - None - an alias for SeparatorField
+ - LabelField - Text output
+ - SeparatorField - Vertical spacing
+ - TextLineField - Prompt for a string value
+ - MultilineTextField - Prompt for multi-line string value
+ - IntegerField - Prompt for an integer
+ - AddressField - Prompt for an address
+ - ChoiceField - Prompt for a choice from provided options
+ - OpenFileNameField - Prompt for file to open
+ - SaveFileNameField - Prompt for file to save to
+ - DirectoryNameField - Prompt for directory name
+ This API is flexible and works both in the UI via a popup dialog and on the command line.
+ :params list fields: A list containing of the above specified classes, strings or None
+ :params str title: The title of the popup dialog.
+ :Example:
+
+ >>> int_f = IntegerField("Specify Integer")
+ >>> tex_f = TextLineField("Specify name")
+ >>> choice_f = ChoiceField("Options", ["Yes", "No", "Maybe"])
+ >>> get_form_input(["Get Data", None, int_f, tex_f, choice_f], "The options")
+ Get Data
+
+ Specify Integer 1337
+ Specify name Peter
+ The options
+ 1) Yes
+ 2) No
+ 3) Maybe
+ Options 1
+ >>> True
+ >>> print tex_f.result, int_f.result, choice_f.result
+ Peter 1337 0
+ """
value = (core.BNFormInputField * len(fields))()
for i in xrange(0, len(fields)):
if isinstance(fields[i], str):
@@ -517,7 +746,7 @@ def get_form_input(fields, title):
return True
-def show_message_box(title, text, buttons = MessageBoxButtonSet.OKButtonSet, icon = MessageBoxIcon.InformationIcon):
+def show_message_box(title, text, buttons=MessageBoxButtonSet.OKButtonSet, icon=MessageBoxIcon.InformationIcon):
"""
``show_message_box`` Displays a configurable message box in the UI, or prompts on the console as appropriate
retrieves a list of all Symbol objects of the provided symbol type in the optionally
diff --git a/python/lowlevelil.py b/python/lowlevelil.py
index 08ca04e6..9f532e6f 100644
--- a/python/lowlevelil.py
+++ b/python/lowlevelil.py
@@ -731,17 +731,18 @@ class LowLevelILFunction(object):
"""
return self.expr(LowLevelILOperation.LLIL_LOAD, addr.index, size=size)
- def store(self, size, addr, value):
+ def store(self, size, addr, value, flags=None):
"""
``store`` Writes ``size`` bytes to expression ``addr`` read from expression ``value``
:param int size: number of bytes to write
:param LowLevelILExpr addr: the expression to write to
:param LowLevelILExpr value: the expression to be written
+ :param str flags: which flags are set by this operation
:return: The expression ``[addr].size = value``
:rtype: LowLevelILExpr
"""
- return self.expr(LowLevelILOperation.LLIL_STORE, addr.index, value.index, size=size)
+ return self.expr(LowLevelILOperation.LLIL_STORE, addr.index, value.index, size=size, flags=flags)
def push(self, size, value):
"""
diff --git a/python/mediumlevelil.py b/python/mediumlevelil.py
index 3377ab6a..eefbe787 100644
--- a/python/mediumlevelil.py
+++ b/python/mediumlevelil.py
@@ -37,6 +37,15 @@ class SSAVariable(object):
def __repr__(self):
return "<ssa %s version %d>" % (repr(self.var), self.version)
+ def __eq__(self, other):
+ return (
+ (self.var.identifier, self.version) ==
+ (other.var.identifier, other.version)
+ )
+
+ def __hash__(self):
+ return hash((self.var.identifier, self.version))
+
class MediumLevelILLabel(object):
def __init__(self, handle = None):
@@ -142,7 +151,7 @@ class MediumLevelILInstruction(object):
MediumLevelILOperation.MLIL_UNIMPL_MEM: [("src", "expr")],
MediumLevelILOperation.MLIL_SET_VAR_SSA: [("dest", "var_ssa"), ("src", "expr")],
MediumLevelILOperation.MLIL_SET_VAR_SSA_FIELD: [("prev", "var_ssa_dest_and_src"), ("offset", "int"), ("src", "expr")],
- MediumLevelILOperation.MLIL_SET_VAR_SPLIT_SSA: [("high", "expr"), ("low", "expr"), ("src", "expr")],
+ MediumLevelILOperation.MLIL_SET_VAR_SPLIT_SSA: [("high", "ssa_var"), ("low", "ssa_var"), ("src", "expr")],
MediumLevelILOperation.MLIL_SET_VAR_ALIASED: [("prev", "var_ssa_dest_and_src"), ("src", "expr")],
MediumLevelILOperation.MLIL_SET_VAR_ALIASED_FIELD: [("prev", "var_ssa_dest_and_src"), ("offset", "int"), ("src", "expr")],
MediumLevelILOperation.MLIL_VAR_SSA: [("src", "var_ssa")],
diff --git a/python/metadata.py b/python/metadata.py
new file mode 100644
index 00000000..554bbcf4
--- /dev/null
+++ b/python/metadata.py
@@ -0,0 +1,266 @@
+# Copyright (c) 2015-2017 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 ctypes
+
+# Binary Ninja components
+import _binaryninjacore as core
+from enums import MetadataType
+
+
+class Metadata(object):
+ def __init__(self, value=None, signed=None, raw=None, handle=None):
+ if handle is not None:
+ self.handle = handle
+ elif isinstance(value, int):
+ if signed:
+ self.handle = core.BNCreateMetadataSignedIntegerData(value)
+ else:
+ self.handle = core.BNCreateMetadataUnsignedIntegerData(value)
+ elif isinstance(value, bool):
+ self.handle = core.BNCreateMetadataBooleanData(value)
+ elif isinstance(value, str):
+ if raw:
+ buffer = (ctypes.c_ubyte * len(value)).from_buffer_copy(value)
+ self.handle = core.BNCreateMetadataRawData(buffer, len(value))
+ else:
+ self.handle = core.BNCreateMetadataStringData(value)
+ elif isinstance(value, float):
+ self.handle = core.BNCreateMetadataDoubleData(value)
+ elif isinstance(value, list):
+ self.handle = core.BNCreateMetadataOfType(MetadataType.ArrayDataType)
+ for elm in value:
+ md = Metadata(elm, signed, raw)
+ core.BNMetadataArrayAppend(self.handle, md.handle)
+ elif isinstance(value, dict):
+ self.handle = core.BNCreateMetadataOfType(MetadataType.KeyValueDataType)
+ for elm in value:
+ md = Metadata(value[elm], signed, raw)
+ core.BNMetadataSetValueForKey(self.handle, str(elm), md.handle)
+ else:
+ raise ValueError("List doesn't not contain type of: int, bool, str, float, list, dict")
+
+ @property
+ def value(self):
+ if self.is_integer:
+ return int(self)
+ elif self.is_string or self.is_raw:
+ return str(self)
+ elif self.is_float:
+ return float(self)
+ elif self.is_boolean:
+ return bool(self)
+ elif self.is_array:
+ return list(self)
+ elif self.is_dict:
+ return self.get_dict()
+ raise TypeError()
+
+ def get_dict(self):
+ if not self.is_dict:
+ raise TypeError()
+ result = {}
+ for key in self:
+ result[key] = self[key]
+ return result
+
+ @property
+ def type(self):
+ return MetadataType(core.BNMetadataGetType(self.handle))
+
+ @property
+ def is_integer(self):
+ return self.is_signed_integer or self.is_unsigned_integer
+
+ @property
+ def is_signed_integer(self):
+ return core.BNMetadataIsSignedInteger(self.handle)
+
+ @property
+ def is_unsigned_integer(self):
+ return core.BNMetadataIsUnsignedInteger(self.handle)
+
+ @property
+ def is_float(self):
+ return core.BNMetadataIsDouble(self.handle)
+
+ @property
+ def is_boolean(self):
+ return core.BNMetadataIsBoolean(self.handle)
+
+ @property
+ def is_string(self):
+ return core.BNMetadataIsString(self.handle)
+
+ @property
+ def is_raw(self):
+ return core.BNMetadataIsRaw(self.handle)
+
+ @property
+ def is_array(self):
+ return core.BNMetadataIsArray(self.handle)
+
+ @property
+ def is_dict(self):
+ return core.BNMetadataIsKeyValueStore(self.handle)
+
+ def remove(self, key_or_index):
+ if isinstance(key_or_index, str) and self.is_dict:
+ core.BNMetadataRemoveKey(self.handle, key_or_index)
+ elif isinstance(key_or_index, int) and self.is_array:
+ core.BNMetadataRemoveIndex(self.handle, key_or_index)
+ else:
+ raise TypeError("remove only valid for dict and array objects")
+
+ def __len__(self):
+ if self.is_array or self.is_dict or self.is_string or self.is_raw:
+ return core.BNMetadataSize(self.handle)
+ raise Exception("Metadata object doesn't support len()")
+
+ def __iter__(self):
+ if self.is_array:
+ for i in xrange(core.BNMetadataSize(self.handle)):
+ yield Metadata(handle=core.BNMetadataGetForIndex(self.handle, i)).value
+ elif self.is_dict:
+ result = core.BNMetadataGetValueStore(self.handle)
+ try:
+ for i in xrange(result.contents.size):
+ yield result.contents.keys[i]
+ finally:
+ core.BNFreeMetadataValueStore(result)
+ else:
+ raise Exception("Metadata object doesn't support iteration")
+
+ def __getitem__(self, value):
+ if self.is_array:
+ if not isinstance(value, int):
+ raise ValueError("Metadata object only supports integers for indexing")
+ if value >= len(self):
+ raise IndexError("Index value out of range")
+ return Metadata(handle=core.BNMetadataGetForIndex(self.handle, value)).value
+ if self.is_dict:
+ if not isinstance(value, str):
+ raise ValueError("Metadata object only supports strings for indexing")
+ handle = core.BNMetadataGetForKey(self.handle, value)
+ if handle is None:
+ raise KeyError(value)
+ return Metadata(handle=handle).value
+
+ raise NotImplementedError("Metadata object doesn't support indexing")
+
+ def __str__(self):
+ if self.is_string:
+ return core.BNMetadataGetString(self.handle)
+ if self.is_raw:
+ length = ctypes.c_ulonglong()
+ length.value = 0
+ native_list = core.BNMetadataGetRaw(self.handle, ctypes.byref(length))
+ out_list = []
+ for i in xrange(length.value):
+ out_list.append(native_list[i])
+ core.BNFreeMetadataRaw(native_list)
+ return ''.join(chr(a) for a in out_list)
+
+ raise ValueError("Metadata object not a string or raw type")
+
+ def __int__(self):
+ if self.is_signed_integer:
+ return core.BNMetadataGetSignedInteger(self.handle)
+ if self.is_unsigned_integer:
+ return core.BNMetadataGetUnsignedInteger(self.handle)
+
+ raise ValueError("Metadata object not of integer type")
+
+ def __float__(self):
+ if not self.is_float:
+ raise ValueError("Metadata object is not float type")
+ return core.BNMetadataGetDouble(self.handle)
+
+ def __nonzero__(self):
+ if not self.is_boolean:
+ raise ValueError("Metadata object is not boolean type")
+ return core.BNMetadataGetBoolean(self.handle)
+
+ def __eq__(self, other):
+ if isinstance(other, int) and self.is_integer:
+ return int(self) == other
+ elif isinstance(other, str) and (self.is_string or self.is_raw):
+ return str(self) == other
+ elif isinstance(other, float) and self.is_float:
+ return float(self) == other
+ elif isinstance(other, bool) and self.is_boolean:
+ return bool(self) == other
+ elif self.is_array and ((isinstance(other, Metadata) and other.is_array) or isinstance(other, list)):
+ if len(self) != len(other):
+ return False
+ for a, b in zip(self, other):
+ if a != b:
+ return False
+ return True
+ elif self.is_dict and ((isinstance(other, Metadata) and other.is_dict) or isinstance(other, dict)):
+ if len(self) != len(other):
+ return False
+ for a, b in zip(self, other):
+ if a != b or self[a] != other[b]:
+ return False
+ return True
+ elif isinstance(other, Metadata) and self.is_integer and other.is_integer:
+ return int(self) == int(other)
+ elif isinstance(other, Metadata) and (self.is_string or self.is_raw) and (other.is_string or other.is_raw):
+ return str(self) == str(other)
+ elif isinstance(other, Metadata) and self.is_float and other.is_float:
+ return float(self) == float(other)
+ elif isinstance(other, Metadata) and self.is_boolean and other.is_boolean:
+ return bool(self) == bool(other)
+ raise NotImplementedError()
+
+ def __ne__(self, other):
+ if isinstance(other, int) and self.is_integer:
+ return int(self) != other
+ elif isinstance(other, str) and (self.is_string or self.is_raw):
+ return str(self) != other
+ elif isinstance(other, float) and self.is_float:
+ return float(self) != other
+ elif isinstance(other, bool):
+ return bool(self) != other
+ elif self.is_array and ((isinstance(other, Metadata) and other.is_array) or isinstance(other, list)):
+ if len(self) != len(other):
+ return True
+ areEqual = True
+ for a, b in zip(self, other):
+ if a != b:
+ areEqual = False
+ return not areEqual
+ elif self.is_dict and ((isinstance(other, Metadata) and other.is_dict) or isinstance(other, dict)):
+ if len(self) != len(other):
+ return True
+ for a, b in zip(self, other):
+ if a != b or self[a] != other[b]:
+ return True
+ return False
+ elif isinstance(other, Metadata) and self.is_integer and other.is_integer:
+ return int(self) != int(other)
+ elif isinstance(other, Metadata) and (self.is_string or self.is_raw) and (other.is_string or other.is_raw):
+ return str(self) != str(other)
+ elif isinstance(other, Metadata) and self.is_float and other.is_float:
+ return float(self) != float(other)
+ elif isinstance(other, Metadata) and self.is_boolean and other.is_boolean:
+ return bool(self) != bool(other)
diff --git a/python/platform.py b/python/platform.py
index 9ba7625f..1c2fdcd3 100644
--- a/python/platform.py
+++ b/python/platform.py
@@ -132,7 +132,7 @@ class Platform(object):
result = core.BNGetPlatformDefaultCallingConvention(self.handle)
if result is None:
return None
- return callingconvention.CallingConvention(None, result)
+ return callingconvention.CallingConvention(handle=result)
@default_calling_convention.setter
def default_calling_convention(self, value):
@@ -150,7 +150,7 @@ class Platform(object):
result = core.BNGetPlatformCdeclCallingConvention(self.handle)
if result is None:
return None
- return callingconvention.CallingConvention(None, result)
+ return callingconvention.CallingConvention(handle=result)
@cdecl_calling_convention.setter
def cdecl_calling_convention(self, value):
@@ -168,7 +168,7 @@ class Platform(object):
result = core.BNGetPlatformStdcallCallingConvention(self.handle)
if result is None:
return None
- return callingconvention.CallingConvention(None, result)
+ return callingconvention.CallingConvention(handle=result)
@stdcall_calling_convention.setter
def stdcall_calling_convention(self, value):
@@ -186,7 +186,7 @@ class Platform(object):
result = core.BNGetPlatformFastcallCallingConvention(self.handle)
if result is None:
return None
- return callingconvention.CallingConvention(None, result)
+ return callingconvention.CallingConvention(handle=result)
@fastcall_calling_convention.setter
def fastcall_calling_convention(self, value):
@@ -204,7 +204,7 @@ class Platform(object):
result = core.BNGetPlatformSystemCallConvention(self.handle)
if result is None:
return None
- return callingconvention.CallingConvention(None, result)
+ return callingconvention.CallingConvention(handle=result)
@system_call_convention.setter
def system_call_convention(self, value):
@@ -222,7 +222,7 @@ class Platform(object):
cc = core.BNGetPlatformCallingConventions(self.handle, count)
result = []
for i in xrange(0, count.value):
- result.append(callingconvention.CallingConvention(None, core.BNNewCallingConventionReference(cc[i])))
+ result.append(callingconvention.CallingConvention(handle=core.BNNewCallingConventionReference(cc[i])))
core.BNFreeCallingConventionList(cc, count.value)
return result
diff --git a/python/types.py b/python/types.py
index ab3fb337..47d99bee 100644
--- a/python/types.py
+++ b/python/types.py
@@ -279,7 +279,7 @@ class Type(object):
result = core.BNGetTypeCallingConvention(self.handle)
if not result.convention:
return None
- return callingconvention.CallingConvention(None, result, confidence = result.confidence)
+ return callingconvention.CallingConvention(None, handle = result, confidence = result.confidence)
@property
def parameters(self):
@@ -854,7 +854,7 @@ class TypeParserResult(object):
self.functions = functions
def __repr__(self):
- return "{types: %s, variables: %s, functions: %s}" % (self.types, self.variables, self.functions)
+ return "<types: %s, variables: %s, functions: %s>" % (self.types, self.variables, self.functions)
def preprocess_source(source, filename=None, include_dirs=[]):