From 61b4bb24e06aa955484293d35fa926c07887544b Mon Sep 17 00:00:00 2001 From: Peter LaFosse Date: Mon, 7 Jun 2021 09:59:54 -0400 Subject: Add type hints to basicblock.py, lowlevelil.py, architecture.py --- python/function.py | 6286 ++++++++++++++++++++++------------------------------ 1 file changed, 2611 insertions(+), 3675 deletions(-) (limited to 'python/function.py') diff --git a/python/function.py b/python/function.py index 9bc83b09..64aabdbb 100644 --- a/python/function.py +++ b/python/function.py @@ -19,1657 +19,1556 @@ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # IN THE SOFTWARE. -from __future__ import absolute_import -import threading -import traceback import ctypes -import numbers -import string -from typing import List +import inspect +from typing import Generator, Optional, List, Tuple, Union, Mapping, Any # Binary Ninja components -import binaryninja -from binaryninja import _binaryninjacore as core -from binaryninja import associateddatastore # Required in the main scope due to being an argument for _FunctionAssociatedDataStore -from binaryninja import highlight -from binaryninja import log -from binaryninja import types -from binaryninja import decorators -from binaryninja import workflow -from binaryninja.enums import (AnalysisSkipReason, FunctionGraphType, BranchType, SymbolType, InstructionTextTokenType, - HighlightStandardColor, HighlightColorStyle, RegisterValueType, ImplicitRegisterExtend, - DisassemblyOption, IntegerDisplayType, InstructionTextTokenContext, VariableSourceType, - FunctionAnalysisSkipOverride, MediumLevelILOperation, DeadStoreElimination) - -# 2-3 compatibility -from binaryninja import range - - -class LookupTableEntry(object): - def __init__(self, from_values, to_value): - self._from_values = from_values - self._to_value = to_value +from . import _binaryninjacore as core +from .enums import (AnalysisSkipReason, FunctionGraphType, SymbolType, InstructionTextTokenType, + HighlightStandardColor, HighlightColorStyle, + DisassemblyOption, IntegerDisplayType, InstructionTextTokenContext, + FunctionAnalysisSkipOverride) +from . import associateddatastore # Required in the main scope due to being an argument for _FunctionAssociatedDataStore +from . import types +from . import architecture +from . import lowlevelil +from . import mediumlevelil +from . import highlevelil +from . import binaryview +from . import basicblock +from . import variable +from . import flowgraph +from . import callingconvention +from . import workflow + +# we define the following as such so the linter doesn't confuse 'highlight' the module with the +# property of the same name. There is probably some other work around but it eludes me. +from . import highlight as _highlight +from . import platform as _platform + +ExpressionIndex = int +InstructionIndex = int +AnyFunctionType = Union['Function', 'lowlevelil.LowLevelILFunction', 'mediumlevelil.MediumLevelILFunction', + 'highlevelil.HighLevelILFunction'] +ILFunctionType = Union['lowlevelil.LowLevelILFunction', 'mediumlevelil.MediumLevelILFunction', + 'highlevelil.HighLevelILFunction'] +ILInstructionType = Union['lowlevelil.LowLevelILInstruction', 'mediumlevelil.MediumLevelILInstruction', + 'highlevelil.HighLevelILInstruction'] + +def _function_name_(): + return inspect.stack()[1][0].f_code.co_name + +class ArchAndAddr(object): + def __init__(self, arch:Optional['architecture.Architecture']=None, addr:int=0): + self._arch = architecture.CoreArchitecture._from_cache(arch) + self._addr = addr def __repr__(self): - return "[%s] -> %#x" % (', '.join(["%#x" % i for i in self.from_values]), self.to_value) + return "archandaddr <%s @ %#x>" % (self._arch.name, self._addr) - def __eq__(self, other): - if not isinstance(other, self.__class__): - return NotImplemented - return (self._from_values, self._to_value) == (other._from_values, other._to_value) + @property + def arch(self) -> 'architecture.Architecture': + return self._arch - def __ne__(self, other): - if not isinstance(other, self.__class__): - return NotImplemented - return not (self == other) + @property + def addr(self) -> int: + return self._addr - def __hash__(self): - return hash((self._from_values, self._to_value)) + +class _FunctionAssociatedDataStore(associateddatastore._AssociatedDataStore): + _defaults = {} + + +class DisassemblySettings(object): + def __init__(self, handle:core.BNDisassemblySettings=None): + if handle is None: + self.handle = core.BNCreateDisassemblySettings() + else: + self.handle = handle + + def __del__(self): + core.BNFreeDisassemblySettings(self.handle) @property - def from_values(self): - """ """ - return self._from_values + def width(self) -> int: + return core.BNGetDisassemblyWidth(self.handle) - @from_values.setter - def from_values(self, value): - """ """ - self._from_values = value + @width.setter + def width(self, value:int) -> None: + core.BNSetDisassemblyWidth(self.handle, value) @property - def to_value(self): - """ """ - return self._to_value + def max_symbol_width(self) -> int: + return core.BNGetDisassemblyMaximumSymbolWidth(self.handle) - @to_value.setter - def to_value(self, value): - """ """ - self._to_value = value + @max_symbol_width.setter + def max_symbol_width(self, value:int) -> None: + core.BNSetDisassemblyMaximumSymbolWidth(self.handle, value) + def is_option_set(self, option:DisassemblyOption) -> bool: + if isinstance(option, str): + option = DisassemblyOption[option] + return core.BNIsDisassemblySettingsOptionSet(self.handle, option) -class RegisterValue(object): - def __init__(self, arch = None, value = None, confidence = types.max_confidence): - self._is_constant = False - self._value = None - self._arch = None - self._reg = None - self._is_constant = False - self._offset = None - if value is None: - self._type = RegisterValueType.UndeterminedValue - else: - self._type = RegisterValueType(value.state) - 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 set_option(self, option:DisassemblyOption, state:bool=True) -> None: + if isinstance(option, str): + option = DisassemblyOption[option] + core.BNSetDisassemblySettingsOption(self.handle, option, state) - def __repr__(self): - if self._type == RegisterValueType.EntryValue: - return "" % self._reg - if self._type == RegisterValueType.ConstantValue: - return "" % self._value - if self._type == RegisterValueType.ConstantPointerValue: - return "" % self._value - if self._type == RegisterValueType.StackFrameOffset: - return "" % self._offset - if self._type == RegisterValueType.ReturnAddressValue: - return "" - if self._type == RegisterValueType.ImportedAddressValue: - return "" % self._value - return "" - def __hash__(self): - if self._type in [RegisterValueType.ConstantValue, RegisterValueType.ConstantPointerValue, RegisterValueType.ImportedAddressValue, RegisterValueType.ReturnAddressValue]: - return hash(self._value) - elif self.type == RegisterValueType.EntryValue: - return hash(self._reg) - elif self._type == RegisterValueType.StackFrameOffset: - return hash(self._offset) +class ILReferenceSource(object): + def __init__(self, func:Optional['Function'], arch:Optional['architecture.Architecture'], addr:int, + il_type:FunctionGraphType, expr_id:ExpressionIndex): + self._function = func + self._arch = arch + self._address = addr + self._il_type = il_type + self._expr_id = expr_id + + @staticmethod + def get_il_name(il_type): + if il_type == FunctionGraphType.NormalFunctionGraph: + return 'disassembly' + if il_type == FunctionGraphType.LowLevelILFunctionGraph: + return 'llil' + if il_type == FunctionGraphType.LiftedILFunctionGraph: + return 'lifted_llil' + if il_type == FunctionGraphType.LowLevelILSSAFormFunctionGraph: + return 'llil_ssa' + if il_type == FunctionGraphType.MediumLevelILFunctionGraph: + return 'mlil' + if il_type == FunctionGraphType.MediumLevelILSSAFormFunctionGraph: + return 'mlil_ssa' + if il_type == FunctionGraphType.MappedMediumLevelILFunctionGraph: + return 'mapped_mlil' + if il_type == FunctionGraphType.MappedMediumLevelILSSAFormFunctionGraph: + return 'mapped_mlil_ssa' + if il_type == FunctionGraphType.HighLevelILFunctionGraph: + return 'hlil' + if il_type == FunctionGraphType.HighLevelILSSAFormFunctionGraph: + return 'hlil_ssa' + + def __repr__(self): + if self._arch: + return "" %\ + (self._arch.name, self._address, self.get_il_name(self._il_type), self._expr_id) + else: + return "" %\ + (self._address, self.get_il_name(self._il_type), self._expr_id) def __eq__(self, other): - if self._type in [RegisterValueType.ConstantValue, RegisterValueType.ConstantPointerValue, RegisterValueType.ImportedAddressValue, RegisterValueType.ReturnAddressValue] and isinstance(other, numbers.Integral): - return self._value == other - elif self._type in [RegisterValueType.ConstantValue, RegisterValueType.ConstantPointerValue, RegisterValueType.ImportedAddressValue, RegisterValueType.ReturnAddressValue] and hasattr(other, 'type') and other.type == self._type: - return self._value == other.value - elif self._type == RegisterValueType.EntryValue and hasattr(other, "type") and other.type == self._type: - return self._reg == other.reg - elif self._type == RegisterValueType.StackFrameOffset and hasattr(other, 'type') and other.type == self._type: - return self._offset == other.offset - elif self._type == RegisterValueType.StackFrameOffset and isinstance(other, numbers.Integral): - return self._offset == other - return NotImplemented + if not isinstance(other, self.__class__): + return NotImplemented + return (self.function, self._arch, self._address, self._il_type, self._expr_id) ==\ + (other._address, other._function, other._arch, other._il_type, other._expr_id) def __ne__(self, other): if not isinstance(other, self.__class__): return NotImplemented return not (self == other) - def _to_api_object(self): - result = core.BNRegisterValue() - result.type = 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 - - @classmethod - def undetermined(self): - return RegisterValue() - - @classmethod - def entry_value(self, arch, reg): - result = RegisterValue() - result._type = RegisterValueType.EntryValue - result._arch = arch - result._reg = reg - return result - - @classmethod - def constant(self, value): - result = RegisterValue() - result._type = RegisterValueType.ConstantValue - result._value = value - result._is_constant = True - return result - - @classmethod - def constant_ptr(self, value): - result = RegisterValue() - result._type = RegisterValueType.ConstantPointerValue - result._value = value - result._is_constant = True - return result - - @classmethod - def stack_frame_offset(self, offset): - result = RegisterValue() - result._type = RegisterValueType.StackFrameOffset - result._offset = offset - return result - - @classmethod - def imported_address(self, value): - result = RegisterValue() - result._type = RegisterValueType.ImportedAddressValue - result._value = value - return result - - @classmethod - def return_address(self): - result = RegisterValue() - result._type = RegisterValueType.ReturnAddressValue - return result + def __hash__(self): + return hash((self._function, self._arch, self._address, self._il_type, self._expr_id)) @property - def is_constant(self): - """Boolean for whether the RegisterValue is known to be constant (read-only)""" - return self._is_constant + def function(self) -> Optional['Function']: + return self._function - @property - def type(self): - """:class:`~enums.RegisterValueType` (read-only)""" - return self._type + @function.setter + def function(self, value:'Function') -> None: + self._function = value @property - def arch(self): - """Architecture where it exists, None otherwise (read-only)""" + def arch(self) -> Optional['architecture.Architecture']: return self._arch - @property - def reg(self): - """Register where the Architecture exists, None otherwise (read-only)""" - return self._reg + @arch.setter + def arch(self, value) -> None: + self._arch = value @property - def value(self): - """Value where it exists, None otherwise (read-only)""" - return self._value + def address(self) -> int: + return self._address + + @address.setter + def address(self, value:int) -> None: + self._address = value @property - def offset(self): - """Offset where it exists, None otherwise (read-only)""" - return self._offset + def il_type(self) -> FunctionGraphType: + return self._il_type + + @il_type.setter + def il_type(self, value:FunctionGraphType) -> None: + self._il_type = value @property - def confidence(self): - """Confidence where it exists, None otherwise (read-only)""" - return self._confidence + def expr_id(self) -> ExpressionIndex: + return self._expr_id + + @expr_id.setter + def expr_id(self, value:ExpressionIndex) -> None: + self._expr_id = value -class ValueRange(object): - def __init__(self, start, end, step): - self._start = start - self._end = end - self._step = step +class VariableReferenceSource(object): + def __init__(self, var:'variable.Variable', src:ILReferenceSource): + self._var = var + self._src = src def __repr__(self): - if self.step == 1: - return "" % (self.start, self.end) - return "" % (self.start, self.end, self.step) + return "" % (repr(self._var), repr(self._src)) def __eq__(self, other): if not isinstance(other, self.__class__): return NotImplemented - return self.start == other.start and self.end == other.end and self.step == other.step + return (self.var == other.var) and (self.src == other.src) - def __contains__(self, other): - if not isinstance(other, numbers.Integral): + def __ne__(self, other): + if not isinstance(other, self.__class__): return NotImplemented - return other in range(self._start, self._end, self._step) + return not (self == other) @property - def start(self): - """ """ - return self._start + def var(self) -> 'variable.Variable': + return self._var - @start.setter - def start(self, value): - """ """ - self._start = value + @var.setter + def var(self, value:'variable.Variable') -> None: + self._var = value @property - def end(self): - """ """ - return self._end + def src(self) -> ILReferenceSource: + return self._src - @end.setter - def end(self, value): - """ """ - self._end = value + @src.setter + def src(self, value:ILReferenceSource) -> None: + self._src = value - @property - def step(self): - """ """ - return self._step - @step.setter - def step(self, value): - """ """ - self._step = value +class Function(object): + _associated_data = {} + def __init__(self, view:Optional['binaryview.BinaryView']=None, handle:Optional[core.BNFunction]=None): + self._advanced_analysis_requests = 0 + assert handle is not None, "creation of standalone 'Function' objects is not implemented" + self.handle = core.handle_of_type(handle, core.BNFunction) + if view is None: + self._view = binaryview.BinaryView(handle = core.BNGetFunctionData(self.handle)) + else: + self._view = view + self._arch = None + self._platform = None -class PossibleValueSet(object): - """ - `class PossibleValueSet` PossibleValueSet is used to define possible values - that a variable can take. It contains methods to instantiate different - value sets such as Constant, Signed/Unsigned Ranges, etc. - """ - def __init__(self, arch = None, value = None): - if value is None: - self._type = RegisterValueType.UndeterminedValue - return - self._type = RegisterValueType(value.state) - if value.state == RegisterValueType.EntryValue: - if arch is None: - self._reg = value.value - else: - self._reg = arch.get_reg_name(value.value) - elif value.state == RegisterValueType.ConstantValue: - self._value = value.value - elif value.state == RegisterValueType.ConstantPointerValue: - self._value = value.value - elif value.state == RegisterValueType.StackFrameOffset: - self._offset = value.value - elif value.state == RegisterValueType.SignedRangeValue: - self._offset = value.value - self._ranges = [] - for i in range(0, value.count): - start = value.ranges[i].start - end = value.ranges[i].end - step = value.ranges[i].step - if start & (1 << 63): - start |= ~((1 << 63) - 1) - if end & (1 << 63): - end |= ~((1 << 63) - 1) - self._ranges.append(ValueRange(start, end, step)) - elif value.state == RegisterValueType.UnsignedRangeValue: - self._offset = value.value - self._ranges = [] - for i in range(0, value.count): - start = value.ranges[i].start - end = value.ranges[i].end - step = value.ranges[i].step - self._ranges.append(ValueRange(start, end, step)) - elif value.state == RegisterValueType.LookupTableValue: - self._table = [] - self._mapping = {} - for i in range(0, value.count): - from_list = [] - for j in range(0, value.table[i].fromCount): - from_list.append(value.table[i].fromValues[j]) - self._mapping[value.table[i].fromValues[j]] = value.table[i].toValue - self._table.append(LookupTableEntry(from_list, value.table[i].toValue)) - elif (value.state == RegisterValueType.InSetOfValues) or (value.state == RegisterValueType.NotInSetOfValues): - self._values = set() - for i in range(0, value.count): - self._values.add(value.valueSet[i]) - self._count = value.count + def __del__(self): + if self.handle is not None: + if self._advanced_analysis_requests > 0: + core.BNReleaseAdvancedFunctionAnalysisDataMultiple(self.handle, self._advanced_analysis_requests) + core.BNFreeFunction(self.handle) def __repr__(self): - if self._type == RegisterValueType.EntryValue: - return "" % self.reg - if self._type == RegisterValueType.ConstantValue: - return "" % self.value - if self._type == RegisterValueType.ConstantPointerValue: - return "" % self.value - if self._type == RegisterValueType.StackFrameOffset: - return "" % self._offset - if self._type == RegisterValueType.SignedRangeValue: - return "" % repr(self.ranges) - if self._type == RegisterValueType.UnsignedRangeValue: - return "" % repr(self.ranges) - if self._type == RegisterValueType.LookupTableValue: - return "" % ', '.join([repr(i) for i in self.table]) - if self._type == RegisterValueType.InSetOfValues: - return "" % '[{}]'.format(', '.join(hex(i) for i in sorted(self.values))) - if self._type == RegisterValueType.NotInSetOfValues: - return "" % '[{}]'.format(', '.join(hex(i) for i in sorted(self.values))) - if self._type == RegisterValueType.ReturnAddressValue: - return "" - return "" - - def __contains__(self, other): - if self.type in [RegisterValueType.ConstantValue, RegisterValueType.ConstantPointerValue] and isinstance(other, numbers.Integral): - return self.value == other - if self.type in [RegisterValueType.ConstantValue, RegisterValueType.ConstantPointerValue] and hasattr(other, "value"): - return self.value == other.value - if not isinstance(other, numbers.Integral): - return NotImplemented - #Initial implementation only checks numbers, no set logic - if self.type == RegisterValueType.StackFrameOffset: - return NotImplemented - if self.type in [RegisterValueType.SignedRangeValue, RegisterValueType.UnsignedRangeValue]: - for rng in self.ranges: - if other in rng: - return True - return False - if self.type == RegisterValueType.InSetOfValues: - return other in self.values - if self.type == RegisterValueType.NotInSetOfValues: - return not other in self.values - return NotImplemented + arch = self.arch + if arch: + return "" % (arch.name, self.start) + else: + return "" % self.start - def __eq__(self, other): - if self.type in [RegisterValueType.ConstantValue, RegisterValueType.ConstantPointerValue] and isinstance(other, numbers.Integral): - return self.value == other + def __eq__(self, other:'Function') -> bool: if not isinstance(other, self.__class__): return NotImplemented - if self.type in [RegisterValueType.ConstantValue, RegisterValueType.ConstantPointerValue]: - return self.value == other.value - elif self.type == RegisterValueType.StackFrameOffset: - return self.offset == other.offset - elif self.type in [RegisterValueType.SignedRangeValue, RegisterValueType.UnsignedRangeValue]: - return self.ranges == other.ranges - elif self.type in [RegisterValueType.InSetOfValues, RegisterValueType.NotInSetOfValues]: - return self.values == other.values - elif self.type == RegisterValueType.UndeterminedValue and hasattr(other, 'type'): - return self.type == other.type - else: - return self == other + return ctypes.addressof(self.handle.contents) == ctypes.addressof(other.handle.contents) - def __ne__(self, other): + def __ne__(self, other:'Function') -> bool: if not isinstance(other, self.__class__): return NotImplemented return not (self == other) - def _to_api_object(self): - result = core.BNPossibleValueSet() - result.state = RegisterValueType(self.type) - if self.type == RegisterValueType.UndeterminedValue: - return result - elif self.type == RegisterValueType.ConstantValue: - result.value = self.value - elif self.type == RegisterValueType.ConstantPointerValue: - result.value = self.value - elif self.type == RegisterValueType.StackFrameOffset: - result.offset = self.value - elif self.type == RegisterValueType.SignedRangeValue: - result.offset = self.value - result.ranges = (core.BNValueRange * self.count)() - for i in range(0, self.count): - start = self.ranges[i].start - end = self.ranges[i].end - if start & (1 << 63): - start |= ~((1 << 63) - 1) - if end & (1 << 63): - end |= ~((1 << 63) - 1) - value_range = core.BNValueRange() - value_range.start = start - value_range.end = end - value_range.step = self.ranges[i].step - result.ranges[i] = value_range - result.count = self.count - elif self.type == RegisterValueType.UnsignedRangeValue: - result.offset = self.value - result.ranges = (core.BNValueRange * self.count)() - for i in range(0, self.count): - value_range = core.BNValueRange() - value_range.start = self.ranges[i].start - value_range.end = self.ranges[i].end - value_range.step = self.ranges[i].step - result.ranges[i] = value_range - result.count = self.count - elif self.type == RegisterValueType.LookupTableValue: - result.table = [] - result.mapping = {} - for i in range(self.count): - from_list = [] - for j in range(0, self.table[i].fromCount): - from_list.append(self.table[i].fromValues[j]) - result.mapping[self.table[i].fromValues[j]] = result.table[i].toValue - result.table.append(LookupTableEntry(from_list, result.table[i].toValue)) - result.count = self.count - elif (self.type == RegisterValueType.InSetOfValues) or (self.type == RegisterValueType.NotInSetOfValues): - values = (ctypes.c_longlong * self.count)() - i = 0 - for value in self.values: - values[i] = value - i += 1 - result.valueSet = ctypes.cast(values, ctypes.POINTER(ctypes.c_longlong)) - result.count = self.count - return result - - @property - def type(self): - """ """ - return self._type + def __lt__(self, other:'Function') -> bool: + if not isinstance(other, self.__class__): + return NotImplemented + return self.start < other.start - @type.setter - def type(self, value): - """ """ - self._type = value + def __gt__(self, other:'Function') -> bool: + if not isinstance(other, self.__class__): + return NotImplemented + return self.start > other.start - @property - def reg(self): - """ """ - return self._reg + def __le__(self, other:'Function') -> bool: + if not isinstance(other, self.__class__): + return NotImplemented + return self.start <= other.start - @reg.setter - def reg(self, value): - """ """ - self._reg = value + def __ge__(self, other:'Function') -> bool: + if not isinstance(other, self.__class__): + return NotImplemented + return self.start >= other.start - @property - def value(self): - """ """ - return self._value + def __hash__(self): + return hash((self.start, self.arch, self.platform)) - @value.setter - def value(self, value): - """ """ - self._value = value + def __getitem__(self, i) -> 'basicblock.BasicBlock': + count = ctypes.c_ulonglong() + blocks = core.BNGetFunctionBasicBlockList(self.handle, count) + assert blocks is not None, "core.BNGetFunctionBasicBlockList returned None" + try: + if i < 0: + i = count.value + i + if i < -count.value or i >= count.value: + raise IndexError("index out of range") + if i < 0: + i = count.value + i + core_block = core.BNNewBasicBlockReference(blocks[i]) + assert core_block is not None + return basicblock.BasicBlock(core_block, self._view) + finally: + core.BNFreeBasicBlockList(blocks, count.value) - @property - def offset(self): - """ """ - return self._offset + def __iter__(self) -> Generator['basicblock.BasicBlock', None, None]: + count = ctypes.c_ulonglong() + blocks = core.BNGetFunctionBasicBlockList(self.handle, count) + assert blocks is not None, "core.BNGetFunctionBasicBlockList returned None" + try: + for i in range(0, count.value): + block = core.BNNewBasicBlockReference(blocks[i]) + assert block is not None + yield basicblock.BasicBlock(block, self._view) + finally: + core.BNFreeBasicBlockList(blocks, count.value) - @offset.setter - def offset(self, value): - """ """ - self._offset = value + def __str__(self): + result = "" + for token in self.type_tokens: + result += token.text + return result - @property - def ranges(self): - """ """ - return self._ranges + @classmethod + def _unregister(cls, func:'core.BNFunction') -> None: + handle = ctypes.cast(func, ctypes.c_void_p) + if handle.value in cls._associated_data: + del cls._associated_data[handle.value] - @ranges.setter - def ranges(self, value): - """ """ - self._ranges = value + @staticmethod + def set_default_session_data(name:str, value) -> None: + _FunctionAssociatedDataStore.set_default(name, value) @property - def table(self): - """ """ - return self._table + def name(self) -> str: + """Symbol name for the function""" + return self.symbol.name - @table.setter - def table(self, value): - """ """ - self._table = value + @name.setter + def name(self, value:Union[str, 'types.Symbol']) -> None: # type: ignore + if value is None: + if self.symbol is not None: + self.view.undefine_user_symbol(self.symbol) + elif isinstance(value, str): + symbol = types.Symbol(SymbolType.FunctionSymbol, self.start, value) + self.view.define_user_symbol(symbol) + elif isinstance(value, types.Symbol): + self.view.define_user_symbol(value) @property - def mapping(self): - """ """ - return self._mapping - - @mapping.setter - def mapping(self, value): - """ """ - self._mapping = value + def view(self) -> 'binaryview.BinaryView': + """Function view (read-only)""" + return self._view @property - def values(self): - """ """ - return self._values + def arch(self) -> Optional['architecture.Architecture']: + """Function architecture (read-only)""" + if self._arch: + return self._arch + else: + arch = core.BNGetFunctionArchitecture(self.handle) + assert arch is not None + self._arch = architecture.CoreArchitecture._from_cache(arch) + return self._arch - @values.setter - def values(self, value): - """ """ - self._values = value + @property + def platform(self) -> Optional['_platform.Platform']: + """Function platform (read-only)""" + if self._platform: + return self._platform + else: + plat = core.BNGetFunctionPlatform(self.handle) + if plat is None: + return None + self._platform = _platform.Platform(handle = plat) + return self._platform @property - def count(self): - """ """ - return self._count + def start(self) -> int: + """Function start address (read-only)""" + return core.BNGetFunctionStart(self.handle) - @count.setter - def count(self, value): - self._count = value - - @classmethod - def undetermined(self): + @property + def total_bytes(self) -> int: """ - Create a PossibleValueSet object of type UndeterminedValue. - - :return: PossibleValueSet object of type UndeterminedValue - :rtype: PossibleValueSet + Total bytes of a function calculated by summing each basic_block. Because basic blocks can overlap and + have gaps between them this may or may not be equivalent to a .size property. """ - return PossibleValueSet() + return sum(map(len, self)) - @classmethod - def constant(self, value): - """ - Create a constant valued PossibleValueSet object. + @property + def highest_address(self) -> int: + """The highest (largest) virtual address contained in a function.""" + return core.BNGetFunctionHighestAddress(self.handle) - :param int value: Integer value of the constant - :rtype: PossibleValueSet - """ - result = PossibleValueSet() - result.type = RegisterValueType.ConstantValue - result.value = value + @property + def lowest_address(self) -> int: + """The lowest (smallest) virtual address contained in a function.""" + return core.BNGetFunctionLowestAddress(self.handle) + + @property + def address_ranges(self) -> List['variable.AddressRange']: + """All of the address ranges covered by a function""" + count = ctypes.c_ulonglong(0) + range_list = core.BNGetFunctionAddressRanges(self.handle, count) + assert range_list is not None, "core.BNGetFunctionAddressRanges returned None" + result = [] + for i in range(0, count.value): + result.append(variable.AddressRange(range_list[i].start, range_list[i].end)) + core.BNFreeAddressRanges(range_list) return result - @classmethod - def constant_ptr(self, value): - """ - Create constant pointer valued PossibleValueSet object. + @property + def symbol(self) -> 'types.Symbol': + """Function symbol(read-only)""" + sym = core.BNGetFunctionSymbol(self.handle) + assert sym is not None, "core.BNGetFunctionSymbol returned None" + return types.Symbol(None, None, None, handle = sym) - :param int value: Integer value of the constant pointer - :rtype: PossibleValueSet + @property + def auto(self) -> bool: """ - result = PossibleValueSet() - result.type = RegisterValueType.ConstantPointerValue - result.value = value - return result - - @classmethod - def stack_frame_offset(self, offset): + Whether function was automatically discovered (read-only) as a result of some creation of a 'user' function. + 'user' functions may or may not have been created by a user through the or API. For instance the entry point + into a function is always created a 'user' function. 'user' functions should be considered the root of auto + analysis. """ - Create a PossibleValueSet object for a stack frame offset. + return core.BNWasFunctionAutomaticallyDiscovered(self.handle) - :param int value: Integer value of the offset - :rtype: PossibleValueSet - """ - result = PossibleValueSet() - result.type = RegisterValueType.StackFrameOffset - result.offset = offset - return result + @property + def can_return(self) -> 'types.BoolWithConfidence': + """Whether function can return""" + result = core.BNCanFunctionReturn(self.handle) + return types.BoolWithConfidence(result.value, confidence = result.confidence) - @classmethod - def signed_range_value(self, ranges): - """ - Create a PossibleValueSet object for a signed range of values. + @can_return.setter + def can_return(self, value:'types.BoolWithConfidence') -> None: + bc = core.BNBoolWithConfidence() + bc.value = bool(value) + if hasattr(value, 'confidence'): + bc.confidence = value.confidence + else: + bc.confidence = core.max_confidence + core.BNSetUserFunctionCanReturn(self.handle, bc) - :param list(ValueRange) ranges: List of ValueRanges - :rtype: PossibleValueSet - :Example: + @property + def explicitly_defined_type(self) -> bool: + """Whether function has explicitly defined types (read-only)""" + return core.BNFunctionHasExplicitlyDefinedType(self.handle) - >>> v_1 = ValueRange(-5, -1, 1) - >>> v_2 = ValueRange(7, 10, 1) - >>> val = PossibleValueSet.signed_range_value([v_1, v_2]) - , ]> - """ - result = PossibleValueSet() - result.value = 0 - result.type = RegisterValueType.SignedRangeValue - result.ranges = ranges - result.count = len(ranges) + @property + def needs_update(self) -> bool: + """Whether the function has analysis that needs to be updated (read-only)""" + return core.BNIsFunctionUpdateNeeded(self.handle) + + @property + def basic_blocks(self) -> Generator['basicblock.BasicBlock', None, None]: + """Generator of BasicBlock objects (read-only)""" + count = ctypes.c_ulonglong() + blocks = core.BNGetFunctionBasicBlockList(self.handle, count) + assert blocks is not None, "core.BNGetFunctionBasicBlockList returned None" + try: + for i in range(0, count.value): + block = core.BNNewBasicBlockReference(blocks[i]) + assert block is not None + yield basicblock.BasicBlock(block, self._view) + finally: + core.BNFreeBasicBlockList(blocks, count.value) + + @property + def comments(self) -> Mapping[int, str]: + """Dict of comments (read-only)""" + count = ctypes.c_ulonglong() + addrs = core.BNGetCommentedAddresses(self.handle, count) + assert addrs is not None, "core.BNGetCommentedAddresses returned None" + result = {} + for i in range(0, count.value): + result[addrs[i]] = self.get_comment_at(addrs[i]) + core.BNFreeAddressList(addrs) return result - @classmethod - def unsigned_range_value(self, ranges): + def create_user_tag(self, type:'binaryview.TagType', data:str="") -> 'binaryview.Tag': + """Create a _user_ Tag object""" + return self.create_tag(type, data, True) + + def create_auto_tag(self, type:'binaryview.TagType', data:str="") -> 'binaryview.Tag': + return self.create_tag(type, data, False) + # """Create an _auto_ Tag object""" + + def create_tag(self, type:'binaryview.TagType', data:str="", user:bool=True) -> 'binaryview.Tag': """ - Create a PossibleValueSet object for a unsigned signed range of values. + ``create_tag`` creates a new Tag object but does not add it anywhere. + Use :py:meth:`create_user_address_tag` or + :py:meth:`create_user_function_tag` to create and add in one step. - :param list(ValueRange) ranges: List of ValueRanges - :rtype: PossibleValueSet + :param TagType type: The Tag Type for this Tag + :param str data: Additional data for the Tag + :return: The created Tag + :rtype: Tag :Example: - >>> v_1 = ValueRange(0, 5, 1) - >>> v_2 = ValueRange(7, 10, 1) - >>> val = PossibleValueSet.unsigned_range_value([v_1, v_2]) - , ]> + >>> tt = bv.tag_types["Crashes"] + >>> tag = current_function.create_tag(tt, "Null pointer dereference", True) + >>> current_function.add_user_address_tag(here, tag) + >>> """ - result = PossibleValueSet() - result.value = 0 - result.type = RegisterValueType.UnsignedRangeValue - result.ranges = ranges - result.count = len(ranges) - return result + return self.view.create_tag(type, data, user) - @classmethod - def in_set_of_values(self, values): + @property + def address_tags(self) -> Generator[Tuple['architecture.Architecture', int, 'binaryview.Tag'], None, None]: """ - Create a PossibleValueSet object for a value in a set of values. + ``address_tags`` gets a list of all address Tags in the function. + Tags are returned as a list of (arch, address, Tag) tuples. - :param list(int) values: List of integer values - :rtype: PossibleValueSet + :rtype: Generator((Architecture, int, Tag)) """ - result = PossibleValueSet() - result.type = RegisterValueType.InSetOfValues - result.values = set(values) - result.count = len(values) - return result + count = ctypes.c_ulonglong() + tags = core.BNGetAddressTagReferences(self.handle, count) + assert tags is not None, "core.BNGetAddressTagReferences returned None" + try: + for i in range(0, count.value): + arch = architecture.CoreArchitecture._from_cache(tags[i].arch) + core_tag = core.BNNewTagReference(tags[i].tag) + assert core_tag is not None, "core.BNNewTagReference returned None" + tag = binaryview.Tag(core_tag) + yield (arch, tags[i].addr, tag) + finally: + core.BNFreeTagReferences(tags, count.value) - @classmethod - def not_in_set_of_values(self, values): + def get_address_tags_at(self, addr:int, arch:Optional['architecture.Architecture']=None) -> Generator['binaryview.Tag', None, None]: """ - Create a PossibleValueSet object for a value NOT in a set of values. + ``get_address_tags_at`` gets a generator of all Tags in the function at a given address. - :param list(int) values: List of integer values - :rtype: PossibleValueSet + :param int addr: Address to get tags at + :param Architecture arch: Architecture for the block in which the Tag is added (optional) + :return: A Generator of Tags """ - result = PossibleValueSet() - result.type = RegisterValueType.NotInSetOfValues - result.values = set(values) - result.count = len(values) - return result + if arch is None: + if self.arch is None: + raise Exception("Can't get address tags for function with no architecture specified") + arch = self.arch + count = ctypes.c_ulonglong() + tags = core.BNGetAddressTags(self.handle, arch.handle, addr, count) + assert tags is not None, "core.BNGetAddressTags returned None" + try: + for i in range(0, count.value): + core_tag = core.BNNewTagReference(tags[i]) + assert core_tag is not None + yield binaryview.Tag(core_tag) + finally: + core.BNFreeTagList(tags, count.value) - @classmethod - def lookup_table_value(self, lookup_table, mapping): - """ - Create a PossibleValueSet object for a value which is a member of a - lookuptable. - :param list(LookupTableEntry) lookup_table: List of table entries - :param dict of (int, int) mapping: Mapping used for resolution - :rtype: PossibleValueSet + def add_user_address_tag(self, addr:int, tag:'binaryview.Tag', arch:Optional['architecture.Architecture']=None) -> None: """ - result = PossibleValueSet() - result.type = RegisterValueType.LookupTableValue - result.table = lookup_table - result.mapping = mapping - return result + ``add_user_address_tag`` adds an already-created Tag object at a given address. + Since this adds a user tag, it will be added to the current undo buffer. + If you want want to create the tag as well, consider using + :meth:`create_user_address_tag ` -class ArchAndAddr(object): - def __init__(self, arch = None, addr = 0): - self._arch = binaryninja.architecture.CoreArchitecture._from_cache(arch) - self._addr = addr + :param int addr: Address at which to add the tag + :param Tag tag: Tag object to be added + :param Architecture arch: Architecture for the block in which the Tag is added (optional) + :rtype: None + """ + if arch is None: + if self.arch is None: + raise Exception(f"Can't call add_user_address_tag for function with no architecture specified") + arch = self.arch + core.BNAddUserAddressTag(self.handle, arch.handle, addr, tag.handle) - def __repr__(self): - return "archandaddr <%s @ %#x>" % (self._arch.name, self._addr) + def create_user_address_tag(self, addr:int, type:'binaryview.TagType', data:str, unique:bool=False, + arch:Optional['architecture.Architecture']=None) -> 'binaryview.Tag': + """ + ``create_user_address_tag`` creates and adds a Tag object at a given + address. Since this adds a user tag, it will be added to the current + undo buffer. To create tags associated with an address that is not + inside of a function, use :py:meth:`create_user_data_tag `. - @property - def arch(self): - return self._arch + :param int addr: Address at which to add the tag + :param TagType type: Tag Type for the Tag that is created + :param str data: Additional data for the Tag + :param bool unique: If a tag already exists at this location with this data, don't add another + :param Architecture arch: Architecture for the block in which the Tag is added (optional) + :return: The created Tag + :rtype: Tag + """ + if arch is None: + if self.arch is None: + raise Exception(f"Can't call {_function_name_()} for function with no architecture specified") + arch = self.arch + if unique: + tags = self.get_address_tags_at(addr, arch) + for tag in tags: + if tag.type == type and tag.data == data: + return tag - @property - def addr(self): - return self._addr + tag = self.create_tag(type, data, True) + core.BNAddUserAddressTag(self.handle, arch.handle, addr, tag.handle) + return tag -class StackVariableReference(object): - 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 + def remove_user_address_tag(self, addr:int, tag:'binaryview.TagType', arch:Optional['architecture.Architecture']=None) -> None: + """ + ``remove_user_address_tag`` removes a Tag object at a given address. + Since this removes a user tag, it will be added to the current undo buffer. - def __repr__(self): - if self._source_operand is None: - if self._referenced_offset != self._var.storage: - return "" % (self._name, self._referenced_offset - self._var.storage) - return "" % self._name - if self._referenced_offset != self._var.storage: - return "" % (self._source_operand, self._name, self._var.storage) - return "" % (self._source_operand, self._name) + :param int addr: Address at which to remove the tag + :param Tag tag: Tag object to be added + :param Architecture arch: Architecture for the block in which the Tag is added (optional) + :rtype: None + """ + if arch is None: + if self.arch is None: + raise Exception(f"Can't call {_function_name_()} for function with no architecture specified") + arch = self.arch + core.BNRemoveUserAddressTag(self.handle, arch.handle, addr, tag.handle) - def __eq__(self, other): - if not isinstance(other, self.__class__): - return NotImplemented - return (self._source_operand, self._type, self._name, self._var, self._referenced_offset, self._size) == \ - (other._source_operand, other._type, other._name, other._var, other._referenced_offset, other._size) + def add_auto_address_tag(self, addr:int, tag:'binaryview.TagType', arch:Optional['architecture.Architecture']=None) -> None: + """ + ``add_auto_address_tag`` adds an already-created Tag object at a given address. + If you want want to create the tag as well, consider using + :meth:`create_auto_address_tag ` - def __ne__(self, other): - if not isinstance(other, self.__class__): - return NotImplemented - return not (self == other) + :param int addr: Address at which to add the tag + :param Tag tag: Tag object to be added + :param Architecture arch: Architecture for the block in which the Tag is added (optional) + :rtype: None + """ + if arch is None: + if self.arch is None: + raise Exception(f"Can't call add_auto_address_tag for function with no architecture specified") + arch = self.arch + core.BNAddAutoAddressTag(self.handle, arch.handle, addr, tag.handle) - def __hash__(self): - return hash((self._source_operand, self._type, self._name, self._var, self._referenced_offset, self._size)) + def create_auto_address_tag(self, addr:int, type:'binaryview.TagType', data:str, unique:bool=False, arch:Optional['architecture.Architecture']=None) -> 'binaryview.Tag': + """ + ``create_auto_address_tag`` creates and adds a Tag object at a given address. - @property - def source_operand(self): - """ """ - return self._source_operand + :param int addr: Address at which to add the tag + :param TagType type: Tag Type for the Tag that is created + :param str data: Additional data for the Tag + :param bool unique: If a tag already exists at this location with this data, don't add another + :param Architecture arch: Architecture for the block in which the Tag is added (optional) + :return: The created Tag + :rtype: Tag + """ + if arch is None: + if self.arch is None: + raise Exception(f"Can't call {_function_name_()} for function with no architecture specified") + arch = self.arch + if unique: + tags = self.get_address_tags_at(addr, arch) + for tag in tags: + if tag.type == type and tag.data == data: + return tag - @source_operand.setter - def source_operand(self, value): - self._source_operand = value + tag = self.create_tag(type, data, False) + core.BNAddAutoAddressTag(self.handle, arch.handle, addr, tag.handle) + return tag @property - def type(self): - """ """ - return self._type - - @type.setter - def type(self, value): - self._type = value + def function_tags(self) -> Generator['binaryview.Tag', None, None]: + """ + ``function_tags`` gets a list of all function Tags for the function. - @property - def name(self): - """ """ - return self._name + :rtype: Generator(Tag) + """ + count = ctypes.c_ulonglong() + tags = core.BNGetFunctionTags(self.handle, count) + assert tags is not None, "core.BNGetFunctionTags returned None" + try: + for i in range(0, count.value): + core_tag = core.BNNewTagReference(tags[i]) + assert core_tag is not None + yield binaryview.Tag(core_tag) + finally: + core.BNFreeTagList(tags, count.value) - @name.setter - def name(self, value): - self._name = value + def add_user_function_tag(self, tag:'binaryview.Tag') -> None: + """ + ``add_user_function_tag`` adds an already-created Tag object as a function tag. + Since this adds a user tag, it will be added to the current undo buffer. + If you want want to create the tag as well, consider using + :meth:`create_user_function_tag ` - @property - def var(self): - """ """ - return self._var + :param Tag tag: Tag object to be added + :rtype: None + """ + core.BNAddUserFunctionTag(self.handle, tag.handle) - @var.setter - def var(self, value): - self._var = value + def create_user_function_tag(self, type:'binaryview.TagType', data:str, unique:bool=False) -> 'binaryview.Tag': + """ + ``add_user_function_tag`` creates and adds a Tag object as a function tag. + Since this adds a user tag, it will be added to the current undo buffer. - @property - def referenced_offset(self): - """ """ - return self._referenced_offset + :param TagType type: Tag Type for the Tag that is created + :param str data: Additional data for the Tag + :param bool unique: If a tag already exists with this data, don't add another + :return: The created Tag + :rtype: Tag + """ + if unique: + for tag in self.function_tags: + if tag.type == type and tag.data == data: + return tag - @referenced_offset.setter - def referenced_offset(self, value): - self._referenced_offset = value + tag = self.create_tag(type, data, True) + core.BNAddUserFunctionTag(self.handle, tag.handle) + return tag - @property - def size(self): - """ """ - return self._size + def remove_user_function_tag(self, tag:'binaryview.Tag') -> None: + """ + ``remove_user_function_tag`` removes a Tag object as a function tag. + Since this removes a user tag, it will be added to the current undo buffer. - @size.setter - def size(self, value): - self._size = value + :param Tag tag: Tag object to be added + :rtype: None + """ + core.BNRemoveUserFunctionTag(self.handle, tag.handle) -@decorators.passive -class Variable(object): - def __init__(self, func, source_type, index, storage, name = None, var_type = None, identifier = None): - self._function = func - self._source_type = source_type - self._index = index - self._storage = storage - self._identifier = identifier - self._name = name - self._type = var_type + def add_auto_function_tag(self, tag:'binaryview.Tag') -> None: + """ + ``add_auto_function_tag`` adds an already-created Tag object as a function tag. + If you want want to create the tag as well, consider using + :meth:`create_auto_function_tag ` + :param Tag tag: Tag object to be added + :rtype: None + """ + core.BNAddAutoFunctionTag(self.handle, tag.handle) - def __repr__(self): - if self.type is None: - return f"" - return f"" + def create_auto_function_tag(self, type:'binaryview.TagType', data:str, unique:bool=False) -> 'binaryview.Tag': + """ + ``create_auto_function_tag`` creates and adds a Tag object as a function tag. - def __str__(self): - return self.name + :param TagType type: Tag Type for the Tag that is created + :param str data: Additional data for the Tag + :param bool unique: If a tag already exists with this data, don't add another + :return: The created Tag + :rtype: Tag + """ + if unique: + for tag in self.function_tags: + if tag.type == type and tag.data == data: + return tag - def __eq__(self, other): - if not isinstance(other, self.__class__): - return NotImplemented - return (self.identifier, self.function) == (other.identifier, other.function) + tag = self.create_tag(type, data, False) + core.BNAddAutoFunctionTag(self.handle, tag.handle) + return tag - def __ne__(self, other): - if not isinstance(other, self.__class__): - return NotImplemented - return not (self == other) + @property + def low_level_il(self) -> 'lowlevelil.LowLevelILFunction': + """returns LowLevelILFunction used to represent Function low level IL (read-only)""" + return lowlevelil.LowLevelILFunction(self.arch, core.BNGetFunctionLowLevelIL(self.handle), self) - def __hash__(self): - return hash((self.identifier, self.function)) + @property + def llil(self) -> 'lowlevelil.LowLevelILFunction': + """returns LowLevelILFunction used to represent Function low level IL (read-only)""" + return lowlevelil.LowLevelILFunction(self.arch, core.BNGetFunctionLowLevelIL(self.handle), self) @property - def function(self): - """Function where the variable is defined""" - return self._function + def llil_if_available(self) -> Optional['lowlevelil.LowLevelILFunction']: + """returns LowLevelILFunction used to represent Function low level IL, or None if not loaded (read-only)""" + result = core.BNGetFunctionLowLevelILIfAvailable(self.handle) + if not result: + return None + return lowlevelil.LowLevelILFunction(self.arch, result, self) - @function.setter - def function(self, value): - self._function = value + @property + def lifted_il(self) -> 'lowlevelil.LowLevelILFunction': + """returns LowLevelILFunction used to represent lifted IL (read-only)""" + return lowlevelil.LowLevelILFunction(self.arch, core.BNGetFunctionLiftedIL(self.handle), self) @property - def source_type(self): - """:class:`~enums.VariableSourceType`""" - if not isinstance(self._source_type, VariableSourceType): - self._source_type = VariableSourceType(self._source_type) + def lifted_il_if_available(self) -> Optional['lowlevelil.LowLevelILFunction']: + """returns LowLevelILFunction used to represent lifted IL, or None if not loaded (read-only)""" + result = core.BNGetFunctionLiftedILIfAvailable(self.handle) + if not result: + return None + return lowlevelil.LowLevelILFunction(self.arch, result, self) - return self._source_type + @property + def medium_level_il(self) -> 'mediumlevelil.MediumLevelILFunction': + """Function medium level IL (read-only)""" + return mediumlevelil.MediumLevelILFunction(self.arch, core.BNGetFunctionMediumLevelIL(self.handle), self) - @source_type.setter - def source_type(self, value): - self._source_type = value + @property + def mlil(self) -> 'mediumlevelil.MediumLevelILFunction': + """Function medium level IL (read-only)""" + return mediumlevelil.MediumLevelILFunction(self.arch, core.BNGetFunctionMediumLevelIL(self.handle), self) @property - def index(self): - """ """ - return self._index + def mlil_if_available(self) -> Optional['mediumlevelil.MediumLevelILFunction']: + """Function medium level IL, or None if not loaded (read-only)""" + result = core.BNGetFunctionMediumLevelILIfAvailable(self.handle) + if not result: + return None + return mediumlevelil.MediumLevelILFunction(self.arch, result, self) - @index.setter - def index(self, value): - self._index = value + @property + def high_level_il(self) -> 'highlevelil.HighLevelILFunction': + """Function high level IL (read-only)""" + return highlevelil.HighLevelILFunction(self.arch, core.BNGetFunctionHighLevelIL(self.handle), self) @property - def storage(self): - """Stack offset for StackVariableSourceType, register index for RegisterVariableSourceType""" - return self._storage + def hlil(self) -> 'highlevelil.HighLevelILFunction': + """Function high level IL (read-only)""" + return highlevelil.HighLevelILFunction(self.arch, core.BNGetFunctionHighLevelIL(self.handle), self) - @storage.setter - def storage(self, value): - self._storage = value + @property + def hlil_if_available(self) -> Optional['highlevelil.HighLevelILFunction']: + """Function high level IL, or None if not loaded (read-only)""" + result = core.BNGetFunctionHighLevelILIfAvailable(self.handle) + if not result: + return None + return highlevelil.HighLevelILFunction(self.arch, result, self) @property - def identifier(self): - """ """ - if self._identifier is None: - self._identifier = core.BNToVariableIdentifier(self.to_BNVariable()) - return self._identifier + def function_type(self) -> 'types.Type': + """ + Function type object, can be set with either a string representing the function prototype + (`str(function)` shows examples) or a :py:class:`Type` object + """ + return types.Type(core.BNGetFunctionType(self.handle), platform = self.platform) - @identifier.setter - def identifier(self, value): - self._identifier = value + @function_type.setter + def function_type(self, value:'types.Type') -> None: + if isinstance(value, str): + (value, new_name) = self.view.parse_type_string(value) + self.name = str(new_name) + self.set_user_type(value) @property - def name(self): - """Name of the variable, set to an empty string to delete""" - if self._name is None: - if self._function is not None: - self._name = core.BNGetVariableName(self._function.handle, self.to_BNVariable()) - return self._name + def stack_layout(self) -> Generator['variable.Variable', None, None]: + """List of function stack variables (read-only)""" + count = ctypes.c_ulonglong() + v = core.BNGetStackLayout(self.handle, count) + assert v is not None, "core.BNGetStackLayout returned None" + try: + for i in range(0, count.value): + yield variable.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), platform = self.platform, confidence = v[i].typeConfidence)) + finally: + core.BNFreeVariableNameAndTypeList(v, count.value) - @name.setter - def name(self, value): - self._name = value + @property + def vars(self) -> Generator['variable.Variable', None, None]: + """Generator of function variables (read-only)""" + count = ctypes.c_ulonglong() + v = core.BNGetFunctionVariables(self.handle, count) + assert v is not None, "core.BNGetFunctionVariables returned None" + try: + for i in range(0, count.value): + yield variable.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), platform = self.platform, confidence = v[i].typeConfidence)) + finally: + core.BNFreeVariableNameAndTypeList(v, count.value) @property - def type(self): - """ """ - if self._type is None: - if self._function is not None: - var_type_conf = core.BNGetVariableType(self._function.handle, self.to_BNVariable()) - if var_type_conf.type: - self._type = types.Type(var_type_conf.type, platform = self._function.platform, confidence = var_type_conf.confidence) - return self._type + def indirect_branches(self) -> List['variable.IndirectBranchInfo']: + """List of indirect branches (read-only)""" + count = ctypes.c_ulonglong() + branches = core.BNGetIndirectBranches(self.handle, count) + assert branches is not None, "core.BNGetIndirectBranches returned None" + result = [] + for i in range(0, count.value): + result.append(variable.IndirectBranchInfo(architecture.CoreArchitecture._from_cache(branches[i].sourceArch), branches[i].sourceAddr, architecture.CoreArchitecture._from_cache(branches[i].destArch), branches[i].destAddr, branches[i].autoDefined)) + core.BNFreeIndirectBranchList(branches) + return result - @type.setter - def type(self, value): - self._type = value + @property + def unresolved_indirect_branches(self) -> List[int]: + """List of unresolved indirect branches (read-only)""" + count = ctypes.c_ulonglong() + addrs = core.BNGetUnresolvedIndirectBranches(self.handle, count) + assert addrs is not None, "core.BNGetUnresolvedIndirectBranches returned None" + result = [] + for i in range(0, count.value): + result.append(addrs[i]) + core.BNFreeAddressList(addrs) + return result - def to_BNVariable(self): - v = core.BNVariable() - v.type = self.source_type - v.index = self._index - v.storage = self._storage - return v + @property + def has_unresolved_indirect_branches(self) -> bool: + """Has unresolved indirect branches (read-only)""" + return core.BNHasUnresolvedIndirectBranches(self.handle) @property - def dead_store_elimination(self): - if self._function is not None and self._identifier is not None: - return DeadStoreElimination(core.BNGetFunctionVariableDeadStoreElimination(self._function.handle, self.to_BNVariable())) - return None + def session_data(self) -> Any: + """Dictionary object where plugins can store arbitrary data associated with the function""" + handle = ctypes.cast(self.handle, ctypes.c_void_p) + if handle.value not in Function._associated_data: + obj = _FunctionAssociatedDataStore() + Function._associated_data[handle.value] = obj + return obj + else: + return Function._associated_data[handle.value] - @dead_store_elimination.setter - def dead_store_elimination(self, value): - core.BNSetFunctionVariableDeadStoreElimination(self._function.handle, self.to_BNVariable(), value) + @property + def analysis_performance_info(self) -> Mapping[str, int]: + count = ctypes.c_ulonglong() + info = core.BNGetFunctionAnalysisPerformanceInfo(self.handle, count) + assert info is not None, "core.BNGetFunctionAnalysisPerformanceInfo returned None" + result = {} + for i in range(0, count.value): + result[info[i].name] = info[i].seconds + core.BNFreeAnalysisPerformanceInfo(info, count.value) + return result - @classmethod - def from_identifier(self, func, identifier, name=None, var_type=None): - var = core.BNFromVariableIdentifier(identifier) - return Variable(func, VariableSourceType(var.type), var.index, var.storage, name, var_type, identifier) + @property + def type_tokens(self) -> List['InstructionTextToken']: + """Text tokens for this function's prototype""" + return self.get_type_tokens()[0].tokens -class ConstantReference(object): - def __init__(self, val, size, ptr, intermediate): - self._value = val - self._size = size - self._pointer = ptr - self._intermediate = intermediate + @property + def return_type(self) -> Optional['types.Type']: + """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) - def __repr__(self): - if self.pointer: - return "" % self.value - if self.size == 0: - return "" % self.value - return "" % (self.value, self.size) + @return_type.setter + def return_type(self, value:'types.Type') -> None: + 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 value(self): - """ """ - return self._value + def return_regs(self) -> 'types.RegisterSet': + """Registers that are used for the return value""" + result = core.BNGetFunctionReturnRegisters(self.handle) + assert result is not None, "core.BNGetFunctionReturnRegisters returned None" + if self.arch is None: + raise Exception("Can not get property return_regs with unspecified Architecture") + reg_set = [] + for i in range(0, result.count): + reg_set.append(self.arch.get_reg_name(result.regs[i])) + regs = types.RegisterSet(reg_set, confidence = result.confidence) + core.BNFreeRegisterSet(result) + return regs - @value.setter - def value(self, value): - self._value = value + @return_regs.setter + def return_regs(self, value:Union['types.RegisterSet', List['architecture.RegisterType']]) -> None: # type: ignore + regs = core.BNRegisterSetWithConfidence() + regs.regs = (ctypes.c_uint * len(value))() + regs.count = len(value) + if self.arch is None: + raise Exception("Can not get property return_regs with unspecified Architecture") + for i in range(0, len(value)): + regs.regs[i] = self.arch.get_reg_index(value[i]) + if isinstance(value, types.RegisterSet): + regs.confidence = value.confidence + else: + regs.confidence = core.max_confidence + core.BNSetUserFunctionReturnRegisters(self.handle, regs) @property - def size(self): - """ """ - return self._size + def calling_convention(self) -> Optional['callingconvention.CallingConvention']: + """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) - @size.setter - def size(self, value): - self._size = value + @calling_convention.setter + def calling_convention(self, value:'callingconvention.CallingConvention') -> None: + 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 pointer(self): - """ """ - return self._pointer + def parameter_vars(self) -> 'variable.ParameterVariables': + """List of variables for the incoming function parameters""" + result = core.BNGetFunctionParameterVariables(self.handle) + var_list = [] + for i in range(0, result.count): + var_list.append(variable.Variable(self, result.vars[i].type, result.vars[i].index, result.vars[i].storage)) + confidence = result.confidence + core.BNFreeParameterVariables(result) + return variable.ParameterVariables(var_list, confidence, self) - @pointer.setter - def pointer(self, value): - self._pointer = value + @parameter_vars.setter + def parameter_vars(self, value:Optional[Union['variable.ParameterVariables', List['variable.Variable']]]) -> None: # type: ignore + 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 range(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 isinstance(value, types.RegisterSet): + var_conf.confidence = value.confidence + else: + var_conf.confidence = core.max_confidence + core.BNSetUserFunctionParameterVariables(self.handle, var_conf) @property - def intermediate(self): - """ """ - return self._intermediate - - @intermediate.setter - def intermediate(self, value): - self._intermediate = value + def has_variable_arguments(self) -> 'types.BoolWithConfidence': + """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:Union[bool, 'types.BoolWithConfidence']) -> None: # type: ignore + bc = core.BNBoolWithConfidence() + bc.value = bool(value) + if isinstance(value, types.BoolWithConfidence): + bc.confidence = value.confidence + else: + bc.confidence = core.max_confidence + core.BNSetUserFunctionHasVariableArguments(self.handle, bc) -class UserVariableValueInfo(object): - def __init__(self, var, def_site, value): - self.var = var - self.def_site = def_site - self.value = value + @property + def stack_adjustment(self) -> 'types.SizeWithConfidence': + """Number of bytes removed from the stack after return""" + result = core.BNGetFunctionStackAdjustment(self.handle) + return types.SizeWithConfidence(result.value, confidence = result.confidence) - def __repr__(self): - return " %s>" % (self.var, self.def_site.arch.name, self.def_site.addr, self.value) + @stack_adjustment.setter + def stack_adjustment(self, value:'types.SizeWithConfidence') -> None: + oc = core.BNOffsetWithConfidence() + oc.value = int(value) + if hasattr(value, 'confidence'): + oc.confidence = value.confidence + else: + oc.confidence = core.max_confidence + core.BNSetUserFunctionStackAdjustment(self.handle, oc) + @property + def reg_stack_adjustments(self) -> Mapping['architecture.RegisterStackName', 'types.RegisterStackAdjustmentWithConfidence']: + """Number of entries removed from each register stack after return""" + count = ctypes.c_ulonglong() + adjust = core.BNGetFunctionRegisterStackAdjustments(self.handle, count) + assert adjust is not None, "core.BNGetFunctionRegisterStackAdjustments returned None" + if self.arch is None: + raise Exception("Can not get property return_regs with unspecified Architecture") + result = {} + for i in range(0, count.value): + name = self.arch.get_reg_stack_name(adjust[i].regStack) + value = types.RegisterStackAdjustmentWithConfidence(adjust[i].adjustment, + confidence = adjust[i].confidence) + result[name] = value + core.BNFreeRegisterStackAdjustments(adjust) + return result -class IndirectBranchInfo(object): - def __init__(self, source_arch, source_addr, dest_arch, dest_addr, auto_defined): - self.source_arch = source_arch - self.source_addr = source_addr - self.dest_arch = dest_arch - self.dest_addr = dest_addr - self.auto_defined = auto_defined + @reg_stack_adjustments.setter + def reg_stack_adjustments(self, + value:Mapping['architecture.RegisterStackName', Union[int, 'types.RegisterStackAdjustmentWithConfidence']]) -> None: # type: ignore + adjust = (core.BNRegisterStackAdjustment * len(value))() + if self.arch is None: + raise Exception("Can not get property return_regs with unspecified Architecture") + i = 0 + for reg_stack in value.keys(): + adjust[i].regStack = self.arch.get_reg_stack_index(reg_stack) + entry = value[reg_stack] + if isinstance(entry, types.RegisterStackAdjustmentWithConfidence): + adjust[i].adjustment = entry.value + adjust[i].confidence = entry.confidence + else: + adjust[i].adjustment = int(entry) + adjust[i].confidence = core.max_confidence + i += 1 + core.BNSetUserFunctionRegisterStackAdjustments(self.handle, adjust, len(value)) - def __repr__(self): - return " %s:%#x>" % (self.source_arch.name, self.source_addr, self.dest_arch.name, self.dest_addr) + @property + def clobbered_regs(self) -> 'types.RegisterSet': + """Registers that are modified by this function""" + result = core.BNGetFunctionClobberedRegisters(self.handle) + if self.arch is None: + raise Exception("Can not get property return_regs with unspecified Architecture") + reg_set = [] + for i in range(0, result.count): + reg_set.append(self.arch.get_reg_name(result.regs[i])) + regs = types.RegisterSet(reg_set, confidence = result.confidence) + core.BNFreeRegisterSet(result) + return regs -class ParameterVariables(object): - def __init__(self, var_list, confidence = types.max_confidence, func = None): - self._vars = var_list - self._confidence = confidence - self._func = func - - def __repr__(self): - return repr(self._vars) - - def __len__(self): - return len(self._vars) + @clobbered_regs.setter + def clobbered_regs(self, value:Union['types.RegisterSet', List['architecture.RegisterType']]) -> None: # type: ignore + regs = core.BNRegisterSetWithConfidence() - def __iter__(self): - for var in self._vars: - yield var + if self.arch is None: + raise Exception("Can not get property return_regs with unspecified Architecture") + regs.regs = (ctypes.c_uint * len(value))() + regs.count = len(value) + for i in range(0, len(value)): + regs.regs[i] = self.arch.get_reg_index(value[i]) + if isinstance(value, types.RegisterSet): + regs.confidence = value.confidence + else: + regs.confidence = core.max_confidence + core.BNSetUserFunctionClobberedRegisters(self.handle, regs) - def __getitem__(self, idx): - return self._vars[idx] + @property + def global_pointer_value(self) -> variable.RegisterValue: + """Discovered value of the global pointer register, if the function uses one (read-only)""" + result = core.BNGetFunctionGlobalPointerValue(self.handle) + return variable.RegisterValue(self.arch, result.value, confidence = result.confidence) - def __setitem__(self, idx, value): - self._vars[idx] = value - if self._func is not None: - self._func.parameter_vars = self + @property + def comment(self) -> str: + """Gets the comment for the current function""" + return core.BNGetFunctionComment(self.handle) - def with_confidence(self, confidence): - return ParameterVariables(list(self._vars), confidence, self._func) + @comment.setter + def comment(self, comment:str) -> None: + """Sets a comment for the current function""" + return core.BNSetFunctionComment(self.handle, comment) @property - def vars(self): - """ """ - return self._vars + def llil_basic_blocks(self) -> Generator['lowlevelil.LowLevelILBasicBlock', None, None]: + """A generator of all LowLevelILBasicBlock objects in the current function""" + for block in self.llil: + yield block - @vars.setter - def vars(self, value): - self._vars = value + @property + def mlil_basic_blocks(self) -> Generator['mediumlevelil.MediumLevelILBasicBlock', None, None]: + """A generator of all MediumLevelILBasicBlock objects in the current function""" + for block in self.mlil: + yield block @property - def confidence(self): - """ """ - return self._confidence + def instructions(self) -> Generator[Tuple[List['InstructionTextToken'], int], None, None]: + """A generator of instruction tokens and their start addresses for the current function""" + for block in self.basic_blocks: + start = block.start + for i in block: + yield (i[0], start) + start += i[1] - @confidence.setter - def confidence(self, value): - self._confidence = value + @property + def llil_instructions(self) -> Generator['lowlevelil.LowLevelILInstruction', None, None]: + """Deprecated method provided for compatibility. Use llil.instructions instead. Was: A generator of llil instructions of the current function""" + return self.llil.instructions + @property + def mlil_instructions(self) -> Generator['mediumlevelil.MediumLevelILInstruction', None, None]: + """Deprecated method provided for compatibility. Use mlil.instructions instead. Was: A generator of mlil instructions of the current function""" + return self.mlil.instructions -class ILReferenceSource(object): - def __init__(self, func, arch, addr, il_type, expr_id): - self._function = func - self._arch = arch - self._address = addr - self._il_type = il_type - self._expr_id = expr_id + @property + def too_large(self) -> bool: + """Whether the function is too large to automatically perform analysis (read-only)""" + return core.BNIsFunctionTooLarge(self.handle) - def get_il_name(self, il_type): - if il_type == FunctionGraphType.NormalFunctionGraph: - return 'disassembly' - if il_type == FunctionGraphType.LowLevelILFunctionGraph: - return 'llil' - if il_type == FunctionGraphType.LiftedILFunctionGraph: - return 'lifted_llil' - if il_type == FunctionGraphType.LowLevelILSSAFormFunctionGraph: - return 'llil_ssa' - if il_type == FunctionGraphType.MediumLevelILFunctionGraph: - return 'mlil' - if il_type == FunctionGraphType.MediumLevelILSSAFormFunctionGraph: - return 'mlil_ssa' - if il_type == FunctionGraphType.MappedMediumLevelILFunctionGraph: - return 'mapped_mlil' - if il_type == FunctionGraphType.MappedMediumLevelILSSAFormFunctionGraph: - return 'mapped_mlil_ssa' - if il_type == FunctionGraphType.HighLevelILFunctionGraph: - return 'hlil' - if il_type == FunctionGraphType.HighLevelILSSAFormFunctionGraph: - return 'hlil_ssa' + @property + def analysis_skipped(self) -> bool: + """Whether automatic analysis was skipped for this function, set to true to disable analysis.""" + return core.BNIsFunctionAnalysisSkipped(self.handle) - def __repr__(self): - if self._arch: - return "" %\ - (self._arch.name, self._address, self.get_il_name(self._il_type), self.expr_id) + @analysis_skipped.setter + def analysis_skipped(self, skip:bool) -> None: + if skip: + core.BNSetFunctionAnalysisSkipOverride(self.handle, FunctionAnalysisSkipOverride.AlwaysSkipFunctionAnalysis) else: - return "" %\ - (self._address, self.get_il_name(self._il_type), self.expr_id) - - def __eq__(self, other): - if not isinstance(other, self.__class__): - return NotImplemented - return (self.function, self.arch, self.address, self.il_type, self.expr_id) ==\ - (other.address, other.function, other.arch, other.il_type, other.expr_id) + core.BNSetFunctionAnalysisSkipOverride(self.handle, FunctionAnalysisSkipOverride.NeverSkipFunctionAnalysis) - def __ne__(self, other): - if not isinstance(other, self.__class__): - return NotImplemented - return not (self == other) + @property + def analysis_skip_reason(self) -> AnalysisSkipReason: + """Function analysis skip reason""" + return AnalysisSkipReason(core.BNGetAnalysisSkipReason(self.handle)) - def __lt__(self, other): - if not isinstance(other, self.__class__): - return NotImplemented - if self.function < other.function: - return True - if self.function > other.function: - return False - if self.arch < other.arch: - return True - if self.arch > other.arch: - return False - if self.address < other.address: - return True - if self.address > other.address: - return False - if self.il_type < other.il_type: - return True - if self.il_type > other.il_type: - return False - return self.expr_id < other.expr_id + @property + def analysis_skip_override(self) -> FunctionAnalysisSkipOverride: + """Override for skipping of automatic analysis""" + return FunctionAnalysisSkipOverride(core.BNGetFunctionAnalysisSkipOverride(self.handle)) - def __gt__(self, other): - if not isinstance(other, self.__class__): - return NotImplemented - if self.function > other.function: - return True - if self.function < other.function: - return False - if self.arch > other.arch: - return True - if self.arch < other.arch: - return False - if self.address > other.address: - return True - if self.address < other.address: - return False - if self.il_type > other.il_type: - return True - if self.il_type < other.il_type: - return False - return self.expr_id > other.expr_id + @analysis_skip_override.setter + def analysis_skip_override(self, override:FunctionAnalysisSkipOverride) -> None: + core.BNSetFunctionAnalysisSkipOverride(self.handle, override) - def __ge__(self, other): - if not isinstance(other, self.__class__): - return NotImplemented - return (self == other) or (self > other) + @property + def unresolved_stack_adjustment_graph(self) -> Optional['flowgraph.CoreFlowGraph']: + """Flow graph of unresolved stack adjustments (read-only)""" + graph = core.BNGetUnresolvedStackAdjustmentGraph(self.handle) + if not graph: + return None + return flowgraph.CoreFlowGraph(graph) - def __le__(self, other): - if not isinstance(other, self.__class__): - return NotImplemented - return (self == other) or (self < other) + def mark_recent_use(self) -> None: + core.BNMarkFunctionAsRecentlyUsed(self.handle) - def __hash__(self): - return hash((self._function, self._arch, self._address, self._il_type, self._expr_id)) + def get_comment_at(self, addr:int) -> str: + return core.BNGetCommentForAddress(self.handle, addr) - @property - def function(self): - """ """ - return self._function + def set_comment(self, addr:int, comment:str) -> None: + """Deprecated method provided for compatibility. Use set_comment_at instead.""" + core.BNSetCommentForAddress(self.handle, addr, comment) - @function.setter - def function(self, value): - self._function = value + def set_comment_at(self, addr:int, comment:str) -> None: + """ + ``set_comment_at`` sets a comment for the current function at the address specified - @property - def arch(self): - """ """ - return self._arch + :param int addr: virtual address within the current function to apply the comment to + :param str comment: string comment to apply + :rtype: None + :Example: - @arch.setter - def arch(self, value): - self._arch = value + >>> current_function.set_comment_at(here, "hi") - @property - def address(self): - """ """ - return self._address + """ + core.BNSetCommentForAddress(self.handle, addr, comment) - @address.setter - def address(self, value): - self._address = value + def add_user_code_ref(self, from_addr:int, to_addr:int, arch:Optional['architecture.Architecture']=None) -> None: + """ + ``add_user_code_ref`` places a user-defined cross-reference from the instruction at + the given address and architecture to the specified target address. If the specified + source instruction is not contained within this function, no action is performed. + To remove the reference, use :func:`remove_user_code_ref`. - @property - def il_type(self): - """ """ - return self._il_type + :param int from_addr: virtual address of the source instruction + :param int to_addr: virtual address of the xref's destination. + :param Architecture arch: (optional) architecture of the source instruction + :rtype: None + :Example: - @il_type.setter - def il_type(self, value): - self._il_type = value + >>> current_function.add_user_code_ref(here, 0x400000) - @property - def expr_id(self): - """ """ - return self._expr_id + """ - @expr_id.setter - def expr_id(self, value): - self._expr_id = value + if arch is None: + if self.arch is None: + raise Exception(f"Can't call {_function_name_()} for function with no architecture specified") + arch = self.arch + core.BNAddUserCodeReference(self.handle, arch.handle, from_addr, to_addr) -class VariableReferenceSource(object): - def __init__(self, var, src): - self._var = var - self._src = src + def remove_user_code_ref(self, from_addr:int, to_addr:int, from_arch:Optional['architecture.Architecture']=None) -> None: + """ + ``remove_user_code_ref`` removes a user-defined cross-reference. + If the given address is not contained within this function, or if there is no + such user-defined cross-reference, no action is performed. - def __repr__(self): - return "" % (repr(self._var), repr(self._src)) + :param int from_addr: virtual address of the source instruction + :param int to_addr: virtual address of the xref's destination. + :param Architecture from_arch: (optional) architecture of the source instruction + :rtype: None + :Example: - def __eq__(self, other): - if not isinstance(other, self.__class__): - return NotImplemented - return (self.var == other.var) and (self.src == other.src) + >>> current_function.remove_user_code_ref(here, 0x400000) - def __ne__(self, other): - if not isinstance(other, self.__class__): - return NotImplemented - return not (self == other) + """ - def __lt__(self, other): - if not isinstance(other, self.__class__): - return NotImplemented - if self.var < other.var: - return True - if self.var > other.var: - return False - return self.src < other.src + if from_arch is None: + if self.arch is None: + raise Exception(f"Can't call {_function_name_()} for function with no architecture specified") + from_arch = self.arch - def __gt__(self, other): - if not isinstance(other, self.__class__): - return NotImplemented - if self.var > other.var: - return True - if self.var < other.var: - return False - return self.src > other.src + core.BNRemoveUserCodeReference(self.handle, from_arch.handle, from_addr, to_addr) - def __ge__(self, other): - if not isinstance(other, self.__class__): - return NotImplemented - if self.var >= other.var: - return True - if self.var < other.var: - return False - return self.src >= other.src + def add_user_type_ref(self, from_addr:int, name:'types.QualifiedNameType', + from_arch:Optional['architecture.Architecture']=None) -> None: + """ + ``add_user_type_ref`` places a user-defined type cross-reference from the instruction at + the given address and architecture to the specified type. If the specified + source instruction is not contained within this function, no action is performed. + To remove the reference, use :func:`remove_user_type_ref`. - def __le__(self, other): - if not isinstance(other, self.__class__): - return NotImplemented - if self.var <= other.var: - return True - if self.var > other.var: - return False - return self.src <= other.src + :param int from_addr: virtual address of the source instruction + :param QualifiedName name: name of the referenced type + :param Architecture from_arch: (optional) architecture of the source instruction + :rtype: None + :Example: - @property - def var(self): - return self._var - @var.setter - def var(self, value): - self._var = value + >>> current_function.add_user_code_ref(here, 'A') - @property - def src(self): - return self._src + """ - @src.setter - def src(self, value): - self._src = value + if from_arch is None: + if self.arch is None: + raise Exception(f"Can't call {_function_name_()} for function with no architecture specified") + from_arch = self.arch + _name = types.QualifiedName(name)._get_core_struct() + core.BNAddUserTypeReference(self.handle, from_arch.handle, from_addr, _name) -class _FunctionAssociatedDataStore(associateddatastore._AssociatedDataStore): - _defaults = {} + def remove_user_type_ref(self, from_addr:int, name:'types.QualifiedNameType', from_arch:Optional['architecture.Architecture']=None) -> None: + """ + ``remove_user_type_ref`` removes a user-defined type cross-reference. + If the given address is not contained within this function, or if there is no + such user-defined cross-reference, no action is performed. + :param int from_addr: virtual address of the source instruction + :param QualifiedName name: name of the referenced type + :param Architecture from_arch: (optional) architecture of the source instruction + :rtype: None + :Example: -class AddressRange(object): - def __init__(self, start, end): - self._start = start - self._end = end + >>> current_function.remove_user_type_ref(here, 'A') - def __repr__(self): - return "<%#x-%#x>" % (self._start, self._end) + """ - def __len__(self): - return self._end - self.start + if from_arch is None: + if self.arch is None: + raise Exception(f"Can't call {_function_name_()} for function with no architecture specified") + from_arch = self.arch - def __eq__(self, other): - if not isinstance(other, self.__class__): - return NotImplemented - return (self._start, self._end) == (other._start, other._end) + _name = types.QualifiedName(name)._get_core_struct() + core.BNRemoveUserTypeReference(self.handle, from_arch.handle, from_addr, _name) - def __ne__(self, other): - if not isinstance(other, self.__class__): - return NotImplemented - return not (self == other) - - def __hash__(self): - return hash((self._start, self._end)) + def add_user_type_field_ref(self, from_addr:int, name:'types.QualifiedNameType', offset:int, + from_arch:Optional['architecture.Architecture']=None, size:int=0) -> None: + """ + ``add_user_type_field_ref`` places a user-defined type field cross-reference from the + instruction at the given address and architecture to the specified type. If the specified + source instruction is not contained within this function, no action is performed. + To remove the reference, use :func:`remove_user_type_field_ref`. - @property - def length(self): - return self._end - self._start + :param int from_addr: virtual address of the source instruction + :param QualifiedName name: name of the referenced type + :param int offset: offset of the field, relative to the type + :param Architecture from_arch: (optional) architecture of the source instruction + :param int size: (optional) the size of the access + :rtype: None + :Example: - @property - def start(self): - """ """ - return self._start + >>> current_function.add_user_type_field_ref(here, 'A', 0x8) - @start.setter - def start(self, value): - self._start = value + """ - @property - def end(self): - """ """ - return self._end + if from_arch is None: + if self.arch is None: + raise Exception(f"Can't call {_function_name_()} for function with no architecture specified") + from_arch = self.arch - @end.setter - def end(self, value): - self._end = value + _name = types.QualifiedName(name)._get_core_struct() + core.BNAddUserTypeFieldReference(self.handle, from_arch.handle, from_addr, _name,\ + offset, size) + def remove_user_type_field_ref(self, from_addr:int, name:'types.QualifiedNameType', offset:int, + from_arch:Optional['architecture.Architecture']=None, size:int=0) -> None: + """ + ``remove_user_type_field_ref`` removes a user-defined type field cross-reference. + If the given address is not contained within this function, or if there is no + such user-defined cross-reference, no action is performed. -class Function(object): - _associated_data = {} + :param int from_addr: virtual address of the source instruction + :param QualifiedName name: name of the referenced type + :param int offset: offset of the field, relative to the type + :param Architecture from_arch: (optional) architecture of the source instruction + :param int size: (optional) the size of the access + :rtype: None + :Example: - def __init__(self, view = None, handle = None): - self._advanced_analysis_requests = 0 - if handle is None: - self.handle = None - raise NotImplementedError("creation of standalone 'Function' objects is not implemented") - self.handle = core.handle_of_type(handle, core.BNFunction) - if view is None: - self._view = binaryninja.binaryview.BinaryView(handle = core.BNGetFunctionData(self.handle)) - else: - self._view = view - self._arch = None - self._platform = None + >>> current_function.remove_user_type_field_ref(here, 'A', 0x8) - def __del__(self): - if self.handle is not None: - if self._advanced_analysis_requests > 0: - core.BNReleaseAdvancedFunctionAnalysisDataMultiple(self.handle, self._advanced_analysis_requests) - core.BNFreeFunction(self.handle) + """ - def __repr__(self): - arch = self.arch - if arch: - return "" % (arch.name, self.start) - else: - return "" % self.start + if from_arch is None: + if self.arch is None: + raise Exception(f"Can't call {_function_name_()} for function with no architecture specified") + from_arch = self.arch - def __eq__(self, other): - if not isinstance(other, self.__class__): - return NotImplemented - return ctypes.addressof(self.handle.contents) == ctypes.addressof(other.handle.contents) + _name = types.QualifiedName(name)._get_core_struct() + core.BNRemoveUserTypeFieldReference(self.handle, from_arch.handle, from_addr, _name,\ + offset, size) - def __ne__(self, other): - if not isinstance(other, self.__class__): - return NotImplemented - return not (self == other) + def get_low_level_il_at(self, addr:int, arch:Optional['architecture.Architecture']=None): + """ + ``get_low_level_il_at`` gets the LowLevelILInstruction corresponding to the given virtual address - def __lt__(self, other): - if not isinstance(other, self.__class__): - return NotImplemented - return self.start < other.start + :param int addr: virtual address of the function to be queried + :param Architecture arch: (optional) Architecture for the given function + :rtype: LowLevelILInstruction + :Example: - def __gt__(self, other): - if not isinstance(other, self.__class__): - return NotImplemented - return self.start > other.start + >>> func = next(bv.functions) + >>> func.get_low_level_il_at(func.start) + + """ + if arch is None: + if self.arch is None: + raise Exception(f"Can't call {_function_name_()} for function with no architecture specified") + arch = self.arch - def __le__(self, other): - if not isinstance(other, self.__class__): - return NotImplemented - return self.start <= other.start + idx = core.BNGetLowLevelILForInstruction(self.handle, arch.handle, addr) - def __ge__(self, other): - if not isinstance(other, self.__class__): - return NotImplemented - return self.start >= other.start + if idx == len(self.llil): + return None - def __hash__(self): - return hash((self.start, self.arch.name, self.platform.name)) + return self.llil[idx] - def __getitem__(self, i): - count = ctypes.c_ulonglong() - blocks = core.BNGetFunctionBasicBlockList(self.handle, count) - try: - if i < 0: - i = count.value + i - if i < -len(self.basic_blocks) or i >= count.value: - raise IndexError("index out of range") - if i < 0: - i = len(self.basic_blocks) + i - block = binaryninja.basicblock.BasicBlock(core.BNNewBasicBlockReference(blocks[i]), self._view) - return block - finally: - core.BNFreeBasicBlockList(blocks, count.value) + def get_llil_at(self, addr:int, arch:Optional['architecture.Architecture']=None) -> Optional['lowlevelil.LowLevelILInstruction']: + """ + ``get_llil_at`` gets the LowLevelILInstruction corresponding to the given virtual address - def __iter__(self): - count = ctypes.c_ulonglong() - blocks = core.BNGetFunctionBasicBlockList(self.handle, count) - try: - for i in range(0, count.value): - yield binaryninja.basicblock.BasicBlock(core.BNNewBasicBlockReference(blocks[i]), self._view) - finally: - core.BNFreeBasicBlockList(blocks, count.value) + :param int addr: virtual address of the function to be queried + :param Architecture arch: (optional) Architecture for the given function + :rtype: LowLevelILInstruction + :Example: - def __str__(self): - result = "" - for token in self.type_tokens: - result += token.text - return result + >>> func = next(bv.functions) + >>> func.get_llil_at(func.start) + + """ + if arch is None: + if self.arch is None: + raise Exception(f"Can't call {_function_name_()} for function with no architecture specified") + arch = self.arch - @classmethod - def _unregister(cls, func): - handle = ctypes.cast(func, ctypes.c_void_p) - if handle.value in cls._associated_data: - del cls._associated_data[handle.value] + idx = core.BNGetLowLevelILForInstruction(self.handle, arch.handle, addr) - @classmethod - def set_default_session_data(cls, name, value): - _FunctionAssociatedDataStore.set_default(name, value) + if idx == len(self.llil): + return None - @property - def name(self): - """Symbol name for the function""" - return self.symbol.name + return self.llil[idx] - @name.setter - def name(self, value): - if value is None: - if self.symbol is not None: - self.view.undefine_user_symbol(self.symbol) - else: - symbol = types.Symbol(SymbolType.FunctionSymbol, self.start, value) - self.view.define_user_symbol(symbol) + def get_llils_at(self, addr:int, arch:Optional['architecture.Architecture']=None) -> List['lowlevelil.LowLevelILInstruction']: + """ + ``get_llils_at`` gets the LowLevelILInstruction(s) corresponding to the given virtual address - @property - def view(self): - """Function view (read-only)""" - return self._view + :param int addr: virtual address of the function to be queried + :param Architecture arch: (optional) Architecture for the given function + :rtype: list(LowLevelILInstruction) + :Example: - @property - def arch(self): - """Function architecture (read-only)""" - if self._arch: - return self._arch - else: - arch = core.BNGetFunctionArchitecture(self.handle) - if arch is None: - return None - self._arch = binaryninja.architecture.CoreArchitecture._from_cache(arch) - return self._arch + >>> func = next(bv.functions) + >>> func.get_llils_at(func.start) + [] + """ + if arch is None: + if self.arch is None: + raise Exception(f"Can't call get_llils_at for function with no architecture specified") + arch = self.arch + count = ctypes.c_ulonglong() + instrs = core.BNGetLowLevelILInstructionsForAddress(self.handle, arch.handle, addr, count) + assert instrs is not None, "core.BNGetLowLevelILInstructionsForAddress returned None" + result = [] + for i in range(0, count.value): + result.append(self.llil[instrs[i]]) + core.BNFreeILInstructionList(instrs) + return result - @property - def platform(self): - """Function platform (read-only)""" - if self._platform: - return self._platform - else: - plat = core.BNGetFunctionPlatform(self.handle) - if plat is None: - return None - self._platform = binaryninja.platform.Platform(handle = plat) - return self._platform + def get_low_level_il_exits_at(self, addr:int, arch:Optional['architecture.Architecture']=None) -> List[int]: + if arch is None: + if self.arch is None: + raise Exception(f"Can't call {_function_name_()} for function with no architecture specified") + arch = self.arch + count = ctypes.c_ulonglong() + exits = core.BNGetLowLevelILExitsForInstruction(self.handle, arch.handle, addr, count) + assert exits is not None, "core.BNGetLowLevelILExitsForInstruction returned None" + result = [] + for i in range(0, count.value): + result.append(exits[i]) + core.BNFreeILInstructionList(exits) + return result - @property - def start(self): - """Function start address (read-only)""" - return core.BNGetFunctionStart(self.handle) + def get_reg_value_at(self, addr:int, reg:'architecture.RegisterType', + arch:Optional['architecture.Architecture']=None) -> 'variable.RegisterValue': + """ + ``get_reg_value_at`` gets the value the provided string register address corresponding to the given virtual address - @property - def total_bytes(self): - """Total bytes of a function calculated by summing each basic_block. Because basic blocks can overlap and have gaps between them this may or may not be equivalent to a .size property.""" - return sum(map(len, self)) + :param int addr: virtual address of the instruction to query + :param str reg: string value of native register to query + :param Architecture arch: (optional) Architecture for the given function + :rtype: variable.RegisterValue + :Example: - @property - def highest_address(self): - """The highest virtual address contained in a function.""" - return core.BNGetFunctionHighestAddress(self.handle) + >>> func.get_reg_value_at(0x400dbe, 'rdi') + + """ + if arch is None: + if self.arch is None: + raise Exception(f"Can't call {_function_name_()} for function with no architecture specified") + arch = self.arch + reg = arch.get_reg_index(reg) + value = core.BNGetRegisterValueAtInstruction(self.handle, arch.handle, addr, reg) + result = variable.RegisterValue(arch, value) + return result @property - def lowest_address(self): - """The lowest virtual address contained in a function.""" - return core.BNGetFunctionLowestAddress(self.handle) + def auto_address_tags(self): + """ + ``auto_address_tags`` gets a list of all auto-defined address Tags in the function. + Tags are returned as a list of (arch, address, Tag) tuples. - @property - def address_ranges(self): - """All of the address ranges covered by a function""" - count = ctypes.c_ulonglong(0) - range_list = core.BNGetFunctionAddressRanges(self.handle, count) + :rtype: list((Architecture, int, Tag)) + """ + count = ctypes.c_ulonglong() + tags = core.BNGetAutoAddressTagReferences(self.handle, count) + assert tags is not None, "core.BNGetAutoAddressTagReferences returned None" result = [] for i in range(0, count.value): - result.append(AddressRange(range_list[i].start, range_list[i].end)) - core.BNFreeAddressRanges(range_list) + arch = architecture.CoreArchitecture._from_cache(tags[i].arch) + tag_ref = core.BNNewTagReference(tags[i].tag) + assert tag_ref is not None, "core.BNNewTagReference returned None" + tag = binaryview.Tag(tag_ref) + result.append((arch, tags[i].addr, tag)) + core.BNFreeTagReferences(tags, count.value) return result @property - def symbol(self): - """Function symbol(read-only)""" - sym = core.BNGetFunctionSymbol(self.handle) - if sym is None: - return None - return types.Symbol(None, None, None, handle = sym) + def user_address_tags(self): + """ + ``user_address_tags`` gets a list of all user address Tags in the function. + Tags are returned as a list of (arch, address, Tag) tuples. - @property - def auto(self): - """Whether function was automatically discovered (read-only)""" - return core.BNWasFunctionAutomaticallyDiscovered(self.handle) + :rtype: list((Architecture, int, Tag)) + """ + count = ctypes.c_ulonglong() + tags = core.BNGetUserAddressTagReferences(self.handle, count) + assert tags is not None, "core.BNGetUserAddressTagReferences returned" + result = [] + for i in range(0, count.value): + arch = architecture.CoreArchitecture._from_cache(tags[i].arch) + tag_ref = core.BNNewTagReference(tags[i].tag) + assert tag_ref is not None, "core.BNNewTagReference returned None" + tag = binaryview.Tag(tag_ref) + result.append((arch, tags[i].addr, tag)) + core.BNFreeTagReferences(tags, count.value) + return result - @property - def can_return(self): - """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): - """Whether function has explicitly defined types (read-only)""" - return core.BNFunctionHasExplicitlyDefinedType(self.handle) - - @property - def needs_update(self): - """Whether the function has analysis that needs to be updated (read-only)""" - return core.BNIsFunctionUpdateNeeded(self.handle) - - @property - def basic_blocks(self): - """List of basic blocks (read-only)""" - count = ctypes.c_ulonglong() - blocks = core.BNGetFunctionBasicBlockList(self.handle, count) - result = [] - for i in range(0, count.value): - result.append(binaryninja.basicblock.BasicBlock(core.BNNewBasicBlockReference(blocks[i]), self._view)) - core.BNFreeBasicBlockList(blocks, count.value) - return result - - @property - def comments(self): - """Dict of comments (read-only)""" - count = ctypes.c_ulonglong() - addrs = core.BNGetCommentedAddresses(self.handle, count) - result = {} - for i in range(0, count.value): - result[addrs[i]] = self.get_comment_at(addrs[i]) - core.BNFreeAddressList(addrs) - return result - - def create_user_tag(self, type, data): - return self.create_tag(type, data, True) - - def create_auto_tag(self, type, data): - return self.create_tag(type, data, False) - - def create_tag(self, type, data, user=True): + def get_reg_value_after(self, addr:int, reg:'architecture.RegisterType', + arch:Optional['architecture.Architecture']=None) -> 'variable.RegisterValue': """ - ``create_tag`` creates a new Tag object but does not add it anywhere. - Use :py:meth:`create_user_address_tag` or - :py:meth:`create_user_function_tag` to create and add in one step. + ``get_reg_value_after`` gets the value instruction address corresponding to the given virtual address - :param TagType type: The Tag Type for this Tag - :param str data: Additional data for the Tag - :return: The created Tag - :rtype: Tag + :param int addr: virtual address of the instruction to query + :param str reg: string value of native register to query + :param Architecture arch: (optional) Architecture for the given function + :rtype: variable.RegisterValue :Example: - >>> tt = bv.tag_types["Crashes"] - >>> tag = current_function.create_tag(tt, "Null pointer dereference", True) - >>> current_function.add_user_address_tag(here, tag) - >>> - """ - return self.view.create_tag(type, data, user) - - @property - def address_tags(self): - """ - ``address_tags`` gets a list of all address Tags in the function. - Tags are returned as a list of (arch, address, Tag) tuples. - - :rtype: list((Architecture, int, Tag)) - """ - count = ctypes.c_ulonglong() - tags = core.BNGetAddressTagReferences(self.handle, count) - result = [] - for i in range(0, count.value): - arch = binaryninja.architecture.CoreArchitecture._from_cache(tags[i].arch) - tag = binaryninja.binaryview.Tag(core.BNNewTagReference(tags[i].tag)) - result.append((arch, tags[i].addr, tag)) - core.BNFreeTagReferences(tags, count.value) - return result - - @property - def auto_address_tags(self): - """ - ``auto_address_tags`` gets a list of all auto-defined address Tags in the function. - Tags are returned as a list of (arch, address, Tag) tuples. - - :rtype: list((Architecture, int, Tag)) - """ - count = ctypes.c_ulonglong() - tags = core.BNGetAutoAddressTagReferences(self.handle, count) - result = [] - for i in range(0, count.value): - arch = binaryninja.architecture.CoreArchitecture._from_cache(tags[i].arch) - tag = binaryninja.binaryview.Tag(core.BNNewTagReference(tags[i].tag)) - result.append((arch, tags[i].addr, tag)) - core.BNFreeTagReferences(tags, count.value) - return result - - @property - def user_address_tags(self): - """ - ``user_address_tags`` gets a list of all user address Tags in the function. - Tags are returned as a list of (arch, address, Tag) tuples. - - :rtype: list((Architecture, int, Tag)) - """ - count = ctypes.c_ulonglong() - tags = core.BNGetUserAddressTagReferences(self.handle, count) - result = [] - for i in range(0, count.value): - arch = binaryninja.architecture.CoreArchitecture._from_cache(tags[i].arch) - tag = binaryninja.binaryview.Tag(core.BNNewTagReference(tags[i].tag)) - result.append((arch, tags[i].addr, tag)) - core.BNFreeTagReferences(tags, count.value) - return result - - def get_address_tags_at(self, addr, arch=None): - """ - ``get_address_tags_at`` gets a list of all Tags in the function at a given address. - - :param int addr: Address to get tags at - :param Architecture arch: Architecture for the block in which the Tag is located (optional) - :return: A list of Tags - :rtype: list(Tag) + >>> func.get_reg_value_after(0x400dbe, 'rdi') + """ if arch is None: + if self.arch is None: + raise Exception("Can't call get_reg_value_after for function with no architecture specified") arch = self.arch - count = ctypes.c_ulonglong() - tags = core.BNGetAddressTags(self.handle, arch.handle, addr, count) - result = [] - for i in range(0, count.value): - result.append(binaryninja.binaryview.Tag(core.BNNewTagReference(tags[i]))) - core.BNFreeTagList(tags, count.value) + reg = arch.get_reg_index(reg) + value = core.BNGetRegisterValueAfterInstruction(self.handle, arch.handle, addr, reg) + result = variable.RegisterValue(arch, value) return result def get_auto_address_tags_at(self, addr, arch=None): @@ -1682,12 +1581,17 @@ class Function(object): :rtype: list(Tag) """ if arch is None: + if self.arch is None: + raise Exception("Can't call get_auto_address_tags_at for function with no architecture specified") arch = self.arch count = ctypes.c_ulonglong() tags = core.BNGetAutoAddressTags(self.handle, arch.handle, addr, count) + assert tags is not None, "core.BNGetAutoAddressTags returned None" result = [] for i in range(0, count.value): - result.append(binaryninja.binaryview.Tag(core.BNNewTagReference(tags[i]))) + tag_ref = core.BNNewTagReference(tags[i]) + assert tag_ref is not None, "core.BNNewTagReference returned None" + result.append(binaryview.Tag(tag_ref)) core.BNFreeTagList(tags, count.value) return result @@ -1701,12 +1605,17 @@ class Function(object): :rtype: list(Tag) """ if arch is None: + if self.arch is None: + raise Exception("Can't call get_user_address_tags_at for function with no architecture specified") arch = self.arch count = ctypes.c_ulonglong() tags = core.BNGetUserAddressTags(self.handle, arch.handle, addr, count) + assert tags is not None, "core.BNGetUserAddressTags returned None" result = [] for i in range(0, count.value): - result.append(binaryninja.binaryview.Tag(core.BNNewTagReference(tags[i]))) + tag_ref = core.BNNewTagReference(tags[i]) + assert tag_ref is not None, "core.BNNewTagReference returned None" + result.append(binaryview.Tag(tag_ref)) core.BNFreeTagList(tags, count.value) return result @@ -1721,12 +1630,17 @@ class Function(object): :rtype: list(Tag) """ if arch is None: + if self.arch is None: + raise Exception("Can't call get_address_tags_of_type for function with no architecture specified") arch = self.arch count = ctypes.c_ulonglong() tags = core.BNGetAddressTagsOfType(self.handle, arch.handle, addr, tag_type.handle, count) + assert tags is not None, "core.BNGetAddressTagsOfType returned None" result = [] for i in range(0, count.value): - result.append(binaryninja.binaryview.Tag(core.BNNewTagReference(tags[i]))) + tag_ref = core.BNNewTagReference(tags[i]) + assert tag_ref is not None, "core.BNNewTagReference returned None" + result.append(binaryview.Tag(tag_ref)) core.BNFreeTagList(tags, count.value) return result @@ -1741,12 +1655,17 @@ class Function(object): :rtype: list(Tag) """ if arch is None: + if self.arch is None: + raise Exception("Can't call get_auto_address_tags_of_type for function with no architecture specified") arch = self.arch count = ctypes.c_ulonglong() tags = core.BNGetAutoAddressTagsOfType(self.handle, arch.handle, addr, tag_type.handle, count) + assert tags is not None, "core.BNGetAutoAddressTagsOfType returned None" result = [] for i in range(0, count.value): - result.append(binaryninja.binaryview.Tag(core.BNNewTagReference(tags[i]))) + tag_ref = core.BNNewTagReference(tags[i]) + assert tag_ref is not None, "core.BNNewTagReference returned None" + result.append(binaryview.Tag(tag_ref)) core.BNFreeTagList(tags, count.value) return result @@ -1761,12 +1680,17 @@ class Function(object): :rtype: list(Tag) """ if arch is None: + if self.arch is None: + raise Exception("Can't call get_user_address_tags_of_type for function with no architecture specified") arch = self.arch count = ctypes.c_ulonglong() tags = core.BNGetUserAddressTagsOfType(self.handle, arch.handle, addr, tag_type.handle, count) + assert tags is not None, "core.BNGetUserAddressTagsOfType returned None" result = [] for i in range(0, count.value): - result.append(binaryninja.binaryview.Tag(core.BNNewTagReference(tags[i]))) + tag_ref = core.BNNewTagReference(tags[i]) + assert tag_ref is not None, "core.BNNewTagReference returned None" + result.append(binaryview.Tag(tag_ref)) core.BNFreeTagList(tags, count.value) return result @@ -1781,12 +1705,17 @@ class Function(object): :rtype: list((Architecture, int, Tag)) """ if arch is None: + if self.arch is None: + raise Exception("Can't call get_address_tags_in_range for function with no architecture specified") arch = self.arch count = ctypes.c_ulonglong() refs = core.BNGetAddressTagsInRange(self.handle, arch.handle, address_range.start, address_range.end, count) + assert refs is not None, "core.BNGetAddressTagsInRange returned None" result = [] for i in range(0, count.value): - tag = binaryninja.binaryview.Tag(core.BNNewTagReference(refs[i].tag)) + tag_ref = core.BNNewTagReference(refs[i].tag) + assert tag_ref is not None, "core.BNNewTagReference returned None" + tag = binaryview.Tag(tag_ref) result.append((arch, refs[i].addr, tag)) core.BNFreeTagReferences(refs, count.value) return result @@ -1802,12 +1731,17 @@ class Function(object): :rtype: list((Architecture, int, Tag)) """ if arch is None: + if self.arch is None: + raise Exception("Can't call get_auto_address_tags_in_range for function with no architecture specified") arch = self.arch count = ctypes.c_ulonglong() refs = core.BNGetAutoAddressTagsInRange(self.handle, arch.handle, address_range.start, address_range.end, count) + assert refs is not None, "core.BNGetAutoAddressTagsInRange returned None" result = [] for i in range(0, count.value): - tag = binaryninja.binaryview.Tag(core.BNNewTagReference(refs[i].tag)) + tag_ref = core.BNNewTagReference(refs[i].tag) + assert tag_ref is not None, "core.BNNewTagReference returned None" + tag = binaryview.Tag(tag_ref) result.append((arch, refs[i].addr, tag)) core.BNFreeTagReferences(refs, count.value) return result @@ -1823,72 +1757,72 @@ class Function(object): :rtype: list((Architecture, int, Tag)) """ if arch is None: + if self.arch is None: + raise Exception("Can't call get_user_address_tags_in_range for function with no architecture specified") arch = self.arch count = ctypes.c_ulonglong() refs = core.BNGetUserAddressTagsInRange(self.handle, arch.handle, address_range.start, address_range.end, count) + assert refs is not None, "core.BNGetUserAddressTagsInRange returned None" result = [] for i in range(0, count.value): - tag = binaryninja.binaryview.Tag(core.BNNewTagReference(refs[i].tag)) + tag_ref = core.BNNewTagReference(refs[i].tag) + assert tag_ref is not None, "core.BNNewTagReference returned None" + tag = binaryview.Tag(tag_ref) result.append((arch, refs[i].addr, tag)) core.BNFreeTagReferences(refs, count.value) return result - def add_user_address_tag(self, addr, tag, arch=None): + def get_stack_contents_at(self, addr:int, offset:int, size:int, + arch:Optional['architecture.Architecture']=None) -> 'variable.RegisterValue': """ - ``add_user_address_tag`` adds an already-created Tag object at a given address. - Since this adds a user tag, it will be added to the current undo buffer. - If you want want to create the tag as well, consider using - :meth:`create_user_address_tag ` + ``get_stack_contents_at`` returns the RegisterValue for the item on the stack in the current function at the + given virtual address ``addr``, stack offset ``offset`` and size of ``size``. Optionally specifying the architecture. - :param int addr: Address at which to add the tag - :param Tag tag: Tag object to be added - :param Architecture arch: Architecture for the block in which the Tag is added (optional) - :rtype: None - """ - if arch is None: - arch = self.arch - core.BNAddUserAddressTag(self.handle, arch.handle, addr, tag.handle) + :param int addr: virtual address of the instruction to query + :param int offset: stack offset base of stack + :param int size: size of memory to query + :param Architecture arch: (optional) Architecture for the given function + :rtype: variable.RegisterValue - def create_user_address_tag(self, addr, type, data, unique=False, arch=None): - """ - ``create_user_address_tag`` creates and adds a Tag object at a given - address. Since this adds a user tag, it will be added to the current - undo buffer. To create tags associated with an address that is not - inside of a function, use :py:meth:`create_user_data_tag `. + .. note:: Stack base is zero on entry into the function unless the architecture places the return address on the \ + stack as in (x86/x86_64) where the stack base will start at address_size - :param int addr: Address at which to add the tag - :param TagType type: Tag Type for the Tag that is created - :param str data: Additional data for the Tag - :param bool unique: If a tag already exists at this location with this data, don't add another - :param Architecture arch: Architecture for the block in which the Tag is added (optional) - :return: The created Tag - :rtype: Tag + :Example: + + >>> func.get_stack_contents_at(0x400fad, -16, 4) + """ if arch is None: + if self.arch is None: + raise Exception(f"Can't call {_function_name_()} for function with no architecture specified") arch = self.arch - if unique: - tags = self.get_address_tags_at(addr, arch) - for tag in tags: - if tag.type == type and tag.data == data: - return tag - - tag = self.create_tag(type, data, True) - core.BNAddUserAddressTag(self.handle, arch.handle, addr, tag.handle) - return tag + value = core.BNGetStackContentsAtInstruction(self.handle, arch.handle, addr, offset, size) + result = variable.RegisterValue(arch, value) + return result - def remove_user_address_tag(self, addr, tag, arch=None): - """ - ``remove_user_address_tag`` removes a Tag object at a given address. - Since this removes a user tag, it will be added to the current undo buffer. + def get_stack_contents_after(self, addr:int, offset:int, size:int, + arch:Optional['architecture.Architecture']=None) -> 'variable.RegisterValue': + if arch is None: + if self.arch is None: + raise Exception(f"Can't call {_function_name_()} for function with no architecture specified") + arch = self.arch + value = core.BNGetStackContentsAfterInstruction(self.handle, arch.handle, addr, offset, size) + result = variable.RegisterValue(arch, value) + return result - :param int addr: Address at which to remove the tag - :param Tag tag: Tag object to be removed - :param Architecture arch: Architecture for the block in which the Tag is located (optional) - :rtype: None - """ + def get_parameter_at(self, addr:int, func_type:Optional['types.Type'], i:int, + arch:Optional['architecture.Architecture']=None) -> 'variable.RegisterValue': if arch is None: + if self.arch is None: + raise Exception(f"Can't call {_function_name_()} for function with no architecture specified") arch = self.arch - core.BNRemoveUserAddressTag(self.handle, arch.handle, addr, tag.handle) + + _func_type = None + if func_type is not None: + _func_type = func_type.handle + value = core.BNGetParameterValueAtInstruction(self.handle, arch.handle, addr, _func_type, i) + result = variable.RegisterValue(arch, value) + return result def remove_user_address_tags_of_type(self, addr, tag_type, arch=None): """ @@ -1901,58 +1835,60 @@ class Function(object): :rtype: None """ if arch is None: + if self.arch is None: + raise Exception(f"Can't call {_function_name_()} for function with no architecture specified") arch = self.arch core.BNRemoveUserAddressTagsOfType(self.handle, arch.handle, addr, tag_type.handle) - def add_auto_address_tag(self, addr, tag, arch=None): - """ - ``add_auto_address_tag`` adds an already-created Tag object at a given address. - If you want want to create the tag as well, consider using - :meth:`create_auto_address_tag ` + def get_parameter_at_low_level_il_instruction(self, instr:'lowlevelil.InstructionIndex', + func_type:'types.Type', i:int) -> 'variable.RegisterValue': + _func_type = None + if func_type is not None: + _func_type = func_type.handle + value = core.BNGetParameterValueAtLowLevelILInstruction(self.handle, instr, _func_type, i) + result = variable.RegisterValue(self.arch, value) + return result - :param int addr: Address at which to add the tag - :param Tag tag: Tag object to be added - :param Architecture arch: Architecture for the block in which the Tag is added (optional) - :rtype: None - """ + def get_regs_read_by(self, addr:int, arch:Optional['architecture.Architecture']=None) -> List['architecture.RegisterName']: if arch is None: + if self.arch is None: + raise Exception(f"Can't call {_function_name_()} for function with no architecture specified") arch = self.arch - core.BNAddAutoAddressTag(self.handle, arch.handle, addr, tag.handle) - - def create_auto_address_tag(self, addr, type, data, unique=False, arch=None): - """ - ``create_auto_address_tag`` creates and adds a Tag object at a given address. + count = ctypes.c_ulonglong() + regs = core.BNGetRegistersReadByInstruction(self.handle, arch.handle, addr, count) + assert regs is not None, "core.BNGetRegistersReadByInstruction returned None" + result = [] + for i in range(0, count.value): + result.append(arch.get_reg_name(regs[i])) + core.BNFreeRegisterList(regs) + return result - :param int addr: Address at which to add the tag - :param TagType type: Tag Type for the Tag that is created - :param str data: Additional data for the Tag - :param bool unique: If a tag already exists at this location with this data, don't add another - :param Architecture arch: Architecture for the block in which the Tag is added (optional) - :return: The created Tag - :rtype: Tag - """ + def get_regs_written_by(self, addr:int, arch:Optional['architecture.Architecture']=None) -> List['architecture.RegisterName']: if arch is None: + if self.arch is None: + raise Exception(f"Can't call {_function_name_()} for function with no architecture specified") arch = self.arch - if unique: - tags = self.get_address_tags_at(addr, arch) - for tag in tags: - if tag.type == type and tag.data == data: - return tag - - tag = self.create_tag(type, data, False) - core.BNAddAutoAddressTag(self.handle, arch.handle, addr, tag.handle) - return tag + count = ctypes.c_ulonglong() + regs = core.BNGetRegistersWrittenByInstruction(self.handle, arch.handle, addr, count) + assert regs is not None, "core.BNGetRegistersWrittenByInstruction returned None" + result = [] + for i in range(0, count.value): + result.append(arch.get_reg_name(regs[i])) + core.BNFreeRegisterList(regs) + return result - def remove_auto_address_tag(self, addr, tag, arch=None): + def remove_auto_address_tag(self, addr:int, tag:'binaryview.TagType', arch:Optional['architecture.Architecture']=None) -> None: """ ``remove_auto_address_tag`` removes a Tag object at a given address. - :param int addr: Address at which to remove the tag + :param int addr: Address at which to add the tag :param Tag tag: Tag object to be added - :param Architecture arch: Architecture for the block in which the Tag is located (optional) + :param Architecture arch: Architecture for the block in which the Tag is added (optional) :rtype: None """ if arch is None: + if self.arch is None: + raise Exception(f"Can't call remove_auto_address_tag with no architecture specified") arch = self.arch core.BNRemoveAutoAddressTag(self.handle, arch.handle, addr, tag.handle) @@ -1966,22 +1902,27 @@ class Function(object): :rtype: None """ if arch is None: + if self.arch is None: + raise Exception(f"Can't call {_function_name_()} for function with no architecture specified") arch = self.arch core.BNRemoveAutoAddressTagsOfType(self.handle, arch.handle, addr, tag_type.handle) - @property - def function_tags(self): - """ - ``function_tags`` gets a list of all function Tags for the function. - - :rtype: list(Tag) - """ + def get_stack_vars_referenced_by(self, addr:int, + arch:Optional['architecture.Architecture']=None) -> List['variable.StackVariableReference']: + if arch is None: + if self.arch is None: + raise Exception(f"Can't call {_function_name_()} for function with no architecture specified") + arch = self.arch count = ctypes.c_ulonglong() - tags = core.BNGetFunctionTags(self.handle, count) + refs = core.BNGetStackVariablesReferencedByInstruction(self.handle, arch.handle, addr, count) + assert refs is not None, "core.BNGetStackVariablesReferencedByInstruction returned None" result = [] for i in range(0, count.value): - result.append(binaryninja.binaryview.Tag(core.BNNewTagReference(tags[i]))) - core.BNFreeTagList(tags, count.value) + var_type = types.Type(core.BNNewTypeReference(refs[i].type), platform = self.platform, confidence = refs[i].typeConfidence) + result.append(variable.StackVariableReference(refs[i].sourceOperand, var_type, + refs[i].name, variable.Variable.from_identifier(self, refs[i].varIdentifier, refs[i].name, var_type), + refs[i].referencedOffset, refs[i].size)) + core.BNFreeStackVariableReferenceList(refs, count.value) return result @property @@ -1993,9 +1934,12 @@ class Function(object): """ count = ctypes.c_ulonglong() tags = core.BNGetAutoFunctionTags(self.handle, count) + assert tags is not None, "core.BNGetAutoFunctionTags returned None" result = [] for i in range(0, count.value): - result.append(binaryninja.binaryview.Tag(core.BNNewTagReference(tags[i]))) + tag_ref = core.BNNewTagReference(tags[i]) + assert tag_ref is not None, "core.BNNewTagReference returned None" + result.append(binaryview.Tag(tag_ref)) core.BNFreeTagList(tags, count.value) return result @@ -2008,25 +1952,71 @@ class Function(object): """ count = ctypes.c_ulonglong() tags = core.BNGetUserFunctionTags(self.handle, count) + assert tags is not None, "core.BNGetUserFunctionTags returned None" result = [] for i in range(0, count.value): - result.append(binaryninja.binaryview.Tag(core.BNNewTagReference(tags[i]))) + tag_ref = core.BNNewTagReference(tags[i]) + assert tag_ref is not None, "core.BNNewTagReference returned None" + result.append(binaryview.Tag(tag_ref)) core.BNFreeTagList(tags, count.value) return result - def get_function_tags_of_type(self, tag_type): - """ - ``get_function_tags_of_type`` gets a list of all function Tags with a given type. + def get_lifted_il_at(self, addr:int, + arch:Optional['architecture.Architecture']=None) -> Optional['lowlevelil.LowLevelILInstruction']: + if arch is None: + if self.arch is None: + raise Exception(f"Can't call {_function_name_()} for function with no architecture specified") + arch = self.arch - :param TagType tag_type: TagType object to match in searching - :return: A list of data Tags - :rtype: list(Tag) + idx = core.BNGetLiftedILForInstruction(self.handle, arch.handle, addr) + + if idx == len(self.lifted_il): + return None + + return self.lifted_il[idx] + + def get_lifted_ils_at(self, addr:int, + arch:Optional['architecture.Architecture']=None) -> List['lowlevelil.LowLevelILInstruction']: + """ + ``get_lifted_ils_at`` gets the Lifted IL Instruction(s) corresponding to the given virtual address + + :param int addr: virtual address of the function to be queried + :param Architecture arch: (optional) Architecture for the given function + :rtype: list(LowLevelILInstruction) + :Example: + >>> func = next(bv.functions) + >>> func.get_lifted_ils_at(func.start) + [] + """ + if arch is None: + if self.arch is None: + raise Exception(f"Can't call {_function_name_()} for function with no architecture specified") + arch = self.arch + count = ctypes.c_ulonglong() + instrs = core.BNGetLiftedILInstructionsForAddress(self.handle, arch.handle, addr, count) + assert instrs is not None, "core.BNGetLiftedILInstructionsForAddress returned None" + result = [] + for i in range(0, count.value): + result.append(self.lifted_il[instrs[i]]) + core.BNFreeILInstructionList(instrs) + return result + + def get_function_tags_of_type(self, tag_type): + """ + ``get_function_tags_of_type`` gets a list of all function Tags with a given type. + + :param TagType tag_type: TagType object to match in searching + :return: A list of data Tags + :rtype: list(Tag) """ count = ctypes.c_ulonglong() tags = core.BNGetFunctionTagsOfType(self.handle, tag_type.handle, count) + assert tags is not None, "core.BNGetFunctionTagsOfType returned None" result = [] for i in range(0, count.value): - result.append(binaryninja.binaryview.Tag(core.BNNewTagReference(tags[i]))) + tag_ref = core.BNNewTagReference(tags[i]) + assert tag_ref is not None, "core.BNNewTagReference returned None" + result.append(binaryview.Tag(tag_ref)) core.BNFreeTagList(tags, count.value) return result @@ -2040,9 +2030,12 @@ class Function(object): """ count = ctypes.c_ulonglong() tags = core.BNGetAutoFunctionTagsOfType(self.handle, tag_type.handle, count) + assert tags is not None, "core.BNGetAutoFunctionTagsOfType returned None" result = [] for i in range(0, count.value): - result.append(binaryninja.binaryview.Tag(core.BNNewTagReference(tags[i]))) + tag_ref = core.BNNewTagReference(tags[i]) + assert tag_ref is not None, "core.BNNewTagReference returned None" + result.append(binaryview.Tag(tag_ref)) core.BNFreeTagList(tags, count.value) return result @@ -2056,54 +2049,15 @@ class Function(object): """ count = ctypes.c_ulonglong() tags = core.BNGetUserFunctionTagsOfType(self.handle, tag_type.handle, count) + assert tags is not None, "core.BNGetUserFunctionTagsOfType returned None" result = [] for i in range(0, count.value): - result.append(binaryninja.binaryview.Tag(core.BNNewTagReference(tags[i]))) + tag_ref = core.BNNewTagReference(tags[i]) + assert tag_ref is not None, "core.BNNewTagReference returned None" + result.append(binaryview.Tag(tag_ref)) core.BNFreeTagList(tags, count.value) return result - def add_user_function_tag(self, tag): - """ - ``add_user_function_tag`` adds an already-created Tag object as a function tag. - Since this adds a user tag, it will be added to the current undo buffer. - If you want want to create the tag as well, consider using - :meth:`create_user_function_tag ` - - :param Tag tag: Tag object to be added - :rtype: None - """ - core.BNAddUserFunctionTag(self.handle, tag.handle) - - def create_user_function_tag(self, type, data, unique=False): - """ - ``add_user_function_tag`` creates and adds a Tag object as a function tag. - Since this adds a user tag, it will be added to the current undo buffer. - - :param TagType type: Tag Type for the Tag that is created - :param str data: Additional data for the Tag - :param bool unique: If a tag already exists with this data, don't add another - :return: The created Tag - :rtype: Tag - """ - if unique: - for tag in self.function_tags: - if tag.type == type and tag.data == data: - return tag - - tag = self.create_tag(type, data, True) - core.BNAddUserFunctionTag(self.handle, tag.handle) - return tag - - def remove_user_function_tag(self, tag): - """ - ``remove_user_function_tag`` removes a Tag object as a function tag. - Since this removes a user tag, it will be added to the current undo buffer. - - :param Tag tag: Tag object to be removed - :rtype: None - """ - core.BNRemoveUserFunctionTag(self.handle, tag.handle) - def remove_user_function_tags_of_type(self, tag_type): """ ``remove_user_function_tags_of_type`` removes all function Tag objects on a function of a given type @@ -2114,41 +2068,26 @@ class Function(object): """ core.BNRemoveUserFunctionTagsOfType(self.handle, tag_type.handle) - def add_auto_function_tag(self, tag): - """ - ``add_user_function_tag`` adds an already-created Tag object as a function tag. - If you want want to create the tag as well, consider using - :meth:`create_auto_function_tag ` - - :param Tag tag: Tag object to be added - :rtype: None - """ - core.BNAddAutoFunctionTag(self.handle, tag.handle) - - def create_auto_function_tag(self, type, data, unique=False): - """ - ``add_user_function_tag`` creates and adds a Tag object as a function tag. - - :param TagType type: Tag Type for the Tag that is created - :param str data: Additional data for the Tag - :param bool unique: If a tag already exists with this data, don't add another - :return: The created Tag - :rtype: Tag - """ - if unique: - for tag in self.function_tags: - if tag.type == type and tag.data == data: - return tag - - tag = self.create_tag(type, data, False) - core.BNAddAutoFunctionTag(self.handle, tag.handle) - return tag + def get_constants_referenced_by(self, addr:int, + arch:'architecture.Architecture'=None) -> List[variable.ConstantReference]: + if arch is None: + if self.arch is None: + raise Exception(f"Can't call get_constants_referenced_by with no architecture specified") + arch = self.arch + count = ctypes.c_ulonglong() + refs = core.BNGetConstantsReferencedByInstruction(self.handle, arch.handle, addr, count) + assert refs is not None, "core.BNGetConstantsReferencedByInstruction returned None" + result = [] + for i in range(0, count.value): + result.append(variable.ConstantReference(refs[i].value, refs[i].size, refs[i].pointer, refs[i].intermediate)) + core.BNFreeConstantReferenceList(refs) + return result - def remove_auto_function_tag(self, tag): + def remove_auto_function_tag(self, tag:'binaryview.Tag') -> None: """ ``remove_user_function_tag`` removes a Tag object as a function tag. - :param Tag tag: Tag object to be removed + :param Tag tag: Tag object to be added :rtype: None """ core.BNRemoveAutoFunctionTag(self.handle, tag.handle) @@ -2162,174 +2101,141 @@ class Function(object): """ core.BNRemoveAutoFunctionTagsOfType(self.handle, tag_type.handle) - @property - def low_level_il(self): - """Deprecated property provided for compatibility. Use llil instead.""" - return binaryninja.lowlevelil.LowLevelILFunction(self.arch, core.BNGetFunctionLowLevelIL(self.handle), self) - - @property - def llil(self): - """returns LowLevelILFunction used to represent Function low level IL (read-only)""" - return binaryninja.lowlevelil.LowLevelILFunction(self.arch, core.BNGetFunctionLowLevelIL(self.handle), self) - - @property - def llil_if_available(self): - """returns LowLevelILFunction used to represent Function low level IL, or None if not loaded (read-only)""" - result = core.BNGetFunctionLowLevelILIfAvailable(self.handle) - if not result: - return None - return binaryninja.lowlevelil.LowLevelILFunction(self.arch, result, self) - - @property - def lifted_il(self): - """returns LowLevelILFunction used to represent lifted IL (read-only)""" - return binaryninja.lowlevelil.LowLevelILFunction(self.arch, core.BNGetFunctionLiftedIL(self.handle), self) - - @property - def lifted_il_if_available(self): - """returns LowLevelILFunction used to represent lifted IL, or None if not loaded (read-only)""" - result = core.BNGetFunctionLiftedILIfAvailable(self.handle) - if not result: - return None - return binaryninja.lowlevelil.LowLevelILFunction(self.arch, result, self) - - @property - def medium_level_il(self): - """Deprecated property provided for compatibility. Use mlil instead.""" - return binaryninja.mediumlevelil.MediumLevelILFunction(self.arch, core.BNGetFunctionMediumLevelIL(self.handle), self) - - @property - def mlil(self): - """Function medium level IL (read-only)""" - return binaryninja.mediumlevelil.MediumLevelILFunction(self.arch, core.BNGetFunctionMediumLevelIL(self.handle), self) - - @property - def mlil_if_available(self): - """Function medium level IL, or None if not loaded (read-only)""" - result = core.BNGetFunctionMediumLevelILIfAvailable(self.handle) - if not result: - return None - return binaryninja.mediumlevelil.MediumLevelILFunction(self.arch, result, self) - - @property - def high_level_il(self): - """Deprecated property provided for compatibility. Use hlil instead.""" - return binaryninja.highlevelil.HighLevelILFunction(self.arch, core.BNGetFunctionHighLevelIL(self.handle), self) - - @property - def hlil(self): - """Function high level IL (read-only)""" - return binaryninja.highlevelil.HighLevelILFunction(self.arch, core.BNGetFunctionHighLevelIL(self.handle), self) - - @property - def hlil_if_available(self): - """Function high level IL, or None if not loaded (read-only)""" - result = core.BNGetFunctionHighLevelILIfAvailable(self.handle) - if not result: - return None - return binaryninja.highlevelil.HighLevelILFunction(self.arch, result, self) - - @property - def function_type(self): - """Function type object, can be set with either a string representing the function prototype (`str(function)` shows examples) or a :py:class:`Type` object""" - return types.Type(core.BNGetFunctionType(self.handle), platform = self.platform) - - @function_type.setter - def function_type(self, value): - if isinstance(value, str): - (value, new_name) = self.view.parse_type_string(value) - self.name = str(new_name) - self.set_user_type(value) - - @property - def stack_layout(self): - """List of function stack variables (read-only)""" + def get_lifted_il_flag_uses_for_definition(self, i:'lowlevelil.InstructionIndex', + flag:'architecture.FlagType') -> List['lowlevelil.LowLevelILInstruction']: + if self.arch is None: + raise Exception(f"Can't call {_function_name_()} for function with no architecture specified") + flag = self.arch.get_flag_index(flag) count = ctypes.c_ulonglong() - v = core.BNGetStackLayout(self.handle, count) + instrs = core.BNGetLiftedILFlagUsesForDefinition(self.handle, i, flag, count) + assert instrs is not None, "core.BNGetLiftedILFlagUsesForDefinition returned None" result = [] - for i in range(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), platform = self.platform, confidence = v[i].typeConfidence))) - result.sort(key = lambda x: x.identifier) - core.BNFreeVariableNameAndTypeList(v, count.value) + for j in range(0, count.value): + result.append(instrs[lowlevelil.InstructionIndex(j)]) + core.BNFreeILInstructionList(instrs) return result - @property - def vars(self): - """List of function variables (read-only)""" + def get_lifted_il_flag_definitions_for_use(self, i:'lowlevelil.InstructionIndex', + flag:'architecture.FlagType') -> List['lowlevelil.InstructionIndex']: + if self.arch is None: + raise Exception(f"Can't call {_function_name_()} for function with no architecture specified") + + flag = self.arch.get_flag_index(flag) count = ctypes.c_ulonglong() - v = core.BNGetFunctionVariables(self.handle, count) + instrs = core.BNGetLiftedILFlagDefinitionsForUse(self.handle, i, flag, count) + assert instrs is not None, "core.BNGetLiftedILFlagDefinitionsForUse returned None" result = [] - for i in range(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), platform = self.platform, confidence = v[i].typeConfidence))) - result.sort(key = lambda x: x.identifier) - core.BNFreeVariableNameAndTypeList(v, count.value) + for j in range(0, count.value): + result.append(instrs[lowlevelil.InstructionIndex(j)]) + core.BNFreeILInstructionList(instrs) return result - @property - def indirect_branches(self): - """List of indirect branches (read-only)""" + def get_flags_read_by_lifted_il_instruction(self, i:'lowlevelil.InstructionIndex') -> \ + List['architecture.RegisterName']: + if self.arch is None: + raise Exception(f"Can't call {_function_name_()} for function with no architecture specified") + count = ctypes.c_ulonglong() - branches = core.BNGetIndirectBranches(self.handle, count) + flags = core.BNGetFlagsReadByLiftedILInstruction(self.handle, i, count) + assert flags is not None, "core.BNGetFlagsReadByLiftedILInstruction returned None" result = [] - for i in range(0, count.value): - result.append(IndirectBranchInfo(binaryninja.architecture.CoreArchitecture._from_cache(branches[i].sourceArch), branches[i].sourceAddr, binaryninja.architecture.CoreArchitecture._from_cache(branches[i].destArch), branches[i].destAddr, branches[i].autoDefined)) - core.BNFreeIndirectBranchList(branches) + for j in range(0, count.value): + result.append(self.arch._flags_by_index[flags[j]]) + core.BNFreeRegisterList(flags) return result - @property - def unresolved_indirect_branches(self): - """List of unresolved indirect branches (read-only)""" + def get_flags_written_by_lifted_il_instruction(self, i:'lowlevelil.InstructionIndex') -> \ + List['architecture.FlagName']: count = ctypes.c_ulonglong() - addrs = core.BNGetUnresolvedIndirectBranches(self.handle, count) + if self.arch is None: + raise Exception(f"Can't call {_function_name_()} for function with no architecture specified") + + flags = core.BNGetFlagsWrittenByLiftedILInstruction(self.handle, i, count) + assert flags is not None, "core.BNGetFlagsWrittenByLiftedILInstruction returned None" result = [] - for i in range(0, count.value): - result.append(addrs[i]) - core.BNFreeAddressList(addrs) + for j in range(0, count.value): + result.append(self.arch._flags_by_index[flags[j]]) + core.BNFreeRegisterList(flags) return result - @property - def has_unresolved_indirect_branches(self): - """Has unresolved indirect branches (read-only)""" - return core.BNHasUnresolvedIndirectBranches(self.handle) - - @property - def session_data(self): - """Dictionary object where plugins can store arbitrary data associated with the function""" - handle = ctypes.cast(self.handle, ctypes.c_void_p) - if handle.value not in Function._associated_data: - obj = _FunctionAssociatedDataStore() - Function._associated_data[handle.value] = obj - return obj + def create_graph(self, graph_type:FunctionGraphType=FunctionGraphType.NormalFunctionGraph, + settings:'DisassemblySettings'=None) -> flowgraph.CoreFlowGraph: + if settings is not None: + settings_obj = settings.handle else: - return Function._associated_data[handle.value] + settings_obj = None + return flowgraph.CoreFlowGraph(core.BNCreateFunctionGraph(self.handle, graph_type, settings_obj)) - @property - def analysis_performance_info(self): + def apply_imported_types(self, sym:'types.Symbol', type:'types.Type'=None) -> None: + core.BNApplyImportedTypes(self.handle, sym.handle, None if type is None else type.handle) + + def apply_auto_discovered_type(self, func_type:'types.Type') -> None: + core.BNApplyAutoDiscoveredFunctionType(self.handle, func_type.handle) + + def set_auto_indirect_branches(self, source:int, branches:List[Tuple['architecture.Architecture', int]], + source_arch:Optional['architecture.Architecture']=None) -> None: + if source_arch is None: + if self.arch is None: + raise Exception(f"Can't call {_function_name_()} for function with no architecture specified") + source_arch = self.arch + branch_list = (core.BNArchitectureAndAddress * len(branches))() + for i in range(len(branches)): + branch_list[i].arch = branches[i][0].handle + branch_list[i].address = branches[i][1] + core.BNSetAutoIndirectBranches(self.handle, source_arch.handle, source, branch_list, len(branches)) + + def set_user_indirect_branches(self, source:int, branches:List[Tuple['architecture.Architecture', int]], + source_arch:Optional['architecture.Architecture']=None) -> None: + if source_arch is None: + if self.arch is None: + raise Exception(f"Can't call {_function_name_()} for function with no architecture specified") + source_arch = self.arch + branch_list = (core.BNArchitectureAndAddress * len(branches))() + for i in range(len(branches)): + branch_list[i].arch = branches[i][0].handle + branch_list[i].address = branches[i][1] + core.BNSetUserIndirectBranches(self.handle, source_arch.handle, source, branch_list, len(branches)) + + def get_indirect_branches_at(self, addr:int, arch:Optional['architecture.Architecture']=None) -> List['variable.IndirectBranchInfo']: + if arch is None: + if self.arch is None: + raise Exception(f"Can't call {_function_name_()} for function with no architecture specified") + arch = self.arch count = ctypes.c_ulonglong() - info = core.BNGetFunctionAnalysisPerformanceInfo(self.handle, count) - result = {} - for i in range(0, count.value): - result[info[i].name] = info[i].seconds - core.BNFreeAnalysisPerformanceInfo(info, count.value) - return result + branches = core.BNGetIndirectBranchesAt(self.handle, arch.handle, addr, count) + try: + assert branches is not None, "core.BNGetIndirectBranchesAt returned None" + result = [] + for i in range(count.value): + result.append(variable.IndirectBranchInfo(architecture.CoreArchitecture._from_cache(branches[i].sourceArch), branches[i].sourceAddr, architecture.CoreArchitecture._from_cache(branches[i].destArch), branches[i].destAddr, branches[i].autoDefined)) + return result + finally: + core.BNFreeIndirectBranchList(branches) - @property - def type_tokens(self): - """Text tokens for this function's prototype""" - return self.get_type_tokens()[0].tokens + def get_block_annotations(self, addr:int, arch:Optional['architecture.Architecture']=None) -> \ + List[List['InstructionTextToken']]: + if arch is None: + if self.arch is None: + raise Exception("can not get_block_annotations if Function.arch is None") + arch = self.arch + count = ctypes.c_ulonglong(0) + lines = core.BNGetFunctionBlockAnnotations(self.handle, arch.handle, addr, count) + try: + assert lines is not None, "core.BNGetFunctionBlockAnnotations returned None" + result = [] + for i in range(count.value): + result.append(InstructionTextToken._from_core_struct(lines[i].tokens, lines[i].count)) + return result + finally: + core.BNFreeInstructionTextLines(lines, count.value) - @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) + def set_auto_type(self, value:'types.Type') -> None: + core.BNSetFunctionAutoType(self.handle, value.handle) - @return_type.setter - def return_type(self, value): + def set_user_type(self, value:'types.Type') -> None: + core.BNSetFunctionUserType(self.handle, value.handle) + + def set_auto_return_type(self, value:'types.Type') -> None: type_conf = core.BNTypeWithConfidence() if value is None: type_conf.type = None @@ -2337,42 +2243,24 @@ class Function(object): else: type_conf.type = value.handle type_conf.confidence = value.confidence - core.BNSetUserFunctionReturnType(self.handle, type_conf) + core.BNSetAutoFunctionReturnType(self.handle, type_conf) - @property - def return_regs(self): - """Registers that are used for the return value""" - result = core.BNGetFunctionReturnRegisters(self.handle) - reg_set = [] - for i in range(0, result.count): - reg_set.append(self.arch.get_reg_name(result.regs[i])) - regs = types.RegisterSet(reg_set, confidence = result.confidence) - core.BNFreeRegisterSet(result) - return regs - - @return_regs.setter - def return_regs(self, value): + def set_auto_return_regs(self, value:Union['types.RegisterSet', List['architecture.RegisterType']]) -> None: regs = core.BNRegisterSetWithConfidence() regs.regs = (ctypes.c_uint * len(value))() regs.count = len(value) + if self.arch is None: + raise Exception("can not set_auto_return_regs if Function.arch is None") + for i in range(0, len(value)): regs.regs[i] = self.arch.get_reg_index(value[i]) - if hasattr(value, 'confidence'): + if isinstance(value, types.RegisterSet): regs.confidence = value.confidence else: - regs.confidence = types.max_confidence - core.BNSetUserFunctionReturnRegisters(self.handle, regs) - - @property - def calling_convention(self): - """Calling convention used by the function""" - result = core.BNGetFunctionCallingConvention(self.handle) - if not result.convention: - return None - return binaryninja.callingconvention.CallingConvention(None, handle = result.convention, confidence = result.confidence) + regs.confidence = core.max_confidence + core.BNSetAutoFunctionReturnRegisters(self.handle, regs) - @calling_convention.setter - def calling_convention(self, value): + def set_auto_calling_convention(self, value:'callingconvention.CallingConvention') -> None: conv_conf = core.BNCallingConventionWithConfidence() if value is None: conv_conf.convention = None @@ -2380,23 +2268,16 @@ class Function(object): 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 range(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, self) + core.BNSetAutoFunctionCallingConvention(self.handle, conv_conf) - @parameter_vars.setter - def parameter_vars(self, value): + def set_auto_parameter_vars(self, value:Optional[Union[List['variable.Variable'], 'variable.Variable', \ + 'variable.ParameterVariables']]) -> None: if value is None: var_list = [] + elif isinstance(value, variable.Variable): + var_list = [value] + elif isinstance(value, variable.ParameterVariables): + var_list = value.vars else: var_list = list(value) var_conf = core.BNParameterVariablesWithConfidence() @@ -2408,62 +2289,45 @@ class Function(object): var_conf.vars[i].storage = var_list[i].storage if value is None: var_conf.confidence = 0 - elif hasattr(value, 'confidence'): + elif isinstance(value, variable.ParameterVariables): 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) + var_conf.confidence = core.max_confidence + core.BNSetAutoFunctionParameterVariables(self.handle, var_conf) - @has_variable_arguments.setter - def has_variable_arguments(self, value): + def set_auto_has_variable_arguments(self, value:Union[bool, 'types.BoolWithConfidence']) -> None: bc = core.BNBoolWithConfidence() bc.value = bool(value) - if hasattr(value, 'confidence'): + if isinstance(value, types.BoolWithConfidence): bc.confidence = value.confidence else: - bc.confidence = types.max_confidence - core.BNSetUserFunctionHasVariableArguments(self.handle, bc) + bc.confidence = core.max_confidence + core.BNSetAutoFunctionHasVariableArguments(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) + def set_auto_can_return(self, value:Union[bool, 'types.BoolWithConfidence']) -> None: + bc = core.BNBoolWithConfidence() + bc.value = bool(value) + if isinstance(value, types.BoolWithConfidence): + bc.confidence = value.confidence + else: + bc.confidence = core.max_confidence + core.BNSetAutoFunctionCanReturn(self.handle, bc) - @stack_adjustment.setter - def stack_adjustment(self, value): + def set_auto_stack_adjustment(self, value:Union[int, 'types.SizeWithConfidence']) -> None: oc = core.BNOffsetWithConfidence() oc.value = int(value) - if hasattr(value, 'confidence'): + if isinstance(value, types.SizeWithConfidence): oc.confidence = value.confidence else: - oc.confidence = types.max_confidence - core.BNSetUserFunctionStackAdjustment(self.handle, oc) - - @property - def reg_stack_adjustments(self): - """Number of entries removed from each register stack after return""" - count = ctypes.c_ulonglong() - adjust = core.BNGetFunctionRegisterStackAdjustments(self.handle, count) - result = {} - for i in range(0, count.value): - name = self.arch.get_reg_stack_name(adjust[i].regStack) - value = types.RegisterStackAdjustmentWithConfidence(adjust[i].adjustment, - confidence = adjust[i].confidence) - result[name] = value - core.BNFreeRegisterStackAdjustments(adjust) - return result + oc.confidence = core.max_confidence + core.BNSetAutoFunctionStackAdjustment(self.handle, oc) - @reg_stack_adjustments.setter - def reg_stack_adjustments(self, value): + def set_auto_reg_stack_adjustments(self, value:Mapping['architecture.RegisterStackName', 'types.RegisterStackAdjustmentWithConfidence']): adjust = (core.BNRegisterStackAdjustment * len(value))() i = 0 + if self.arch is None: + raise Exception("can not set_auto_reg_stack_adjustments if Function.arch is None") + for reg_stack in value.keys(): adjust[i].regStack = self.arch.get_reg_stack_index(reg_stack) if isinstance(value[reg_stack], types.RegisterStackAdjustmentWithConfidence): @@ -2471,1997 +2335,866 @@ class Function(object): adjust[i].confidence = value[reg_stack].confidence else: adjust[i].adjustment = value[reg_stack] - adjust[i].confidence = types.max_confidence + adjust[i].confidence = core.max_confidence i += 1 - core.BNSetUserFunctionRegisterStackAdjustments(self.handle, adjust, len(value)) - - @property - def clobbered_regs(self): - """Registers that are modified by this function""" - result = core.BNGetFunctionClobberedRegisters(self.handle) - reg_set = [] - for i in range(0, result.count): - reg_set.append(self.arch.get_reg_name(result.regs[i])) - regs = types.RegisterSet(reg_set, confidence = result.confidence) - core.BNFreeRegisterSet(result) - return regs + core.BNSetAutoFunctionRegisterStackAdjustments(self.handle, adjust, len(value)) - @clobbered_regs.setter - def clobbered_regs(self, value): + def set_auto_clobbered_regs(self, value:List['architecture.RegisterType']) -> None: regs = core.BNRegisterSetWithConfidence() regs.regs = (ctypes.c_uint * len(value))() regs.count = len(value) + if self.arch is None: + raise Exception("can not set_auto_clobbered_regs if Function.arch") + for i in range(0, len(value)): regs.regs[i] = self.arch.get_reg_index(value[i]) - if hasattr(value, 'confidence'): + if isinstance(value, types.RegisterSet): 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) - - @property - def llil_basic_blocks(self): - """A generator of all LowLevelILBasicBlock objects in the current function""" - for block in self.llil: - yield block - - @property - def mlil_basic_blocks(self): - """A generator of all MediumLevelILBasicBlock objects in the current function""" - for block in self.mlil: - yield block - - @property - def instructions(self): - """A generator of instruction tokens and their start addresses for the current function""" - for block in self.basic_blocks: - start = block.start - for i in block: - yield (i[0], start) - start += i[1] + regs.confidence = core.max_confidence + core.BNSetAutoFunctionClobberedRegisters(self.handle, regs) - @property - def llil_instructions(self): - """Deprecated method provided for compatibility. Use llil.instructions instead. Was: A generator of llil instructions of the current function""" - return self.llil.instructions + def get_int_display_type(self, instr_addr:int, value:int, operand:int, arch:Optional['architecture.Architecture']=None) -> IntegerDisplayType: + if arch is None: + if self.arch is None: + raise Exception(f"Can't call {_function_name_()} for function with no architecture specified") + arch = self.arch + return IntegerDisplayType(core.BNGetIntegerConstantDisplayType(self.handle, arch.handle, instr_addr, value, operand)) - @property - def mlil_instructions(self): - """Deprecated method provided for compatibility. Use mlil.instructions instead. Was: A generator of mlil instructions of the current function""" - return self.mlil.instructions + def set_int_display_type(self, instr_addr:int, value:int, operand:int, display_type:IntegerDisplayType, arch:Optional['architecture.Architecture']=None) -> None: + """ - @property - def too_large(self): - """Whether the function is too large to automatically perform analysis (read-only)""" - return core.BNIsFunctionTooLarge(self.handle) + :param int instr_addr: + :param int value: + :param int operand: + :param enums.IntegerDisplayType display_type: + :param Architecture arch: (optional) + """ + if arch is None: + if self.arch is None: + raise Exception(f"Can't call {_function_name_()} for function with no architecture specified") + arch = self.arch + if isinstance(display_type, str): + display_type = IntegerDisplayType[display_type] + core.BNSetIntegerConstantDisplayType(self.handle, arch.handle, instr_addr, value, operand, display_type) - @property - def analysis_skipped(self): - """Whether automatic analysis was skipped for this function, set to true to disable analysis.""" - return core.BNIsFunctionAnalysisSkipped(self.handle) + def reanalyze(self) -> None: + """ + ``reanalyze`` causes this functions to be reanalyzed. This function does not wait for the analysis to finish. - @analysis_skipped.setter - def analysis_skipped(self, skip): - if skip: - core.BNSetFunctionAnalysisSkipOverride(self.handle, FunctionAnalysisSkipOverride.AlwaysSkipFunctionAnalysis) - else: - core.BNSetFunctionAnalysisSkipOverride(self.handle, FunctionAnalysisSkipOverride.NeverSkipFunctionAnalysis) + :rtype: None + """ + core.BNReanalyzeFunction(self.handle) - @property - def analysis_skip_reason(self): - """Function analysis skip reason""" - return AnalysisSkipReason(core.BNGetAnalysisSkipReason(self.handle)) + def request_advanced_analysis_data(self) -> None: + core.BNRequestAdvancedFunctionAnalysisData(self.handle) + self._advanced_analysis_requests += 1 - @property - def analysis_skip_override(self): - """Override for skipping of automatic analysis""" - return FunctionAnalysisSkipOverride(core.BNGetFunctionAnalysisSkipOverride(self.handle)) + def release_advanced_analysis_data(self) -> None: + core.BNReleaseAdvancedFunctionAnalysisData(self.handle) + self._advanced_analysis_requests -= 1 - @analysis_skip_override.setter - def analysis_skip_override(self, override): - core.BNSetFunctionAnalysisSkipOverride(self.handle, override) + def get_basic_block_at(self, addr:int, arch:Optional['architecture.Architecture']=None) -> Optional['basicblock.BasicBlock']: + """ + ``get_basic_block_at`` returns the BasicBlock of the optionally specified Architecture ``arch`` at the given + address ``addr``. - @property - def unresolved_stack_adjustment_graph(self): - """Flow graph of unresolved stack adjustments (read-only)""" - graph = core.BNGetUnresolvedStackAdjustmentGraph(self.handle) - if not graph: + :param int addr: Address of the BasicBlock to retrieve. + :param Architecture arch: (optional) Architecture of the basic block if different from the Function's self.arch + :Example: + >>> current_function.get_basic_block_at(current_function.start) + + """ + if arch is None: + if self.arch is None: + raise Exception(f"Can't call {_function_name_()} for function with no architecture specified") + arch = self.arch + block = core.BNGetFunctionBasicBlockAtAddress(self.handle, arch.handle, addr) + if not block: return None - return binaryninja.flowgraph.CoreFlowGraph(graph) - - def mark_recent_use(self): - core.BNMarkFunctionAsRecentlyUsed(self.handle) - - def get_comment_at(self, addr): - return core.BNGetCommentForAddress(self.handle, addr) + return basicblock.BasicBlock(block, self._view) - def set_comment(self, addr, comment): - """Deprecated method provided for compatibility. Use set_comment_at instead.""" - core.BNSetCommentForAddress(self.handle, addr, comment) - - def set_comment_at(self, addr, comment): + def get_instr_highlight(self, addr:int, arch:Optional['architecture.Architecture']=None) -> '_highlight.HighlightColor': """ - ``set_comment_at`` sets a comment for the current function at the address specified - - :param int addr: virtual address within the current function to apply the comment to - :param str comment: string comment to apply - :rtype: None :Example: - - >>> current_function.set_comment_at(here, "hi") - + >>> current_function.set_user_instr_highlight(here, highlight.HighlightColor(red=0xff, blue=0xff, green=0)) + >>> current_function.get_instr_highlight(here) + """ - core.BNSetCommentForAddress(self.handle, addr, comment) + if arch is None: + if self.arch is None: + raise Exception(f"Can't call {_function_name_()} for function with no architecture specified") + arch = self.arch + color = core.BNGetInstructionHighlight(self.handle, arch.handle, addr) + if color.style == HighlightColorStyle.StandardHighlightColor: + return _highlight.HighlightColor(color = color.color, alpha = color.alpha) + elif color.style == HighlightColorStyle.MixedHighlightColor: + return _highlight.HighlightColor(color = color.color, mix_color = color.mixColor, mix = color.mix, alpha = color.alpha) + elif color.style == HighlightColorStyle.CustomHighlightColor: + return _highlight.HighlightColor(red = color.r, green = color.g, blue = color.b, alpha = color.alpha) + return _highlight.HighlightColor(color = HighlightStandardColor.NoHighlightColor) - def add_user_code_ref(self, from_addr, to_addr, from_arch=None): + def set_auto_instr_highlight(self, addr:int, color:Union['_highlight.HighlightColor', HighlightStandardColor], + arch:Optional['architecture.Architecture']=None): """ - ``add_user_code_ref`` places a user-defined cross-reference from the instruction at - the given address and architecture to the specified target address. If the specified - source instruction is not contained within this function, no action is performed. - To remove the reference, use :func:`remove_user_code_ref`. - - :param int from_addr: virtual address of the source instruction - :param int to_addr: virtual address of the xref's destination. - :param Architecture from_arch: (optional) architecture of the source instruction - :rtype: None - :Example: + ``set_auto_instr_highlight`` highlights the instruction at the specified address with the supplied color - >>> current_function.add_user_code_ref(here, 0x400000) + .. 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|highlight.HighlightColor color: Color value to use for highlighting + :param Architecture arch: (optional) Architecture of the instruction if different from self.arch """ + if arch is None: + if self.arch is None: + raise Exception(f"Can't call {_function_name_()} for function with no architecture specified") + arch = self.arch + if not isinstance(color, HighlightStandardColor) and not isinstance(color, _highlight.HighlightColor): + raise ValueError("Specified color is not one of HighlightStandardColor, _highlight.HighlightColor") + if isinstance(color, HighlightStandardColor): + color = _highlight.HighlightColor(color = color) + core.BNSetAutoInstructionHighlight(self.handle, arch.handle, addr, color._get_core_struct()) - if from_arch is None: - from_arch = self.arch - - core.BNAddUserCodeReference(self.handle, from_arch.handle, from_addr, to_addr) - def remove_user_code_ref(self, from_addr, to_addr, from_arch=None): + def set_user_instr_highlight(self, addr:int, color:Union['_highlight.HighlightColor', HighlightStandardColor], + arch:Optional['architecture.Architecture']=None): """ - ``remove_user_code_ref`` removes a user-defined cross-reference. - If the given address is not contained within this function, or if there is no - such user-defined cross-reference, no action is performed. + ``set_user_instr_highlight`` highlights the instruction at the specified address with the supplied color - :param int from_addr: virtual address of the source instruction - :param int to_addr: virtual address of the xref's destination. - :param Architecture from_arch: (optional) architecture of the source instruction - :rtype: None + :param int addr: virtual address of the instruction to be highlighted + :param HighlightStandardColor|highlight.HighlightColor color: Color value to use for highlighting + :param Architecture arch: (optional) Architecture of the instruction if different from self.arch :Example: - >>> current_function.remove_user_code_ref(here, 0x400000) - + >>> current_function.set_user_instr_highlight(here, HighlightStandardColor.BlueHighlightColor) + >>> current_function.set_user_instr_highlight(here, highlight.HighlightColor(red=0xff, blue=0xff, green=0)) """ + if arch is None: + if self.arch is None: + raise Exception(f"Can't call {_function_name_()} for function with no architecture specified") + arch = self.arch + if not isinstance(color, HighlightStandardColor) and not isinstance(color, _highlight.HighlightColor): + raise ValueError("Specified color is not one of HighlightStandardColor, highlight.HighlightColor") + if isinstance(color, HighlightStandardColor): + color = _highlight.HighlightColor(color) + core.BNSetUserInstructionHighlight(self.handle, arch.handle, addr, color._get_core_struct()) - if from_arch is None: - from_arch = self.arch - - core.BNRemoveUserCodeReference(self.handle, from_arch.handle, from_addr, to_addr) + def create_auto_stack_var(self, offset:int, var_type:'types.Type', name:str) -> None: + tc = core.BNTypeWithConfidence() + tc.type = var_type.handle + tc.confidence = var_type.confidence + core.BNCreateAutoStackVariable(self.handle, offset, tc, name) - def add_user_type_ref(self, from_addr, name, from_arch=None): - """ - ``add_user_type_ref`` places a user-defined type cross-reference from the instruction at - the given address and architecture to the specified type. If the specified - source instruction is not contained within this function, no action is performed. - To remove the reference, use :func:`remove_user_type_ref`. - - :param int from_addr: virtual address of the source instruction - :param QualifiedName name: name of the referenced type - :param Architecture from_arch: (optional) architecture of the source instruction - :rtype: None - :Example: - - >>> current_function.add_user_code_ref(here, 'A') - - """ - - if from_arch is None: - from_arch = self.arch - - name = types.QualifiedName(name)._get_core_struct() - core.BNAddUserTypeReference(self.handle, from_arch.handle, from_addr, name) - - def remove_user_type_ref(self, from_addr, name, from_arch=None): - """ - ``remove_user_type_ref`` removes a user-defined type cross-reference. - If the given address is not contained within this function, or if there is no - such user-defined cross-reference, no action is performed. - - :param int from_addr: virtual address of the source instruction - :param QualifiedName name: name of the referenced type - :param Architecture from_arch: (optional) architecture of the source instruction - :rtype: None - :Example: - - >>> current_function.remove_user_type_ref(here, 'A') - - """ - - if from_arch is None: - from_arch = self.arch - - name = types.QualifiedName(name)._get_core_struct() - core.BNRemoveUserTypeReference(self.handle, from_arch.handle, from_addr, name) - - def add_user_type_field_ref(self, from_addr, name, offset, from_arch = None, size = 0): - """ - ``add_user_type_field_ref`` places a user-defined type field cross-reference from the - instruction at the given address and architecture to the specified type. If the specified - source instruction is not contained within this function, no action is performed. - To remove the reference, use :func:`remove_user_type_field_ref`. - - :param int from_addr: virtual address of the source instruction - :param QualifiedName name: name of the referenced type - :param int offset: offset of the field, relative to the type - :param Architecture from_arch: (optional) architecture of the source instruction - :param int size: (optional) the size of the access - :rtype: None - :Example: - - >>> current_function.add_user_type_field_ref(here, 'A', 0x8) + def create_user_stack_var(self, offset:int, var_type:'types.Type', name:str) -> None: + 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:int) -> None: + core.BNDeleteAutoStackVariable(self.handle, offset) - if from_arch is None: - from_arch = self.arch + def delete_user_stack_var(self, offset:int) -> None: + core.BNDeleteUserStackVariable(self.handle, offset) - name = types.QualifiedName(name)._get_core_struct() - core.BNAddUserTypeFieldReference(self.handle, from_arch.handle, from_addr, name,\ - offset, size) + def create_auto_var(self, var:'variable.Variable', var_type:'types.Type', name:str, + ignore_disjoint_uses:bool=False) -> None: + var_data = core.BNVariable() + var_data.type = var.source_type + var_data.index = var.index + var_data.storage = var.storage + 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 remove_user_type_field_ref(self, from_addr, name, offset, from_arch = None, size = 0): - """ - ``remove_user_type_field_ref`` removes a user-defined type field cross-reference. - If the given address is not contained within this function, or if there is no - such user-defined cross-reference, no action is performed. + def create_user_var(self, var:'variable.Variable', var_type:'types.Type', name:str, + ignore_disjoint_uses:bool=False) -> None: + var_data = core.BNVariable() + var_data.type = var.source_type + var_data.index = var.index + var_data.storage = var.storage + tc = core.BNTypeWithConfidence() + tc.type = var_type.handle + tc.confidence = var_type.confidence + core.BNCreateUserVariable(self.handle, var_data, tc, name, ignore_disjoint_uses) - :param int from_addr: virtual address of the source instruction - :param QualifiedName name: name of the referenced type - :param int offset: offset of the field, relative to the type - :param Architecture from_arch: (optional) architecture of the source instruction - :param int size: (optional) the size of the access - :rtype: None - :Example: + def delete_auto_var(self, var:'variable.Variable') -> None: + var_data = core.BNVariable() + var_data.type = var.source_type + var_data.index = var.index + var_data.storage = var.storage + core.BNDeleteAutoVariable(self.handle, var_data) - >>> current_function.remove_user_type_field_ref(here, 'A', 0x8) + def delete_user_var(self, var:'variable.Variable') -> None: + var_data = core.BNVariable() + var_data.type = var.source_type + var_data.index = var.index + var_data.storage = var.storage + core.BNDeleteUserVariable(self.handle, var_data) - """ + def is_var_user_defined(self, var:'variable.Variable') -> bool: + var_data = core.BNVariable() + var_data.type = var.source_type + var_data.index = var.index + var_data.storage = var.storage + return core.BNIsVariableUserDefined(self.handle, var_data) - if from_arch is None: - from_arch = self.arch + def get_stack_var_at_frame_offset(self, offset:int, addr:int, arch:Optional['architecture.Architecture']=None) -> \ + Optional['variable.Variable']: + if arch is None: + if self.arch is None: + raise Exception(f"Can't call {_function_name_()} for function with no architecture specified") + arch = self.arch + found_var = core.BNVariableNameAndType() + if not core.BNGetStackVariableAtFrameOffset(self.handle, arch.handle, addr, offset, found_var): + return None + result = variable.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), platform = self.platform, + confidence = found_var.typeConfidence)) + core.BNFreeVariableNameAndType(found_var) + return result - name = types.QualifiedName(name)._get_core_struct() - core.BNRemoveUserTypeFieldReference(self.handle, from_arch.handle, from_addr, name,\ - offset, size) + def get_type_tokens(self, settings:'DisassemblySettings'=None) -> List['DisassemblyTextLine']: + _settings = None + if settings is not None: + _settings = settings.handle + count = ctypes.c_ulonglong() + lines = core.BNGetFunctionTypeTokens(self.handle, settings, count) + assert lines is not None, "core.BNGetFunctionTypeTokens returned None" + result = [] + for i in range(0, count.value): + addr = lines[i].addr + color = _highlight.HighlightColor._from_core_struct(lines[i].highlight) + tokens = InstructionTextToken._from_core_struct(lines[i].tokens, lines[i].count) + result.append(DisassemblyTextLine(tokens, addr, color = color)) + core.BNFreeDisassemblyTextLines(lines, count.value) + return result - def get_low_level_il_at(self, addr, arch=None): - """ - ``get_low_level_il_at`` gets the LowLevelILInstruction corresponding to the given virtual address + def get_reg_value_at_exit(self, reg:'architecture.RegisterType') -> 'variable.RegisterValue': + if self.arch is None: + raise Exception("can not get_reg_value_at_exit if Function.arch is") - :param int addr: virtual address of the function to be queried - :param Architecture arch: (optional) Architecture for the given function - :rtype: LowLevelILInstruction - :Example: + result = core.BNGetFunctionRegisterValueAtExit(self.handle, self.arch.get_reg_index(reg)) + return variable.RegisterValue(self.arch, result.value, confidence = result.confidence) - >>> func = bv.functions[0] - >>> func.get_low_level_il_at(func.start) - - """ + def set_auto_call_stack_adjustment(self, addr:int, adjust:Union[int, 'types.SizeWithConfidence'],\ + arch:Optional['architecture.Architecture']=None) -> None: if arch is None: + if self.arch is None: + raise Exception(f"Can't call {_function_name_()} for function with no architecture specified") arch = self.arch + if not isinstance(adjust, types.SizeWithConfidence): + adjust = types.SizeWithConfidence(adjust) + core.BNSetAutoCallStackAdjustment(self.handle, arch.handle, addr, adjust.value, adjust.confidence) - idx = core.BNGetLowLevelILForInstruction(self.handle, arch.handle, addr) - - if idx == len(self.llil): - return None + def set_auto_call_reg_stack_adjustment(self, addr:int, adjust:Mapping['architecture.RegisterStackName', int],\ + arch:Optional['architecture.Architecture']=None) -> None: + if arch is None: + if self.arch is None: + raise Exception(f"Can't call {_function_name_()} for function with no architecture specified") + arch = self.arch + adjust_buf = (core.BNRegisterStackAdjustment * len(adjust))() + i = 0 + for reg_stack in adjust.keys(): + adjust_buf[i].regStack = arch.get_reg_stack_index(reg_stack) + value = adjust[reg_stack] + if not isinstance(value, types.RegisterStackAdjustmentWithConfidence): + value = types.RegisterStackAdjustmentWithConfidence(value) + adjust_buf[i].adjustment = value.value + adjust_buf[i].confidence = value.confidence + i += 1 + core.BNSetAutoCallRegisterStackAdjustment(self.handle, arch.handle, addr, adjust_buf, len(adjust)) - return self.llil[idx] + def set_auto_call_reg_stack_adjustment_for_reg_stack(self, addr:int, reg_stack:'architecture.RegisterStackType', + adjust, arch:Optional['architecture.Architecture']=None) -> None: + if arch is None: + if self.arch is None: + raise Exception(f"Can't call {_function_name_()} for function with no architecture specified") + arch = self.arch + reg_stack = arch.get_reg_stack_index(reg_stack) + if not isinstance(adjust, types.RegisterStackAdjustmentWithConfidence): + adjust = types.RegisterStackAdjustmentWithConfidence(adjust) + core.BNSetAutoCallRegisterStackAdjustmentForRegisterStack(self.handle, arch.handle, addr, reg_stack, + adjust.value, adjust.confidence) - def get_llil_at(self, addr, arch=None): - """ - ``get_llil_at`` gets the LowLevelILInstruction corresponding to the given virtual address + def set_call_type_adjustment(self, addr:int, adjust_type:'types.Type', arch:Optional['architecture.Architecture']=None) -> \ + None: + if arch is None: + if self.arch is None: + raise Exception(f"Can't call {_function_name_()} for function with no architecture specified") + arch = self.arch + if adjust_type is None: + tc = None + else: + tc = core.BNTypeWithConfidence() + tc.type = adjust_type.handle + tc.confidence = adjust_type.confidence + core.BNSetUserCallTypeAdjustment(self.handle, arch.handle, addr, tc) - :param int addr: virtual address of the function to be queried - :param Architecture arch: (optional) Architecture for the given function - :rtype: LowLevelILInstruction - :Example: + def set_call_stack_adjustment(self, addr:int, adjust:Union[int, 'types.SizeWithConfidence'], + arch:Optional['architecture.Architecture']=None): + if arch is None: + if self.arch is None: + raise Exception(f"Can't call {_function_name_()} for function with no architecture specified") + arch = self.arch + if not isinstance(adjust, types.SizeWithConfidence): + adjust = types.SizeWithConfidence(adjust) + core.BNSetUserCallStackAdjustment(self.handle, arch.handle, addr, adjust.value, adjust.confidence) - >>> func = bv.functions[0] - >>> func.get_llil_at(func.start) - - """ + def set_call_reg_stack_adjustment(self, addr:int, + adjust:Mapping['architecture.RegisterStackName', 'types.RegisterStackAdjustmentWithConfidence'], + arch:Optional['architecture.Architecture']=None) -> None: if arch is None: + if self.arch is None: + raise Exception(f"Can't call {_function_name_()} for function with no architecture specified") arch = self.arch + adjust_buf = (core.BNRegisterStackAdjustment * len(adjust))() + i = 0 + for reg_stack in adjust.keys(): + adjust_buf[i].regStack = arch.get_reg_stack_index(reg_stack) + value = adjust[reg_stack] + if not isinstance(value, types.RegisterStackAdjustmentWithConfidence): + value = types.RegisterStackAdjustmentWithConfidence(value) + adjust_buf[i].adjustment = value.value + adjust_buf[i].confidence = value.confidence + i += 1 + core.BNSetUserCallRegisterStackAdjustment(self.handle, arch.handle, addr, adjust_buf, len(adjust)) - idx = core.BNGetLowLevelILForInstruction(self.handle, arch.handle, addr) + def set_call_reg_stack_adjustment_for_reg_stack(self, addr:int, reg_stack:'architecture.RegisterStackType', + adjust:Union[int, 'types.RegisterStackAdjustmentWithConfidence'], arch:Optional['architecture.Architecture']=None) -> None: + if arch is None: + if self.arch is None: + raise Exception(f"Can't call {_function_name_()} for function with no architecture specified") + arch = self.arch + reg_stack = arch.get_reg_stack_index(reg_stack) + if not isinstance(adjust, types.RegisterStackAdjustmentWithConfidence): + adjust = types.RegisterStackAdjustmentWithConfidence(adjust) + core.BNSetUserCallRegisterStackAdjustmentForRegisterStack(self.handle, arch.handle, addr, reg_stack, + adjust.value, adjust.confidence) - if idx == len(self.llil): + def get_call_type_adjustment(self, addr:int, arch:Optional['architecture.Architecture']=None) -> Optional['types.Type']: + if arch is None: + if self.arch is None: + raise Exception(f"Can't call {_function_name_()} for function with no architecture specified") + arch = self.arch + result = core.BNGetCallTypeAdjustment(self.handle, arch.handle, addr) + if not result.type: return None + platform = self.platform + return types.Type(result.type, platform = platform, confidence = result.confidence) - return self.llil[idx] - - def get_llils_at(self, addr, arch=None): - """ - ``get_llils_at`` gets the LowLevelILInstruction(s) corresponding to the given virtual address - - :param int addr: virtual address of the function to be queried - :param Architecture arch: (optional) Architecture for the given function - :rtype: list(LowLevelILInstruction) - :Example: + def get_call_stack_adjustment(self, addr:int, arch:Optional['architecture.Architecture']=None) -> 'types.SizeWithConfidence': + if arch is None: + if self.arch is None: + raise Exception(f"Can't call {_function_name_()} for function with no architecture specified") + arch = self.arch + result = core.BNGetCallStackAdjustment(self.handle, arch.handle, addr) + return types.SizeWithConfidence(result.value, confidence = result.confidence) - >>> func = bv.functions[0] - >>> func.get_llils_at(func.start) - [] - """ + def get_call_reg_stack_adjustment(self, addr:int, arch:Optional['architecture.Architecture']=None) -> \ + Mapping['architecture.RegisterName', 'types.RegisterStackAdjustmentWithConfidence']: if arch is None: + if self.arch is None: + raise Exception(f"Can't call {_function_name_()} for function with no architecture specified") arch = self.arch count = ctypes.c_ulonglong() - instrs = core.BNGetLowLevelILInstructionsForAddress(self.handle, arch.handle, addr, count) - result = [] + adjust = core.BNGetCallRegisterStackAdjustment(self.handle, arch.handle, addr, count) + assert adjust is not None, "core.BNGetCallRegisterStackAdjustment returned None" + result = {} for i in range(0, count.value): - result.append(self.llil[instrs[i]]) - core.BNFreeILInstructionList(instrs) + result[arch.get_reg_stack_name(adjust[i].regStack)] = types.RegisterStackAdjustmentWithConfidence( + adjust[i].adjustment, confidence = adjust[i].confidence) + core.BNFreeRegisterStackAdjustments(adjust) return result - def get_low_level_il_exits_at(self, addr, arch=None): + def get_call_reg_stack_adjustment_for_reg_stack(self, addr:int, reg_stack:'architecture.RegisterStackType',\ + arch:Optional['architecture.Architecture']=None) -> 'types.RegisterStackAdjustmentWithConfidence': if arch is None: + if self.arch is None: + raise Exception(f"Can't call {_function_name_()} for function with no architecture specified") arch = self.arch - count = ctypes.c_ulonglong() - exits = core.BNGetLowLevelILExitsForInstruction(self.handle, arch.handle, addr, count) - result = [] - for i in range(0, count.value): - result.append(exits[i]) - core.BNFreeILInstructionList(exits) + reg_stack = arch.get_reg_stack_index(reg_stack) + adjust = core.BNGetCallRegisterStackAdjustmentForRegisterStack(self.handle, arch.handle, addr, reg_stack) + result = types.RegisterStackAdjustmentWithConfidence(adjust.adjustment, confidence = adjust.confidence) return result - def get_reg_value_at(self, addr, reg, arch=None): + def is_call_instruction(self, addr:int, arch:Optional['architecture.Architecture']=None) -> bool: + if arch is None: + if self.arch is None: + raise Exception(f"Can't call {_function_name_()} for function with no architecture specified") + arch = self.arch + return core.BNIsCallInstruction(self.handle, arch.handle, addr) + + def set_user_var_value(self, var:'variable.Variable', def_addr:int, value:'variable.PossibleValueSet') -> None: """ - ``get_reg_value_at`` gets the value the provided string register address corresponding to the given virtual address + `set_user_var_value` allows the user to specify a PossibleValueSet value for an MLIL variable at its \ + definition site. - :param int addr: virtual address of the instruction to query - :param str reg: string value of native register to query - :param Architecture arch: (optional) Architecture for the given function - :rtype: binaryninja.function.RegisterValue - :Example: + .. warning:: Setting the variable value, triggers a reanalysis of the function and allows the dataflow \ + to compute and propagate values which depend on the current variable. This implies that branch conditions \ + whose values can be determined statically will be computed, leading to potential branch elimination at \ + the HLIL layer. - >>> func.get_reg_value_at(0x400dbe, 'rdi') - - """ - if arch is None: - arch = self.arch - reg = arch.get_reg_index(reg) - value = core.BNGetRegisterValueAtInstruction(self.handle, arch.handle, addr, reg) - result = RegisterValue(arch, value) - return result + :param Variable var: Variable for which the value is to be set + :param int def_addr: Address of the definition site of the variable + :param PossibleValueSet value: Informed value of the variable + :rtype: None + + :Example: - def get_reg_value_after(self, addr, reg, arch=None): + >>> var = current_mlil[0].operands[0] + >>> def_site = 0x40108d + >>> value = PossibleValueSet.constant(5) + >>> current_function.set_user_var_value(var, def_site, value) """ - ``get_reg_value_after`` gets the value instruction address corresponding to the given virtual address + if self.arch is None: + raise Exception("can not set_user_var_value if Function.arch is None") + var_defs = self.mlil.get_var_definitions(var) + if var_defs is None: + raise ValueError("Could not get definition for Variable") + found = False + for site in var_defs: + if site.address == def_addr: + found = True + break + if not found: + raise ValueError("No definition for Variable found at given address") + def_site = core.BNArchitectureAndAddress() + def_site.arch = self.arch.handle + def_site.address = def_addr - :param int addr: virtual address of the instruction to query - :param str reg: string value of native register to query - :param Architecture arch: (optional) Architecture for the given function - :rtype: binaryninja.function.RegisterValue - :Example: + var_data = core.BNVariable() + var_data.type = var.source_type + var_data.index = var.index + var_data.storage = var.storage + core.BNSetUserVariableValue(self.handle, var_data, def_site, value._to_api_object()) - >>> func.get_reg_value_after(0x400dbe, 'rdi') - + def clear_user_var_value(self, var:'variable.Variable', def_addr:int) -> None: """ - if arch is None: - arch = self.arch - reg = arch.get_reg_index(reg) - value = core.BNGetRegisterValueAfterInstruction(self.handle, arch.handle, addr, reg) - result = RegisterValue(arch, value) - return result + Clears a previously defined user variable value. - def get_stack_contents_at(self, addr, offset, size, arch=None): + :param Variable var: Variable for which the value was informed + :param int def_addr: Address of the definition site of the variable + :rtype: None """ - ``get_stack_contents_at`` returns the RegisterValue for the item on the stack in the current function at the - given virtual address ``addr``, stack offset ``offset`` and size of ``size``. Optionally specifying the architecture. + var_defs = self.mlil.get_var_definitions(var) + if var_defs is None: + raise ValueError("Could not get definition for Variable") - :param int addr: virtual address of the instruction to query - :param int offset: stack offset base of stack - :param int size: size of memory to query - :param Architecture arch: (optional) Architecture for the given function - :rtype: binaryninja.function.RegisterValue + if self.arch is None: + raise Exception("can not clear_user_var_value if Function.arch is None") + found = False + for site in var_defs: + if site.address == def_addr: + found = True + break + if not found: + raise ValueError("No definition for Variable found at given address") + def_site = core.BNArchitectureAndAddress() + def_site.arch = self.arch.handle + def_site.address = def_addr - .. note:: Stack base is zero on entry into the function unless the architecture places the return address on the \ - stack as in (x86/x86_64) where the stack base will start at address_size + var_data = core.BNVariable() + var_data.type = var.source_type + var_data.index = var.index + var_data.storage = var.storage + core.BNClearUserVariableValue(self.handle, var_data, def_site) - :Example: + def get_all_user_var_values(self) -> \ + Mapping['variable.Variable', Mapping['ArchAndAddr', 'variable.PossibleValueSet']]: + """ + Returns a map of current defined user variable values. - >>> func.get_stack_contents_at(0x400fad, -16, 4) - + :returns: Map of user current defined user variable values and their definition sites. + :type: dict of (Variable, dict of (ArchAndAddr, PossibleValueSet)) """ - if arch is None: - arch = self.arch - value = core.BNGetStackContentsAtInstruction(self.handle, arch.handle, addr, offset, size) - result = RegisterValue(arch, value) + count = ctypes.c_ulonglong(0) + var_values = core.BNGetAllUserVariableValues(self.handle, count) + assert var_values is not None, "core.BNGetAllUserVariableValues returned None" + result = {} + i = 0 + for i in range(count.value): + var_val = var_values[i] + var = variable.Variable(self, var_val.var.type, var_val.var.index, var_val.var.storage) + if var not in result: + result[var] = {} + def_site = ArchAndAddr(var_val.defSite.arch, var_val.defSite.address) + result[var][def_site] = variable.PossibleValueSet(def_site.arch, var_val.value) + core.BNFreeUserVariableValues(var_values) return result - def get_stack_contents_after(self, addr, offset, size, arch=None): - if arch is None: - arch = self.arch - value = core.BNGetStackContentsAfterInstruction(self.handle, arch.handle, addr, offset, size) - result = RegisterValue(arch, value) - return result + def clear_all_user_var_values(self) -> None: + """ + Clear all user defined variable values. - def get_parameter_at(self, addr, func_type, i, arch=None): - if arch is None: - arch = self.arch - if func_type is not None: - func_type = func_type.handle - value = core.BNGetParameterValueAtInstruction(self.handle, arch.handle, addr, func_type, i) - result = RegisterValue(arch, value) - return result + :rtype: None + """ + all_values = self.get_all_user_var_values() + for var in all_values: + for def_site in all_values[var]: + self.clear_user_var_value(var, def_site.addr) - def get_parameter_at_low_level_il_instruction(self, instr, func_type, i): - if func_type is not None: - func_type = func_type.handle - value = core.BNGetParameterValueAtLowLevelILInstruction(self.handle, instr, func_type, i) - result = RegisterValue(self.arch, value) - return result + def request_debug_report(self, name:str) -> None: + """ + ``request_debug_report`` can generate internal debug reports for a variety of analysis. + Current list of possible values include: - def get_regs_read_by(self, addr, arch=None): - if arch is None: - arch = self.arch - count = ctypes.c_ulonglong() - regs = core.BNGetRegistersReadByInstruction(self.handle, arch.handle, addr, count) - result = [] - for i in range(0, count.value): - result.append(arch.get_reg_name(regs[i])) - core.BNFreeRegisterList(regs) - return result + - mlil_translator + - stack_adjust_graph + - high_level_il - def get_regs_written_by(self, addr, arch=None): - if arch is None: - arch = self.arch - count = ctypes.c_ulonglong() - regs = core.BNGetRegistersWrittenByInstruction(self.handle, arch.handle, addr, count) - result = [] - for i in range(0, count.value): - result.append(arch.get_reg_name(regs[i])) - core.BNFreeRegisterList(regs) - return result + :param str name: Name of the debug report + :rtype: None + """ + core.BNRequestFunctionDebugReport(self.handle, name) + self.view.update_analysis() - def get_stack_vars_referenced_by(self, addr, arch=None): - if arch is None: - arch = self.arch - count = ctypes.c_ulonglong() - refs = core.BNGetStackVariablesReferencedByInstruction(self.handle, arch.handle, addr, count) + @property + def call_sites(self) -> List['binaryview.ReferenceSource']: + """ + ``call_sites`` returns a list of possible call sites contained in this function. + This includes ordinary calls, tail calls, and indirect jumps. Not all of the returned call sites + are necessarily true call sites; some may simply be unresolved indirect jumps, for example. + + :return: List of References that represent the sources of possible calls in this function + :rtype: list(ReferenceSource) + """ + count = ctypes.c_ulonglong(0) + refs = core.BNGetFunctionCallSites(self.handle, count) + assert refs is not None, "core.BNGetFunctionCallSites returned None" result = [] for i in range(0, count.value): - 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].size)) - core.BNFreeStackVariableReferenceList(refs, count.value) + if refs[i].func: + func = Function(self.view, core.BNNewFunctionReference(refs[i].func)) + else: + func = None + if refs[i].arch: + arch = architecture.CoreArchitecture._from_cache(refs[i].arch) + else: + arch = None + addr = refs[i].addr + result.append(binaryview.ReferenceSource(func, arch, addr)) + core.BNFreeCodeReferences(refs, count.value) return result - def get_constants_referenced_by(self, addr, arch=None): - if arch is None: - arch = self.arch - count = ctypes.c_ulonglong() - refs = core.BNGetConstantsReferencedByInstruction(self.handle, arch.handle, addr, count) + @property + def callees(self) -> List['Function']: + """ + ``callees`` returns a list of functions that this function calls + This does not include the address of those calls, rather just the function objects themselves. Use :py:meth:`call_sites` to identify the location of these calls. + + :return: List of Functions that this function calls + :rtype: list(Function) + """ + called = [] + for callee_addr in self.callee_addresses: + func = self.view.get_function_at(callee_addr, self.platform) + if func is not None: + called.append(func) + return called + + @property + def callee_addresses(self) -> List[int]: + """ + ``callee_addressses`` returns a list of start addresses for functions that call this function. + Does not point to the actual address where the call occurs, just the start of the function that contains the reference. + + :return: List of start addresess for Functions that call this function + :rtype: list(int) + """ result = [] - for i in range(0, count.value): - result.append(ConstantReference(refs[i].value, refs[i].size, refs[i].pointer, refs[i].intermediate)) - core.BNFreeConstantReferenceList(refs) + for ref in self.call_sites: + result.extend(self.view.get_callees(ref.address, ref.function, ref.arch)) return result - def get_lifted_il_at(self, addr, arch=None): - if arch is None: - arch = self.arch + @property + def callers(self) -> List[int]: + """ + ``callers`` returns a list of functions that call this function + Does not point to the actual address where the call occurs, just the start of the function that contains the call. - idx = core.BNGetLiftedILForInstruction(self.handle, arch.handle, addr) + :return: List of start addresess for Functions that call this function + :rtype: list(int) + """ + functions = [] + for ref in self.view.get_code_refs(self.start): + if ref.function is not None: + functions.append(ref.function) + return functions - if idx == len(self.lifted_il): + @property + def workflow(self): + handle = core.BNGetWorkflowForFunction(self.handle) + if handle is None: return None + return workflow.Workflow(handle = handle) - return self.lifted_il[idx] - - def get_lifted_ils_at(self, addr, arch=None): + def get_mlil_var_refs(self, var:'variable.Variable') -> List[ILReferenceSource]: """ - ``get_lifted_ils_at`` gets the Lifted IL Instruction(s) corresponding to the given virtual address + ``get_mlil_var_refs`` returns a list of ILReferenceSource objects (IL xrefs or cross-references) + that reference the given variable. The variable is a local variable that can be either on the stack, + in a register, or in a flag. + This function is related to get_hlil_var_refs(), which returns variable references collected + from HLIL. The two can be different in several cases, e.g., multiple variables in MLIL can be merged + into a single variable in HLIL. - :param int addr: virtual address of the function to be queried - :param Architecture arch: (optional) Architecture for the given function - :rtype: list(LowLevelILInstruction) + :param Variable var: Variable for which to query the xref + :return: List of IL References for the given variable + :rtype: list(ILReferenceSource) :Example: - >>> func = bv.functions[0] - >>> func.get_lifted_ils_at(func.start) - [] + >>> var = current_mlil[0].operands[0] + >>> current_function.get_mlil_var_refs(var) """ - if arch is None: - arch = self.arch - count = ctypes.c_ulonglong() - instrs = core.BNGetLiftedILInstructionsForAddress(self.handle, arch.handle, addr, count) - result = [] - for i in range(0, count.value): - result.append(self.lifted_il[instrs[i]]) - core.BNFreeILInstructionList(instrs) - return result - - def get_lifted_il_flag_uses_for_definition(self, i, flag): - flag = self.arch.get_flag_index(flag) - count = ctypes.c_ulonglong() - instrs = core.BNGetLiftedILFlagUsesForDefinition(self.handle, i, flag, count) - result = [] - for i in range(0, count.value): - result.append(instrs[i]) - core.BNFreeILInstructionList(instrs) - return result - - def get_lifted_il_flag_definitions_for_use(self, i, flag): - flag = self.arch.get_flag_index(flag) - count = ctypes.c_ulonglong() - instrs = core.BNGetLiftedILFlagDefinitionsForUse(self.handle, i, flag, count) - result = [] - for i in range(0, count.value): - result.append(instrs[i]) - core.BNFreeILInstructionList(instrs) - return result - - def get_flags_read_by_lifted_il_instruction(self, i): - count = ctypes.c_ulonglong() - flags = core.BNGetFlagsReadByLiftedILInstruction(self.handle, i, count) + count = ctypes.c_ulonglong(0) + var_data = core.BNVariable() + var_data.type = var.source_type + var_data.index = var.index + var_data.storage = var.storage + refs = core.BNGetMediumLevelILVariableReferences(self.handle, var_data, count) + assert refs is not None, "core.BNGetMediumLevelILVariableReferences returned None" result = [] for i in range(0, count.value): - result.append(self.arch._flags_by_index[flags[i]]) - core.BNFreeRegisterList(flags) - return result + if refs[i].func: + func = Function(self.view, core.BNNewFunctionReference(refs[i].func)) + else: + func = None + if refs[i].arch: + arch = architecture.CoreArchitecture._from_cache(refs[i].arch) + else: + arch = None - def get_flags_written_by_lifted_il_instruction(self, i): - count = ctypes.c_ulonglong() - flags = core.BNGetFlagsWrittenByLiftedILInstruction(self.handle, i, count) - result = [] - for i in range(0, count.value): - result.append(self.arch._flags_by_index[flags[i]]) - core.BNFreeRegisterList(flags) + result.append(ILReferenceSource( + func, arch, refs[i].addr, refs[i].type, refs[i].exprId)) + core.BNFreeILReferences(refs, count.value) return result - def create_graph(self, graph_type = FunctionGraphType.NormalFunctionGraph, settings = None): - if settings is not None: - settings_obj = settings.handle - else: - settings_obj = None - return binaryninja.flowgraph.CoreFlowGraph(core.BNCreateFunctionGraph(self.handle, graph_type, settings_obj)) - - def apply_imported_types(self, sym, type=None): - core.BNApplyImportedTypes(self.handle, sym.handle, None if type is None else type.handle) - - def apply_auto_discovered_type(self, func_type): - core.BNApplyAutoDiscoveredFunctionType(self.handle, func_type.handle) - - def set_auto_indirect_branches(self, source, branches, source_arch=None): - if source_arch is None: - source_arch = self.arch - branch_list = (core.BNArchitectureAndAddress * len(branches))() - for i in range(len(branches)): - branch_list[i].arch = branches[i][0].handle - branch_list[i].address = branches[i][1] - core.BNSetAutoIndirectBranches(self.handle, source_arch.handle, source, branch_list, len(branches)) - - def set_user_indirect_branches(self, source, branches, source_arch=None): - if source_arch is None: - source_arch = self.arch - branch_list = (core.BNArchitectureAndAddress * len(branches))() - for i in range(len(branches)): - branch_list[i].arch = branches[i][0].handle - branch_list[i].address = branches[i][1] - core.BNSetUserIndirectBranches(self.handle, source_arch.handle, source, branch_list, len(branches)) + def get_mlil_var_refs_from(self, addr:int, length:int=None, arch:Optional['architecture.Architecture']=None) -> \ + List[VariableReferenceSource]: + """ + ``get_mlil_var_refs_from`` returns a list of variables referenced by code in the function ``func``, + of the architecture ``arch``, and at the address ``addr``. If no function is specified, references from + all functions and containing the address will be returned. If no architecture is specified, the + architecture of the function will be used. + This function is related to get_hlil_var_refs_from(), which returns variable references collected + from HLIL. The two can be different in several cases, e.g., multiple variables in MLIL can be merged + into a single variable in HLIL. - def get_indirect_branches_at(self, addr, arch=None): - if arch is None: - arch = self.arch - count = ctypes.c_ulonglong() - branches = core.BNGetIndirectBranchesAt(self.handle, arch.handle, addr, count) + :param int addr: virtual address to query for variable references + :param int length: optional length of query + :param Architecture arch: optional architecture of query + :return: list of variable reference sources + :rtype: list(VariableReferenceSource) + """ result = [] - for i in range(count.value): - result.append(IndirectBranchInfo(binaryninja.architecture.CoreArchitecture._from_cache(branches[i].sourceArch), branches[i].sourceAddr, binaryninja.architecture.CoreArchitecture._from_cache(branches[i].destArch), branches[i].destAddr, branches[i].autoDefined)) - core.BNFreeIndirectBranchList(branches) - return result + count = ctypes.c_ulonglong(0) - def get_block_annotations(self, addr, arch=None): if arch is None: + if self.arch is None: + raise Exception(f"Can't call {_function_name_()} for function with no architecture specified") arch = self.arch - count = ctypes.c_ulonglong(0) - lines = core.BNGetFunctionBlockAnnotations(self.handle, arch.handle, addr, count) - result = [] - for i in range(count.value): - result.append(InstructionTextToken.get_instruction_lines(lines[i].tokens, lines[i].count)) - core.BNFreeInstructionTextLines(lines, count.value) - return result - - def set_auto_type(self, value): - core.BNSetFunctionAutoType(self.handle, value.handle) - - 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_return_regs(self, value): - regs = core.BNRegisterSetWithConfidence() - regs.regs = (ctypes.c_uint * len(value))() - regs.count = len(value) - for i in range(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.BNSetAutoFunctionReturnRegisters(self.handle, regs) - - 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 range(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 + if length is None: + refs = core.BNGetMediumLevelILVariableReferencesFrom(self.handle, arch.handle, addr, count) + assert refs is not None, "core.BNGetMediumLevelILVariableReferencesFrom returned None" else: - var_conf.confidence = types.max_confidence - core.BNSetAutoFunctionParameterVariables(self.handle, var_conf) + refs = core.BNGetMediumLevelILVariableReferencesInRange(self.handle, arch.handle, addr, length, count) + assert refs is not None, "core.BNGetMediumLevelILVariableReferencesInRange returned None" + for i in range(0, count.value): + var = variable.Variable(self, refs[i].var.type, refs[i].var.index, refs[i].var.storage) + if refs[i].source.func: + func = Function(self.view, core.BNNewFunctionReference(refs[i].source.func)) + else: + func = None + if refs[i].source.arch: + _arch = architecture.CoreArchitecture._from_cache(refs[i].source.arch) + else: + _arch = arch - 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) + src = ILReferenceSource(func, _arch, refs[i].source.addr, refs[i].source.type, refs[i].source.exprId) + result.append(VariableReferenceSource(var, src)) + core.BNFreeVariableReferenceSourceList(refs, count.value) + return result - 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 get_hlil_var_refs(self, var:'variable.Variable') -> List[ILReferenceSource]: + """ + ``get_hlil_var_refs`` returns a list of ILReferenceSource objects (IL xrefs or cross-references) + that reference the given variable. The variable is a local variable that can be either on the stack, + in a register, or in a flag. - def set_auto_stack_adjustment(self, value): - oc = core.BNOffsetWithConfidence() - oc.value = int(value) - if hasattr(value, 'confidence'): - oc.confidence = value.confidence - else: - oc.confidence = types.max_confidence - core.BNSetAutoFunctionStackAdjustment(self.handle, oc) + :param Variable var: Variable for which to query the xref + :return: List of IL References for the given variable + :rtype: list(ILReferenceSource) + :Example: - def set_auto_reg_stack_adjustments(self, value): - adjust = (core.BNRegisterStackAdjustment * len(value))() - i = 0 - for reg_stack in value.keys(): - adjust[i].regStack = self.arch.get_reg_stack_index(reg_stack) - if isinstance(value[reg_stack], types.RegisterStackAdjustmentWithConfidence): - adjust[i].adjustment = value[reg_stack].value - adjust[i].confidence = value[reg_stack].confidence + >>> var = current_hlil[0].operands[0] + >>> current_function.get_hlil_var_refs(var) + """ + count = ctypes.c_ulonglong(0) + var_data = core.BNVariable() + var_data.type = var.source_type + var_data.index = var.index + var_data.storage = var.storage + refs = core.BNGetHighLevelILVariableReferences(self.handle, var_data, count) + assert refs is not None, "core.BNGetHighLevelILVariableReferences returned None" + result = [] + for i in range(0, count.value): + if refs[i].func: + func = Function(self.view, core.BNNewFunctionReference(refs[i].func)) else: - adjust[i].adjustment = value[reg_stack] - adjust[i].confidence = types.max_confidence - i += 1 - core.BNSetAutoFunctionRegisterStackAdjustments(self.handle, adjust, len(value)) - - def set_auto_clobbered_regs(self, value): - regs = core.BNRegisterSetWithConfidence() - regs.regs = (ctypes.c_uint * len(value))() - regs.count = len(value) - for i in range(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 - return IntegerDisplayType(core.BNGetIntegerConstantDisplayType(self.handle, arch.handle, instr_addr, value, operand)) + func = None + if refs[i].arch: + arch = architecture.CoreArchitecture._from_cache(refs[i].arch) + else: + arch = None + result.append(ILReferenceSource( + func, arch, refs[i].addr, refs[i].type, refs[i].exprId)) + core.BNFreeILReferences(refs, count.value) + return result - def set_int_display_type(self, instr_addr, value, operand, display_type, arch=None): + def get_hlil_var_refs_from(self, addr:int, length:int=None, arch:Optional['architecture.Architecture']=None) -> \ + List[VariableReferenceSource]: """ + ``get_hlil_var_refs_from`` returns a list of variables referenced by code in the function ``func``, + of the architecture ``arch``, and at the address ``addr``. If no function is specified, references from + all functions and containing the address will be returned. If no architecture is specified, the + architecture of the function will be used. - :param int instr_addr: - :param int value: - :param int operand: - :param enums.IntegerDisplayType display_type: - :param Architecture arch: (optional) + :param int addr: virtual address to query for variable references + :param int length: optional length of query + :param Architecture arch: optional architecture of query + :return: list of variables reference sources + :rtype: list(VariableReferenceSource) """ + result = [] + count = ctypes.c_ulonglong(0) if arch is None: + if self.arch is None: + raise Exception("can not get_block_annotations if Function.arch is None") arch = self.arch - if isinstance(display_type, str): - display_type = IntegerDisplayType[display_type] - core.BNSetIntegerConstantDisplayType(self.handle, arch.handle, instr_addr, value, operand, display_type) - - def reanalyze(self): - """ - ``reanalyze`` causes this functions to be reanalyzed. This function does not wait for the analysis to finish. - - :rtype: None - """ - core.BNReanalyzeFunction(self.handle) + if length is None: + refs = core.BNGetHighLevelILVariableReferencesFrom(self.handle, arch.handle, addr, count) + assert refs is not None, "core.BNGetHighLevelILVariableReferencesFrom returned None" + else: + refs = core.BNGetHighLevelILVariableReferencesInRange(self.handle, arch.handle, addr, length, count) + assert refs is not None, "core.BNGetHighLevelILVariableReferencesInRange returned None" + for i in range(0, count.value): + var = variable.Variable(self, refs[i].var.type, refs[i].var.index, refs[i].var.storage) + if refs[i].source.func: + func = Function(self.view, core.BNNewFunctionReference(refs[i].source.func)) + else: + func = None + if refs[i].source.arch: + _arch = architecture.CoreArchitecture._from_cache(refs[i].source.arch) + else: + _arch = arch - @property - def workflow(self): - handle = core.BNGetWorkflowForFunction(self.handle) - if handle is None: - return None - return binaryninja.Workflow(handle = handle) - - def request_advanced_analysis_data(self): - core.BNRequestAdvancedFunctionAnalysisData(self.handle) - self._advanced_analysis_requests += 1 - - def release_advanced_analysis_data(self): - core.BNReleaseAdvancedFunctionAnalysisData(self.handle) - self._advanced_analysis_requests -= 1 - - def get_basic_block_at(self, addr, arch=None): - """ - ``get_basic_block_at`` returns the BasicBlock of the optionally specified Architecture ``arch`` at the given - address ``addr``. - - :param int addr: Address of the BasicBlock to retrieve. - :param Architecture arch: (optional) Architecture of the basic block if different from the Function's self.arch - :Example: - >>> current_function.get_basic_block_at(current_function.start) - - """ - if arch is None: - arch = self.arch - block = core.BNGetFunctionBasicBlockAtAddress(self.handle, arch.handle, addr) - if not block: - return None - return binaryninja.basicblock.BasicBlock(block, self._view) - - def get_instr_highlight(self, addr, arch=None): - """ - :Example: - >>> current_function.set_user_instr_highlight(here, highlight.HighlightColor(red=0xff, blue=0xff, green=0)) - >>> current_function.get_instr_highlight(here) - - """ - if arch is None: - arch = self.arch - color = core.BNGetInstructionHighlight(self.handle, arch.handle, addr) - if color.style == HighlightColorStyle.StandardHighlightColor: - return highlight.HighlightColor(color = color.color, alpha = color.alpha) - elif color.style == HighlightColorStyle.MixedHighlightColor: - return highlight.HighlightColor(color = color.color, mix_color = color.mixColor, mix = color.mix, alpha = color.alpha) - elif color.style == HighlightColorStyle.CustomHighlightColor: - return highlight.HighlightColor(red = color.r, green = color.g, blue = color.b, alpha = color.alpha) - return highlight.HighlightColor(color = HighlightStandardColor.NoHighlightColor) - - def set_auto_instr_highlight(self, addr, color, arch=None): - """ - ``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. - - :param int addr: virtual address of the instruction to be highlighted - :param HighlightStandardColor|highlight.HighlightColor color: Color value to use for highlighting - :param Architecture arch: (optional) Architecture of the instruction if different from self.arch - """ - if arch is None: - arch = self.arch - if not isinstance(color, HighlightStandardColor) and not isinstance(color, highlight.HighlightColor): - raise ValueError("Specified color is not one of HighlightStandardColor, highlight.HighlightColor") - if isinstance(color, HighlightStandardColor): - color = highlight.HighlightColor(color = color) - core.BNSetAutoInstructionHighlight(self.handle, arch.handle, addr, color._get_core_struct()) - - def set_user_instr_highlight(self, addr, color, arch=None): - """ - ``set_user_instr_highlight`` highlights the instruction at the specified address with the supplied color - - :param int addr: virtual address of the instruction to be highlighted - :param HighlightStandardColor|highlight.HighlightColor color: Color value to use for highlighting - :param Architecture arch: (optional) Architecture of the instruction if different from self.arch - :Example: - - >>> current_function.set_user_instr_highlight(here, HighlightStandardColor.BlueHighlightColor) - >>> current_function.set_user_instr_highlight(here, highlight.HighlightColor(red=0xff, blue=0xff, green=0)) - """ - if arch is None: - arch = self.arch - if not isinstance(color, HighlightStandardColor) and not isinstance(color, highlight.HighlightColor): - raise ValueError("Specified color is not one of HighlightStandardColor, highlight.HighlightColor") - if isinstance(color, HighlightStandardColor): - color = highlight.HighlightColor(color) - core.BNSetUserInstructionHighlight(self.handle, arch.handle, addr, color._get_core_struct()) - - def create_auto_stack_var(self, offset, var_type, 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): - 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) - - def delete_user_stack_var(self, offset): - core.BNDeleteUserStackVariable(self.handle, offset) - - def create_auto_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 - 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 - 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() - var_data.type = var.source_type - var_data.index = var.index - var_data.storage = var.storage - core.BNDeleteAutoVariable(self.handle, var_data) - - def delete_user_var(self, var): - var_data = core.BNVariable() - var_data.type = var.source_type - var_data.index = var.index - var_data.storage = var.storage - core.BNDeleteUserVariable(self.handle, var_data) - - def is_var_user_defined(self, var): - var_data = core.BNVariable() - var_data.type = var.source_type - var_data.index = var.index - var_data.storage = var.storage - return core.BNIsVariableUserDefined(self.handle, var_data) - - def get_stack_var_at_frame_offset(self, offset, addr, arch=None): - if arch is None: - arch = self.arch - found_var = core.BNVariableNameAndType() - 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), 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 range(0, count.value): - addr = lines[i].addr - color = highlight.HighlightColor._from_core_struct(lines[i].highlight) - tokens = InstructionTextToken.get_instruction_lines(lines[i].tokens, lines[i].count) - result.append(DisassemblyTextLine(tokens, addr, color = color)) - 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) - - def set_auto_call_stack_adjustment(self, addr, adjust, arch=None): - if arch is None: - arch = self.arch - if not isinstance(adjust, types.SizeWithConfidence): - adjust = types.SizeWithConfidence(adjust) - core.BNSetAutoCallStackAdjustment(self.handle, arch.handle, addr, adjust.value, adjust.confidence) - - def set_auto_call_reg_stack_adjustment(self, addr, adjust, arch=None): - if arch is None: - arch = self.arch - adjust_buf = (core.BNRegisterStackAdjustment * len(adjust))() - i = 0 - for reg_stack in adjust.keys(): - adjust_buf[i].regStack = arch.get_reg_stack_index(reg_stack) - value = adjust[reg_stack] - if not isinstance(value, types.RegisterStackAdjustmentWithConfidence): - value = types.RegisterStackAdjustmentWithConfidence(value) - adjust_buf[i].adjustment = value.value - adjust_buf[i].confidence = value.confidence - i += 1 - core.BNSetAutoCallRegisterStackAdjustment(self.handle, arch.handle, addr, adjust_buf, len(adjust)) - - def set_auto_call_reg_stack_adjustment_for_reg_stack(self, addr, reg_stack, adjust, arch=None): - if arch is None: - arch = self.arch - reg_stack = arch.get_reg_stack_index(reg_stack) - if not isinstance(adjust, types.RegisterStackAdjustmentWithConfidence): - adjust = types.RegisterStackAdjustmentWithConfidence(adjust) - core.BNSetAutoCallRegisterStackAdjustmentForRegisterStack(self.handle, arch.handle, addr, reg_stack, - adjust.value, adjust.confidence) - - def set_call_type_adjustment(self, addr, adjust_type, arch=None): - if arch is None: - arch = self.arch - if adjust_type is None: - tc = None - else: - tc = core.BNTypeWithConfidence() - tc.type = adjust_type.handle - tc.confidence = adjust_type.confidence - core.BNSetUserCallTypeAdjustment(self.handle, arch.handle, addr, tc) - - def set_call_stack_adjustment(self, addr, adjust, arch=None): - if arch is None: - arch = self.arch - if not isinstance(adjust, types.SizeWithConfidence): - adjust = types.SizeWithConfidence(adjust) - core.BNSetUserCallStackAdjustment(self.handle, arch.handle, addr, adjust.value, adjust.confidence) - - def set_call_reg_stack_adjustment(self, addr, adjust, arch=None): - if arch is None: - arch = self.arch - adjust_buf = (core.BNRegisterStackAdjustment * len(adjust))() - i = 0 - for reg_stack in adjust.keys(): - adjust_buf[i].regStack = arch.get_reg_stack_index(reg_stack) - value = adjust[reg_stack] - if not isinstance(value, types.RegisterStackAdjustmentWithConfidence): - value = types.RegisterStackAdjustmentWithConfidence(value) - adjust_buf[i].adjustment = value.value - adjust_buf[i].confidence = value.confidence - i += 1 - core.BNSetUserCallRegisterStackAdjustment(self.handle, arch.handle, addr, adjust_buf, len(adjust)) - - def set_call_reg_stack_adjustment_for_reg_stack(self, addr, reg_stack, adjust, arch=None): - if arch is None: - arch = self.arch - reg_stack = arch.get_reg_stack_index(reg_stack) - if not isinstance(adjust, types.RegisterStackAdjustmentWithConfidence): - adjust = types.RegisterStackAdjustmentWithConfidence(adjust) - core.BNSetUserCallRegisterStackAdjustmentForRegisterStack(self.handle, arch.handle, addr, reg_stack, - adjust.value, adjust.confidence) - - def get_call_type_adjustment(self, addr, arch=None): - if arch is None: - arch = self.arch - result = core.BNGetCallTypeAdjustment(self.handle, arch.handle, addr) - if result.type: - platform = self.platform - return types.Type(result.type, platform = platform, confidence = result.confidence) - return None - - def get_call_stack_adjustment(self, addr, arch=None): - if arch is None: - arch = self.arch - result = core.BNGetCallStackAdjustment(self.handle, arch.handle, addr) - return types.SizeWithConfidence(result.value, confidence = result.confidence) - - def get_call_reg_stack_adjustment(self, addr, arch=None): - if arch is None: - arch = self.arch - count = ctypes.c_ulonglong() - adjust = core.BNGetCallRegisterStackAdjustment(self.handle, arch.handle, addr, count) - result = {} - for i in range(0, count.value): - result[arch.get_reg_stack_name(adjust[i].regStack)] = types.RegisterStackAdjustmentWithConfidence( - adjust[i].adjustment, confidence = adjust[i].confidence) - core.BNFreeRegisterStackAdjustments(adjust) - return result - - def get_call_reg_stack_adjustment_for_reg_stack(self, addr, reg_stack, arch=None): - if arch is None: - arch = self.arch - reg_stack = arch.get_reg_stack_index(reg_stack) - adjust = core.BNGetCallRegisterStackAdjustmentForRegisterStack(self.handle, arch.handle, addr, reg_stack) - result = types.RegisterStackAdjustmentWithConfidence(adjust.adjustment, confidence = adjust.confidence) - return result - - def is_call_instruction(self, addr, arch=None): - if arch is None: - arch = self.arch - return core.BNIsCallInstruction(self.handle, arch.handle, addr) - - def set_user_var_value(self, var, def_addr, value): - """ - `set_user_var_value` allows the user to specify a PossibleValueSet value for an MLIL variable at its \ - definition site. - - .. warning:: Setting the variable value, triggers a reanalysis of the function and allows the dataflow \ - to compute and propagate values which depend on the current variable. This implies that branch conditions \ - whose values can be determined statically will be computed, leading to potential branch elimination at \ - the HLIL layer. - - :param Variable var: Variable for which the value is to be set - :param int def_addr: Address of the definition site of the variable - :param PossibleValueSet value: Informed value of the variable - :rtype: None - - :Example: - - >>> var = current_mlil[0].operands[0] - >>> def_site = 0x40108d - >>> value = PossibleValueSet.constant(5) - >>> current_function.set_user_var_value(var, def_site, value) - """ - var_defs = self.mlil.get_var_definitions(var) - if var_defs is None: - raise ValueError("Could not get definition for Variable") - found = False - for site in var_defs: - if site.address == def_addr: - found = True - break - if not found: - raise ValueError("No definition for Variable found at given address") - def_site = core.BNArchitectureAndAddress() - def_site.arch = self.arch.handle - def_site.address = def_addr - - var_data = core.BNVariable() - var_data.type = var.source_type - var_data.index = var.index - var_data.storage = var.storage - core.BNSetUserVariableValue(self.handle, var_data, def_site, value._to_api_object()) - - def clear_user_var_value(self, var, def_addr): - """ - Clears a previously defined user variable value. - - :param Variable var: Variable for which the value was informed - :param int def_addr: Address of the definition site of the variable - :rtype: None - """ - var_defs = self.mlil.get_var_definitions(var) - if var_defs is None: - raise ValueError("Could not get definition for Variable") - found = False - for site in var_defs: - if site.address == def_addr: - found = True - break - if not found: - raise ValueError("No definition for Variable found at given address") - def_site = core.BNArchitectureAndAddress() - def_site.arch = self.arch.handle - def_site.address = def_addr - - var_data = core.BNVariable() - var_data.type = var.source_type - var_data.index = var.index - var_data.storage = var.storage - core.BNClearUserVariableValue(self.handle, var_data, def_site) - - def get_all_user_var_values(self): - """ - Returns a map of current defined user variable values. - - :returns: Map of user current defined user variable values and their definition sites. - :type: dict of (Variable, dict of (ArchAndAddr, PossibleValueSet)) - """ - count = ctypes.c_ulonglong(0) - var_values = core.BNGetAllUserVariableValues(self.handle, count) - result = {} - i = 0 - for i in range(count.value): - var_val = var_values[i] - var = Variable(self, var_val.var.type, var_val.var.index, var_val.var.storage) - if var not in result: - result[var] = {} - def_site = ArchAndAddr(var_val.defSite.arch, var_val.defSite.address) - result[var][def_site] = PossibleValueSet(def_site.arch, var_val.value) - core.BNFreeUserVariableValues(var_values) - return result - - def clear_all_user_var_values(self): - """ - Clear all user defined variable values. - - :rtype: None - """ - all_values = self.get_all_user_var_values() - for var in all_values: - for def_site in all_values[var]: - self.clear_user_var_value(var, def_site.addr) - - def request_debug_report(self, name): - """ - ``request_debug_report`` can generate interanl debug reports for a variety of analysis. - Current list of possible values include: - - - mlil_translator - - stack_adjust_graph - - high_level_il - - :param str name: Name of the debug report - :rtype: None - """ - core.BNRequestFunctionDebugReport(self.handle, name) - self.view.update_analysis() - - @property - def call_sites(self): - """ - ``call_sites`` returns a list of possible call sites contained in this function. - This includes ordinary calls, tail calls, and indirect jumps. Not all of the returned call sites - are necessarily true call sites; some may simply be unresolved indirect jumps, for example. - - :return: List of References that represent the sources of possible calls in this function - :rtype: list(ReferenceSource) - """ - count = ctypes.c_ulonglong(0) - refs = core.BNGetFunctionCallSites(self.handle, count) - result = [] - for i in range(0, count.value): - if refs[i].func: - func = binaryninja.function.Function(self, core.BNNewFunctionReference(refs[i].func)) - else: - func = None - if refs[i].arch: - arch = binaryninja.architecture.CoreArchitecture._from_cache(refs[i].arch) - else: - arch = None - addr = refs[i].addr - result.append(binaryninja.architecture.ReferenceSource(func, arch, addr)) - core.BNFreeCodeReferences(refs, count.value) - return result - - @property - def callees(self): - """ - ``callees`` returns a list of functions that this function calls - This does not include the address of those calls, rather just the function objects themselves. Use :py:meth:`call_sites` to identify the location of these calls. - - :return: List of Functions that this function calls - :rtype: list(Function) - """ - called = [] - for callee_addr in self.callee_addresses: - func = self.view.get_function_at(callee_addr, self.platform) - if func is not None: - called.append(func) - return called - - @property - def callee_addresses(self): - """ - ``callee_addressses`` returns a list of start addresses for functions that call this function. - Does not point to the actual address where the call occurs, just the start of the function that contains the reference. - - :return: List of start addresess for Functions that call this function - :rtype: list(int) - """ - result = [] - for ref in self.call_sites: - result.extend(self.view.get_callees(ref.address, ref.function, ref.arch)) - return result - - @property - def callers(self): - """ - ``callers`` returns a list of functions that call this function - Does not point to the actual address where the call occurs, just the start of the function that contains the call. - - :return: List of start addresess for Functions that call this function - :rtype: list(int) - """ - functions = [] - for ref in self.view.get_code_refs(self.start): - if ref.function is not None: - functions.append(ref.function) - return functions - - def get_mlil_var_refs(self, var): - """ - ``get_mlil_var_refs`` returns a list of ILReferenceSource objects (IL xrefs or cross-references) - that reference the given variable. The variable is a local variable that can be either on the stack, - in a register, or in a flag. - This function is related to get_hlil_var_refs(), which returns variable references collected - from HLIL. The two can be different in several cases, e.g., multiple variables in MLIL can be merged - into a single variable in HLIL. - - :param Variable var: Variable for which to query the xref - :return: List of IL References for the given variable - :rtype: list(ILReferenceSource) - :Example: - - >>> var = current_mlil[0].operands[0] - >>> current_function.get_mlil_var_refs(var) - """ - count = ctypes.c_ulonglong(0) - var_data = core.BNVariable() - var_data.type = var.source_type - var_data.index = var.index - var_data.storage = var.storage - refs = core.BNGetMediumLevelILVariableReferences(self.handle, var_data, count) - result = [] - for i in range(0, count.value): - if refs[i].func: - func = binaryninja.function.Function(self, core.BNNewFunctionReference(refs[i].func)) - else: - func = None - if refs[i].arch: - arch = binaryninja.architecture.CoreArchitecture._from_cache(refs[i].arch) - else: - arch = None - - result.append(binaryninja.ILReferenceSource( - func, arch, refs[i].addr, refs[i].type, refs[i].exprId)) - core.BNFreeILReferences(refs, count.value) - return result - - def get_mlil_var_refs_from(self, addr, length = None, arch = None): - """ - ``get_mlil_var_refs_from`` returns a list of variables referenced by code in the function ``func``, - of the architecture ``arch``, and at the address ``addr``. If no function is specified, references from - all functions and containing the address will be returned. If no architecture is specified, the - architecture of the function will be used. - This function is related to get_hlil_var_refs_from(), which returns variable references collected - from HLIL. The two can be different in several cases, e.g., multiple variables in MLIL can be merged - into a single variable in HLIL. - - :param int addr: virtual address to query for variable references - :param int length: optional length of query - :param Architecture arch: optional architecture of query - :return: list of variable reference sources - :rtype: list(VariableReferenceSource) - """ - result = [] - count = ctypes.c_ulonglong(0) - if length is None: - refs = core.BNGetMediumLevelILVariableReferencesFrom(self.handle, self.arch.handle, addr, count) - else: - refs = core.BNGetMediumLevelILVariableReferencesInRange(self.handle, self.arch.handle, addr, length, count) - for i in range(0, count.value): - var = Variable(self, refs[i].var.type, refs[i].var.index, refs[i].var.storage) - if refs[i].source.func: - func = binaryninja.function.Function(self, core.BNNewFunctionReference(refs[i].source.func)) - else: - func = None - if refs[i].source.arch: - arch = binaryninja.architecture.CoreArchitecture._from_cache(refs[i].source.arch) - else: - arch = None - - src = ILReferenceSource(func, arch, refs[i].source.addr, refs[i].source.type, refs[i].source.exprId) - result.append(VariableReferenceSource(var, src)) - core.BNFreeVariableReferenceSourceList(refs, count.value) - return result - - def get_hlil_var_refs(self, var): - """ - ``get_hlil_var_refs`` returns a list of ILReferenceSource objects (IL xrefs or cross-references) - that reference the given variable. The variable is a local variable that can be either on the stack, - in a register, or in a flag. - - :param Variable var: Variable for which to query the xref - :return: List of IL References for the given variable - :rtype: list(ILReferenceSource) - :Example: - - >>> var = current_hlil[0].operands[0] - >>> current_function.get_hlil_var_refs(var) - """ - count = ctypes.c_ulonglong(0) - var_data = core.BNVariable() - var_data.type = var.source_type - var_data.index = var.index - var_data.storage = var.storage - refs = core.BNGetHighLevelILVariableReferences(self.handle, var_data, count) - result = [] - for i in range(0, count.value): - if refs[i].func: - func = binaryninja.function.Function(self, core.BNNewFunctionReference(refs[i].func)) - else: - func = None - if refs[i].arch: - arch = binaryninja.architecture.CoreArchitecture._from_cache(refs[i].arch) - else: - arch = None - result.append(binaryninja.ILReferenceSource( - func, arch, refs[i].addr, refs[i].type, refs[i].exprId)) - core.BNFreeILReferences(refs, count.value) - return result - - def get_hlil_var_refs_from(self, addr, length = None, arch = None): - """ - ``get_hlil_var_refs_from`` returns a list of variables referenced by code in the function ``func``, - of the architecture ``arch``, and at the address ``addr``. If no function is specified, references from - all functions and containing the address will be returned. If no architecture is specified, the - architecture of the function will be used. - - :param int addr: virtual address to query for variable references - :param int length: optional length of query - :param Architecture arch: optional architecture of query - :return: list of variables reference sources - :rtype: list(VariableReferenceSource) - """ - result = [] - count = ctypes.c_ulonglong(0) - if length is None: - refs = core.BNGetHighLevelILVariableReferencesFrom(self.handle, self.arch.handle, addr, count) - else: - refs = core.BNGetHighLevelILVariableReferencesInRange(self.handle, self.arch.handle, addr, length, count) - for i in range(0, count.value): - var = Variable(self, refs[i].var.type, refs[i].var.index, refs[i].var.storage) - if refs[i].source.func: - func = binaryninja.function.Function(self, core.BNNewFunctionReference(refs[i].source.func)) - else: - func = None - if refs[i].source.arch: - arch = binaryninja.architecture.CoreArchitecture._from_cache(refs[i].source.arch) - else: - arch = None - - src = ILReferenceSource(func, arch, refs[i].source.addr, refs[i].source.type, refs[i].source.exprId) - result.append(VariableReferenceSource(var, src)) - core.BNFreeVariableReferenceSourceList(refs, count.value) - return result - - def get_instruction_containing_address(self, addr, arch = None): - arch = self.arch if arch is None else arch - start = ctypes.c_uint64() - ret = core.BNGetInstructionContainingAddress(self.handle, arch.handle, addr,\ - start) - return ret, start.value - - -class AdvancedFunctionAnalysisDataRequestor(object): - def __init__(self, func = None): - self._function = func - if self._function is not None: - self._function.request_advanced_analysis_data() - - def __del__(self): - if self._function is not None: - self._function.release_advanced_analysis_data() - - @property - def function(self): - return self._function - - @function.setter - def function(self, func): - if self._function is not None: - self._function.release_advanced_analysis_data() - self._function = func - if self._function is not None: - self._function.request_advanced_analysis_data() - - def close(self): - if self._function is not None: - self._function.release_advanced_analysis_data() - self._function = None - - -class DisassemblyTextLine(object): - def __init__(self, tokens, address = None, il_instr = None, color = None): - self._address = address - self._tokens = tokens - self._il_instruction = il_instr - if color is None: - self._highlight = highlight.HighlightColor() - else: - if not isinstance(color, HighlightStandardColor) and not isinstance(color, highlight.HighlightColor): - raise ValueError("Specified color is not one of HighlightStandardColor, highlight.HighlightColor") - if isinstance(color, HighlightStandardColor): - color = highlight.HighlightColor(color) - self._highlight = color - - def __str__(self): - result = "" - for token in self.tokens: - result += token.text - return result - - def __repr__(self): - if self.address is None: - return str(self) - return "<%#x: %s>" % (self.address, str(self)) - - @property - def address(self): - """ """ - return self._address - - @address.setter - def address(self, value): - self._address = value - - @property - def tokens(self): - """ """ - return self._tokens - - @tokens.setter - def tokens(self, value): - self._tokens = value - - @property - def il_instruction(self): - """ """ - return self._il_instruction - - @il_instruction.setter - def il_instruction(self, value): - self._il_instruction = value - - @property - def highlight(self): - """ """ - return self._highlight - - @highlight.setter - def highlight(self, value): - self._highlight = value - - -class DisassemblySettings(object): - def __init__(self, handle = None): - if handle is None: - self.handle = core.BNCreateDisassemblySettings() - else: - self.handle = handle - - def __del__(self): - core.BNFreeDisassemblySettings(self.handle) - - @property - def width(self): - return core.BNGetDisassemblyWidth(self.handle) - - @width.setter - def width(self, value): - core.BNSetDisassemblyWidth(self.handle, value) - - @property - def max_symbol_width(self): - return core.BNGetDisassemblyMaximumSymbolWidth(self.handle) - - @max_symbol_width.setter - def max_symbol_width(self, value): - core.BNSetDisassemblyMaximumSymbolWidth(self.handle, value) - - def is_option_set(self, option): - if isinstance(option, str): - option = DisassemblyOption[option] - return core.BNIsDisassemblySettingsOptionSet(self.handle, option) - - def set_option(self, option, state = True): - if isinstance(option, str): - option = DisassemblyOption[option] - core.BNSetDisassemblySettingsOption(self.handle, option, state) - - -class RegisterInfo(object): - def __init__(self, full_width_reg, size, offset=0, extend=ImplicitRegisterExtend.NoExtend, index=None): - self._full_width_reg = full_width_reg - self._offset = offset - self._size = size - self._extend = extend - self._index = index - - def __repr__(self): - if self._extend == ImplicitRegisterExtend.ZeroExtendToFullWidth: - extend = ", zero extend" - elif self._extend == ImplicitRegisterExtend.SignExtendToFullWidth: - extend = ", sign extend" - else: - extend = "" - return "" % (self._size, self._offset, self._full_width_reg, extend) - - @property - def full_width_reg(self): - """ """ - return self._full_width_reg - - @full_width_reg.setter - def full_width_reg(self, value): - self._full_width_reg = value - - @property - def offset(self): - """ """ - return self._offset - - @offset.setter - def offset(self, value): - self._offset = value - - @property - def size(self): - """ """ - return self._size - - @size.setter - def size(self, value): - self._size = value - - @property - def extend(self): - """ """ - return self._extend - - @extend.setter - def extend(self, value): - self._extend = value - - @property - def index(self): - """ """ - return self._index - - @index.setter - def index(self, value): - self._index = value - - -class RegisterStackInfo(object): - def __init__(self, storage_regs, top_relative_regs, stack_top_reg, index=None): - self._storage_regs = storage_regs - self._top_relative_regs = top_relative_regs - self._stack_top_reg = stack_top_reg - self._index = index - - def __repr__(self): - return "" % (len(self._storage_regs), self._stack_top_reg) - - @property - def storage_regs(self): - """ """ - return self._storage_regs - - @storage_regs.setter - def storage_regs(self, value): - self._storage_regs = value - - @property - def top_relative_regs(self): - """ """ - return self._top_relative_regs - - @top_relative_regs.setter - def top_relative_regs(self, value): - self._top_relative_regs = value - - @property - def stack_top_reg(self): - """ """ - return self._stack_top_reg - - @stack_top_reg.setter - def stack_top_reg(self, value): - self._stack_top_reg = value - - @property - def index(self): - """ """ - return self._index - - @index.setter - def index(self, value): - self._index = value - - -class IntrinsicInput(object): - def __init__(self, type_obj, name=""): - self._name = name - self._type = type_obj - - def __repr__(self): - if len(self._name) == 0: - return "" % str(self._type) - return "" % (str(self._type), self._name) - - @property - def name(self): - """ """ - return self._name - - @name.setter - def name(self, value): - self._name = value - - @property - def type(self): - """ """ - return self._type - - @type.setter - def type(self, value): - self._type = value - - -class IntrinsicInfo(object): - def __init__(self, inputs, outputs, index=None): - self._inputs = inputs - self._outputs = outputs - self._index = index - - def __repr__(self): - return " %s>" % (repr(self._inputs), repr(self._outputs)) - - @property - def inputs(self): - """ """ - return self._inputs - - @inputs.setter - def inputs(self, value): - self._inputs = value - - @property - def outputs(self): - """ """ - return self._outputs - - @outputs.setter - def outputs(self, value): - self._outputs = value - - @property - def index(self): - """ """ - return self._index - - @index.setter - def index(self, value): - self._index = value - - -class InstructionBranch(object): - def __init__(self, branch_type, target = 0, arch = None): - self._type = branch_type - self._target = target - self._arch = arch - - def __repr__(self): - branch_type = self._type - if self._arch is not None: - return "<%s: %s@%#x>" % (branch_type.name, self._arch.name, self._target) - return "<%s: %#x>" % (branch_type, self._target) - - @property - def type(self): - """ """ - return self._type - - @type.setter - def type(self, value): - self._type = value - - @property - def target(self): - """ """ - return self._target - - @target.setter - def target(self, value): - self._target = value - - @property - def arch(self): - """ """ - return self._arch - - @arch.setter - def arch(self, value): - self._arch = value - - -class InstructionInfo(object): - def __init__(self): - self.length = 0 - self.arch_transition_by_target_addr = False - self.branch_delay = False - self.branches = [] - - def add_branch(self, branch_type, target = 0, arch = None): - self._branches.append(InstructionBranch(branch_type, target, arch)) - - def __len__(self): - return self._length - - def __repr__(self): - branch_delay = "" - if self._branch_delay: - branch_delay = ", delay slot" - return "" % (self._length, branch_delay, repr(self._branches)) - - @property - def length(self): - """ """ - return self._length - - @length.setter - def length(self, value): - self._length = value - - @property - def arch_transition_by_target_addr(self): - """ """ - return self._arch_transition_by_target_addr - - @arch_transition_by_target_addr.setter - def arch_transition_by_target_addr(self, value): - self._arch_transition_by_target_addr = value - - @property - def branch_delay(self): - """ """ - return self._branch_delay - - @branch_delay.setter - def branch_delay(self, value): - self._branch_delay = value - - @property - def branches(self): - """ """ - return self._branches - - @branches.setter - def branches(self, value): - self._branches = value - - -class InstructionTextToken(object): - """ - ``class InstructionTextToken`` is used to tell the core about the various components in the disassembly views. - - The below table is provided for documentation purposes but the complete list of TokenTypes is available at: :class:`!enums.InstructionTextTokenType`. Note that types marked as `Not emitted by architectures` are not intended to be used by Architectures during lifting. Rather, they are added by the core during analysis or display. UI plugins, however, may make use of them as appropriate. - - Uses of tokens include plugins that parse the output of an architecture (though parsing IL is recommended), or additionally, applying color schemes appropriately. - - ========================== ============================================ - InstructionTextTokenType Description - ========================== ============================================ - AddressDisplayToken **Not emitted by architectures** - AnnotationToken **Not emitted by architectures** - ArgumentNameToken **Not emitted by architectures** - BeginMemoryOperandToken The start of memory operand - CharacterConstantToken A printable character - CodeRelativeAddressToken **Not emitted by architectures** - CodeSymbolToken **Not emitted by architectures** - DataSymbolToken **Not emitted by architectures** - EndMemoryOperandToken The end of a memory operand - ExternalSymbolToken **Not emitted by architectures** - FieldNameToken **Not emitted by architectures** - FloatingPointToken Floating point number - HexDumpByteValueToken **Not emitted by architectures** - HexDumpInvalidByteToken **Not emitted by architectures** - HexDumpSkippedByteToken **Not emitted by architectures** - HexDumpTextToken **Not emitted by architectures** - ImportToken **Not emitted by architectures** - IndirectImportToken **Not emitted by architectures** - InstructionToken The instruction mnemonic - IntegerToken Integers - KeywordToken **Not emitted by architectures** - LocalVariableToken **Not emitted by architectures** - NameSpaceSeparatorToken **Not emitted by architectures** - NameSpaceToken **Not emitted by architectures** - OpcodeToken **Not emitted by architectures** - OperandSeparatorToken The comma or delimiter that separates tokens - PossibleAddressToken Integers that are likely addresses - RegisterToken Registers - StringToken **Not emitted by architectures** - StructOffsetToken **Not emitted by architectures** - TagToken **Not emitted by architectures** - TextToken Used for anything not of another type. - CommentToken Comments - TypeNameToken **Not emitted by architectures** - ========================== ============================================ - - """ - def __init__(self, token_type, text, value = 0, size = 0, operand = 0xffffffff, - context = InstructionTextTokenContext.NoTokenContext, address = 0, confidence = types.max_confidence, typeNames=[], width=0): - 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 - self._typeNames = typeNames - self._width = width - if width == 0: - self._width = len(self._text) - - @classmethod - def get_instruction_lines(cls, tokens, count=0): - """ Helper method for converting between core.BNInstructionTextToken and InstructionTextToken lists """ - if isinstance(tokens, list): - result = (core.BNInstructionTextToken * len(tokens))() - for j in range(len(tokens)): - result[j].type = tokens[j].type - result[j].text = tokens[j].text - result[j].width = tokens[j].width - result[j].value = tokens[j].value - result[j].size = tokens[j].size - result[j].operand = tokens[j].operand - result[j].context = tokens[j].context - result[j].confidence = tokens[j].confidence - result[j].address = tokens[j].address - result[j].namesCount = len(tokens[j].typeNames) - result[j].typeNames = (ctypes.c_char_p * len(tokens[j].typeNames))() - for i in range(len(tokens[j].typeNames)): - result[j].typeNames[i] = binaryninja.cstr(tokens[j].typeNames[i]) - return result - - result = [] - for j in range(count): - token_type = InstructionTextTokenType(tokens[j].type) - text = tokens[j].text - if not isinstance(text, str): - text = text.decode("charmap") - width = tokens[j].width - value = tokens[j].value - size = tokens[j].size - operand = tokens[j].operand - context = tokens[j].context - confidence = tokens[j].confidence - address = tokens[j].address - typeNames = [] - for i in range(tokens[j].namesCount): - if not isinstance(tokens[j].typeNames[i], str): - typeNames.append(tokens[j].typeNames[i].decode("charmap")) - else: - typeNames.append(tokens[j].typeNames[i]) - result.append(InstructionTextToken(token_type, text, value, size, operand, context, address, confidence, typeNames, width)) + src = ILReferenceSource(func, _arch, refs[i].source.addr, refs[i].source.type, refs[i].source.exprId) + result.append(VariableReferenceSource(var, src)) + core.BNFreeVariableReferenceSourceList(refs, count.value) return result - def __str__(self): - return self._text - - def __repr__(self): - return repr(self._text) - - @property - def type(self): - """ """ - return self._type - - @type.setter - def type(self, value): - self._type = value + def get_instruction_containing_address(self, addr:int, arch:Optional['architecture.Architecture']=None) -> \ + Optional[int]: + if arch is None: + if self.arch is None: + raise Exception(f"Can't call {_function_name_()} for function with no architecture specified") + arch = self.arch - @property - def text(self): - """ """ - return self._text + start = ctypes.c_ulonglong() + if core.BNGetInstructionContainingAddress(self.handle, arch.handle, addr, start): + return start.value + return None - @text.setter - def text(self, value): - self._text = value - @property - def value(self): - """ """ - return self._value +class AdvancedFunctionAnalysisDataRequestor(object): + def __init__(self, func:'Function'=None): + self._function = func + if self._function is not None: + self._function.request_advanced_analysis_data() - @value.setter - def value(self, value): - self._value = value + def __del__(self): + if self._function is not None: + self._function.release_advanced_analysis_data() @property - def size(self): - """ """ - return self._size - - @size.setter - def size(self, value): - self._size = value + def function(self) -> Optional['Function']: + return self._function - @property - def operand(self): - """ """ - return self._operand + @function.setter + def function(self, func:'Function') -> None: + if self._function is not None: + self._function.release_advanced_analysis_data() + self._function = func + if self._function is not None: + self._function.request_advanced_analysis_data() - @operand.setter - def operand(self, value): - self._operand = value + def close(self) -> None: + if self._function is not None: + self._function.release_advanced_analysis_data() + self._function = None - @property - def context(self): - """ """ - return self._context - @context.setter - def context(self, value): - self._context = value +class DisassemblyTextLine(object): + def __init__(self, tokens:List['InstructionTextToken'], address:int=None, il_instr:ILInstructionType=None, + color:Union['_highlight.HighlightColor', HighlightStandardColor]=None): + self._address = address + self._tokens = tokens + self._il_instruction = il_instr + if color is None: + self._highlight = _highlight.HighlightColor() + else: + if not isinstance(color, HighlightStandardColor) and not isinstance(color, _highlight.HighlightColor): + raise ValueError("Specified color is not one of HighlightStandardColor, _highlight.HighlightColor") + if isinstance(color, HighlightStandardColor): + color = _highlight.HighlightColor(color) + self._highlight = color - @property - def confidence(self): - """ """ - return self._confidence + def __str__(self): + return "".join(map(str, self.tokens)) - @confidence.setter - def confidence(self, value): - self._confidence = value + def __repr__(self): + if self.address is None: + return str(self) + return "<%#x: %s>" % (self.address, str(self)) @property - def address(self): - """ """ + def address(self) -> Optional[int]: return self._address @address.setter - def address(self, value): + def address(self, value:int) -> None: self._address = value @property - def typeNames(self): - """ """ - return self._typeNames + def tokens(self) -> List['InstructionTextToken']: + return self._tokens - @typeNames.setter - def typeNames(self, value): - self._typeNames = value + @tokens.setter + def tokens(self, value:List['InstructionTextToken']) -> None: + self._tokens = value @property - def width(self): - return self._width + def il_instruction(self) -> Optional[ILInstructionType]: + return self._il_instruction + + @il_instruction.setter + def il_instruction(self, value:ILInstructionType) -> None: + self._il_instruction = value + + @property + def highlight(self) -> '_highlight.HighlightColor': + return self._highlight + @highlight.setter + def highlight(self, value:'_highlight.HighlightColor') -> None: + self._highlight = value class DisassemblyTextRenderer(object): - def __init__(self, func = None, settings = None, handle = None): + def __init__(self, func:AnyFunctionType=None, settings:'DisassemblySettings'=None, + handle:core.BNDisassemblySettings=None): if handle is None: if func is None: raise ValueError("function required for disassembly") @@ -4470,11 +3203,11 @@ class DisassemblyTextRenderer(object): settings_obj = settings.handle if isinstance(func, Function): self.handle = core.BNCreateDisassemblyTextRenderer(func.handle, settings_obj) - elif isinstance(func, binaryninja.lowlevelil.LowLevelILFunction): + elif isinstance(func, lowlevelil.LowLevelILFunction): self.handle = core.BNCreateLowLevelILDisassemblyTextRenderer(func.handle, settings_obj) - elif isinstance(func, binaryninja.mediumlevelil.MediumLevelILFunction): + elif isinstance(func, mediumlevelil.MediumLevelILFunction): self.handle = core.BNCreateMediumLevelILDisassemblyTextRenderer(func.handle, settings_obj) - elif isinstance(func, binaryninja.highlevelil.HighLevelILFunction): + elif isinstance(func, highlevelil.HighLevelILFunction): self.handle = core.BNCreateHighLevelILDisassemblyTextRenderer(func.handle, settings_obj) else: raise TypeError("invalid function object") @@ -4485,117 +3218,120 @@ class DisassemblyTextRenderer(object): core.BNFreeDisassemblyTextRenderer(self.handle) @property - def function(self): + def function(self) -> 'Function': return Function(handle = core.BNGetDisassemblyTextRendererFunction(self.handle)) @property - def il_function(self): + def il_function(self) -> Optional[ILFunctionType]: llil = core.BNGetDisassemblyTextRendererLowLevelILFunction(self.handle) if llil: - return binaryninja.lowlevelil.LowLevelILFunction(handle = llil) + return lowlevelil.LowLevelILFunction(handle = llil) mlil = core.BNGetDisassemblyTextRendererMediumLevelILFunction(self.handle) if mlil: - return binaryninja.mediumlevelil.MediumLevelILFunction(handle = mlil) + return mediumlevelil.MediumLevelILFunction(handle = mlil) hlil = core.BNGetDisassemblyTextRendererHighLevelILFunction(self.handle) if hlil: - return binaryninja.highlevelil.HighLevelILFunction(handle = hlil) + return highlevelil.HighLevelILFunction(handle = hlil) return None @property - def basic_block(self): + def basic_block(self) -> Optional['basicblock.BasicBlock']: result = core.BNGetDisassemblyTextRendererBasicBlock(self.handle) if result: - return binaryninja.basicblock.BasicBlock(handle = result) + return basicblock.BasicBlock(handle = result) return None @basic_block.setter - def basic_block(self, block): + def basic_block(self, block:'basicblock.BasicBlock') -> None: if block is not None: core.BNSetDisassemblyTextRendererBasicBlock(self.handle, block.handle) else: core.BNSetDisassemblyTextRendererBasicBlock(self.handle, None) @property - def arch(self): - return binaryninja.architecture.CoreArchitecture._from_cache(handle = core.BNGetDisassemblyTextRendererArchitecture(self.handle)) + def arch(self) -> 'architecture.Architecture': + return architecture.CoreArchitecture._from_cache(handle = core.BNGetDisassemblyTextRendererArchitecture(self.handle)) @arch.setter - def arch(self, arch): + def arch(self, arch='architecture.Architecture') -> None: core.BNSetDisassemblyTextRendererArchitecture(self.handle, arch.handle) @property - def settings(self): + def settings(self) -> 'DisassemblySettings': return DisassemblySettings(handle = core.BNGetDisassemblyTextRendererSettings(self.handle)) @settings.setter - def settings(self, settings): + def settings(self, settings:'DisassemblySettings') -> None: if settings is not None: core.BNSetDisassemblyTextRendererSettings(self.handle, settings.handle) core.BNSetDisassemblyTextRendererSettings(self.handle, None) @property - def il(self): + def il(self) -> bool: return core.BNIsILDisassemblyTextRenderer(self.handle) @property - def has_data_flow(self): + def has_data_flow(self) -> bool: return core.BNDisassemblyTextRendererHasDataFlow(self.handle) - def get_instruction_annotations(self, addr): + def get_instruction_annotations(self, addr:int) -> List['InstructionTextToken']: count = ctypes.c_ulonglong() tokens = core.BNGetDisassemblyTextRendererInstructionAnnotations(self.handle, addr, count) - result = InstructionTextToken.get_instruction_lines(tokens, count.value) + assert tokens is not None + result = InstructionTextToken._from_core_struct(tokens, count.value) core.BNFreeInstructionText(tokens, count.value) return result - def get_instruction_text(self, addr): + def get_instruction_text(self, addr:int) -> Generator[Tuple[Optional['DisassemblyTextLine'], int], None, None]: count = ctypes.c_ulonglong() length = ctypes.c_ulonglong() lines = ctypes.POINTER(core.BNDisassemblyTextLine)() if not core.BNGetDisassemblyTextRendererInstructionText(self.handle, addr, length, lines, count): - return None, 0 + yield None, 0 + return il_function = self.il_function - result = [] - for i in range(0, count.value): - addr = lines[i].addr - if (lines[i].instrIndex != 0xffffffffffffffff) and (il_function is not None): - il_instr = il_function[lines[i].instrIndex] - else: - il_instr = None - color = highlight.HighlightColor._from_core_struct(lines[i].highlight) - tokens = InstructionTextToken.get_instruction_lines(lines[i].tokens, lines[i].count) - result.append(DisassemblyTextLine(tokens, addr, il_instr, color)) - core.BNFreeDisassemblyTextLines(lines, count.value) - return (result, length.value) + try: + for i in range(0, count.value): + addr = lines[i].addr + if (lines[i].instrIndex != 0xffffffffffffffff) and (il_function is not None): + il_instr = il_function[lines[i].instrIndex] + else: + il_instr = None + color = _highlight.HighlightColor._from_core_struct(lines[i].highlight) + tokens = InstructionTextToken._from_core_struct(lines[i].tokens, lines[i].count) + yield DisassemblyTextLine(tokens, addr, il_instr, color), length.value + finally: + core.BNFreeDisassemblyTextLines(lines, count.value) - def get_disassembly_text(self, addr): + def get_disassembly_text(self, addr:int) -> Generator[Tuple[Optional['DisassemblyTextLine'], int], None, None]: count = ctypes.c_ulonglong() length = ctypes.c_ulonglong() length.value = 0 lines = ctypes.POINTER(core.BNDisassemblyTextLine)() ok = core.BNGetDisassemblyTextRendererLines(self.handle, addr, length, lines, count) if not ok: - return None, 0 + yield None, 0 + return il_function = self.il_function - result = [] - for i in range(0, count.value): - addr = lines[i].addr - if (lines[i].instrIndex != 0xffffffffffffffff) and (il_function is not None): - il_instr = il_function[lines[i].instrIndex] - else: - il_instr = None - color = highlight.HighlightColor._from_core_struct(lines[i].highlight) - tokens = InstructionTextToken.get_instruction_lines(lines[i].tokens, lines[i].count) - result.append(DisassemblyTextLine(tokens, addr, il_instr, color)) - core.BNFreeDisassemblyTextLines(lines, count.value) - return (result, length.value) + try: + for i in range(0, count.value): + addr = lines[i].addr + if (lines[i].instrIndex != 0xffffffffffffffff) and (il_function is not None): + il_instr = il_function[lines[i].instrIndex] + else: + il_instr = None + color = _highlight.HighlightColor._from_core_struct(lines[i].highlight) + tokens = InstructionTextToken._from_core_struct(lines[i].tokens, lines[i].count) + yield DisassemblyTextLine(tokens, addr, il_instr, color), length.value + finally: + core.BNFreeDisassemblyTextLines(lines, count.value) - def post_process_lines(self, addr, length, in_lines, indent_spaces=""): + def post_process_lines(self, addr:int, length:int, in_lines:Union[str, List[str], List['DisassemblyTextLine']], + indent_spaces:str=''): if isinstance(in_lines, str): in_lines = in_lines.split('\n') line_buf = (core.BNDisassemblyTextLine * len(in_lines))() - for i in range(0, len(in_lines)): - line = in_lines[i] + for i, line in enumerate(in_lines): if isinstance(line, str): line = DisassemblyTextLine([InstructionTextToken(InstructionTextTokenType.TextToken, line)]) if not isinstance(line, DisassemblyTextLine): @@ -4612,46 +3348,49 @@ class DisassemblyTextRenderer(object): else: line_buf[i].instrIndex = 0xffffffffffffffff color = line.highlight - if not isinstance(color, HighlightStandardColor) and not isinstance(color, highlight.HighlightColor): - raise ValueError("Specified color is not one of HighlightStandardColor, highlight.HighlightColor") + if not isinstance(color, HighlightStandardColor) and not isinstance(color, _highlight.HighlightColor): + raise ValueError("Specified color is not one of HighlightStandardColor, _highlight.HighlightColor") if isinstance(color, HighlightStandardColor): - color = highlight.HighlightColor(color) + color = _highlight.HighlightColor(color) line_buf[i].highlight = color._get_core_struct() line_buf[i].count = len(line.tokens) - line_buf[i].tokens = InstructionTextToken.get_instruction_lines(line.tokens) + line_buf[i].tokens = InstructionTextToken._get_core_struct(line.tokens) count = ctypes.c_ulonglong() lines = ctypes.POINTER(core.BNDisassemblyTextLine)() lines = core.BNPostProcessDisassemblyTextRendererLines(self.handle, addr, length, line_buf, len(in_lines), count, indent_spaces) + assert lines is not None, "core.BNPostProcessDisassemblyTextRendererLines returned None" il_function = self.il_function - result = [] - for i in range(0, count.value): - addr = lines[i].addr - if (lines[i].instrIndex != 0xffffffffffffffff) and (il_function is not None): - il_instr = il_function[lines[i].instrIndex] - else: - il_instr = None - color = highlight.HighlightColor._from_core_struct(lines[i].highlight) - tokens = InstructionTextToken.get_instruction_lines(lines[i].tokens, lines[i].count) - result.append(DisassemblyTextLine(tokens, addr, il_instr, color)) - core.BNFreeDisassemblyTextLines(lines, count.value) - return result + try: + for i in range(count.value): + addr = lines[i].addr + if (lines[i].instrIndex != 0xffffffffffffffff) and (il_function is not None): + il_instr = il_function[lines[i].instrIndex] + else: + il_instr = None + color = _highlight.HighlightColor._from_core_struct(lines[i].highlight) + tokens = InstructionTextToken._from_core_struct(lines[i].tokens, lines[i].count) + yield DisassemblyTextLine(tokens, addr, il_instr, color) + finally: + core.BNFreeDisassemblyTextLines(lines, count.value) - def reset_deduplicated_comments(self): + def reset_deduplicated_comments(self) -> None: core.BNResetDisassemblyTextRendererDeduplicatedComments(self.handle) - def add_symbol_token(self, tokens, addr, size, operand = None): + def add_symbol_token(self, tokens:List['InstructionTextToken'], addr:int, size:int, operand:int=None) -> bool: if operand is None: operand = 0xffffffff count = ctypes.c_ulonglong() new_tokens = ctypes.POINTER(core.BNInstructionTextToken)() if not core.BNGetDisassemblyTextRendererSymbolTokens(self.handle, addr, size, operand, new_tokens, count): return False - result = binaryninja.function.InstructionTextToken.get_instruction_lines(new_tokens, count.value) + assert new_tokens is not None + result = InstructionTextToken._from_core_struct(new_tokens, count.value) tokens += result core.BNFreeInstructionText(new_tokens, count.value) return True - def add_stack_var_reference_tokens(self, tokens, ref): + def add_stack_var_reference_tokens(self, tokens:List['InstructionTextToken'], + ref:'variable.StackVariableReference') -> None: stack_ref = core.BNStackVariableReference() if ref.source_operand is None: stack_ref.sourceOperand = 0xffffffff @@ -4669,25 +3408,29 @@ class DisassemblyTextRenderer(object): stack_ref.size = ref.size count = ctypes.c_ulonglong() new_tokens = core.BNGetDisassemblyTextRendererStackVariableReferenceTokens(self.handle, stack_ref, count) - result = InstructionTextToken.get_instruction_lines(new_tokens, count.value) + assert new_tokens is not None + result = InstructionTextToken._from_core_struct(new_tokens, count.value) tokens += result core.BNFreeInstructionText(new_tokens, count.value) - @classmethod - def is_integer_token(self, token): + @staticmethod + def is_integer_token(token:'InstructionTextToken') -> bool: return core.BNIsIntegerToken(token) - def add_integer_token(self, tokens, int_token, addr, arch = None): + def add_integer_token(self, tokens:List['InstructionTextToken'], int_token:'InstructionTextToken', addr:int, + arch:Optional['architecture.Architecture']=None) -> None: if arch is not None: arch = arch.handle - in_token_obj = InstructionTextToken.get_instruction_lines([int_token]) + in_token_obj = InstructionTextToken._get_core_struct([int_token]) count = ctypes.c_ulonglong() new_tokens = core.BNGetDisassemblyTextRendererIntegerTokens(self.handle, in_token_obj, arch, addr, count) - result = InstructionTextToken.get_instruction_lines(new_tokens, count.value) + assert new_tokens is not None + result = InstructionTextToken._from_core_struct(new_tokens, count.value) tokens += result core.BNFreeInstructionText(new_tokens, count.value) - def wrap_comment(self, lines, cur_line, comment, has_auto_annotations, leading_spaces = " ", indent_spaces = ""): + def wrap_comment(self, lines:List['DisassemblyTextLine'], cur_line:'DisassemblyTextLine', comment:str, + has_auto_annotations:bool, leading_spaces:str=" ", indent_spaces:str= "") -> None: cur_line_obj = core.BNDisassemblyTextLine() cur_line_obj.addr = cur_line.address if cur_line.il_instruction is None: @@ -4695,11 +3438,12 @@ class DisassemblyTextRenderer(object): else: cur_line_obj.instrIndex = cur_line.il_instruction.instr_index cur_line_obj.highlight = cur_line.highlight._get_core_struct() - cur_line_obj.tokens = InstructionTextToken.get_instruction_lines(cur_line.tokens) + cur_line_obj.tokens = InstructionTextToken._get_core_struct(cur_line.tokens) cur_line_obj.count = len(cur_line.tokens) count = ctypes.c_ulonglong() new_lines = core.BNDisassemblyTextRendererWrapComment(self.handle, cur_line_obj, count, comment, has_auto_annotations, leading_spaces, indent_spaces) + assert new_lines is not None, "core.BNDisassemblyTextRendererWrapComment returned None" il_function = self.il_function for i in range(0, count.value): addr = new_lines[i].addr @@ -4707,7 +3451,199 @@ class DisassemblyTextRenderer(object): il_instr = il_function[new_lines[i].instrIndex] else: il_instr = None - color = highlight.HighlightColor._from_core_struct(new_lines[i].highlight) - tokens = InstructionTextToken.get_instruction_lines(new_lines[i].tokens, new_lines[i].count) + color = _highlight.HighlightColor._from_core_struct(new_lines[i].highlight) + tokens = InstructionTextToken._from_core_struct(new_lines[i].tokens, new_lines[i].count) lines.append(DisassemblyTextLine(tokens, addr, il_instr, color)) core.BNFreeDisassemblyTextLines(new_lines, count.value) + + +class InstructionTextToken(object): + """ + ``class InstructionTextToken`` is used to tell the core about the various components in the disassembly views. + + The below table is provided for documentation purposes but the complete list of TokenTypes is available at: :class:`!enums.InstructionTextTokenType`. Note that types marked as `Not emitted by architectures` are not intended to be used by Architectures during lifting. Rather, they are added by the core during analysis or display. UI plugins, however, may make use of them as appropriate. + + Uses of tokens include plugins that parse the output of an architecture (though parsing IL is recommended), or additionally, applying color schemes appropriately. + + ========================== ============================================ + InstructionTextTokenType Description + ========================== ============================================ + AddressDisplayToken **Not emitted by architectures** + AnnotationToken **Not emitted by architectures** + ArgumentNameToken **Not emitted by architectures** + BeginMemoryOperandToken The start of memory operand + CharacterConstantToken A printable character + CodeRelativeAddressToken **Not emitted by architectures** + CodeSymbolToken **Not emitted by architectures** + DataSymbolToken **Not emitted by architectures** + EndMemoryOperandToken The end of a memory operand + ExternalSymbolToken **Not emitted by architectures** + FieldNameToken **Not emitted by architectures** + FloatingPointToken Floating point number + HexDumpByteValueToken **Not emitted by architectures** + HexDumpInvalidByteToken **Not emitted by architectures** + HexDumpSkippedByteToken **Not emitted by architectures** + HexDumpTextToken **Not emitted by architectures** + ImportToken **Not emitted by architectures** + IndirectImportToken **Not emitted by architectures** + InstructionToken The instruction mnemonic + IntegerToken Integers + KeywordToken **Not emitted by architectures** + LocalVariableToken **Not emitted by architectures** + NameSpaceSeparatorToken **Not emitted by architectures** + NameSpaceToken **Not emitted by architectures** + OpcodeToken **Not emitted by architectures** + OperandSeparatorToken The comma or delimiter that separates tokens + PossibleAddressToken Integers that are likely addresses + RegisterToken Registers + StringToken **Not emitted by architectures** + StructOffsetToken **Not emitted by architectures** + TagToken **Not emitted by architectures** + TextToken Used for anything not of another type. + CommentToken Comments + TypeNameToken **Not emitted by architectures** + ========================== ============================================ + + """ + def __init__(self, token_type:Union[InstructionTextTokenType, int], text:str, value:int=0, size:int=0, + operand:int=0xffffffff, context:InstructionTextTokenContext=InstructionTextTokenContext.NoTokenContext, + address:int=0, confidence:int=core.max_confidence, typeNames:List[str]=[], width:int=0): + 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 + self._typeNames = typeNames + self._width = width + if width == 0: + self._width = len(self._text) + + @staticmethod + def _from_core_struct(tokens:'ctypes.pointer[core.BNInstructionTextToken]', count:int) -> List['InstructionTextToken']: + result:List['InstructionTextToken'] = [] + for j in range(count): + token_type = InstructionTextTokenType(tokens[j].type) + text = tokens[j].text + if not isinstance(text, str): + text = text.decode("utf-8") + width = tokens[j].width + value = tokens[j].value + size = tokens[j].size + operand = tokens[j].operand + context = tokens[j].context + confidence = tokens[j].confidence + address = tokens[j].address + typeNames = [] + for i in range(tokens[j].namesCount): + if not isinstance(tokens[j].typeNames[i], str): + typeNames.append(tokens[j].typeNames[i].decode("utf-8")) + else: + typeNames.append(tokens[j].typeNames[i]) + result.append(InstructionTextToken(token_type, text, value, size, operand, context, address, confidence, typeNames, width)) + return result + + @staticmethod + def _get_core_struct(tokens:List['InstructionTextToken']) -> ctypes.Array[core.BNInstructionTextToken]: + """ Helper method for converting between core.BNInstructionTextToken and InstructionTextToken lists """ + result = (core.BNInstructionTextToken * len(tokens))() + for j in range(len(tokens)): + result[j].type = tokens[j].type + result[j].text = tokens[j].text + result[j].width = tokens[j].width + result[j].value = tokens[j].value + result[j].size = tokens[j].size + result[j].operand = tokens[j].operand + result[j].context = tokens[j].context + result[j].confidence = tokens[j].confidence + result[j].address = tokens[j].address + result[j].namesCount = len(tokens[j].typeNames) + result[j].typeNames = (ctypes.c_char_p * len(tokens[j].typeNames))() + for i in range(len(tokens[j].typeNames)): + result[j].typeNames[i] = tokens[j].typeNames[i].encode("utf-8") + return result + + + def __str__(self): + return self._text + + def __repr__(self): + return repr(self._text) + + @property + def type(self) -> InstructionTextTokenType: + return self._type + + @type.setter + def type(self, value:InstructionTextTokenType) -> None: + self._type = value + + @property + def text(self) -> str: + return self._text + + @text.setter + def text(self, value:str) -> None: + self._text = value + + @property + def value(self) -> int: + return self._value + + @value.setter + def value(self, value:int) -> None: + self._value = value + + @property + def size(self) -> int: + return self._size + + @size.setter + def size(self, value:int) -> None: + self._size = value + + @property + def operand(self) -> int: + return self._operand + + @operand.setter + def operand(self, value:int) -> None: + self._operand = value + + @property + def context(self) -> InstructionTextTokenContext: + return self._context + + @context.setter + def context(self, value:InstructionTextTokenContext) -> None: + self._context = value + + @property + def confidence(self) -> int: + return self._confidence + + @confidence.setter + def confidence(self, value:int) -> None: + self._confidence = value + + @property + def address(self) -> int: + return self._address + + @address.setter + def address(self, value:int) -> None: + self._address = value + + @property + def typeNames(self) -> List[str]: + return self._typeNames + + @typeNames.setter + def typeNames(self, value:List[str]) -> None: + self._typeNames = value + + @property + def width(self) -> int: + return self._width -- cgit v1.3.1