summaryrefslogtreecommitdiff
path: root/python
diff options
context:
space:
mode:
authorRusty Wagner <rusty@vector35.com>2017-08-31 21:41:25 -0400
committerRusty Wagner <rusty@vector35.com>2017-08-31 21:41:25 -0400
commit7cbb40a71ffb2583862191b7999e436807f9a0e8 (patch)
tree25846f8d0e811b16b28291aeaf6359bee51e28c4 /python
parent980e2f090fb47f7f71a46b03e8c636819f3214ec (diff)
parent0b30396eb319e89e4f69d9cbac12fc3d4b453f53 (diff)
Merge branch 'dev'
Diffstat (limited to 'python')
-rw-r--r--python/__init__.py50
-rw-r--r--python/architecture.py130
-rw-r--r--python/basicblock.py50
-rw-r--r--python/binaryview.py232
-rw-r--r--python/callingconvention.py114
-rw-r--r--python/examples/angr_plugin.py4
-rw-r--r--python/function.py422
-rw-r--r--python/generator.cpp72
-rw-r--r--python/interaction.py257
-rw-r--r--python/lineardisassembly.py2
-rw-r--r--python/lowlevelil.py52
-rw-r--r--python/mediumlevelil.py73
-rw-r--r--python/metadata.py266
-rw-r--r--python/platform.py121
-rw-r--r--python/pluginmanager.py17
-rw-r--r--python/scriptingprovider.py7
-rw-r--r--python/setting.py141
-rw-r--r--python/types.py314
18 files changed, 2030 insertions, 294 deletions
diff --git a/python/__init__.py b/python/__init__.py
index 4f58a6db..f4a8fac8 100644
--- a/python/__init__.py
+++ b/python/__init__.py
@@ -19,6 +19,9 @@
# IN THE SOFTWARE.
+import atexit
+import sys
+
# Binary Ninja components
import _binaryninjacore as core
from .enums import *
@@ -47,6 +50,8 @@ from .undoaction import *
from .highlight import *
from .scriptingprovider import *
from .pluginmanager import *
+from .setting import *
+from .metadata import *
def shutdown():
@@ -56,6 +61,9 @@ def shutdown():
core.BNShutdown()
+atexit.register(shutdown)
+
+
def get_unique_identifier():
return core.BNGetUniqueIdentifierString()
@@ -64,11 +72,51 @@ def get_install_directory():
"""
``get_install_directory`` returns a string pointing to the installed binary currently running
- .warning:: ONLY for use within the Binary Ninja UI, behavior is undefined and unreliable if run headlessly
+ ..warning:: ONLY for use within the Binary Ninja UI, behavior is undefined and unreliable if run headlessly
"""
return core.BNGetInstallDirectory()
+_plugin_api_name = "python2"
+
+
+class PluginManagerLoadPluginCallback(object):
+ """Callback for BNLoadPluginForApi("python2", ...), dynamicly loads python plugins."""
+ def __init__(self):
+ self.cb = ctypes.CFUNCTYPE(
+ ctypes.c_bool,
+ ctypes.c_char_p,
+ ctypes.c_char_p,
+ ctypes.c_void_p)(self._load_plugin)
+
+ def _load_plugin(self, repo_path, plugin_path, ctx):
+ try:
+ repo = RepositoryManager()[repo_path]
+ plugin = repo[plugin_path]
+
+ if plugin.api != _plugin_api_name:
+ raise ValueError("Plugin api name is not " + _plugin_api_name)
+
+ if not plugin.installed:
+ plugin.installed = True
+
+ if repo.full_path not in sys.path:
+ sys.path.append(repo.full_path)
+
+ __import__(plugin.path)
+ log_info("Successfully loaded plugin: {}/{}: ".format(repo_path, plugin_path))
+ return True
+ except KeyError:
+ log_error("Failed to find python plugin: {}/{}".format(repo_path, plugin_path))
+ except ImportError as ie:
+ log_error("Failed to import python plugin: {}/{}: {}".format(repo_path, plugin_path, ie))
+ return False
+
+
+load_plugin = PluginManagerLoadPluginCallback()
+core.BNRegisterForPluginLoading(_plugin_api_name, load_plugin.cb, 0)
+
+
class _DestructionCallbackHandler(object):
def __init__(self):
self._cb = core.BNObjectDestructionCallbacks()
diff --git a/python/architecture.py b/python/architecture.py
index 2eb3c717..72403fec 100644
--- a/python/architecture.py
+++ b/python/architecture.py
@@ -116,6 +116,7 @@ class Architecture(object):
regs = {}
stack_pointer = None
link_reg = None
+ global_regs = []
flags = []
flag_write_types = []
flag_roles = {}
@@ -208,6 +209,13 @@ class Architecture(object):
core.BNFreeRegisterList(flags)
self._flags_written_by_flag_write_type[self._flag_write_types[write_type]] = flag_indexes
self.__dict__["flags_written_by_flag_write_type"][write_type] = flag_names
+
+ count = ctypes.c_ulonglong()
+ regs = core.BNGetArchitectureGlobalRegisters(self.handle, count)
+ self.__dict__["global_regs"] = []
+ for i in xrange(0, count.value):
+ self.global_regs.append(core.BNGetArchitectureRegisterName(self.handle, regs[i]))
+ core.BNFreeRegisterList(regs)
else:
startup._init_plugins()
@@ -250,6 +258,7 @@ class Architecture(object):
self._cb.getStackPointerRegister = self._cb.getStackPointerRegister.__class__(
self._get_stack_pointer_register)
self._cb.getLinkRegister = self._cb.getLinkRegister.__class__(self._get_link_register)
+ self._cb.getGlobalRegisters = self._cb.getGlobalRegisters.__class__(self._get_global_registers)
self._cb.assemble = self._cb.assemble.__class__(self._assemble)
self._cb.isNeverBranchPatchAvailable = self._cb.isNeverBranchPatchAvailable.__class__(
self._is_never_branch_patch_available)
@@ -330,6 +339,8 @@ class Architecture(object):
flags.append(self._flags[flag])
self._flags_written_by_flag_write_type[self._flag_write_types[write_type]] = flags
+ self.__dict__["global_regs"] = self.__class__.global_regs
+
self._pending_reg_lists = {}
self._pending_token_lists = {}
@@ -361,7 +372,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
@@ -478,6 +489,7 @@ class Architecture(object):
token_buf[i].size = tokens[i].size
token_buf[i].operand = tokens[i].operand
token_buf[i].context = tokens[i].context
+ token_buf[i].confidence = tokens[i].confidence
token_buf[i].address = tokens[i].address
result[0] = token_buf
ptr = ctypes.cast(token_buf, ctypes.c_void_p)
@@ -718,6 +730,20 @@ class Architecture(object):
log.log_error(traceback.format_exc())
return 0
+ def _get_global_registers(self, ctxt, count):
+ try:
+ count[0] = len(self.__class__.global_regs)
+ reg_buf = (ctypes.c_uint * len(self.__class__.global_regs))()
+ for i in xrange(0, len(self.__class__.global_regs)):
+ reg_buf[i] = self._all_regs[self.__class__.global_regs[i]]
+ result = ctypes.cast(reg_buf, ctypes.c_void_p)
+ self._pending_reg_lists[result.value] = (result, reg_buf)
+ return result.value
+ except KeyError:
+ log.log_error(traceback.format_exc())
+ count[0] = 0
+ return None
+
def _assemble(self, ctxt, code, addr, result, errors):
try:
data, error_str = self.perform_assemble(code, addr)
@@ -897,7 +923,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
@@ -1163,8 +1189,9 @@ class Architecture(object):
size = tokens[i].size
operand = tokens[i].operand
context = tokens[i].context
+ confidence = tokens[i].confidence
address = tokens[i].address
- result.append(function.InstructionTextToken(token_type, text, value, size, operand, context, address))
+ result.append(function.InstructionTextToken(token_type, text, value, size, operand, context, address, confidence))
core.BNFreeInstructionText(tokens, count.value)
return result, length.value
@@ -1293,7 +1320,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
@@ -1317,7 +1344,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
@@ -1647,99 +1674,6 @@ class Architecture(object):
"""
core.BNSetBinaryViewTypeArchitectureConstant(self.handle, type_name, const_name, value)
- def parse_types_from_source(self, source, filename=None, include_dirs=[], auto_type_source=None):
- """
- ``parse_types_from_source`` parses the source string and any needed headers searching for them in
- the optional list of directories provided in ``include_dirs``.
-
- :param str source: source string to be parsed
- :param str filename: optional source filename
- :param list(str) include_dirs: optional list of string filename include directories
- :param str auto_type_source: optional source of types if used for automatically generated types
- :return: py:class:`TypeParserResult` (a SyntaxError is thrown on parse error)
- :rtype: TypeParserResult
- :Example:
-
- >>> arch.parse_types_from_source('int foo;\\nint bar(int x);\\nstruct bas{int x,y;};\\n')
- ({types: {'bas': <type: struct bas>}, variables: {'foo': <type: int32_t>}, functions:{'bar':
- <type: int32_t(int32_t x)>}}, '')
- >>>
- """
-
- if filename is None:
- filename = "input"
- dir_buf = (ctypes.c_char_p * len(include_dirs))()
- for i in xrange(0, len(include_dirs)):
- dir_buf[i] = str(include_dirs[i])
- parse = core.BNTypeParserResult()
- errors = ctypes.c_char_p()
- result = core.BNParseTypesFromSource(self.handle, source, filename, parse, errors, dir_buf,
- len(include_dirs), auto_type_source)
- error_str = errors.value
- core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte)))
- if not result:
- raise SyntaxError(error_str)
- type_dict = {}
- variables = {}
- functions = {}
- for i in xrange(0, parse.typeCount):
- name = types.QualifiedName._from_core_struct(parse.types[i].name)
- type_dict[name] = types.Type(core.BNNewTypeReference(parse.types[i].type))
- for i in xrange(0, parse.variableCount):
- name = types.QualifiedName._from_core_struct(parse.variables[i].name)
- variables[name] = types.Type(core.BNNewTypeReference(parse.variables[i].type))
- for i in xrange(0, parse.functionCount):
- name = types.QualifiedName._from_core_struct(parse.functions[i].name)
- functions[name] = types.Type(core.BNNewTypeReference(parse.functions[i].type))
- core.BNFreeTypeParserResult(parse)
- return types.TypeParserResult(type_dict, variables, functions)
-
- def parse_types_from_source_file(self, filename, include_dirs=[], auto_type_source=None):
- """
- ``parse_types_from_source_file`` parses the source file ``filename`` and any needed headers searching for them in
- the optional list of directories provided in ``include_dirs``.
-
- :param str filename: filename of file to be parsed
- :param list(str) include_dirs: optional list of string filename include directories
- :param str auto_type_source: optional source of types if used for automatically generated types
- :return: py:class:`TypeParserResult` (a SyntaxError is thrown on parse error)
- :rtype: TypeParserResult
- :Example:
-
- >>> file = "/Users/binja/tmp.c"
- >>> open(file).read()
- 'int foo;\\nint bar(int x);\\nstruct bas{int x,y;};\\n'
- >>> arch.parse_types_from_source_file(file)
- ({types: {'bas': <type: struct bas>}, variables: {'foo': <type: int32_t>}, functions:
- {'bar': <type: int32_t(int32_t x)>}}, '')
- >>>
- """
- dir_buf = (ctypes.c_char_p * len(include_dirs))()
- for i in xrange(0, len(include_dirs)):
- dir_buf[i] = str(include_dirs[i])
- parse = core.BNTypeParserResult()
- errors = ctypes.c_char_p()
- result = core.BNParseTypesFromSourceFile(self.handle, filename, parse, errors, dir_buf,
- len(include_dirs), auto_type_source)
- error_str = errors.value
- core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte)))
- if not result:
- raise SyntaxError(error_str)
- type_dict = {}
- variables = {}
- functions = {}
- for i in xrange(0, parse.typeCount):
- name = types.QualifiedName._from_core_struct(parse.types[i].name)
- type_dict[name] = types.Type(core.BNNewTypeReference(parse.types[i].type))
- for i in xrange(0, parse.variableCount):
- name = types.QualifiedName._from_core_struct(parse.variables[i].name)
- variables[name] = types.Type(core.BNNewTypeReference(parse.variables[i].type))
- for i in xrange(0, parse.functionCount):
- name = types.QualifiedName._from_core_struct(parse.functions[i].name)
- functions[name] = types.Type(core.BNNewTypeReference(parse.functions[i].type))
- core.BNFreeTypeParserResult(parse)
- return types.TypeParserResult(type_dict, variables, functions)
-
def register_calling_convention(self, cc):
"""
``register_calling_convention`` registers a new calling convention for the Architecture.
diff --git a/python/basicblock.py b/python/basicblock.py
index 7858cc60..72f31876 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)
@@ -62,21 +64,33 @@ class BasicBlock(object):
return True
return ctypes.addressof(self.handle.contents) != ctypes.addressof(value.handle.contents)
+ def _create_instance(self, view, handle):
+ """Internal method used to instantiante child instances"""
+ return BasicBlock(view, handle)
+
@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):
@@ -107,7 +121,7 @@ class BasicBlock(object):
for i in xrange(0, count.value):
branch_type = BranchType(edges[i].type)
if edges[i].target:
- target = BasicBlock(self.view, core.BNNewBasicBlockReference(edges[i].target))
+ target = self._create_instance(self.view, core.BNNewBasicBlockReference(edges[i].target))
else:
target = None
result.append(BasicBlockEdge(branch_type, self, target, edges[i].backEdge))
@@ -123,7 +137,7 @@ class BasicBlock(object):
for i in xrange(0, count.value):
branch_type = BranchType(edges[i].type)
if edges[i].target:
- target = BasicBlock(self.view, core.BNNewBasicBlockReference(edges[i].target))
+ target = self._create_instance(self.view, core.BNNewBasicBlockReference(edges[i].target))
else:
target = None
result.append(BasicBlockEdge(branch_type, self, target, edges[i].backEdge))
@@ -136,13 +150,18 @@ class BasicBlock(object):
return core.BNBasicBlockHasUndeterminedOutgoingEdges(self.handle)
@property
+ def can_exit(self):
+ """Whether basic block can return or is tagged as 'No Return' (read-only)"""
+ return core.BNBasicBlockCanExit(self.handle)
+
+ @property
def dominators(self):
"""List of dominators for this basic block (read-only)"""
count = ctypes.c_ulonglong()
blocks = core.BNGetBasicBlockDominators(self.handle, count)
result = []
for i in xrange(0, count.value):
- result.append(BasicBlock(self.view, core.BNNewBasicBlockReference(blocks[i])))
+ result.append(self._create_instance(self.view, core.BNNewBasicBlockReference(blocks[i])))
core.BNFreeBasicBlockList(blocks, count.value)
return result
@@ -153,7 +172,7 @@ class BasicBlock(object):
blocks = core.BNGetBasicBlockStrictDominators(self.handle, count)
result = []
for i in xrange(0, count.value):
- result.append(BasicBlock(self.view, core.BNNewBasicBlockReference(blocks[i])))
+ result.append(self._create_instance(self.view, core.BNNewBasicBlockReference(blocks[i])))
core.BNFreeBasicBlockList(blocks, count.value)
return result
@@ -163,7 +182,7 @@ class BasicBlock(object):
result = core.BNGetBasicBlockImmediateDominator(self.handle)
if not result:
return None
- return BasicBlock(self.view, result)
+ return self._create_instance(self.view, result)
@property
def dominator_tree_children(self):
@@ -172,7 +191,7 @@ class BasicBlock(object):
blocks = core.BNGetBasicBlockDominatorTreeChildren(self.handle, count)
result = []
for i in xrange(0, count.value):
- result.append(BasicBlock(self.view, core.BNNewBasicBlockReference(blocks[i])))
+ result.append(self._create_instance(self.view, core.BNNewBasicBlockReference(blocks[i])))
core.BNFreeBasicBlockList(blocks, count.value)
return result
@@ -183,7 +202,7 @@ class BasicBlock(object):
blocks = core.BNGetBasicBlockDominanceFrontier(self.handle, count)
result = []
for i in xrange(0, count.value):
- result.append(BasicBlock(self.view, core.BNNewBasicBlockReference(blocks[i])))
+ result.append(self._create_instance(self.view, core.BNNewBasicBlockReference(blocks[i])))
core.BNFreeBasicBlockList(blocks, count.value)
return result
@@ -264,8 +283,8 @@ class BasicBlock(object):
idx = start
while idx < end:
data = self.view.read(idx, 16)
- inst_info = self.view.arch.get_instruction_info(data, idx)
- inst_text = self.view.arch.get_instruction_text(data, idx)
+ inst_info = self.arch.get_instruction_info(data, idx)
+ inst_text = self.arch.get_instruction_text(data, idx)
yield inst_text
idx += inst_info.length
@@ -276,9 +295,11 @@ class BasicBlock(object):
def get_disassembly_text(self, settings=None):
"""
``get_disassembly_text`` returns a list of function.DisassemblyTextLine objects for the current basic block.
+
+ :param DisassemblySettings settings: (optional) DisassemblySettings object
:Example:
- >>>current_basic_block.get_disassembly_text()
+ >>> current_basic_block.get_disassembly_text()
[<0x100000f30: _main:>, <0x100000f30: push rbp>, ... ]
"""
settings_obj = None
@@ -298,8 +319,9 @@ class BasicBlock(object):
size = lines[i].tokens[j].size
operand = lines[i].tokens[j].operand
context = lines[i].tokens[j].context
+ confidence = lines[i].tokens[j].confidence
address = lines[i].tokens[j].address
- tokens.append(function.InstructionTextToken(token_type, text, value, size, operand, context, address))
+ tokens.append(function.InstructionTextToken(token_type, text, value, size, operand, context, address, confidence))
result.append(function.DisassemblyTextLine(addr, tokens))
core.BNFreeDisassemblyTextLines(lines, count.value)
return result
@@ -308,7 +330,7 @@ class BasicBlock(object):
"""
``set_auto_highlight`` highlights the current BasicBlock with the supplied color.
- .warning:: Use only in analysis plugins. Do not use in regular plugins, as colors won't be saved to the database.
+ ..warning:: Use only in analysis plugins. Do not use in regular plugins, as colors won't be saved to the database.
:param HighlightStandardColor or highlight.HighlightColor color: Color value to use for highlighting
"""
diff --git a/python/binaryview.py b/python/binaryview.py
index c0ac0abc..d002b38f 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, SectionSemantics)
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)
@@ -211,7 +217,7 @@ class BinaryDataNotificationCallbacks(object):
def _data_var_added(self, ctxt, view, var):
try:
address = var[0].address
- var_type = types.Type(core.BNNewTypeReference(var[0].type))
+ var_type = types.Type(core.BNNewTypeReference(var[0].type), platform = self.view.platform, confidence = var[0].typeConfidence)
auto_discovered = var[0].autoDiscovered
self.notify.data_var_added(self.view, DataVariable(address, var_type, auto_discovered))
except:
@@ -220,7 +226,7 @@ class BinaryDataNotificationCallbacks(object):
def _data_var_removed(self, ctxt, view, var):
try:
address = var[0].address
- var_type = types.Type(core.BNNewTypeReference(var[0].type))
+ var_type = types.Type(core.BNNewTypeReference(var[0].type), platform = self.view.platform, confidence = var[0].typeConfidence)
auto_discovered = var[0].autoDiscovered
self.notify.data_var_removed(self.view, DataVariable(address, var_type, auto_discovered))
except:
@@ -229,7 +235,7 @@ class BinaryDataNotificationCallbacks(object):
def _data_var_updated(self, ctxt, view, var):
try:
address = var[0].address
- var_type = types.Type(core.BNNewTypeReference(var[0].type))
+ var_type = types.Type(core.BNNewTypeReference(var[0].type), platform = self.view.platform, confidence = var[0].typeConfidence)
auto_discovered = var[0].autoDiscovered
self.notify.data_var_updated(self.view, DataVariable(address, var_type, auto_discovered))
except:
@@ -250,14 +256,14 @@ class BinaryDataNotificationCallbacks(object):
def _type_defined(self, ctxt, view, name, type_obj):
try:
qualified_name = types.QualifiedName._from_core_struct(name[0])
- self.notify.type_defined(view, qualified_name, types.Type(core.BNNewTypeReference(type_obj)))
+ self.notify.type_defined(view, qualified_name, types.Type(core.BNNewTypeReference(type_obj), platform = self.view.platform))
except:
log.log_error(traceback.format_exc())
def _type_undefined(self, ctxt, view, name, type_obj):
try:
qualified_name = types.QualifiedName._from_core_struct(name[0])
- self.notify.type_undefined(view, qualified_name, types.Type(core.BNNewTypeReference(type_obj)))
+ self.notify.type_undefined(view, qualified_name, types.Type(core.BNNewTypeReference(type_obj), platform = self.view.platform))
except:
log.log_error(traceback.format_exc())
@@ -416,7 +422,7 @@ class Segment(object):
class Section(object):
- def __init__(self, name, section_type, start, length, linked_section, info_section, info_data, align, entry_size):
+ def __init__(self, name, section_type, start, length, linked_section, info_section, info_data, align, entry_size, semantics):
self.name = name
self.type = section_type
self.start = start
@@ -426,6 +432,7 @@ class Section(object):
self.info_data = info_data
self.align = align
self.entry_size = entry_size
+ self.semantics = SectionSemantics(semantics)
@property
def end(self):
@@ -854,7 +861,7 @@ class BinaryView(object):
result = {}
for i in xrange(0, count.value):
addr = var_list[i].address
- var_type = types.Type(core.BNNewTypeReference(var_list[i].type))
+ var_type = types.Type(core.BNNewTypeReference(var_list[i].type), platform = self.platform, confidence = var_list[i].typeConfidence)
auto_discovered = var_list[i].autoDiscovered
result[addr] = DataVariable(addr, var_type, auto_discovered)
core.BNFreeDataVariables(var_list, count.value)
@@ -868,7 +875,7 @@ class BinaryView(object):
result = {}
for i in xrange(0, count.value):
name = types.QualifiedName._from_core_struct(type_list[i].name)
- result[name] = types.Type(core.BNNewTypeReference(type_list[i].type))
+ result[name] = types.Type(core.BNNewTypeReference(type_list[i].type), platform = self.platform)
core.BNFreeTypeList(type_list, count.value)
return result
@@ -893,7 +900,8 @@ class BinaryView(object):
for i in xrange(0, count.value):
result[section_list[i].name] = Section(section_list[i].name, section_list[i].type, section_list[i].start,
section_list[i].length, section_list[i].linkedSection, section_list[i].infoSection,
- section_list[i].infoData, section_list[i].align, section_list[i].entrySize)
+ section_list[i].infoData, section_list[i].align, section_list[i].entrySize,
+ section_list[i].semantics)
core.BNFreeSectionList(section_list, count.value)
return result
@@ -919,6 +927,12 @@ class BinaryView(object):
else:
return BinaryView._associated_data[handle.value]
+ @property
+ def global_pointer_value(self):
+ """Discovered value of the global pointer register, if the binary uses one (read-only)"""
+ result = core.BNGetGlobalPointerValue(self.handle)
+ return function.RegisterValue(self.arch, result.value, confidence = result.confidence)
+
def __len__(self):
return int(core.BNGetViewLength(self.handle))
@@ -1423,7 +1437,7 @@ class BinaryView(object):
"""
``save_auto_snapshot`` saves the current database to the already created file.
- .. note:: :py:method:`create_database` should have been called prior to executing this method
+ .. note:: :py:meth:`create_database` should have been called prior to executing this method
:param callable() progress_func: optional function to be called with the current progress and total count.
:return: True if it successfully saved the snapshot, False otherwise
@@ -1672,6 +1686,28 @@ class BinaryView(object):
"""
return core.BNIsOffsetExecutable(self.handle, addr)
+ def is_offset_code_semantics(self, addr):
+ """
+ ``is_offset_code_semantics`` checks if an virtual address ``addr`` is semantically valid for code.
+
+ :param int addr: a virtual address to be checked
+ :return: true if the virtual address is valid for writing, false if the virtual address is invalid or error
+ :rtype: bool
+ """
+ return core.BNIsOffsetCodeSemantics(self.handle, addr)
+
+ def is_offset_writable_semantics(self, addr):
+ """
+ ``is_offset_writable_semantics`` checks if an virtual address ``addr`` is semantically writable. Some sections
+ may have writable permissions for linking purposes but can be treated as read-only for the purposes of
+ analysis.
+
+ :param int addr: a virtual address to be checked
+ :return: true if the virtual address is valid for writing, false if the virtual address is invalid or error
+ :rtype: bool
+ """
+ return core.BNIsOffsetWritableSemantics(self.handle, addr)
+
def save(self, dest):
"""
``save`` saves the original binary file to the provided destination ``dest`` along with any modifications.
@@ -1685,11 +1721,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]
@@ -1781,6 +1831,22 @@ class BinaryView(object):
"""
core.BNRemoveUserFunction(self.handle, func.handle)
+ def add_analysis_option(self, name):
+ """
+ ``add_analysis_option`` adds an analysis option. Analysis options elaborate the analysis phase. The user must
+ start analysis by calling either ``update_analysis()`` or ``update_analysis_and_wait()``.
+
+ :param str name: name of the analysis option. Available options:
+ "linearsweep" : apply linearsweep analysis during the next analysis update (run-once semantics)
+
+ :rtype: None
+ :Example:
+
+ >>> bv.add_analysis_option("linearsweep")
+ >>> bv.update_analysis_and_wait()
+ """
+ core.BNAddAnalysisOption(self.handle, name)
+
def update_analysis(self):
"""
``update_analysis`` asynchronously starts the analysis running and returns immediately. Analysis of BinaryViews
@@ -1801,28 +1867,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):
"""
@@ -1847,7 +1892,10 @@ class BinaryView(object):
>>> bv.define_data_var(bv.entry_point, t[0])
>>>
"""
- core.BNDefineDataVariable(self.handle, addr, var_type.handle)
+ tc = core.BNTypeWithConfidence()
+ tc.type = var_type.handle
+ tc.confidence = var_type.confidence
+ core.BNDefineDataVariable(self.handle, addr, tc)
def define_user_data_var(self, addr, var_type):
"""
@@ -1864,7 +1912,10 @@ class BinaryView(object):
>>> bv.define_user_data_var(bv.entry_point, t[0])
>>>
"""
- core.BNDefineUserDataVariable(self.handle, addr, var_type.handle)
+ tc = core.BNTypeWithConfidence()
+ tc.type = var_type.handle
+ tc.confidence = var_type.confidence
+ core.BNDefineUserDataVariable(self.handle, addr, tc)
def undefine_data_var(self, addr):
"""
@@ -1910,13 +1961,29 @@ class BinaryView(object):
var = core.BNDataVariable()
if not core.BNGetDataVariableAtAddress(self.handle, addr, var):
return None
- return DataVariable(var.address, types.Type(var.type), var.autoDiscovered)
+ return DataVariable(var.address, types.Type(var.type, platform = self.platform, 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
@@ -2722,7 +2789,7 @@ class BinaryView(object):
def get_linear_disassembly_position_at(self, addr, settings):
"""
``get_linear_disassembly_position_at`` instantiates a :py:class:`LinearDisassemblyPosition` object for use in
- :py:method:`get_previous_linear_disassembly_lines` or :py:method:`get_next_linear_disassembly_lines`.
+ :py:meth:`get_previous_linear_disassembly_lines` or :py:meth:`get_next_linear_disassembly_lines`.
:param int addr: virtual address of linear disassembly position
:param DisassemblySettings settings: an instantiated :py:class:`DisassemblySettings` object
@@ -2781,8 +2848,9 @@ class BinaryView(object):
size = lines[i].contents.tokens[j].size
operand = lines[i].contents.tokens[j].operand
context = lines[i].contents.tokens[j].context
+ confidence = lines[i].contents.tokens[j].confidence
address = lines[i].contents.tokens[j].address
- tokens.append(function.InstructionTextToken(token_type, text, value, size, operand, context, address))
+ tokens.append(function.InstructionTextToken(token_type, text, value, size, operand, context, address, confidence))
contents = function.DisassemblyTextLine(addr, tokens)
result.append(lineardisassembly.LinearDisassemblyLine(lines[i].type, func, block, lines[i].lineOffset, contents))
@@ -2896,7 +2964,7 @@ class BinaryView(object):
error_str = errors.value
core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte)))
raise SyntaxError(error_str)
- type_obj = types.Type(core.BNNewTypeReference(result.type))
+ type_obj = types.Type(core.BNNewTypeReference(result.type), platform = self.platform)
name = types.QualifiedName._from_core_struct(result.name)
core.BNFreeQualifiedNameAndType(result)
return type_obj, name
@@ -2920,7 +2988,7 @@ class BinaryView(object):
obj = core.BNGetAnalysisTypeByName(self.handle, name)
if not obj:
return None
- return types.Type(obj)
+ return types.Type(obj, platform = self.platform)
def get_type_by_id(self, id):
"""
@@ -2941,7 +3009,7 @@ class BinaryView(object):
obj = core.BNGetAnalysisTypeById(self.handle, id)
if not obj:
return None
- return types.Type(obj)
+ return types.Type(obj, platform = self.platform)
def get_type_name_by_id(self, id):
"""
@@ -3196,17 +3264,17 @@ class BinaryView(object):
return None
return address.value
- def add_auto_section(self, name, start, length, type = "", align = 1, entry_size = 1, linked_section = "",
- info_section = "", info_data = 0):
- core.BNAddAutoSection(self.handle, name, start, length, type, align, entry_size, linked_section,
+ def add_auto_section(self, name, start, length, semantics = SectionSemantics.DefaultSectionSemantics,
+ type = "", align = 1, entry_size = 1, linked_section = "", info_section = "", info_data = 0):
+ core.BNAddAutoSection(self.handle, name, start, length, semantics, type, align, entry_size, linked_section,
info_section, info_data)
def remove_auto_section(self, name):
core.BNRemoveAutoSection(self.handle, name)
- def add_user_section(self, name, start, length, type = "", align = 1, entry_size = 1, linked_section = "",
- info_section = "", info_data = 0):
- core.BNAddUserSection(self.handle, name, start, length, type, align, entry_size, linked_section,
+ def add_user_section(self, name, start, length, semantics = SectionSemantics.DefaultSectionSemantics,
+ type = "", align = 1, entry_size = 1, linked_section = "", info_section = "", info_data = 0):
+ core.BNAddUserSection(self.handle, name, start, length, semantics, type, align, entry_size, linked_section,
info_section, info_data)
def remove_user_section(self, name):
@@ -3219,7 +3287,8 @@ class BinaryView(object):
for i in xrange(0, count.value):
result.append(Section(section_list[i].name, section_list[i].type, section_list[i].start,
section_list[i].length, section_list[i].linkedSection, section_list[i].infoSection,
- section_list[i].infoData, section_list[i].align, section_list[i].entrySize))
+ section_list[i].infoData, section_list[i].align, section_list[i].entrySize,
+ section_list[i].semantics))
core.BNFreeSectionList(section_list, count.value)
return result
@@ -3228,7 +3297,7 @@ class BinaryView(object):
if not core.BNGetSectionByName(self.handle, name, section):
return None
result = Section(section.name, section.type, section.start, section.length, section.linkedSection,
- section.infoSection, section.infoData, section.align, section.entrySize)
+ section.infoSection, section.infoData, section.align, section.entrySize, section.semantics)
core.BNFreeSection(section)
return result
@@ -3243,6 +3312,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 4c87eef6..e72475c9 100644
--- a/python/callingconvention.py
+++ b/python/callingconvention.py
@@ -25,6 +25,9 @@ import ctypes
import _binaryninjacore as core
import architecture
import log
+import types
+import function
+import binaryview
class CallingConvention(object):
@@ -34,14 +37,19 @@ class CallingConvention(object):
float_arg_regs = []
arg_regs_share_index = False
stack_reserved_for_arg_regs = False
+ stack_adjusted_on_return = False
int_return_reg = None
high_int_return_reg = None
float_return_reg = None
+ global_pointer_reg = None
+ implicitly_defined_regs = []
_registered_calling_conventions = []
- def __init__(self, arch, handle = None):
+ 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()
@@ -52,10 +60,15 @@ class CallingConvention(object):
self._cb.freeRegisterList = self._cb.freeRegisterList.__class__(self._free_register_list)
self._cb.areArgumentRegistersSharedIndex = self._cb.areArgumentRegistersSharedIndex.__class__(self._arg_regs_share_index)
self._cb.isStackReservedForArgumentRegisters = self._cb.isStackReservedForArgumentRegisters.__class__(self._stack_reserved_for_arg_regs)
+ self._cb.isStackAdjustedOnReturn = self._cb.isStackAdjustedOnReturn.__class__(self._stack_adjusted_on_return)
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._cb.getGlobalPointerRegister = self._cb.getGlobalPointerRegister.__class__(self._get_global_pointer_reg)
+ self._cb.getImplicitlyDefinedRegisters = self._cb.getImplicitlyDefinedRegisters.__class__(self._get_implicitly_defined_regs)
+ self._cb.getIncomingRegisterValue = self._cb.getIncomingRegisterValue.__class__(self._get_incoming_reg_value)
+ self._cb.getIncomingFlagValue = self._cb.getIncomingFlagValue.__class__(self._get_incoming_flag_value)
+ self.handle = core.BNCreateCallingConvention(arch.handle, name, self._cb)
self.__class__._registered_calling_conventions.append(self)
else:
self.handle = handle
@@ -63,6 +76,7 @@ class CallingConvention(object):
self.__dict__["name"] = core.BNGetCallingConventionName(self.handle)
self.__dict__["arg_regs_share_index"] = core.BNAreArgumentRegistersSharedIndex(self.handle)
self.__dict__["stack_reserved_for_arg_regs"] = core.BNIsStackReservedForArgumentRegisters(self.handle)
+ self.__dict__["stack_adjusted_on_return"] = core.BNIsStackAdjustedOnReturn(self.handle)
count = ctypes.c_ulonglong()
regs = core.BNGetCallerSavedRegisters(self.handle, count)
@@ -109,6 +123,23 @@ class CallingConvention(object):
else:
self.__dict__["float_return_reg"] = self.arch.get_reg_name(reg)
+ reg = core.BNGetGlobalPointerRegister(self.handle)
+ if reg == 0xffffffff:
+ self.__dict__["global_pointer_reg"] = None
+ else:
+ self.__dict__["global_pointer_reg"] = self.arch.get_reg_name(reg)
+
+ count = ctypes.c_ulonglong()
+ regs = core.BNGetImplicitlyDefinedRegisters(self.handle, count)
+ result = []
+ arch = self.arch
+ for i in xrange(0, count.value):
+ result.append(arch.get_reg_name(regs[i]))
+ core.BNFreeRegisterList(regs, count.value)
+ self.__dict__["implicitly_defined_regs"] = result
+
+ self.confidence = confidence
+
def __del__(self):
core.BNFreeCallingConvention(self.handle)
@@ -190,6 +221,13 @@ class CallingConvention(object):
log.log_error(traceback.format_exc())
return False
+ def _stack_adjusted_on_return(self, ctxt):
+ try:
+ return self.__class__.stack_adjusted_on_return
+ except:
+ log.log_error(traceback.format_exc())
+ return False
+
def _get_int_return_reg(self, ctxt):
try:
return self.arch.regs[self.__class__.int_return_reg].index
@@ -215,8 +253,80 @@ class CallingConvention(object):
log.log_error(traceback.format_exc())
return False
+ def _get_global_pointer_reg(self, ctxt):
+ try:
+ if self.__class__.global_pointer_reg is None:
+ return 0xffffffff
+ return self.arch.regs[self.__class__.global_pointer_reg].index
+ except:
+ log.log_error(traceback.format_exc())
+ return False
+
+ def _get_implicitly_defined_regs(self, ctxt, count):
+ try:
+ regs = self.__class__.implicitly_defined_regs
+ count[0] = len(regs)
+ reg_buf = (ctypes.c_uint * len(regs))()
+ for i in xrange(0, len(regs)):
+ reg_buf[i] = self.arch.regs[regs[i]].index
+ result = ctypes.cast(reg_buf, ctypes.c_void_p)
+ self._pending_reg_lists[result.value] = (result, reg_buf)
+ return result.value
+ except:
+ log.log_error(traceback.format_exc())
+ count[0] = 0
+ return None
+
+ def _get_incoming_reg_value(self, ctxt, reg, func, result):
+ try:
+ func_obj = function.Function(binaryview.BinaryView(handle = core.BNGetFunctionData(func)),
+ core.BNNewFunctionReference(func))
+ reg_name = self.arch.get_reg_name(reg)
+ api_obj = self.perform_get_incoming_reg_value(reg_name, func_obj)._to_api_object()
+ except:
+ log.log_error(traceback.format_exc())
+ api_obj = function.RegisterValue()._to_api_object()
+ result[0].state = api_obj.state
+ result[0].value = api_obj.value
+
+ def _get_incoming_flag_value(self, ctxt, reg, func, result):
+ try:
+ func_obj = function.Function(binaryview.BinaryView(handle = core.BNGetFunctionData(func)),
+ core.BNNewFunctionReference(func))
+ reg_name = self.arch.get_reg_name(reg)
+ api_obj = self.perform_get_incoming_flag_value(reg_name, func_obj)._to_api_object()
+ except:
+ log.log_error(traceback.format_exc())
+ api_obj = function.RegisterValue()._to_api_object()
+ result[0].state = api_obj.state
+ result[0].value = api_obj.value
+
def __repr__(self):
return "<calling convention: %s %s>" % (self.arch.name, self.name)
def __str__(self):
return self.name
+
+ def perform_get_incoming_reg_value(self, reg, func):
+ return function.RegisterValue()
+
+ def perform_get_incoming_flag_value(self, reg, func):
+ return function.RegisterValue()
+
+ def with_confidence(self, confidence):
+ return CallingConvention(self.arch, handle = core.BNNewCallingConventionReference(self.handle),
+ confidence = confidence)
+
+ def get_incoming_reg_value(self, reg, func):
+ reg_num = self.arch.get_reg_index(reg)
+ func_handle = None
+ if func is not None:
+ func_handle = func.handle
+ return function.RegisterValue(self.arch, core.BNGetIncomingRegisterValue(self.handle, reg_num, func_handle))
+
+ def get_incoming_flag_value(self, flag, func):
+ reg_num = self.arch.get_flag_index(flag)
+ func_handle = None
+ if func is not None:
+ func_handle = func.handle
+ return function.RegisterValue(self.arch, core.BNGetIncomingFlagValue(self.handle, reg_num, func_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 8beebe66..5daa7b2a 100644
--- a/python/function.py
+++ b/python/function.py
@@ -37,6 +37,7 @@ import lowlevelil
import mediumlevelil
import binaryview
import log
+import callingconvention
class LookupTableEntry(object):
@@ -49,26 +50,59 @@ class LookupTableEntry(object):
class RegisterValue(object):
- def __init__(self, arch, value):
- self.type = RegisterValueType(value.state)
- if value.state == RegisterValueType.EntryValue:
- self.reg = arch.get_reg_name(value.value)
- elif value.state == RegisterValueType.ConstantValue:
- self.value = value.value
- elif value.state == RegisterValueType.StackFrameOffset:
- self.offset = value.value
+ def __init__(self, arch = None, value = None, confidence = types.max_confidence):
+ if value is None:
+ self.type = RegisterValueType.UndeterminedValue
+ else:
+ self.type = RegisterValueType(value.state)
+ self.is_constant = False
+ if value.state == RegisterValueType.EntryValue:
+ self.arch = arch
+ if arch is not None:
+ self.reg = arch.get_reg_name(value.value)
+ else:
+ self.reg = value.value
+ elif (value.state == RegisterValueType.ConstantValue) or (value.state == RegisterValueType.ConstantPointerValue):
+ self.value = value.value
+ self.is_constant = True
+ elif value.state == RegisterValueType.StackFrameOffset:
+ self.offset = value.value
+ elif value.state == RegisterValueType.ImportedAddressValue:
+ self.value = value.value
+ self.confidence = confidence
def __repr__(self):
if self.type == RegisterValueType.EntryValue:
return "<entry %s>" % self.reg
if self.type == RegisterValueType.ConstantValue:
return "<const %#x>" % self.value
+ if self.type == RegisterValueType.ConstantPointerValue:
+ return "<const ptr %#x>" % self.value
if self.type == RegisterValueType.StackFrameOffset:
return "<stack frame offset %#x>" % self.offset
if self.type == RegisterValueType.ReturnAddressValue:
return "<return address>"
+ if self.type == RegisterValueType.ImportedAddressValue:
+ return "<imported address from entry %#x>" % self.value
return "<undetermined>"
+ def _to_api_object(self):
+ result = core.BNRegisterValue()
+ result.state = self.type
+ result.value = 0
+ if self.type == RegisterValueType.EntryValue:
+ if self.arch is not None:
+ result.value = self.arch.get_reg_index(self.reg)
+ else:
+ result.value = self.reg
+ elif (self.type == RegisterValueType.ConstantValue) or (self.type == RegisterValueType.ConstantPointerValue):
+ result.value = self.value
+ elif self.type == RegisterValueType.StackFrameOffset:
+ result.value = self.offset
+ elif self.type == RegisterValueType.ImportedAddressValue:
+ result.value = self.value
+ return result
+
class ValueRange(object):
def __init__(self, start, end, step):
@@ -148,12 +182,13 @@ class PossibleValueSet(object):
class StackVariableReference(object):
- def __init__(self, src_operand, t, name, var, ref_ofs):
+ def __init__(self, src_operand, t, name, var, ref_ofs, size):
self.source_operand = src_operand
self.type = t
self.name = name
self.var = var
self.referenced_offset = ref_ofs
+ self.size = size
if self.source_operand == 0xffffffff:
self.source_operand = None
@@ -183,9 +218,11 @@ class Variable(object):
if name is None:
name = core.BNGetVariableName(func.handle, var)
if var_type is None:
- var_type = core.BNGetVariableType(func.handle, var)
- if var_type:
- var_type = types.Type(var_type)
+ var_type_conf = core.BNGetVariableType(func.handle, var)
+ if var_type_conf.type:
+ var_type = types.Type(var_type_conf.type, platform = func.platform, confidence = var_type_conf.confidence)
+ else:
+ var_type = None
self.name = name
self.type = var_type
@@ -203,6 +240,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):
@@ -231,6 +274,28 @@ class IndirectBranchInfo(object):
return "<branch %s:%#x -> %s:%#x>" % (self.source_arch.name, self.source_addr, self.dest_arch.name, self.dest_addr)
+class ParameterVariables(object):
+ def __init__(self, var_list, confidence = types.max_confidence):
+ self.vars = var_list
+ self.confidence = confidence
+
+ def __repr__(self):
+ return repr(self.vars)
+
+ def __iter__(self):
+ for var in self.vars:
+ yield var
+
+ def __getitem__(self, idx):
+ return self.vars[idx]
+
+ def __len__(self):
+ return len(self.vars)
+
+ def with_confidence(self, confidence):
+ return ParameterVariables(list(self.vars), confidence = confidence)
+
+
class _FunctionAssociatedDataStore(associateddatastore._AssociatedDataStore):
_defaults = {}
@@ -258,6 +323,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)
@@ -323,8 +391,19 @@ class Function(object):
@property
def can_return(self):
- """Whether function can return (read-only)"""
- return core.BNCanFunctionReturn(self.handle)
+ """Whether function can return"""
+ result = core.BNCanFunctionReturn(self.handle)
+ return types.BoolWithConfidence(result.value, confidence = result.confidence)
+
+ @can_return.setter
+ def can_return(self, value):
+ bc = core.BNBoolWithConfidence()
+ bc.value = bool(value)
+ if hasattr(value, 'confidence'):
+ bc.confidence = value.confidence
+ else:
+ bc.confidence = types.max_confidence
+ core.BNSetUserFunctionCanReturn(self.handle, bc)
@property
def explicitly_defined_type(self):
@@ -376,7 +455,7 @@ class Function(object):
@property
def function_type(self):
"""Function type object"""
- return types.Type(core.BNGetFunctionType(self.handle))
+ return types.Type(core.BNGetFunctionType(self.handle), platform = self.platform)
@function_type.setter
def function_type(self, value):
@@ -390,7 +469,7 @@ class Function(object):
result = []
for i in xrange(0, count.value):
result.append(Variable(self, v[i].var.type, v[i].var.index, v[i].var.storage, v[i].name,
- types.Type(handle = core.BNNewTypeReference(v[i].type))))
+ types.Type(handle = core.BNNewTypeReference(v[i].type), platform = self.platform, confidence = v[i].typeConfidence)))
result.sort(key = lambda x: x.identifier)
core.BNFreeVariableList(v, count.value)
return result
@@ -403,7 +482,7 @@ class Function(object):
result = []
for i in xrange(0, count.value):
result.append(Variable(self, v[i].var.type, v[i].var.index, v[i].var.storage, v[i].name,
- types.Type(handle = core.BNNewTypeReference(v[i].type))))
+ types.Type(handle = core.BNNewTypeReference(v[i].type), platform = self.platform, confidence = v[i].typeConfidence)))
result.sort(key = lambda x: x.identifier)
core.BNFreeVariableList(v, count.value)
return result
@@ -440,6 +519,153 @@ class Function(object):
core.BNFreeAnalysisPerformanceInfo(info, count.value)
return result
+ @property
+ def type_tokens(self):
+ """Text tokens for this function's prototype"""
+ return self.get_type_tokens()[0].tokens
+
+ @property
+ def return_type(self):
+ """Return type of the function"""
+ result = core.BNGetFunctionReturnType(self.handle)
+ if not result.type:
+ return None
+ return types.Type(result.type, platform = self.platform, confidence = result.confidence)
+
+ @return_type.setter
+ def return_type(self, value):
+ type_conf = core.BNTypeWithConfidence()
+ if value is None:
+ type_conf.type = None
+ type_conf.confidence = 0
+ else:
+ type_conf.type = value.handle
+ type_conf.confidence = value.confidence
+ core.BNSetUserFunctionReturnType(self.handle, type_conf)
+
+ @property
+ def calling_convention(self):
+ """Calling convention used by the function"""
+ result = core.BNGetFunctionCallingConvention(self.handle)
+ if not result.convention:
+ return None
+ return callingconvention.CallingConvention(None, handle = result.convention, confidence = result.confidence)
+
+ @calling_convention.setter
+ def calling_convention(self, value):
+ conv_conf = core.BNCallingConventionWithConfidence()
+ if value is None:
+ conv_conf.convention = None
+ conv_conf.confidence = 0
+ else:
+ conv_conf.convention = value.handle
+ conv_conf.confidence = value.confidence
+ core.BNSetUserFunctionCallingConvention(self.handle, conv_conf)
+
+ @property
+ def parameter_vars(self):
+ """List of variables for the incoming function parameters"""
+ result = core.BNGetFunctionParameterVariables(self.handle)
+ var_list = []
+ for i in xrange(0, result.count):
+ var_list.append(Variable(self, result.vars[i].type, result.vars[i].index, result.vars[i].storage))
+ confidence = result.confidence
+ core.BNFreeParameterVariables(result)
+ return ParameterVariables(var_list, confidence = confidence)
+
+ @parameter_vars.setter
+ def parameter_vars(self, value):
+ if value is None:
+ var_list = []
+ else:
+ var_list = list(value)
+ var_conf = core.BNParameterVariablesWithConfidence()
+ var_conf.vars = (core.BNVariable * len(var_list))()
+ var_conf.count = len(var_list)
+ for i in xrange(0, len(var_list)):
+ var_conf.vars[i].type = var_list[i].source_type
+ var_conf.vars[i].index = var_list[i].index
+ var_conf.vars[i].storage = var_list[i].storage
+ if value is None:
+ var_conf.confidence = 0
+ elif hasattr(value, 'confidence'):
+ var_conf.confidence = value.confidence
+ else:
+ var_conf.confidence = types.max_confidence
+ core.BNSetUserFunctionParameterVariables(self.handle, var_conf)
+
+ @property
+ def has_variable_arguments(self):
+ """Whether the function takes a variable number of arguments"""
+ result = core.BNFunctionHasVariableArguments(self.handle)
+ return types.BoolWithConfidence(result.value, confidence = result.confidence)
+
+ @has_variable_arguments.setter
+ def has_variable_arguments(self, value):
+ bc = core.BNBoolWithConfidence()
+ bc.value = bool(value)
+ if hasattr(value, 'confidence'):
+ bc.confidence = value.confidence
+ else:
+ bc.confidence = types.max_confidence
+ core.BNSetUserFunctionHasVariableArguments(self.handle, bc)
+
+ @property
+ def stack_adjustment(self):
+ """Number of bytes removed from the stack after return"""
+ result = core.BNGetFunctionStackAdjustment(self.handle)
+ return types.SizeWithConfidence(result.value, confidence = result.confidence)
+
+ @stack_adjustment.setter
+ def stack_adjustment(self, value):
+ sc = core.BNSizeWithConfidence()
+ sc.value = int(value)
+ if hasattr(value, 'confidence'):
+ sc.confidence = value.confidence
+ else:
+ sc.confidence = types.max_confidence
+ core.BNSetUserFunctionStackAdjustment(self.handle, sc)
+
+ @property
+ def clobbered_regs(self):
+ """Registers that are modified by this function"""
+ result = core.BNGetFunctionClobberedRegisters(self.handle)
+ reg_set = []
+ for i in xrange(0, result.count):
+ reg_set.append(self.arch.get_reg_name(result.regs[i]))
+ regs = types.RegisterSet(reg_set, confidence = result.confidence)
+ core.BNFreeClobberedRegisters(result)
+ return regs
+
+ @clobbered_regs.setter
+ def clobbered_regs(self, value):
+ regs = core.BNRegisterSetWithConfidence()
+ regs.regs = (ctypes.c_uint * len(value))()
+ regs.count = len(value)
+ for i in xrange(0, len(value)):
+ regs.regs[i] = self.arch.get_reg_index(value[i])
+ if hasattr(value, 'confidence'):
+ regs.confidence = value.confidence
+ else:
+ regs.confidence = types.max_confidence
+ core.BNSetUserFunctionClobberedRegisters(self.handle, regs)
+
+ @property
+ def global_pointer_value(self):
+ """Discovered value of the global pointer register, if the function uses one (read-only)"""
+ result = core.BNGetFunctionGlobalPointerValue(self.handle)
+ return RegisterValue(self.arch, result.value, confidence = result.confidence)
+
+ @property
+ def comment(self):
+ """Gets the comment for the current function"""
+ return core.BNGetFunctionComment(self.handle)
+
+ @comment.setter
+ def comment(self, comment):
+ """Sets a comment for the current function"""
+ return core.BNSetFunctionComment(self.handle, comment)
+
def __iter__(self):
count = ctypes.c_ulonglong()
blocks = core.BNGetFunctionBasicBlockList(self.handle, count)
@@ -469,6 +695,21 @@ class Function(object):
return core.BNGetCommentForAddress(self.handle, addr)
def set_comment(self, addr, comment):
+ """Deprecated use set_comment_at instead"""
+ core.BNSetCommentForAddress(self.handle, addr, comment)
+
+ def set_comment_at(self, addr, comment):
+ """
+ ``set_comment_at`` sets a comment for the current function at the address specified
+
+ :param addr int: virtual address within the current function to apply the comment to
+ :param comment str: string comment to apply
+ :rtype: None
+ :Example:
+
+ >>> current_function.set_comment_at(here, "hi")
+
+ """
core.BNSetCommentForAddress(self.handle, addr, comment)
def get_low_level_il_at(self, addr, arch=None):
@@ -616,10 +857,10 @@ class Function(object):
refs = core.BNGetStackVariablesReferencedByInstruction(self.handle, arch.handle, addr, count)
result = []
for i in xrange(0, count.value):
- var_type = types.Type(core.BNNewTypeReference(refs[i].type))
+ var_type = types.Type(core.BNNewTypeReference(refs[i].type), platform = self.platform, confidence = refs[i].typeConfidence)
result.append(StackVariableReference(refs[i].sourceOperand, var_type,
refs[i].name, Variable.from_identifier(self, refs[i].varIdentifier, refs[i].name, var_type),
- refs[i].referencedOffset))
+ refs[i].referencedOffset, refs[i].size))
core.BNFreeStackVariableReferenceList(refs, count.value)
return result
@@ -730,8 +971,9 @@ class Function(object):
size = lines[i].tokens[j].size
operand = lines[i].tokens[j].operand
context = lines[i].tokens[j].context
+ confidence = lines[i].tokens[j].confidence
address = lines[i].tokens[j].address
- tokens.append(InstructionTextToken(token_type, text, value, size, operand, context, address))
+ tokens.append(InstructionTextToken(token_type, text, value, size, operand, context, address, confidence))
result.append(tokens)
core.BNFreeInstructionTextLines(lines, count.value)
return result
@@ -742,6 +984,85 @@ class Function(object):
def set_user_type(self, value):
core.BNSetFunctionUserType(self.handle, value.handle)
+ def set_auto_return_type(self, value):
+ type_conf = core.BNTypeWithConfidence()
+ if value is None:
+ type_conf.type = None
+ type_conf.confidence = 0
+ else:
+ type_conf.type = value.handle
+ type_conf.confidence = value.confidence
+ core.BNSetAutoFunctionReturnType(self.handle, type_conf)
+
+ def set_auto_calling_convention(self, value):
+ conv_conf = core.BNCallingConventionWithConfidence()
+ if value is None:
+ conv_conf.convention = None
+ conv_conf.confidence = 0
+ else:
+ conv_conf.convention = value.handle
+ conv_conf.confidence = value.confidence
+ core.BNSetAutoFunctionCallingConvention(self.handle, conv_conf)
+
+ def set_auto_parameter_vars(self, value):
+ if value is None:
+ var_list = []
+ else:
+ var_list = list(value)
+ var_conf = core.BNParameterVariablesWithConfidence()
+ var_conf.vars = (core.BNVariable * len(var_list))()
+ var_conf.count = len(var_list)
+ for i in xrange(0, len(var_list)):
+ var_conf.vars[i].type = var_list[i].source_type
+ var_conf.vars[i].index = var_list[i].index
+ var_conf.vars[i].storage = var_list[i].storage
+ if value is None:
+ var_conf.confidence = 0
+ elif hasattr(value, 'confidence'):
+ var_conf.confidence = value.confidence
+ else:
+ var_conf.confidence = types.max_confidence
+ core.BNSetAutoFunctionParameterVariables(self.handle, var_conf)
+
+ def set_auto_has_variable_arguments(self, value):
+ bc = core.BNBoolWithConfidence()
+ bc.value = bool(value)
+ if hasattr(value, 'confidence'):
+ bc.confidence = value.confidence
+ else:
+ bc.confidence = types.max_confidence
+ core.BNSetAutoFunctionHasVariableArguments(self.handle, bc)
+
+ def set_auto_can_return(self, value):
+ bc = core.BNBoolWithConfidence()
+ bc.value = bool(value)
+ if hasattr(value, 'confidence'):
+ bc.confidence = value.confidence
+ else:
+ bc.confidence = types.max_confidence
+ core.BNSetAutoFunctionCanReturn(self.handle, bc)
+
+ def set_auto_stack_adjustment(self, value):
+ sc = core.BNSizeWithConfidence()
+ sc.value = int(value)
+ if hasattr(value, 'confidence'):
+ sc.confidence = value.confidence
+ else:
+ sc.confidence = types.max_confidence
+ core.BNSetAutoFunctionStackAdjustment(self.handle, sc)
+
+ def set_auto_clobbered_regs(self, value):
+ regs = core.BNRegisterSetWithConfidence()
+ regs.regs = (ctypes.c_uint * len(value))()
+ regs.count = len(value)
+ for i in xrange(0, len(value)):
+ regs.regs[i] = self.arch.get_reg_index(value[i])
+ if hasattr(value, 'confidence'):
+ regs.confidence = value.confidence
+ else:
+ regs.confidence = types.max_confidence
+ core.BNSetAutoFunctionClobberedRegisters(self.handle, regs)
+
def get_int_display_type(self, instr_addr, value, operand, arch=None):
if arch is None:
arch = self.arch
@@ -818,7 +1139,7 @@ class Function(object):
"""
``set_auto_instr_highlight`` highlights the instruction at the specified address with the supplied color
- .warning:: Use only in analysis plugins. Do not use in regular plugins, as colors won't be saved to the database.
+ ..warning:: Use only in analysis plugins. Do not use in regular plugins, as colors won't be saved to the database.
:param int addr: virtual address of the instruction to be highlighted
:param HighlightStandardColor or highlight.HighlightColor color: Color value to use for highlighting
@@ -853,10 +1174,16 @@ class Function(object):
core.BNSetUserInstructionHighlight(self.handle, arch.handle, addr, color._get_core_struct())
def create_auto_stack_var(self, offset, var_type, name):
- core.BNCreateAutoStackVariable(self.handle, offset, var_type.handle, name)
+ tc = core.BNTypeWithConfidence()
+ tc.type = var_type.handle
+ tc.confidence = var_type.confidence
+ core.BNCreateAutoStackVariable(self.handle, offset, tc, name)
def create_user_stack_var(self, offset, var_type, name):
- core.BNCreateUserStackVariable(self.handle, offset, var_type.handle, name)
+ tc = core.BNTypeWithConfidence()
+ tc.type = var_type.handle
+ tc.confidence = var_type.confidence
+ core.BNCreateUserStackVariable(self.handle, offset, tc, name)
def delete_auto_stack_var(self, offset):
core.BNDeleteAutoStackVariable(self.handle, offset)
@@ -869,14 +1196,20 @@ class Function(object):
var_data.type = var.source_type
var_data.index = var.index
var_data.storage = var.storage
- core.BNCreateAutoVariable(self.handle, var_data, var_type.handle, name, ignore_disjoint_uses)
+ tc = core.BNTypeWithConfidence()
+ tc.type = var_type.handle
+ tc.confidence = var_type.confidence
+ core.BNCreateAutoVariable(self.handle, var_data, tc, name, ignore_disjoint_uses)
def create_user_var(self, var, var_type, name, ignore_disjoint_uses = False):
var_data = core.BNVariable()
var_data.type = var.source_type
var_data.index = var.index
var_data.storage = var.storage
- core.BNCreateUserVariable(self.handle, var_data, var_type.handle, name, ignore_disjoint_uses)
+ tc = core.BNTypeWithConfidence()
+ tc.type = var_type.handle
+ tc.confidence = var_type.confidence
+ core.BNCreateUserVariable(self.handle, var_data, tc, name, ignore_disjoint_uses)
def delete_auto_var(self, var):
var_data = core.BNVariable()
@@ -899,10 +1232,38 @@ class Function(object):
if not core.BNGetStackVariableAtFrameOffset(self.handle, arch.handle, addr, offset, found_var):
return None
result = Variable(self, found_var.var.type, found_var.var.index, found_var.var.storage,
- found_var.name, types.Type(handle = core.BNNewTypeReference(found_var.type)))
+ found_var.name, types.Type(handle = core.BNNewTypeReference(found_var.type), platform = self.platform,
+ confidence = found_var.typeConfidence))
core.BNFreeVariableNameAndType(found_var)
return result
+ def get_type_tokens(self, settings=None):
+ if settings is not None:
+ settings = settings.handle
+ count = ctypes.c_ulonglong()
+ lines = core.BNGetFunctionTypeTokens(self.handle, settings, count)
+ result = []
+ for i in xrange(0, count.value):
+ addr = lines[i].addr
+ tokens = []
+ for j in xrange(0, lines[i].count):
+ token_type = InstructionTextTokenType(lines[i].tokens[j].type)
+ text = lines[i].tokens[j].text
+ value = lines[i].tokens[j].value
+ size = lines[i].tokens[j].size
+ operand = lines[i].tokens[j].operand
+ context = lines[i].tokens[j].context
+ 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))
+ core.BNFreeDisassemblyTextLines(lines, count.value)
+ return result
+
+ def get_reg_value_at_exit(self, reg):
+ result = core.BNGetFunctionRegisterValueAtExit(self.handle, self.arch.get_reg_index(reg))
+ return RegisterValue(self.arch, result.value, confidence = result.confidence)
+
class AdvancedFunctionAnalysisDataRequestor(object):
def __init__(self, func = None):
@@ -1043,8 +1404,9 @@ class FunctionGraphBlock(object):
size = lines[i].tokens[j].size
operand = lines[i].tokens[j].operand
context = lines[i].tokens[j].context
+ confidence = lines[i].tokens[j].confidence
address = lines[i].tokens[j].address
- tokens.append(InstructionTextToken(token_type, text, value, size, operand, context, address))
+ tokens.append(InstructionTextToken(token_type, text, value, size, operand, context, address, confidence))
result.append(DisassemblyTextLine(addr, tokens))
core.BNFreeDisassemblyTextLines(lines, count.value)
return result
@@ -1101,8 +1463,9 @@ class FunctionGraphBlock(object):
size = lines[i].tokens[j].size
operand = lines[i].tokens[j].operand
context = lines[i].tokens[j].context
+ confidence = lines[i].tokens[j].confidence
address = lines[i].tokens[j].address
- tokens.append(InstructionTextToken(token_type, text, value, size, operand, context, address))
+ tokens.append(InstructionTextToken(token_type, text, value, size, operand, context, address, confidence))
yield DisassemblyTextLine(addr, tokens)
finally:
core.BNFreeDisassemblyTextLines(lines, count.value)
@@ -1384,13 +1747,14 @@ class InstructionTextToken(object):
"""
def __init__(self, token_type, text, value = 0, size = 0, operand = 0xffffffff,
- context = InstructionTextTokenContext.NoTokenContext, address = 0):
+ context = InstructionTextTokenContext.NoTokenContext, address = 0, confidence = types.max_confidence):
self.type = InstructionTextTokenType(token_type)
self.text = text
self.value = value
self.size = size
self.operand = operand
self.context = InstructionTextTokenContext(context)
+ self.confidence = confidence
self.address = address
def __str__(self):
diff --git a/python/generator.cpp b/python/generator.cpp
index 6f19db66..f838b36d 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->GetStandalonePlatform()->ParseTypesFromSourceFile(argv[1], types, vars, funcs, errors);
+ fprintf(stderr, "Errors: %s", errors.c_str());
if (!ok)
return 1;
@@ -230,22 +237,61 @@ int main(int argc, char* argv[])
fprintf(out, "\n# Structure definitions\n");
+ set<QualifiedName> structsToProcess;
+ set<QualifiedName> finishedStructs;
for (auto& i : types)
+ structsToProcess.insert(i.first);
+ while (structsToProcess.size() != 0)
{
- string name;
- if (i.first.size() != 1)
- continue;
- name = i.first[0];
- if ((i.second->GetClass() == StructureTypeClass) && (i.second->GetStructure()->GetMembers().size() != 0))
+ set<QualifiedName> currentStructList = structsToProcess;
+ structsToProcess.clear();
+ bool processedSome = false;
+ for (auto& i : currentStructList)
{
- fprintf(out, "%s._fields_ = [\n", name.c_str());
- for (auto& j : i.second->GetStructure()->GetMembers())
+ string name;
+ if (i.size() != 1)
+ continue;
+ Ref<Type> type = types[i];
+ name = i[0];
+ if ((type->GetClass() == StructureTypeClass) && (type->GetStructure()->GetMembers().size() != 0))
{
- fprintf(out, "\t\t(\"%s\", ", j.name.c_str());
- OutputType(out, j.type);
- fprintf(out, "),\n");
+ bool requiresDependency = false;
+ for (auto& j : type->GetStructure()->GetMembers())
+ {
+ if ((j.type->GetClass() == NamedTypeReferenceClass) &&
+ (types[j.type->GetNamedTypeReference()->GetName()]->GetClass() == StructureTypeClass) &&
+ (finishedStructs.count(j.type->GetNamedTypeReference()->GetName()) == 0))
+ {
+ // This structure needs another structure that isn't fully defined yet, need to wait
+ // for the dependencies to be defined
+ structsToProcess.insert(i);
+ requiresDependency = true;
+ break;
+ }
+ }
+
+ if (requiresDependency)
+ continue;
+
+ fprintf(out, "%s._fields_ = [\n", name.c_str());
+ for (auto& j : type->GetStructure()->GetMembers())
+ {
+ fprintf(out, "\t\t(\"%s\", ", j.name.c_str());
+ OutputType(out, j.type);
+ fprintf(out, "),\n");
+ }
+ fprintf(out, "\t]\n");
+ finishedStructs.insert(i);
+ processedSome = true;
}
- fprintf(out, "\t]\n");
+ }
+
+ if (!processedSome)
+ {
+ fprintf(stderr, "Detected dependency cycle in structures\n");
+ for (auto& i : structsToProcess)
+ fprintf(stderr, "%s\n", i.GetString().c_str());
+ 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/lineardisassembly.py b/python/lineardisassembly.py
index 5ef8d623..f41fdfcb 100644
--- a/python/lineardisassembly.py
+++ b/python/lineardisassembly.py
@@ -24,7 +24,7 @@ class LinearDisassemblyPosition(object):
``class LinearDisassemblyPosition`` is a helper object containing the position of the current Linear Disassembly.
.. note:: This object should not be instantiated directly. Rather call \
- :py:method:`get_linear_disassembly_position_at` which instantiates this object.
+ :py:meth:`get_linear_disassembly_position_at` which instantiates this object.
"""
def __init__(self, func, block, addr):
self.function = func
diff --git a/python/lowlevelil.py b/python/lowlevelil.py
index c359a79c..75a3f1ad 100644
--- a/python/lowlevelil.py
+++ b/python/lowlevelil.py
@@ -161,6 +161,7 @@ class LowLevelILInstruction(object):
LowLevelILOperation.LLIL_JUMP: [("dest", "expr")],
LowLevelILOperation.LLIL_JUMP_TO: [("dest", "expr"), ("targets", "int_list")],
LowLevelILOperation.LLIL_CALL: [("dest", "expr")],
+ LowLevelILOperation.LLIL_CALL_STACK_ADJUST: [("dest", "expr"), ("stack_adjustment", "int")],
LowLevelILOperation.LLIL_RET: [("dest", "expr")],
LowLevelILOperation.LLIL_NORET: [],
LowLevelILOperation.LLIL_IF: [("condition", "expr"), ("true", "int"), ("false", "int")],
@@ -309,8 +310,9 @@ class LowLevelILInstruction(object):
size = tokens[i].size
operand = tokens[i].operand
context = tokens[i].context
+ confidence = tokens[i].confidence
address = tokens[i].address
- result.append(function.InstructionTextToken(token_type, text, value, size, operand, context, address))
+ result.append(function.InstructionTextToken(token_type, text, value, size, operand, context, address, confidence))
core.BNFreeInstructionText(tokens, count.value)
return result
@@ -327,8 +329,16 @@ class LowLevelILInstruction(object):
core.BNGetLowLevelILNonSSAExprIndex(self.function.handle, self.expr_index))
@property
+ def medium_level_il(self):
+ """Gets the medium level IL expression corresponding to this expression (may be None for eliminated instructions)"""
+ expr = self.function.get_medium_level_il_expr_index(self.expr_index)
+ if expr is None:
+ return None
+ return mediumlevelil.MediumLevelILInstruction(self.function.medium_level_il, expr)
+
+ @property
def mapped_medium_level_il(self):
- """Gets the medium level IL expression corresponding to this expression"""
+ """Gets the mapped medium level IL expression corresponding to this expression"""
expr = self.function.get_mapped_medium_level_il_expr_index(self.expr_index)
if expr is None:
return None
@@ -722,17 +732,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):
"""
@@ -1252,6 +1263,18 @@ class LowLevelILFunction(object):
"""
return self.expr(LowLevelILOperation.LLIL_CALL, dest.index)
+ def call_stack_adjust(self, dest, stack_adjust):
+ """
+ ``call_stack_adjust`` returns an expression which first pushes the address of the next instruction onto the stack
+ then jumps (branches) to the expression ``dest``. After the function exits, ``stack_adjust`` is added to the
+ stack pointer register.
+
+ :param LowLevelILExpr dest: the expression to call
+ :return: The expression ``call(dest), stack += stack_adjust``
+ :rtype: LowLevelILExpr
+ """
+ return self.expr(LowLevelILOperation.LLIL_CALL_STACK_ADJUST, dest.index, stack_adjust)
+
def ret(self, dest):
"""
``ret`` returns an expression which jumps (branches) to the expression ``dest``. ``ret`` is a special alias for
@@ -1651,6 +1674,24 @@ class LowLevelILFunction(object):
result = function.RegisterValue(self.arch, value)
return result
+ def get_medium_level_il_instruction_index(self, instr):
+ med_il = self.medium_level_il
+ if med_il is None:
+ return None
+ result = core.BNGetMediumLevelILInstructionIndex(self.handle, instr)
+ if result >= core.BNGetMediumLevelILInstructionCount(med_il.handle):
+ return None
+ return result
+
+ def get_medium_level_il_expr_index(self, expr):
+ med_il = self.medium_level_il
+ if med_il is None:
+ return None
+ result = core.BNGetMediumLevelILExprIndex(self.handle, expr)
+ if result >= core.BNGetMediumLevelILExprCount(med_il.handle):
+ return None
+ return result
+
def get_mapped_medium_level_il_instruction_index(self, instr):
med_il = self.mapped_medium_level_il
if med_il is None:
@@ -1688,6 +1729,9 @@ class LowLevelILBasicBlock(basicblock.BasicBlock):
else:
return self.il_function[self.end + idx]
+ def _create_instance(self, view, handle):
+ """Internal method by super to instantiante child instances"""
+ return LowLevelILBasicBlock(view, handle, self.il_function)
def LLIL_TEMP(n):
return n | 0x80000000
diff --git a/python/mediumlevelil.py b/python/mediumlevelil.py
index 1274bd9b..07759a47 100644
--- a/python/mediumlevelil.py
+++ b/python/mediumlevelil.py
@@ -26,6 +26,7 @@ from .enums import MediumLevelILOperation, InstructionTextTokenType, ILBranchDep
import function
import basicblock
import lowlevelil
+import types
class SSAVariable(object):
@@ -36,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):
@@ -70,17 +80,20 @@ class MediumLevelILInstruction(object):
MediumLevelILOperation.MLIL_SET_VAR_FIELD: [("dest", "var"), ("offset", "int"), ("src", "expr")],
MediumLevelILOperation.MLIL_SET_VAR_SPLIT: [("high", "var"), ("low", "var"), ("src", "expr")],
MediumLevelILOperation.MLIL_LOAD: [("src", "expr")],
+ MediumLevelILOperation.MLIL_LOAD_STRUCT: [("src", "expr"), ("offset", "int")],
MediumLevelILOperation.MLIL_STORE: [("dest", "expr"), ("src", "expr")],
+ MediumLevelILOperation.MLIL_STORE_STRUCT: [("dest", "expr"), ("offset", "int"), ("src", "expr")],
MediumLevelILOperation.MLIL_VAR: [("src", "var")],
MediumLevelILOperation.MLIL_VAR_FIELD: [("src", "var"), ("offset", "int")],
MediumLevelILOperation.MLIL_ADDRESS_OF: [("src", "var")],
MediumLevelILOperation.MLIL_ADDRESS_OF_FIELD: [("src", "var"), ("offset", "int")],
MediumLevelILOperation.MLIL_CONST: [("constant", "int")],
MediumLevelILOperation.MLIL_CONST_PTR: [("constant", "int")],
+ MediumLevelILOperation.MLIL_IMPORT: [("constant", "int")],
MediumLevelILOperation.MLIL_ADD: [("left", "expr"), ("right", "expr")],
- MediumLevelILOperation.MLIL_ADC: [("left", "expr"), ("right", "expr")],
+ MediumLevelILOperation.MLIL_ADC: [("left", "expr"), ("right", "expr"), ("carry", "expr")],
MediumLevelILOperation.MLIL_SUB: [("left", "expr"), ("right", "expr")],
- MediumLevelILOperation.MLIL_SBB: [("left", "expr"), ("right", "expr")],
+ MediumLevelILOperation.MLIL_SBB: [("left", "expr"), ("right", "expr"), ("carry", "expr")],
MediumLevelILOperation.MLIL_AND: [("left", "expr"), ("right", "expr")],
MediumLevelILOperation.MLIL_OR: [("left", "expr"), ("right", "expr")],
MediumLevelILOperation.MLIL_XOR: [("left", "expr"), ("right", "expr")],
@@ -88,9 +101,9 @@ class MediumLevelILInstruction(object):
MediumLevelILOperation.MLIL_LSR: [("left", "expr"), ("right", "expr")],
MediumLevelILOperation.MLIL_ASR: [("left", "expr"), ("right", "expr")],
MediumLevelILOperation.MLIL_ROL: [("left", "expr"), ("right", "expr")],
- MediumLevelILOperation.MLIL_RLC: [("left", "expr"), ("right", "expr")],
+ MediumLevelILOperation.MLIL_RLC: [("left", "expr"), ("right", "expr"), ("carry", "expr")],
MediumLevelILOperation.MLIL_ROR: [("left", "expr"), ("right", "expr")],
- MediumLevelILOperation.MLIL_RRC: [("left", "expr"), ("right", "expr")],
+ MediumLevelILOperation.MLIL_RRC: [("left", "expr"), ("right", "expr"), ("carry", "expr")],
MediumLevelILOperation.MLIL_MUL: [("left", "expr"), ("right", "expr")],
MediumLevelILOperation.MLIL_MULU_DP: [("left", "expr"), ("right", "expr")],
MediumLevelILOperation.MLIL_MULS_DP: [("left", "expr"), ("right", "expr")],
@@ -139,7 +152,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", "var_ssa"), ("low", "var_ssa"), ("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")],
@@ -153,7 +166,9 @@ class MediumLevelILInstruction(object):
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")],
+ MediumLevelILOperation.MLIL_LOAD_STRUCT_SSA: [("src", "expr"), ("offset", "int"), ("src_memory", "int")],
MediumLevelILOperation.MLIL_STORE_SSA: [("dest", "expr"), ("dest_memory", "int"), ("src_memory", "int"), ("src", "expr")],
+ MediumLevelILOperation.MLIL_STORE_STRUCT_SSA: [("dest", "expr"), ("offset", "int"), ("dest_memory", "int"), ("src_memory", "int"), ("src", "expr")],
MediumLevelILOperation.MLIL_VAR_PHI: [("dest", "var_ssa"), ("src", "var_ssa_list")],
MediumLevelILOperation.MLIL_MEM_PHI: [("dest_memory", "int"), ("src_memory", "int_list")]
}
@@ -169,6 +184,7 @@ class MediumLevelILInstruction(object):
self.operation = MediumLevelILOperation(instr.operation)
self.size = instr.size
self.address = instr.address
+ self.source_operand = instr.sourceOperand
operands = MediumLevelILInstruction.ILOperations[instr.operation]
self.operands = []
i = 0
@@ -265,8 +281,9 @@ class MediumLevelILInstruction(object):
size = tokens[i].size
operand = tokens[i].operand
context = tokens[i].context
+ confidence = tokens[i].confidence
address = tokens[i].address
- result.append(function.InstructionTextToken(token_type, text, value, size, operand, context, address))
+ result.append(function.InstructionTextToken(token_type, text, value, size, operand, context, address, confidence))
core.BNFreeInstructionText(tokens, count.value)
return result
@@ -396,13 +413,25 @@ class MediumLevelILInstruction(object):
result += operand.vars_read
return result
+ @property
+ def expr_type(self):
+ """Type of expression"""
+ result = core.BNGetMediumLevelILExprType(self.function.handle, self.expr_index)
+ if result.type:
+ platform = None
+ if self.function.source_function:
+ platform = self.function.source_function.platform
+ return types.Type(result.type, platform = platform, confidence = result.confidence)
+ return None
+
def get_ssa_var_possible_values(self, ssa_var):
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
value = core.BNGetMediumLevelILPossibleSSAVarValues(self.function.handle, var_data, ssa_var.version, self.instr_index)
- result = function.RegisterValue(self.function.arch, value)
+ result = function.PossibleValueSet(self.function.arch, value)
+ core.BNFreePossibleValueSet(value)
return result
def get_ssa_var_version(self, var):
@@ -783,6 +812,32 @@ class MediumLevelILFunction(object):
core.BNFreeILInstructionList(instrs)
return result
+ def get_var_definitions(self, var):
+ count = ctypes.c_ulonglong()
+ var_data = core.BNVariable()
+ var_data.type = var.source_type
+ var_data.index = var.index
+ var_data.storage = var.storage
+ instrs = core.BNGetMediumLevelILVariableDefinitions(self.handle, var_data, count)
+ result = []
+ for i in xrange(0, count.value):
+ result.append(instrs[i])
+ core.BNFreeILInstructionList(instrs)
+ return result
+
+ def get_var_uses(self, var):
+ count = ctypes.c_ulonglong()
+ var_data = core.BNVariable()
+ var_data.type = var.source_type
+ var_data.index = var.index
+ var_data.storage = var.storage
+ instrs = core.BNGetMediumLevelILVariableUses(self.handle, var_data, count)
+ result = []
+ for i in xrange(0, count.value):
+ result.append(instrs[i])
+ core.BNFreeILInstructionList(instrs)
+ return result
+
def get_ssa_var_value(self, ssa_var):
var_data = core.BNVariable()
var_data.type = ssa_var.var.source_type
@@ -834,3 +889,7 @@ class MediumLevelILBasicBlock(basicblock.BasicBlock):
return self.il_function[idx + self.start]
else:
return self.il_function[self.end + idx]
+
+ def _create_instance(self, view, handle):
+ """Internal method by super to instantiante child instances"""
+ return MediumLevelILBasicBlock(view, handle, self.il_function)
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..5e63d836 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
@@ -234,7 +234,7 @@ class Platform(object):
result = {}
for i in xrange(0, count.value):
name = types.QualifiedName._from_core_struct(type_list[i].name)
- result[name] = types.Type(core.BNNewTypeReference(type_list[i].type))
+ result[name] = types.Type(core.BNNewTypeReference(type_list[i].type), platform = self)
core.BNFreeTypeList(type_list, count.value)
return result
@@ -246,7 +246,7 @@ class Platform(object):
result = {}
for i in xrange(0, count.value):
name = types.QualifiedName._from_core_struct(type_list[i].name)
- result[name] = types.Type(core.BNNewTypeReference(type_list[i].type))
+ result[name] = types.Type(core.BNNewTypeReference(type_list[i].type), platform = self)
core.BNFreeTypeList(type_list, count.value)
return result
@@ -258,7 +258,7 @@ class Platform(object):
result = {}
for i in xrange(0, count.value):
name = types.QualifiedName._from_core_struct(type_list[i].name)
- result[name] = types.Type(core.BNNewTypeReference(type_list[i].type))
+ result[name] = types.Type(core.BNNewTypeReference(type_list[i].type), platform = self)
core.BNFreeTypeList(type_list, count.value)
return result
@@ -270,7 +270,7 @@ class Platform(object):
result = {}
for i in xrange(0, count.value):
name = types.QualifiedName._from_core_struct(call_list[i].name)
- t = types.Type(core.BNNewTypeReference(call_list[i].type))
+ t = types.Type(core.BNNewTypeReference(call_list[i].type), platform = self)
result[call_list[i].number] = (name, t)
core.BNFreeSystemCallList(call_list, count.value)
return result
@@ -325,21 +325,21 @@ class Platform(object):
obj = core.BNGetPlatformTypeByName(self.handle, name)
if not obj:
return None
- return types.Type(obj)
+ return types.Type(obj, platform = self)
def get_variable_by_name(self, name):
name = types.QualifiedName(name)._get_core_struct()
obj = core.BNGetPlatformVariableByName(self.handle, name)
if not obj:
return None
- return types.Type(obj)
+ return types.Type(obj, platform = self)
def get_function_by_name(self, name):
name = types.QualifiedName(name)._get_core_struct()
obj = core.BNGetPlatformFunctionByName(self.handle, name)
if not obj:
return None
- return types.Type(obj)
+ return types.Type(obj, platform = self)
def get_system_call_name(self, number):
return core.BNGetPlatformSystemCallName(self.handle, number)
@@ -348,7 +348,7 @@ class Platform(object):
obj = core.BNGetPlatformSystemCallType(self.handle, number)
if not obj:
return None
- return types.Type(obj)
+ return types.Type(obj, platform = self)
def generate_auto_platform_type_id(self, name):
name = types.QualifiedName(name)._get_core_struct()
@@ -360,3 +360,96 @@ class Platform(object):
def get_auto_platform_type_id_source(self):
return core.BNGetAutoPlatformTypeIdSource(self.handle)
+
+ def parse_types_from_source(self, source, filename=None, include_dirs=[], auto_type_source=None):
+ """
+ ``parse_types_from_source`` parses the source string and any needed headers searching for them in
+ the optional list of directories provided in ``include_dirs``.
+
+ :param str source: source string to be parsed
+ :param str filename: optional source filename
+ :param list(str) include_dirs: optional list of string filename include directories
+ :param str auto_type_source: optional source of types if used for automatically generated types
+ :return: :py:class:`TypeParserResult` (a SyntaxError is thrown on parse error)
+ :rtype: TypeParserResult
+ :Example:
+
+ >>> platform.parse_types_from_source('int foo;\\nint bar(int x);\\nstruct bas{int x,y;};\\n')
+ ({types: {'bas': <type: struct bas>}, variables: {'foo': <type: int32_t>}, functions:{'bar':
+ <type: int32_t(int32_t x)>}}, '')
+ >>>
+ """
+
+ if filename is None:
+ filename = "input"
+ dir_buf = (ctypes.c_char_p * len(include_dirs))()
+ for i in xrange(0, len(include_dirs)):
+ dir_buf[i] = str(include_dirs[i])
+ parse = core.BNTypeParserResult()
+ errors = ctypes.c_char_p()
+ result = core.BNParseTypesFromSource(self.handle, source, filename, parse, errors, dir_buf,
+ len(include_dirs), auto_type_source)
+ error_str = errors.value
+ core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte)))
+ if not result:
+ raise SyntaxError(error_str)
+ type_dict = {}
+ variables = {}
+ functions = {}
+ for i in xrange(0, parse.typeCount):
+ name = types.QualifiedName._from_core_struct(parse.types[i].name)
+ type_dict[name] = types.Type(core.BNNewTypeReference(parse.types[i].type), platform = self)
+ for i in xrange(0, parse.variableCount):
+ name = types.QualifiedName._from_core_struct(parse.variables[i].name)
+ variables[name] = types.Type(core.BNNewTypeReference(parse.variables[i].type), platform = self)
+ for i in xrange(0, parse.functionCount):
+ name = types.QualifiedName._from_core_struct(parse.functions[i].name)
+ functions[name] = types.Type(core.BNNewTypeReference(parse.functions[i].type), platform = self)
+ core.BNFreeTypeParserResult(parse)
+ return types.TypeParserResult(type_dict, variables, functions)
+
+ def parse_types_from_source_file(self, filename, include_dirs=[], auto_type_source=None):
+ """
+ ``parse_types_from_source_file`` parses the source file ``filename`` and any needed headers searching for them in
+ the optional list of directories provided in ``include_dirs``.
+
+ :param str filename: filename of file to be parsed
+ :param list(str) include_dirs: optional list of string filename include directories
+ :param str auto_type_source: optional source of types if used for automatically generated types
+ :return: :py:class:`TypeParserResult` (a SyntaxError is thrown on parse error)
+ :rtype: TypeParserResult
+ :Example:
+
+ >>> file = "/Users/binja/tmp.c"
+ >>> open(file).read()
+ 'int foo;\\nint bar(int x);\\nstruct bas{int x,y;};\\n'
+ >>> platform.parse_types_from_source_file(file)
+ ({types: {'bas': <type: struct bas>}, variables: {'foo': <type: int32_t>}, functions:
+ {'bar': <type: int32_t(int32_t x)>}}, '')
+ >>>
+ """
+ dir_buf = (ctypes.c_char_p * len(include_dirs))()
+ for i in xrange(0, len(include_dirs)):
+ dir_buf[i] = str(include_dirs[i])
+ parse = core.BNTypeParserResult()
+ errors = ctypes.c_char_p()
+ result = core.BNParseTypesFromSourceFile(self.handle, filename, parse, errors, dir_buf,
+ len(include_dirs), auto_type_source)
+ error_str = errors.value
+ core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte)))
+ if not result:
+ raise SyntaxError(error_str)
+ type_dict = {}
+ variables = {}
+ functions = {}
+ for i in xrange(0, parse.typeCount):
+ name = types.QualifiedName._from_core_struct(parse.types[i].name)
+ type_dict[name] = types.Type(core.BNNewTypeReference(parse.types[i].type), platform = self)
+ for i in xrange(0, parse.variableCount):
+ name = types.QualifiedName._from_core_struct(parse.variables[i].name)
+ variables[name] = types.Type(core.BNNewTypeReference(parse.variables[i].type), platform = self)
+ for i in xrange(0, parse.functionCount):
+ name = types.QualifiedName._from_core_struct(parse.functions[i].name)
+ functions[name] = types.Type(core.BNNewTypeReference(parse.functions[i].type), platform = self)
+ core.BNFreeTypeParserResult(parse)
+ return types.TypeParserResult(type_dict, variables, functions)
diff --git a/python/pluginmanager.py b/python/pluginmanager.py
index c0f70260..6896d699 100644
--- a/python/pluginmanager.py
+++ b/python/pluginmanager.py
@@ -144,6 +144,12 @@ class Repository(object):
def __repr__(self):
return "<{} - {}/{}>".format(self.path, self.remote_reference, self.local_reference)
+ def __getitem__(self, plugin_path):
+ for plugin in self.plugins:
+ if plugin_path == plugin.path:
+ return plugin
+ raise KeyError()
+
@property
def url(self):
"""String url of the git repository where the plugin repository's are stored"""
@@ -155,6 +161,11 @@ class Repository(object):
return core.BNRepositoryGetRepoPath(self.handle)
@property
+ def full_path(self):
+ """String full path the repository"""
+ return core.BNRepositoryGetPluginsPath(self.handle)
+
+ @property
def local_reference(self):
"""String for the local git reference (ie 'master')"""
return core.BNRepositoryGetLocalReference(self.handle)
@@ -190,6 +201,12 @@ class RepositoryManager(object):
def __init__(self, handle=None):
self.handle = core.BNGetRepositoryManager()
+ def __getitem__(self, repo_path):
+ for repo in self.repositories:
+ if repo_path == repo.path:
+ return repo
+ raise KeyError()
+
def check_for_updates(self):
"""Check for updates for all managed Repository objects"""
return core.BNRepositoryManagerCheckForUpdates(self.handle)
diff --git a/python/scriptingprovider.py b/python/scriptingprovider.py
index 94616d59..ed9688b9 100644
--- a/python/scriptingprovider.py
+++ b/python/scriptingprovider.py
@@ -335,7 +335,6 @@ class _PythonScriptingInstanceOutput(object):
self.buffer = ""
self.encoding = 'UTF-8'
self.errors = None
- self.isatty = False
self.mode = 'w'
self.name = 'PythonScriptingInstanceOutput'
self.newlines = None
@@ -349,6 +348,9 @@ class _PythonScriptingInstanceOutput(object):
def flush(self):
pass
+ def isatty(self):
+ return False
+
def next(self):
raise IOError("File not open for reading")
@@ -412,6 +414,9 @@ class _PythonScriptingInstanceInput(object):
def __init__(self, orig):
self.orig = orig
+ def isatty(self):
+ return False
+
def read(self, size):
interpreter = None
if "value" in dir(PythonScriptingInstance._interpreter):
diff --git a/python/setting.py b/python/setting.py
new file mode 100644
index 00000000..d58c8955
--- /dev/null
+++ b/python/setting.py
@@ -0,0 +1,141 @@
+# 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
+
+
+class Setting(object):
+ def __init__(self, plugin_name="core"):
+ self.plugin_name = plugin_name
+
+ def get_bool(self, name, default_value=False):
+ return core.BNSettingGetBool(self.plugin_name, name, default_value)
+
+ def get_integer(self, name, default_value=0):
+ return core.BNSettingGetInteger(self.plugin_name, name, default_value)
+
+ def get_string(self, name, default_value=""):
+ return core.BNSettingGetString(self.plugin_name, name, default_value)
+
+ def get_integer_list(self, name, default_value=[]):
+ length = ctypes.c_ulonglong()
+ length.value = len(default_value)
+ default_list = (ctypes.c_longlong * len(default_value))()
+ for i in range(len(default_value)):
+ default_list[i] = default_value[i]
+ result = core.BNSettingGetIntegerList(self.plugin_name, name, default_list, ctypes.byref(length))
+ out_list = []
+ for i in xrange(length.value):
+ out_list.append(result[i])
+ core.BNFreeSettingIntegerList(result)
+ return out_list
+
+ def get_string_list(self, name, default_value=[]):
+ length = ctypes.c_ulonglong()
+ length.value = len(default_value)
+ default_list = (ctypes.c_char_p * len(default_value))()
+ for i in range(len(default_value)):
+ default_list[i] = default_value[i]
+ result = core.BNSettingGetStringList(self.plugin_name, name, default_list, ctypes.byref(length))
+ out_list = []
+ for i in xrange(length.value):
+ out_list.append(result[i])
+ core.BNFreeStringList(result, length)
+ return out_list
+
+ def get_double(self, name, default_value=0.0):
+ return core.BNSettingGetDouble(self.plugin_name, name, default_value)
+
+ def is_bool(self, name):
+ return core.BNSettingIsBool(self.plugin_name, name)
+
+ def is_integer(self, name):
+ return core.BNSettingIsInteger(self.plugin_name, name)
+
+ def is_string(self, name):
+ return core.BNSettingIsString(self.plugin_name, name)
+
+ def is_string_list(self, name):
+ return core.BNSettingIsStringList(self.plugin_name, name)
+
+ def is_integer_list(self, name):
+ return core.BNSettingIsIntegerList(self.plugin_name, name)
+
+ def is_double(self, name):
+ return core.BNSettingIsDouble(self.plugin_name, name)
+
+ def is_present(self, name):
+ return core.BNSettingIsPresent(self.plugin_name, name)
+
+ def set_bool(self, name, value, auto_flush=True):
+ return core.BNSettingSetBool(self.plugin_name, name, value, auto_flush)
+
+ def set_integer(self, name, value, auto_flush=True):
+ return core.BNSettingSetInteger(self.plugin_name, name, value, auto_flush)
+
+ def set_string(self, name, value, auto_flush=True):
+ return core.BNSettingSetString(self.plugin_name, name, value, auto_flush)
+
+ def set_integer_list(self, name, value, auto_flush=True):
+ length = ctypes.c_ulonglong()
+ length.value = len(value)
+ default_list = (ctypes.c_longlong * len(value))()
+ for i in xrange(len(value)):
+ default_list[i] = value[i]
+
+ return core.BNSettingSetIntegerList(self.plugin_name, name, default_list, length, auto_flush)
+
+ def set_string_list(self, name, value, auto_flush=True):
+ length = ctypes.c_ulonglong()
+ length.value = len(value)
+ default_list = (ctypes.c_char_p * len(value))()
+ for i in xrange(len(value)):
+ default_list[i] = str(value[i])
+
+ return core.BNSettingSetStringList(self.plugin_name, name, default_list, length, auto_flush)
+
+ def set_double(self, name, value, auto_flush=True):
+ return core.BNSettingSetDouble(self.plugin_name, name, value, auto_flush)
+
+ def set(self, name, value, auto_flush=True):
+ if isinstance(value, bool):
+ return self.set_bool(name, value, auto_flush)
+ elif isinstance(value, int):
+ return self.set_integer(name, value, auto_flush)
+ elif isinstance(value, str):
+ return self.set_string(name, value, auto_flush)
+ elif isinstance(value, list) and len(value) == 0:
+ return self.set_integer_list(name, value, auto_flush)
+ elif isinstance(value, list) and len(value) > 0 and isinstance(value[0], int):
+ return self.set_integer_list(name, value, auto_flush)
+ elif isinstance(value, list) and len(value) > 0 and isinstance(value[0], str):
+ return self.set_string_list(name, value, auto_flush)
+ elif isinstance(value, float):
+ return self.set_double(name, value, auto_flush)
+ raise ValueError("value is not one of (int, bool, float, str, [int], [str]) types")
+
+ def remove_setting_group(self, auto_flush=True):
+ core.BNSettingRemoveSettingGroup(self.plugin_name, auto_flush)
+
+ def remove_setting(self, setting, auto_flush=True):
+ core.BNSettingRemoveSetting(self.plugin_name, setting, auto_flush) \ No newline at end of file
diff --git a/python/types.py b/python/types.py
index f8e416f4..2557db2c 100644
--- a/python/types.py
+++ b/python/types.py
@@ -18,11 +18,13 @@
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
+max_confidence = 255
+
import ctypes
# Binary Ninja components
import _binaryninjacore as core
-from enums import SymbolType, TypeClass, NamedTypeReferenceClass, InstructionTextTokenType, StructureType
+from enums import SymbolType, TypeClass, NamedTypeReferenceClass, InstructionTextTokenType, StructureType, ReferenceType, VariableSourceType
import callingconvention
import function
@@ -197,9 +199,23 @@ class Symbol(object):
raise AttributeError("attribute '%s' is read only" % name)
+class FunctionParameter(object):
+ def __init__(self, param_type, name = "", location = None):
+ self.type = param_type
+ self.name = name
+ self.location = location
+
+ def __repr__(self):
+ if (self.location is not None) and (self.location.name != self.name):
+ return "%s %s%s @ %s" % (self.type.get_string_before_name(), self.name, self.type.get_string_after_name(), self.location.name)
+ return "%s %s%s" % (self.type.get_string_before_name(), self.name, self.type.get_string_after_name())
+
+
class Type(object):
- def __init__(self, handle):
+ def __init__(self, handle, platform = None, confidence = max_confidence):
self.handle = handle
+ self.confidence = confidence
+ self.platform = platform
def __del__(self):
core.BNFreeType(self.handle)
@@ -232,12 +248,14 @@ class Type(object):
@property
def signed(self):
"""Wether type is signed (read-only)"""
- return core.BNIsTypeSigned(self.handle)
+ result = core.BNIsTypeSigned(self.handle)
+ return BoolWithConfidence(result.value, confidence = result.confidence)
@property
def const(self):
"""Whether type is const (read-only)"""
- return core.BNIsTypeConst(self.handle)
+ result = core.BNIsTypeConst(self.handle)
+ return BoolWithConfidence(result.value, confidence = result.confidence)
@property
def modified(self):
@@ -248,33 +266,33 @@ class Type(object):
def target(self):
"""Target (read-only)"""
result = core.BNGetChildType(self.handle)
- if result is None:
+ if not result.type:
return None
- return Type(result)
+ return Type(result.type, platform = self.platform, confidence = result.confidence)
@property
def element_type(self):
"""Target (read-only)"""
result = core.BNGetChildType(self.handle)
- if result is None:
+ if not result.type:
return None
- return Type(result)
+ return Type(result.type, platform = self.platform, confidence = result.confidence)
@property
def return_value(self):
"""Return value (read-only)"""
result = core.BNGetChildType(self.handle)
- if result is None:
+ if not result.type:
return None
- return Type(result)
+ return Type(result.type, platform = self.platform, confidence = result.confidence)
@property
def calling_convention(self):
"""Calling convention (read-only)"""
result = core.BNGetTypeCallingConvention(self.handle)
- if result is None:
+ if not result.convention:
return None
- return callingconvention.CallingConvention(None, result)
+ return callingconvention.CallingConvention(None, handle = result.convention, confidence = result.confidence)
@property
def parameters(self):
@@ -283,19 +301,32 @@ class Type(object):
params = core.BNGetTypeParameters(self.handle, count)
result = []
for i in xrange(0, count.value):
- result.append((Type(core.BNNewTypeReference(params[i].type)), params[i].name))
+ param_type = Type(core.BNNewTypeReference(params[i].type), platform = self.platform, confidence = params[i].typeConfidence)
+ if params[i].defaultLocation:
+ param_location = None
+ else:
+ name = params[i].name
+ if (params[i].location.type == VariableSourceType.RegisterVariableSourceType) and (self.platform is not None):
+ name = self.platform.arch.get_reg_name(params[i].location.storage)
+ elif params[i].location.type == VariableSourceType.StackVariableSourceType:
+ name = "arg_%x" % params[i].location.storage
+ param_location = function.Variable(None, params[i].location.type, params[i].location.index,
+ params[i].location.storage, name, param_type)
+ result.append(FunctionParameter(param_type, params[i].name, param_location))
core.BNFreeTypeParameterList(params, count.value)
return result
@property
def has_variable_arguments(self):
"""Whether type has variable arguments (read-only)"""
- return core.BNTypeHasVariableArguments(self.handle)
+ result = core.BNTypeHasVariableArguments(self.handle)
+ return BoolWithConfidence(result.value, confidence = result.confidence)
@property
def can_return(self):
"""Whether type can return (read-only)"""
- return core.BNFunctionTypeCanReturn(self.handle)
+ result = core.BNFunctionTypeCanReturn(self.handle)
+ return BoolWithConfidence(result.value, confidence = result.confidence)
@property
def structure(self):
@@ -326,23 +357,51 @@ class Type(object):
"""Type count (read-only)"""
return core.BNGetTypeElementCount(self.handle)
+ @property
+ def offset(self):
+ """Offset into structure (read-only)"""
+ return core.BNGetTypeOffset(self.handle)
+
+ @property
+ def stack_adjustment(self):
+ """Stack adjustment for function (read-only)"""
+ result = core.BNGetTypeStackAdjustment(self.handle)
+ return SizeWithConfidence(result.value, confidence = result.confidence)
+
def __str__(self):
- return core.BNGetTypeString(self.handle)
+ platform = None
+ if self.platform is not None:
+ platform = self.platform.handle
+ return core.BNGetTypeString(self.handle, platform)
def __repr__(self):
+ if self.confidence < max_confidence:
+ return "<type: %s, %d%% confidence>" % (str(self), (self.confidence * 100) / max_confidence)
return "<type: %s>" % str(self)
def get_string_before_name(self):
- return core.BNGetTypeStringBeforeName(self.handle)
+ platform = None
+ if self.platform is not None:
+ platform = self.platform.handle
+ return core.BNGetTypeStringBeforeName(self.handle, platform)
def get_string_after_name(self):
- return core.BNGetTypeStringAfterName(self.handle)
+ platform = None
+ if self.platform is not None:
+ platform = self.platform.handle
+ return core.BNGetTypeStringAfterName(self.handle, platform)
@property
def tokens(self):
"""Type string as a list of tokens (read-only)"""
+ return self.get_tokens()
+
+ def get_tokens(self, base_confidence = max_confidence):
count = ctypes.c_ulonglong()
- tokens = core.BNGetTypeTokens(self.handle, count)
+ platform = None
+ if self.platform is not None:
+ platform = self.platform.handle
+ tokens = core.BNGetTypeTokens(self.handle, platform, base_confidence, count)
result = []
for i in xrange(0, count.value):
token_type = InstructionTextTokenType(tokens[i].type)
@@ -351,14 +410,18 @@ class Type(object):
size = tokens[i].size
operand = tokens[i].operand
context = tokens[i].context
+ confidence = tokens[i].confidence
address = tokens[i].address
- result.append(function.InstructionTextToken(token_type, text, value, size, operand, context, address))
+ result.append(function.InstructionTextToken(token_type, text, value, size, operand, context, address, confidence))
core.BNFreeTokenList(tokens, count.value)
return result
- def get_tokens_before_name(self):
+ def get_tokens_before_name(self, base_confidence = max_confidence):
count = ctypes.c_ulonglong()
- tokens = core.BNGetTypeTokensBeforeName(self.handle, count)
+ platform = None
+ if self.platform is not None:
+ platform = self.platform.handle
+ tokens = core.BNGetTypeTokensBeforeName(self.handle, platform, base_confidence, count)
result = []
for i in xrange(0, count.value):
token_type = InstructionTextTokenType(tokens[i].type)
@@ -367,14 +430,18 @@ class Type(object):
size = tokens[i].size
operand = tokens[i].operand
context = tokens[i].context
+ confidence = tokens[i].confidence
address = tokens[i].address
- result.append(function.InstructionTextToken(token_type, text, value, size, operand, context, address))
+ result.append(function.InstructionTextToken(token_type, text, value, size, operand, context, address, confidence))
core.BNFreeTokenList(tokens, count.value)
return result
- def get_tokens_after_name(self):
+ def get_tokens_after_name(self, base_confidence = max_confidence):
count = ctypes.c_ulonglong()
- tokens = core.BNGetTypeTokensAfterName(self.handle, count)
+ platform = None
+ if self.platform is not None:
+ platform = self.platform.handle
+ tokens = core.BNGetTypeTokensAfterName(self.handle, platform, base_confidence, count)
result = []
for i in xrange(0, count.value):
token_type = InstructionTextTokenType(tokens[i].type)
@@ -383,8 +450,9 @@ class Type(object):
size = tokens[i].size
operand = tokens[i].operand
context = tokens[i].context
+ confidence = tokens[i].confidence
address = tokens[i].address
- result.append(function.InstructionTextToken(token_type, text, value, size, operand, context, address))
+ result.append(function.InstructionTextToken(token_type, text, value, size, operand, context, address, confidence))
core.BNFreeTokenList(tokens, count.value)
return result
@@ -397,14 +465,23 @@ class Type(object):
return Type(core.BNCreateBoolType())
@classmethod
- def int(self, width, sign = True, altname=""):
+ def int(self, width, sign = None, altname=""):
"""
``int`` class method for creating an int Type.
:param int width: width of the integer in bytes
:param bool sign: optional variable representing signedness
"""
- return Type(core.BNCreateIntegerType(width, sign, altname))
+ if sign is None:
+ sign = BoolWithConfidence(True, confidence = 0)
+ elif not isinstance(sign, BoolWithConfidence):
+ sign = BoolWithConfidence(sign)
+
+ sign_conf = core.BNBoolWithConfidence()
+ sign_conf.value = sign.value
+ sign_conf.confidence = sign.confidence
+
+ return Type(core.BNCreateIntegerType(width, sign_conf, altname))
@classmethod
def float(self, width):
@@ -444,15 +521,43 @@ class Type(object):
return Type(core.BNCreateEnumerationType(e.handle, width))
@classmethod
- def pointer(self, arch, t, const=False):
- return Type(core.BNCreatePointerType(arch.handle, t.handle, const))
+ def pointer(self, arch, t, const=None, volatile=None, ref_type=None):
+ if const is None:
+ const = BoolWithConfidence(False, confidence = 0)
+ elif not isinstance(const, BoolWithConfidence):
+ const = BoolWithConfidence(const)
+
+ if volatile is None:
+ volatile = BoolWithConfidence(False, confidence = 0)
+ elif not isinstance(volatile, BoolWithConfidence):
+ volatile = BoolWithConfidence(volatile)
+
+ if ref_type is None:
+ ref_type = ReferenceType.PointerReferenceType
+
+ type_conf = core.BNTypeWithConfidence()
+ type_conf.type = t.handle
+ type_conf.confidence = t.confidence
+
+ const_conf = core.BNBoolWithConfidence()
+ const_conf.value = const.value
+ const_conf.confidence = const.confidence
+
+ volatile_conf = core.BNBoolWithConfidence()
+ volatile_conf.value = volatile.value
+ volatile_conf.confidence = volatile.confidence
+
+ return Type(core.BNCreatePointerType(arch.handle, type_conf, const_conf, volatile_conf, ref_type))
@classmethod
def array(self, t, count):
- return Type(core.BNCreateArrayType(t.handle, count))
+ type_conf = core.BNTypeWithConfidence()
+ type_conf.type = t.handle
+ type_conf.confidence = t.confidence
+ return Type(core.BNCreateArrayType(type_conf, count))
@classmethod
- def function(self, ret, params, calling_convention=None, variable_arguments=False):
+ def function(self, ret, params, calling_convention=None, variable_arguments=None, stack_adjust=None):
"""
``function`` class method for creating an function Type.
@@ -461,18 +566,62 @@ class Type(object):
:param CallingConvention calling_convention: optional argument for function calling convention
:param bool variable_arguments: optional argument for functions that have a variable number of arguments
"""
- param_buf = (core.BNNameAndType * len(params))()
+ param_buf = (core.BNFunctionParameter * len(params))()
for i in xrange(0, len(params)):
if isinstance(params[i], Type):
param_buf[i].name = ""
param_buf[i].type = params[i].handle
+ param_buf[i].typeConfidence = params[i].confidence
+ param_buf[i].defaultLocation = True
+ elif isinstance(params[i], FunctionParameter):
+ param_buf[i].name = params[i].name
+ param_buf[i].type = params[i].type.handle
+ param_buf[i].typeConfidence = params[i].type.confidence
+ if params[i].location is None:
+ param_buf[i].defaultLocation = True
+ else:
+ param_buf[i].defaultLocation = False
+ param_buf[i].location.type = params[i].location.type
+ param_buf[i].location.index = params[i].location.index
+ param_buf[i].location.storage = params[i].location.storage
else:
param_buf[i].name = params[i][1]
- param_buf[i].type = params[i][0]
- if calling_convention is not None:
- calling_convention = calling_convention.handle
- return Type(core.BNCreateFunctionType(ret.handle, calling_convention, param_buf, len(params),
- variable_arguments))
+ param_buf[i].type = params[i][0].handle
+ param_buf[i].typeConfidence = params[i][0].confidence
+ param_buf[i].defaultLocation = True
+
+ ret_conf = core.BNTypeWithConfidence()
+ ret_conf.type = ret.handle
+ ret_conf.confidence = ret.confidence
+
+ conv_conf = core.BNCallingConventionWithConfidence()
+ if calling_convention is None:
+ conv_conf.convention = None
+ conv_conf.confidence = 0
+ else:
+ conv_conf.convention = calling_convention.handle
+ conv_conf.confidence = calling_convention.confidence
+
+ if variable_arguments is None:
+ variable_arguments = BoolWithConfidence(False, confidence = 0)
+ elif not isinstance(variable_arguments, BoolWithConfidence):
+ variable_arguments = BoolWithConfidence(variable_arguments)
+
+ vararg_conf = core.BNBoolWithConfidence()
+ vararg_conf.value = variable_arguments.value
+ vararg_conf.confidence = variable_arguments.confidence
+
+ if stack_adjust is None:
+ stack_adjust = SizeWithConfidence(0, confidence = 0)
+ elif not isinstance(stack_adjust, SizeWithConfidence):
+ stack_adjust = SizeWithConfidence(stack_adjust)
+
+ stack_adjust_conf = core.BNSizeWithConfidence()
+ stack_adjust_conf.value = stack_adjust.value
+ stack_adjust_conf.confidence = stack_adjust.confidence
+
+ return Type(core.BNCreateFunctionType(ret_conf, conv_conf, param_buf, len(params),
+ vararg_conf, stack_adjust_conf))
@classmethod
def generate_auto_type_id(self, source, name):
@@ -488,6 +637,9 @@ class Type(object):
def get_auto_demanged_type_id_source(self):
return core.BNGetAutoDemangledTypeIdSource()
+ def with_confidence(self, confidence):
+ return Type(handle = core.BNNewTypeReference(self.handle), platform = self.platform, confidence = confidence)
+
def __setattr__(self, name, value):
try:
object.__setattr__(self, name, value)
@@ -495,6 +647,73 @@ class Type(object):
raise AttributeError("attribute '%s' is read only" % name)
+class BoolWithConfidence(object):
+ def __init__(self, value, confidence = max_confidence):
+ self.value = value
+ self.confidence = confidence
+
+ def __str__(self):
+ return str(self.value)
+
+ def __repr__(self):
+ return repr(self.value)
+
+ def __bool__(self):
+ return self.value
+
+ def __nonzero__(self):
+ return self.value
+
+
+class SizeWithConfidence(object):
+ def __init__(self, value, confidence = max_confidence):
+ self.value = value
+ self.confidence = confidence
+
+ def __str__(self):
+ return str(self.value)
+
+ def __repr__(self):
+ return repr(self.value)
+
+ def __int__(self):
+ return self.value
+
+
+class RegisterSet(object):
+ def __init__(self, reg_list, confidence = max_confidence):
+ self.regs = reg_list
+ self.confidence = confidence
+
+ def __repr__(self):
+ return repr(self.regs)
+
+ def __iter__(self):
+ for reg in self.regs:
+ yield reg
+
+ def __getitem__(self, idx):
+ return self.regs[idx]
+
+ def __len__(self):
+ return len(self.regs)
+
+ def with_confidence(self, confidence):
+ return RegisterSet(list(self.regs), confidence = confidence)
+
+
+class ReferenceTypeWithConfidence(object):
+ def __init__(self, value, confidence = max_confidence):
+ self.value = value
+ self.confidence = confidence
+
+ def __str__(self):
+ return str(self.value)
+
+ def __repr__(self):
+ return repr(self.value)
+
+
class NamedTypeReference(object):
def __init__(self, type_class = NamedTypeReferenceClass.UnknownNamedTypeClass, type_id = None, name = None, handle = None):
if handle is None:
@@ -611,7 +830,7 @@ class Structure(object):
members = core.BNGetStructureMembers(self.handle, count)
result = []
for i in xrange(0, count.value):
- result.append(StructureMember(Type(core.BNNewTypeReference(members[i].type)),
+ result.append(StructureMember(Type(core.BNNewTypeReference(members[i].type), confidence = members[i].typeConfidence),
members[i].name, members[i].offset))
core.BNFreeStructureMemberList(members, count.value)
return result
@@ -664,16 +883,25 @@ class Structure(object):
return "<struct: size %#x>" % self.width
def append(self, t, name = ""):
- core.BNAddStructureMember(self.handle, t.handle, name)
+ tc = core.BNTypeWithConfidence()
+ tc.type = t.handle
+ tc.confidence = t.confidence
+ core.BNAddStructureMember(self.handle, tc, name)
def insert(self, offset, t, name = ""):
- core.BNAddStructureMemberAtOffset(self.handle, t.handle, name, offset)
+ tc = core.BNTypeWithConfidence()
+ tc.type = t.handle
+ tc.confidence = t.confidence
+ core.BNAddStructureMemberAtOffset(self.handle, tc, name, offset)
def remove(self, i):
core.BNRemoveStructureMember(self.handle, i)
def replace(self, i, t, name = ""):
- core.BNReplaceStructureMember(self.handle, i, t.handle, name)
+ tc = core.BNTypeWithConfidence()
+ tc.type = t.handle
+ tc.confidence = t.confidence
+ core.BNReplaceStructureMember(self.handle, i, tc, name)
class EnumerationMember(object):
@@ -746,7 +974,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=[]):