Source code for binaryninja.variable

# coding=utf-8
# Copyright (c) 2015-2024 Vector 35 Inc
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
# deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.

import ctypes
from typing import List, Generator, Optional, Union, Set, Dict
from dataclasses import dataclass

import binaryninja
from . import _binaryninjacore as core
from . import databuffer
from . import decorators
from .enums import RegisterValueType, VariableSourceType, DeadStoreElimination, FunctionGraphType

FunctionOrILFunction = Union["binaryninja.function.Function", "binaryninja.lowlevelil.LowLevelILFunction",
                             "binaryninja.mediumlevelil.MediumLevelILFunction",
                             "binaryninja.highlevelil.HighLevelILFunction"]


[docs] @dataclass(frozen=True) class LookupTableEntry: from_values: List[int] to_value: int type: RegisterValueType = RegisterValueType.LookupTableValue def __repr__(self): return f"[{', '.join([f'{i:#x}' for i in self.from_values])}] -> {self.to_value:#x}"
[docs] @dataclass(frozen=True) class RegisterValue: value: int offset: int type: RegisterValueType = RegisterValueType.UndeterminedValue confidence: int = core.max_confidence size: int = 0 def _to_core_struct(self) -> core.BNRegisterValue: result = core.BNRegisterValue() result.state = self.type result.value = self.value result.offset = self.offset result.size = self.size return result def _to_core_struct_with_confidence(self): result = core.BNRegisterValueWithConfidence() result.value = self._to_core_struct() result.confidence = self.confidence return result def __bool__(self): return self.value != 0 def __int__(self): return self.value def __eq__(self, other): if isinstance(other, int): return int(self) == other elif isinstance(other, bool): return bool(self) == other elif isinstance(other, self.__class__): return (self.type, self.offset, self.type, self.confidence) == (other.type, other.offset, other.type, other.confidence) assert False, f"no comparison for types {repr(self)} and {repr(other)}"
[docs] @classmethod def from_BNRegisterValue( cls, reg_value: Union[core.BNRegisterValue, core.BNRegisterValueWithConfidence], arch: Optional['binaryninja.architecture.Architecture'] = None ) -> 'RegisterValue': confidence = core.max_confidence if isinstance(reg_value, core.BNRegisterValueWithConfidence): confidence = reg_value.confidence reg_value = reg_value.value if reg_value.state == RegisterValueType.EntryValue: reg = None if arch is not None: reg = arch.get_reg_name(binaryninja.architecture.RegisterIndex(reg_value.value)) return EntryRegisterValue(reg_value.value, reg=reg, confidence=confidence) elif reg_value.state == RegisterValueType.ConstantValue: return ConstantRegisterValue(reg_value.value, confidence=confidence) elif reg_value.state == RegisterValueType.ConstantPointerValue: return ConstantPointerRegisterValue(reg_value.value, confidence=confidence) elif reg_value.state == RegisterValueType.StackFrameOffset: return StackFrameOffsetRegisterValue(reg_value.value, confidence=confidence) elif reg_value.state == RegisterValueType.ImportedAddressValue: return ImportedAddressRegisterValue(reg_value.value, confidence=confidence) elif reg_value.state == RegisterValueType.UndeterminedValue: return Undetermined() elif reg_value.state == RegisterValueType.ReturnAddressValue: return ReturnAddressRegisterValue(reg_value.value, confidence=confidence) elif reg_value.state == RegisterValueType.ExternalPointerValue: return ExternalPointerRegisterValue(reg_value.value, reg_value.offset, confidence=confidence) elif reg_value.state & RegisterValueType.ConstantDataValue == RegisterValueType.ConstantDataValue: return ConstantDataRegisterValue(reg_value.value, 0, RegisterValueType(reg_value.state), confidence=confidence, size=reg_value.size) assert False, f"RegisterValueType {reg_value.state} not handled"
[docs] @dataclass(frozen=True, eq=False) class Undetermined(RegisterValue): value: int = 0 offset: int = 0 type: RegisterValueType = RegisterValueType.UndeterminedValue def __repr__(self): return "<undetermined>"
[docs] @dataclass(frozen=True, eq=False) class ConstantRegisterValue(RegisterValue): offset: int = 0 type: RegisterValueType = RegisterValueType.ConstantValue def __repr__(self): return f"<const {self.value:#x}>"
[docs] @dataclass(frozen=True, eq=False) class ConstantPointerRegisterValue(RegisterValue): offset: int = 0 type: RegisterValueType = RegisterValueType.ConstantPointerValue def __repr__(self): return f"<const ptr {self.value:#x}>"
[docs] @dataclass(frozen=True, eq=False) class ImportedAddressRegisterValue(RegisterValue): offset: int = 0 type: RegisterValueType = RegisterValueType.ImportedAddressValue def __repr__(self): return f"<imported address from entry {self.value:#x}>"
[docs] @dataclass(frozen=True, eq=False) class ReturnAddressRegisterValue(RegisterValue): offset: int = 0 type: RegisterValueType = RegisterValueType.ReturnAddressValue def __repr__(self): return "<return address>"
[docs] @dataclass(frozen=True, eq=False) class EntryRegisterValue(RegisterValue): value: int = 0 offset: int = 0 type: RegisterValueType = RegisterValueType.EntryValue reg: Optional['binaryninja.architecture.RegisterName'] = None def __repr__(self): if self.reg is not None: return f"<entry {self.reg}>" return f"<entry {self.value}>"
[docs] @dataclass(frozen=True, eq=False) class StackFrameOffsetRegisterValue(RegisterValue): offset: int = 0 type: RegisterValueType = RegisterValueType.StackFrameOffset def __repr__(self): return f"<stack frame offset {self.value:#x}>"
[docs] @dataclass(frozen=True, eq=False) class ExternalPointerRegisterValue(RegisterValue): type: RegisterValueType = RegisterValueType.ExternalPointerValue def __repr__(self): return f"<external {self.value:#x} + offset {self.offset:#x}>"
[docs] @dataclass(frozen=True, eq=False) class ConstantDataRegisterValue(RegisterValue): def __repr__(self): if self.type == RegisterValueType.ConstantDataZeroExtendValue: return f"<const data {{zx.{self.size}({self.value:#x})}}>" if self.type == RegisterValueType.ConstantDataSignExtendValue: return f"<const data {{sx.{self.size}({self.value:#x})}}>" if self.type == RegisterValueType.ConstantDataAggregateValue: return f"<const data {{aggregate.{self.size}}} @ {self.value:#x}>" return f"<const data {{invalid}} {self.type} {self.value:#x}>"
[docs] @dataclass(frozen=True, eq=False) class ConstantData(RegisterValue): function: '_function.Function' = None def __repr__(self): if self.type == RegisterValueType.ConstantDataZeroExtendValue: return f"<{self.__class__.__name__}: {{zx.{self.size}({self.value:#x})}}>" if self.type == RegisterValueType.ConstantDataSignExtendValue: return f"<{self.__class__.__name__}: {{sx.{self.size}({self.value:#x})}}>" if self.type == RegisterValueType.ConstantDataAggregateValue: return f"<{self.__class__.__name__}: {{aggregate.{self.size}}} @ {self.value:#x}>" return f"<{self.__class__.__name__}: {{invalid}} {self.type} {self.value:#x}>" @property def data(self) -> databuffer.DataBuffer: if self.function is None: raise ValueError(f"ConstantData requires a Function instance: {self.size}") return self.function.get_constant_data(self.type, self.value, self.size)
[docs] @dataclass(frozen=True) class ValueRange: start: int end: int step: int def __repr__(self): if self.step == 1: return f"<range: {self.start:#x} to {self.end:#x}>" return f"<range: {self.start:#x} to {self.end:#x}, step {self.step:#x}>" def __contains__(self, other): if not isinstance(other, int): return NotImplemented return other in range(self.start, self.end, self.step)
[docs] @decorators.passive class PossibleValueSet: """ `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.ConstantDataValue == RegisterValueType.ConstantDataValue: self._value = value.value self._size = value.size 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 __repr__(self): if self._type == RegisterValueType.EntryValue: return f"<entry {self.reg}>" if self._type == RegisterValueType.ConstantValue: return f"<const {self.value:#x}>" if self._type == RegisterValueType.ConstantPointerValue: return f"<const ptr {self.value:#x}>" if self._type == RegisterValueType.StackFrameOffset: return f"<stack frame offset {self._offset:#x}>" if self._type == RegisterValueType.ConstantDataZeroExtendValue: return f"<const data {{zx.{self._size}({self.value:#x})}}>" if self._type == RegisterValueType.ConstantDataSignExtendValue: return f"<const data {{sx.{self._size}({self.value:#x})}}>" if self._type == RegisterValueType.ConstantDataAggregateValue: return f"<const data {{aggregate.{self._size}}} @ {self.value:#x}>" if self._type == RegisterValueType.SignedRangeValue: return f"<signed ranges: {repr(self.ranges)}>" if self._type == RegisterValueType.UnsignedRangeValue: return f"<unsigned ranges: {repr(self.ranges)}>" if self._type == RegisterValueType.LookupTableValue: return f"<table: {', '.join([repr(i) for i in self.table])}>" if self._type == RegisterValueType.InSetOfValues: return f"<in set([{', '.join(hex(i) for i in sorted(self.values))}])>" if self._type == RegisterValueType.NotInSetOfValues: return f"<not in set([{', '.join(hex(i) for i in sorted(self.values))}])>" if self._type == RegisterValueType.ReturnAddressValue: return "<return address>" return "<undetermined>" def __contains__(self, other): if self.type in [RegisterValueType.ConstantValue, RegisterValueType.ConstantPointerValue ] and isinstance(other, int): 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, int): 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 def __eq__(self, other): if self.type in [RegisterValueType.ConstantValue, RegisterValueType.ConstantPointerValue ] and isinstance(other, int): return self.value == other 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 & RegisterValueType.ConstantDataValue == RegisterValueType.ConstantDataValue: return self.value == other.value and self._size == other._size 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 def __ne__(self, other): if not isinstance(other, self.__class__): return NotImplemented return not (self == other) def _to_core_struct(self) -> core.BNPossibleValueSet: 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.ConstantDataValue == RegisterValueType.ConstantDataValue: result.value = self.value result.size = self.size 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, len(self.table[i].from_values)): from_list.append(self.table[i].from_values[j]) result.mapping[self.table[i].from_values[j]] = result.table[i].to_value result.table.append(LookupTableEntry(from_list, result.table[i].to_value)) 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 int_ptr = ctypes.POINTER(ctypes.c_longlong) result.valueSet = ctypes.cast(values, int_ptr) result.count = self.count return result @property def type(self) -> RegisterValueType: return self._type @property def reg(self) -> 'binaryninja.architecture.RegisterName': return self._reg @property def value(self) -> int: return self._value @property def offset(self) -> int: return self._offset @property def size(self) -> int: return self._size @property def ranges(self) -> List[ValueRange]: return self._ranges @property def table(self) -> List[LookupTableEntry]: return self._table @property def mapping(self) -> Dict[int, int]: return self._mapping @property def values(self) -> Set[int]: return self._values @property def count(self) -> int: return self._count
[docs] @staticmethod def undetermined() -> 'PossibleValueSet': """ Create a PossibleValueSet object of type UndeterminedValue. :return: PossibleValueSet object of type UndeterminedValue :rtype: PossibleValueSet """ return PossibleValueSet()
[docs] @staticmethod def constant(value: int) -> 'PossibleValueSet': """ Create a constant valued PossibleValueSet object. :param int value: Integer value of the constant :rtype: PossibleValueSet """ result = PossibleValueSet() result._type = RegisterValueType.ConstantValue result._value = value return result
[docs] @staticmethod def constant_ptr(value: int) -> 'PossibleValueSet': """ Create constant pointer valued PossibleValueSet object. :param int value: Integer value of the constant pointer :rtype: PossibleValueSet """ result = PossibleValueSet() result._type = RegisterValueType.ConstantPointerValue result._value = value return result
[docs] @staticmethod def stack_frame_offset(offset: int) -> 'PossibleValueSet': """ Create a PossibleValueSet object for a stack frame offset. :param int offset: Integer value of the offset :rtype: PossibleValueSet """ result = PossibleValueSet() result._type = RegisterValueType.StackFrameOffset result._offset = offset return result
[docs] @staticmethod def signed_range_value(ranges: List[ValueRange]) -> 'PossibleValueSet': """ Create a PossibleValueSet object for a signed range of values. :param list(ValueRange) ranges: List of ValueRanges :rtype: PossibleValueSet :Example: >>> v_1 = ValueRange(-5, -1, 1) >>> v_2 = ValueRange(7, 10, 1) >>> val = PossibleValueSet.signed_range_value([v_1, v_2]) <signed ranges: [<range: -0x5 to -0x1>, <range: 0x7 to 0xa>]> """ result = PossibleValueSet() result._value = 0 result._type = RegisterValueType.SignedRangeValue result._ranges = ranges result._count = len(ranges) return result
[docs] @staticmethod def unsigned_range_value(ranges: List[ValueRange]) -> 'PossibleValueSet': """ Create a PossibleValueSet object for a unsigned signed range of values. :param list(ValueRange) ranges: List of ValueRanges :rtype: PossibleValueSet :Example: >>> v_1 = ValueRange(0, 5, 1) >>> v_2 = ValueRange(7, 10, 1) >>> val = PossibleValueSet.unsigned_range_value([v_1, v_2]) <unsigned ranges: [<range: 0x0 to 0x5>, <range: 0x7 to 0xa>]> """ result = PossibleValueSet() result._value = 0 result._type = RegisterValueType.UnsignedRangeValue result._ranges = ranges result._count = len(ranges) return result
[docs] @staticmethod def in_set_of_values(values: Union[List[int], Set[int]]) -> 'PossibleValueSet': """ Create a PossibleValueSet object for a value in a set of values. :param list(int) values: List of integer values :rtype: PossibleValueSet """ result = PossibleValueSet() result._type = RegisterValueType.InSetOfValues result._values = set(values) result._count = len(values) return result
[docs] @staticmethod def not_in_set_of_values(values) -> 'PossibleValueSet': """ Create a PossibleValueSet object for a value NOT in a set of values. :param list(int) values: List of integer values :rtype: PossibleValueSet """ result = PossibleValueSet() result._type = RegisterValueType.NotInSetOfValues result._values = set(values) result._count = len(values) return result
[docs] @staticmethod def lookup_table_value(lookup_table, mapping) -> 'PossibleValueSet': """ 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 """ result = PossibleValueSet() result._type = RegisterValueType.LookupTableValue result._table = lookup_table result._mapping = mapping return result
[docs] @dataclass(frozen=True) class StackVariableReference: _source_operand: Optional[int] type: 'binaryninja.types.Type' name: str var: 'Variable' referenced_offset: int size: int def __repr__(self): if self.source_operand is None: if self.referenced_offset != self.var.storage: return f"<ref to {self.name}{self.referenced_offset - self.var.storage:+#x}>" return f"<ref to {self.name}>" if self.referenced_offset != self.var.storage: return f"<operand {self.source_operand} ref to {self.var.storage}{self.var.storage:+#x}>" return f"<operand {self.source_operand} ref to {self.name}>" @property def source_operand(self): if self._source_operand == 0xffffffff: return None return self._source_operand
[docs] @dataclass(frozen=True, order=True) class CoreVariable: """ ``class CoreVariable`` is the base class for other variable types, such as :py:meth:`VariableNameAndType` and :py:meth:`Variable` :cvar index: Internal identifier :cvar storage: If this variable is a stack variable (`source_type == VariableSourceType.StackVariableSourceType`), then the storage location is the offset onto the stack that contains the first byte of this variable. Otherwise it's used as an internal identifier. """ _source_type: int index: int storage: int @property def identifier(self) -> int: """A UID for a variable within a function.""" return core.BNToVariableIdentifier(self.to_BNVariable()) @property def source_type(self) -> VariableSourceType: """Whether this variable was created based off of an underlying register, stack location, or flag.""" return VariableSourceType(self._source_type)
[docs] def to_BNVariable(self): v = core.BNVariable() v.type = self._source_type v.index = self.index v.storage = self.storage return v
[docs] @classmethod def from_BNVariable(cls, var: core.BNVariable): return cls(var.type, var.index, var.storage)
[docs] @classmethod def from_identifier(cls, identifier): var = core.BNFromVariableIdentifier(identifier) return cls(var.type, var.index, var.storage)
[docs] @dataclass(frozen=True, order=True) class VariableNameAndType(CoreVariable): """ ``class VariableNameAndType`` is a lightweight wrapper around a variable and its name, useful for shuttling between APIs that require them both. While :py:meth:`Variable` has :py:meth:`Variable.name` and :py:meth:`Variable.type` fields, those require additional core calls each time you fetch them. :cvar name: The variable's name :cvar type: The variable's type """ name: str type: 'binaryninja.types.Type'
[docs] @classmethod def from_identifier(cls, identifier, name, type): var = core.BNFromVariableIdentifier(identifier) return cls(name, type, var.type, var.index, var.storage)
[docs] @classmethod def from_core_variable(cls, var, name, type): return cls(name, type, var.type, var.index, var.storage)
[docs] class Variable(CoreVariable): """ ``class Variable`` represents variables in Binary Ninja. Variables are resolved in medium level IL, so variables objects are only valid for MLIL and above. """ def __init__(self, func: FunctionOrILFunction, source_type: VariableSourceType, index: int, storage: int): super(Variable, self).__init__(int(source_type), index, storage) if isinstance(func, binaryninja.function.Function): self._function = func self._il_function = None else: self._function = func.source_function self._il_function = func
[docs] @classmethod def from_variable_name_and_type(cls, func: FunctionOrILFunction, var: VariableNameAndType): return cls(func, VariableSourceType(var.type), var.index, var.storage)
[docs] @classmethod def from_core_variable(cls, func: FunctionOrILFunction, var: CoreVariable): return cls(func, var.source_type, var.index, var.storage)
[docs] @classmethod def from_BNVariable(cls, func: FunctionOrILFunction, var: core.BNVariable): return cls(func, var.type, var.index, var.storage)
[docs] @classmethod def from_identifier(cls, func: FunctionOrILFunction, identifier: int): var = core.BNFromVariableIdentifier(identifier) return cls(func, VariableSourceType(var.type), var.index, var.storage)
def __repr__(self): if self.type is not None: return f"<var {self.type.get_string_before_name()} {self.name}{self.type.get_string_after_name()}>" else: return f"<var {self.name}>" def __str__(self): return self.name def __eq__(self, other): if not isinstance(other, self.__class__): return NotImplemented return super().__eq__(other) and (self._function == other._function) 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 return super().__lt__(other) and self._function < other._function def __gt__(self, other): if not isinstance(other, self.__class__): return NotImplemented return super().__gt__(other) and self._function > other._function def __le__(self, other): if not isinstance(other, self.__class__): return NotImplemented return super().__le__(other) and self._function <= other._function def __ge__(self, other): if not isinstance(other, self.__class__): return NotImplemented return super().__ge__(other) and self._function >= other._function def __hash__(self): return hash((self._function, super().__hash__())) @property def core_variable(self) -> CoreVariable: """Retrieve the underlying :py:meth:`CoreVariable` class""" return CoreVariable(self._source_type, self.index, self.storage) @property def var_name_and_type(self) -> VariableNameAndType: """Convert to :py:meth:`VariableNameAndType` """ return VariableNameAndType.from_core_variable(self, self.name, self.type) @property def name(self) -> str: """Name of the variable, Settings thisslow because it ensures that analysis has been updated. """ return core.BNGetVariableNameOrDefault(self._function.handle, self.to_BNVariable()) @name.setter def name(self, name: Optional[str]) -> None: self.set_name_async(name) self._function.view.update_analysis_and_wait() @property def last_seen_name(self) -> str: """Name of the variable, or the name most recently assigned if the variable has since been removed (read-only). """ return core.BNGetLastSeenVariableNameOrDefault(self._function.handle, self.to_BNVariable()) @property def type(self) -> Optional['binaryninja.types.Type']: var_type_conf = core.BNGetVariableType(self._function.handle, self.to_BNVariable()) if var_type_conf.type: return binaryninja.types.Type.create(var_type_conf.type, self._function.platform, var_type_conf.confidence) return None @type.setter def type(self, new_type: 'binaryninja.types.Type') -> None: self.set_type_async(new_type) self._function.view.update_analysis_and_wait() @property def ssa_versions(self) -> Generator[int, None, None]: """Returns the SSA versions associated with this variable. Doesn't return anything for aliased variables.""" if self._il_function is None: raise NotImplementedError("No IL function associated with variable") version_count = ctypes.c_ulonglong() if self._il_function.il_form in [ FunctionGraphType.MediumLevelILFunctionGraph, FunctionGraphType.MediumLevelILSSAFormFunctionGraph ]: versions = core.BNGetMediumLevelILVariableSSAVersions( self._il_function.handle, self.to_BNVariable(), version_count ) elif self._il_function.il_form in [ FunctionGraphType.HighLevelILFunctionGraph, FunctionGraphType.HighLevelILSSAFormFunctionGraph ]: versions = core.BNGetHighLevelILVariableSSAVersions( self._il_function.handle, self.to_BNVariable(), version_count ) else: raise NotImplementedError("Unsupported IL form") if versions is None: raise NotImplementedError("No SSA versions; is this an aliased variable?") try: for version_i in range(version_count.value): yield versions[version_i] finally: core.BNFreeILInstructionList(versions) @property def dead_store_elimination(self) -> DeadStoreElimination: """returns the dead store elimination setting for this variable""" return DeadStoreElimination( core.BNGetFunctionVariableDeadStoreElimination(self._function.handle, self.to_BNVariable()) ) @dead_store_elimination.setter def dead_store_elimination(self, value): core.BNSetFunctionVariableDeadStoreElimination(self._function.handle, self.to_BNVariable(), value) @property def is_parameter_variable(self) -> bool: """returns whether this variable is a function parameter""" return self in self._function.parameter_vars @property def offset_to_next_variable(self) -> Optional[int]: """returns number of bytes to the next variable on the stack""" if self.source_type != VariableSourceType.StackVariableSourceType: return None for i, var in enumerate(self._function.stack_layout): if var == self: if i+1 < len(self._function.stack_layout): return abs(self.storage - self._function.stack_layout[i+1].storage) else: return abs(self.storage) return None @property def function(self) -> 'binaryninja.function.Function': """returns the source Function object which this variable belongs to""" return self._function @property def il_function(self) -> 'function.ILFunctionType': """returns the IL Function object which this variable belongs to""" return self.var._il_function
[docs] def set_name_async(self, name: Optional[str]) -> None: """ ``set_name_async`` provides a way to asynchronously set the name of a variable. This method should be used when speed is of concern. """ if name is None: name = "" self._function.create_user_var(self, self.type, name)
[docs] def set_type_async(self, new_type: 'binaryninja.types.Type') -> None: """ ``set_type_async`` provides a way to asynchronously set the type of a variable. This method should be used when speed is of concern. """ self._function.create_user_var(self, new_type, self.name)
[docs] def set_name_and_type_async(self, name: Optional[str], new_type: 'binaryninja.types.Type') -> None: """ ``set_name_and_type_async`` provides a way to asynchronously set both the name and type of a variable. This method should be used when speed is of concern. """ self._function.create_user_var(self, new_type, name)
[docs] @dataclass(frozen=True) class ConstantReference: value: int size: int pointer: bool intermediate: bool def __repr__(self): if self.pointer: return "<constant pointer %#x>" % self.value if self.size == 0: return "<constant %#x>" % self.value return "<constant %#x size %d>" % (self.value, self.size)
[docs] @dataclass(frozen=True) class IndirectBranchInfo: source_arch: 'binaryninja.architecture.Architecture' source_addr: int dest_arch: 'binaryninja.architecture.Architecture' dest_addr: int auto_defined: bool def __repr__(self): return f"<branch {self.source_arch.name}:{self.source_addr:#x} -> {self.dest_arch.name}:{self.dest_addr:#x}>"
[docs] @decorators.passive class ParameterVariables: def __init__( self, var_list: List[Variable], confidence: int = core.max_confidence, func: Optional['binaryninja.function.Function'] = None ): self._vars = var_list self._confidence = confidence self._func = func def __repr__(self): return f"<ParameterVariables: {str(self._vars)}>" def __len__(self): return len(self._vars) def __iter__(self) -> Generator['Variable', None, None]: for var in self._vars: yield var def __eq__(self, other) -> bool: return (self._vars, self._confidence, self._func) == (other._vars, other._confidence, other._func) def __getitem__(self, idx) -> 'Variable': return self._vars[idx] def __setitem__(self, idx: int, value: 'Variable'): self._vars[idx] = value if self._func is not None: self._func.parameter_vars = self
[docs] def with_confidence(self, confidence: int) -> 'ParameterVariables': return ParameterVariables(list(self._vars), confidence, self._func)
@property def vars(self) -> List['Variable']: return self._vars @property def confidence(self) -> int: return self._confidence @property def function(self) -> Optional['binaryninja.function.Function']: return self._func
[docs] @dataclass(frozen=True, order=True) class AddressRange: start: int # Inclusive starting address end: int # Exclusive ending address def __repr__(self): return f"<{self.start:#x}-{self.end:#x}>" def __contains__(self, i: int): return self.start <= i < self.end