diff options
Diffstat (limited to 'python')
45 files changed, 6878 insertions, 6529 deletions
diff --git a/python/__init__.py b/python/__init__.py index 8e63016d..0b05f5b5 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -22,50 +22,49 @@ import atexit import sys import ctypes -from time import gmtime +from time import gmtime, struct_time import os - -from binaryninja.compatibility import * +from typing import Mapping, Optional # Binary Ninja components import binaryninja._binaryninjacore as core - -from binaryninja.enums import * -from binaryninja.databuffer import * -from binaryninja.filemetadata import * -from binaryninja.fileaccessor import * -from binaryninja.binaryview import * -from binaryninja.debuginfo import * -from binaryninja.transform import * -from binaryninja.architecture import * -from binaryninja.basicblock import * -from binaryninja.function import * -from binaryninja.log import * -from binaryninja.lowlevelil import * -from binaryninja.mediumlevelil import * -from binaryninja.highlevelil import * -from binaryninja.types import * -from binaryninja.typelibrary import * -from binaryninja.functionrecognizer import * -from binaryninja.update import * -from binaryninja.plugin import * -from binaryninja.callingconvention import * -from binaryninja.platform import * -from binaryninja.demangle import * -from binaryninja.mainthread import * -from binaryninja.interaction import * -from binaryninja.lineardisassembly import * -from binaryninja.highlight import * -from binaryninja.scriptingprovider import * -from binaryninja.downloadprovider import * -from binaryninja.pluginmanager import * -from binaryninja.settings import * -from binaryninja.metadata import * -from binaryninja.flowgraph import * -from binaryninja.datarender import * -from binaryninja.websocketprovider import * -from binaryninja.workflow import * +import binaryninja +from .enums import * +from .databuffer import * +from .filemetadata import * +from .fileaccessor import * +from .binaryview import * +from .transform import * +from .architecture import * +from .basicblock import * +from .function import * +from .log import * +from .lowlevelil import * +from .mediumlevelil import * +from .highlevelil import * +from .types import * +from .typelibrary import * +from .functionrecognizer import * +from .update import * +from .plugin import * +from .callingconvention import * +from .platform import * +from .demangle import * +from .mainthread import * +from .interaction import * +from .lineardisassembly import * +from .highlight import * +from .scriptingprovider import * +from .downloadprovider import * +from .pluginmanager import * +from .settings import * +from .metadata import * +from .flowgraph import * +from .datarender import * +from .variable import * +from .websocketprovider import * +from .workflow import * def shutdown(): @@ -101,13 +100,13 @@ class _DestructionCallbackHandler(object): core.BNRegisterObjectDestructionCallbacks(self._cb) def destruct_binary_view(self, ctxt, view): - BinaryView._unregister(view) + binaryninja.binaryview.BinaryView._unregister(view) def destruct_file_metadata(self, ctxt, f): - FileMetadata._unregister(f) + binaryninja.filemetadata.FileMetadata._unregister(f) def destruct_function(self, ctxt, func): - Function._unregister(func) + binaryninja.function.Function._unregister(func) _enable_default_log = True @@ -134,13 +133,13 @@ def _init_plugins(): _destruct_callbacks = _DestructionCallbackHandler() -def disable_default_log(): +def disable_default_log() -> None: '''Disable default logging in headless mode for the current session. By default, logging in headless operation is controlled by the 'python.log.minLevel' settings.''' global _enable_default_log _enable_default_log = False close_logs() -def bundled_plugin_path(): +def bundled_plugin_path() -> Optional[str]: """ ``bundled_plugin_path`` returns a string containing the current plugin path inside the `install path <https://docs.binary.ninja/getting-started.html#binary-path>`_ @@ -149,7 +148,7 @@ def bundled_plugin_path(): """ return core.BNGetBundledPluginDirectory() -def user_plugin_path(): +def user_plugin_path() -> Optional[str]: """ ``user_plugin_path`` returns a string containing the current plugin path inside the `user directory <https://docs.binary.ninja/getting-started.html#user-folder>`_ @@ -158,7 +157,7 @@ def user_plugin_path(): """ return core.BNGetUserPluginDirectory() -def user_directory(): +def user_directory() -> Optional[str]: """ ``user_directory`` returns a string containing the path to the `user directory <https://docs.binary.ninja/getting-started.html#user-folder>`_ @@ -167,7 +166,7 @@ def user_directory(): """ return core.BNGetUserDirectory() -def core_version(): +def core_version() -> Optional[str]: """ ``core_version`` returns a string containing the current version @@ -176,7 +175,7 @@ def core_version(): """ return core.BNGetVersionString() -def core_build_id(): +def core_build_id() -> int: """ ``core_build_id`` returns a integer containing the current build id @@ -185,7 +184,7 @@ def core_build_id(): """ return core.BNGetBuildId() -def core_serial(): +def core_serial() -> Optional[str]: """ ``core_serial`` returns a string containing the current serial number @@ -194,28 +193,28 @@ def core_serial(): """ return core.BNGetSerialNumber() -def core_expires(): +def core_expires() -> struct_time: '''License Expiration''' return gmtime(core.BNGetLicenseExpirationTime()) -def core_product(): +def core_product() -> Optional[str]: '''Product string from the license file''' return core.BNGetProduct() -def core_product_type(): +def core_product_type() -> Optional[str]: '''Product type from the license file''' return core.BNGetProductType() -def core_license_count(): +def core_license_count() -> int: '''License count from the license file''' return core.BNGetLicenseCount() -def core_ui_enabled(): +def core_ui_enabled() -> bool: '''Indicates that a UI exists and the UI has invoked BNInitUI''' return core.BNIsUIEnabled() -def core_set_license(licenseData): +def core_set_license(licenseData:str) -> None: ''' ``core_set_license`` is used to initialize the core with a license file that doesn't necessarily reside on a file system. This is especially useful for headless environments such as docker where loading the license file via an environment variable allows for greater security of the license file itself. @@ -227,15 +226,16 @@ def core_set_license(licenseData): >>> import os >>> core_set_license(os.environ['BNLICENSE']) #Do this before creating any BinaryViews >>> with open_view("/bin/ls") as bv: - ... print(len(bv.functions)) + ... print(len(list(bv.functions))) 128 ''' core.BNSetLicense(licenseData) -def get_memory_usage_info(): +def get_memory_usage_info() -> Mapping[str, int]: count = ctypes.c_ulonglong() info = core.BNGetMemoryUsageInfo(count) + assert info is not None, "core.BNGetMemoryUsageInfo returned None" result = {} for i in range(0, count.value): result[info[i].name] = info[i].value @@ -243,7 +243,7 @@ def get_memory_usage_info(): return result -def open_view(*args, **kwargs): +def open_view(*args, **kwargs) -> Optional[BinaryView]: """ `open_view` is a convenience wrapper for :py:class:`get_view_of_file_with_options` that opens a BinaryView object. @@ -259,7 +259,7 @@ def open_view(*args, **kwargs): :Example: >>> from binaryninja import * >>> with open_view("/bin/ls") as bv: - ... print(len(bv.functions)) + ... print(len(list(bv.functions))) ... 128 diff --git a/python/architecture.py b/python/architecture.py index 2206f50e..51b1ae00 100644 --- a/python/architecture.py +++ b/python/architecture.py @@ -20,66 +20,323 @@ import traceback import ctypes -import abc +from typing import Generator, Union, List, Optional, Mapping, Tuple, NewType # Binary Ninja components -from binaryninja import _binaryninjacore as core -from binaryninja.enums import (Endianness, ImplicitRegisterExtend, BranchType, - InstructionTextTokenType, LowLevelILFlagCondition, FlagRole) import binaryninja -from binaryninja import log -from binaryninja import lowlevelil -from binaryninja import types -from binaryninja import databuffer -from binaryninja import platform -from binaryninja import callingconvention +from . import _binaryninjacore as core +from .enums import (Endianness, ImplicitRegisterExtend, BranchType, + LowLevelILFlagCondition, FlagRole, LowLevelILOperation) +from . import log +from . import lowlevelil +from . import types +from . import databuffer +from . import platform +from . import callingconvention +from . import typelibrary +from . import function +from . import binaryview -# 2-3 compatibility -from binaryninja import range -from binaryninja import with_metaclass -import numbers +RegisterIndex = NewType('RegisterIndex', int) +RegisterStackIndex = NewType('RegisterStackIndex', int) +FlagIndex = NewType('FlagIndex', int) +SemanticClassIndex = NewType('SemanticClassIndex', int) +SemanticGroupIndex = NewType('SemanticGroupIndex', int) +IntrinsicIndex = NewType('IntrinsicIndex', int) +FlagWriteTypeIndex = NewType('FlagWriteTypeIndex', int) +RegisterName = NewType('RegisterName', str) +RegisterStackName = NewType('RegisterStackName', str) +FlagName = NewType('FlagName', str) +SemanticClassName = NewType('SemanticClassName', str) +SemanticGroupName = NewType('SemanticGroupName', str) +IntrinsicName = NewType('IntrinsicName', str) +FlagWriteTypeName = NewType('FlagWriteTypeName', str) -class _ArchitectureMetaClass(type): +RegisterType = Union[RegisterName, 'lowlevelil.ILRegister', RegisterIndex] +FlagType = Union[FlagName, 'lowlevelil.ILFlag', FlagIndex] +RegisterStackType = Union[RegisterStackName, 'lowlevelil.ILRegisterStack', RegisterStackIndex] +SemanticClassType = Union[SemanticClassName, 'lowlevelil.ILSemanticFlagClass', SemanticClassIndex] +SemanticGroupType = Union[SemanticGroupName, 'lowlevelil.ILSemanticFlagGroup', SemanticGroupIndex] +IntrinsicType = Union[IntrinsicName, 'lowlevelil.ILIntrinsic', IntrinsicIndex] + + + +class RegisterInfo(object): + def __init__(self, full_width_reg:RegisterName, size:int, offset:int=0, + extend:ImplicitRegisterExtend=ImplicitRegisterExtend.NoExtend, index:RegisterIndex=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 "<reg: size %d, offset %d in %s%s>" % (self._size, self._offset, self._full_width_reg, extend) @property - def list(self): - binaryninja._init_plugins() - count = ctypes.c_ulonglong() - archs = core.BNGetArchitectureList(count) - result = [] - for i in range(0, count.value): - result.append(CoreArchitecture._from_cache(archs[i])) - core.BNFreeArchitectureList(archs) - return result + def full_width_reg(self) -> RegisterName: + return self._full_width_reg + + @full_width_reg.setter + def full_width_reg(self, value:RegisterName) -> None: + self._full_width_reg = value + + @property + def offset(self) -> int: + return self._offset + + @offset.setter + def offset(self, value:int) -> None: + self._offset = value + + @property + def size(self) -> int: + return self._size + + @size.setter + def size(self, value:int) -> None: + self._size = value + + @property + def extend(self) -> ImplicitRegisterExtend: + return self._extend + + @extend.setter + def extend(self, value:ImplicitRegisterExtend) -> None: + self._extend = value + + @property + def index(self) -> Optional[RegisterIndex]: + return self._index + + @index.setter + def index(self, value:RegisterIndex) -> None: + self._index = value + + +class RegisterStackInfo(object): + def __init__(self, storage_regs:List[RegisterName], top_relative_regs:List[RegisterName], + stack_top_reg:RegisterName, index:RegisterStackIndex=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 "<reg stack: %d regs, stack top in %s>" % (len(self._storage_regs), self._stack_top_reg) + + @property + def storage_regs(self) -> List[RegisterName]: + return self._storage_regs + + @storage_regs.setter + def storage_regs(self, value:List[RegisterName]) -> None: + self._storage_regs = value + + @property + def top_relative_regs(self) -> List[RegisterName]: + return self._top_relative_regs + + @top_relative_regs.setter + def top_relative_regs(self, value:List[RegisterName]) -> None: + self._top_relative_regs = value + + @property + def stack_top_reg(self) -> RegisterName: + return self._stack_top_reg + + @stack_top_reg.setter + def stack_top_reg(self, value:RegisterName) -> None: + self._stack_top_reg = value + + @property + def index(self) -> Optional[RegisterStackIndex]: + return self._index + + @index.setter + def index(self, value:RegisterStackIndex) -> None: + 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 "<input: %s>" % str(self._type) + return "<input: %s %s>" % (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 - def __iter__(self): + @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 "<intrinsic: %s -> %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 "<instr: %d bytes%s, %s>" % (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 _ArchitectureMetaClass(type): + def __iter__(self) -> Generator['Architecture', None, None]: binaryninja._init_plugins() count = ctypes.c_ulonglong() archs = core.BNGetArchitectureList(count) + if archs is None: + return try: for i in range(0, count.value): yield CoreArchitecture._from_cache(archs[i]) finally: core.BNFreeArchitectureList(archs) - def __getitem__(cls, name): + def __getitem__(cls:'_ArchitectureMetaClass', name:str) -> 'Architecture': binaryninja._init_plugins() arch = core.BNGetArchitectureByName(name) if arch is None: raise KeyError("'%s' is not a valid architecture" % str(name)) return CoreArchitecture._from_cache(arch) - def register(cls): - binaryninja._init_plugins() - if cls.name is None: - raise ValueError("architecture 'name' is not defined") - arch = cls() - cls._registered_cb = arch._cb - arch.handle = core.BNRegisterArchitecture(cls.name, arch._cb) - -class Architecture(with_metaclass(_ArchitectureMetaClass, object)): +class Architecture(metaclass=_ArchitectureMetaClass): """ ``class Architecture`` is the parent class for all CPU architectures. Subclasses of Architecture implement assembly, disassembly, IL lifting, and patching. @@ -120,7 +377,7 @@ class Architecture(with_metaclass(_ArchitectureMetaClass, object)): global_regs = [] system_regs = [] flags = [] - flag_write_types = [] + flag_write_types:List[FlagWriteTypeName] = [] semantic_flag_classes = [] semantic_flag_groups = [] flag_roles = {} @@ -217,119 +474,117 @@ class Architecture(with_metaclass(_ArchitectureMetaClass, object)): self._cb.invertBranch = self._cb.invertBranch.__class__(self._invert_branch) self._cb.skipAndReturnValue = self._cb.skipAndReturnValue.__class__(self._skip_and_return_value) - self.__dict__["endianness"] = self.__class__.endianness - self.__dict__["address_size"] = self.__class__.address_size - self.__dict__["default_int_size"] = self.__class__.default_int_size - self.__dict__["instr_alignment"] = self.__class__.instr_alignment - self.__dict__["max_instr_length"] = self.__class__.max_instr_length - self.__dict__["opcode_display_length"] = self.__class__.opcode_display_length - self.__dict__["stack_pointer"] = self.__class__.stack_pointer - self.__dict__["link_reg"] = self.__class__.link_reg + self.__dict__['endianness'] = self.__class__.endianness + self.__dict__['address_size'] = self.__class__.address_size + self.__dict__['default_int_size'] = self.__class__.default_int_size + self.__dict__['instr_alignment'] = self.__class__.instr_alignment + self.__dict__['max_instr_length'] = self.__class__.max_instr_length + self.__dict__['opcode_display_length'] = self.__class__.opcode_display_length + self.__dict__['stack_pointer'] = self.__class__.stack_pointer + self.__dict__['link_reg'] = self.__class__.link_reg - self._all_regs = {} - self._full_width_regs = {} - self._regs_by_index = {} - self.__dict__["regs"] = self.__class__.regs - reg_index = 0 + self._all_regs:Mapping[RegisterName, RegisterIndex] = {} + self._full_width_regs:Mapping[RegisterName, RegisterIndex] = {} + self._regs_by_index:Mapping[RegisterIndex, RegisterName] = {} + self.regs:Mapping[RegisterName, RegisterInfo] = self.__class__.regs + reg_index = RegisterIndex(0) # Registers used for storage in register stacks must be sequential, so allocate these in order first - self._all_reg_stacks = {} - self._reg_stacks_by_index = {} - self.__dict__["reg_stacks"] = self.__class__.reg_stacks - reg_stack_index = 0 - for reg_stack in self.reg_stacks: - info = self.reg_stacks[reg_stack] + self._all_reg_stacks:Mapping[RegisterStackName, RegisterStackIndex] = {} + self._reg_stacks_by_index:Mapping[RegisterStackIndex, RegisterStackName] = {} + self.reg_stacks:Mapping[RegisterStackName, RegisterStackInfo] = self.__class__.reg_stacks + reg_stack_index = RegisterStackIndex(0) + for reg_stack, info in self.reg_stacks.items(): for reg in info.storage_regs: self._all_regs[reg] = reg_index self._regs_by_index[reg_index] = reg self.regs[reg].index = reg_index - reg_index += 1 + reg_index = RegisterIndex(reg_index + 1) for reg in info.top_relative_regs: self._all_regs[reg] = reg_index self._regs_by_index[reg_index] = reg self.regs[reg].index = reg_index - reg_index += 1 + reg_index = RegisterIndex(reg_index + 1) if reg_stack not in self._all_reg_stacks: self._all_reg_stacks[reg_stack] = reg_stack_index self._reg_stacks_by_index[reg_stack_index] = reg_stack self.reg_stacks[reg_stack].index = reg_stack_index - reg_stack_index += 1 + reg_stack_index = RegisterStackIndex(reg_stack_index + 1) - for reg in self.regs: - info = self.regs[reg] + for reg, info in self.regs.items(): if reg not in self._all_regs: self._all_regs[reg] = reg_index self._regs_by_index[reg_index] = reg self.regs[reg].index = reg_index - reg_index += 1 + reg_index = RegisterIndex(reg_index + 1) if info.full_width_reg not in self._all_regs: self._all_regs[info.full_width_reg] = reg_index self._regs_by_index[reg_index] = info.full_width_reg self.regs[info.full_width_reg].index = reg_index - reg_index += 1 + reg_index = RegisterIndex(reg_index + 1) if info.full_width_reg not in self._full_width_regs: self._full_width_regs[info.full_width_reg] = self._all_regs[info.full_width_reg] - self._flags = {} - self._flags_by_index = {} - self.__dict__["flags"] = self.__class__.flags - flag_index = 0 + self._flags:Mapping[FlagName, FlagIndex] = {} + self._flags_by_index:Mapping[FlagIndex, FlagName] = {} + self.flags:List[FlagName] = self.__class__.flags + flag_index = FlagIndex(0) for flag in self.__class__.flags: if flag not in self._flags: self._flags[flag] = flag_index self._flags_by_index[flag_index] = flag - flag_index += 1 + flag_index = FlagIndex(flag_index + 1) - self._flag_write_types = {} - self._flag_write_types_by_index = {} - self.__dict__["flag_write_types"] = self.__class__.flag_write_types - write_type_index = 1 + self._flag_write_types:Mapping[FlagWriteTypeName, FlagWriteTypeIndex] = {} + self._flag_write_types_by_index:Mapping[FlagWriteTypeIndex, FlagWriteTypeName] = {} + self.flag_write_types:List[FlagWriteTypeName] = self.__class__.flag_write_types + write_type_index = FlagWriteTypeIndex(1) for write_type in self.__class__.flag_write_types: if write_type not in self._flag_write_types: self._flag_write_types[write_type] = write_type_index self._flag_write_types_by_index[write_type_index] = write_type - write_type_index += 1 + write_type_index = FlagWriteTypeIndex(write_type_index + 1) - self._semantic_flag_classes = {} - self._semantic_flag_classes_by_index = {} - self.__dict__["semantic_flag_classes"] = self.__class__.semantic_flag_classes - semantic_class_index = 1 + self._semantic_flag_classes:Mapping[SemanticClassName, SemanticClassIndex] = {} + self._semantic_flag_classes_by_index:Mapping[SemanticClassIndex, SemanticClassName] = {} + self.semantic_flag_classes:List[SemanticClassName] = self.__class__.semantic_flag_classes + semantic_class_index = SemanticClassIndex(1) for sem_class in self.__class__.semantic_flag_classes: if sem_class not in self._semantic_flag_classes: self._semantic_flag_classes[sem_class] = semantic_class_index self._semantic_flag_classes_by_index[semantic_class_index] = sem_class - semantic_class_index += 1 + semantic_class_index = SemanticClassIndex(semantic_class_index + 1) - self._semantic_flag_groups = {} - self._semantic_flag_groups_by_index = {} - self.__dict__["semantic_flag_groups"] = self.__class__.semantic_flag_groups - semantic_group_index = 0 + self._semantic_flag_groups:Mapping[SemanticGroupName, SemanticGroupIndex] = {} + self._semantic_flag_groups_by_index:Mapping[SemanticGroupIndex, SemanticGroupName] = {} + self.semantic_flag_groups:List[SemanticGroupName] = self.__class__.semantic_flag_groups + semantic_group_index = SemanticGroupIndex(0) for sem_group in self.__class__.semantic_flag_groups: if sem_group not in self._semantic_flag_groups: self._semantic_flag_groups[sem_group] = semantic_group_index self._semantic_flag_groups_by_index[semantic_group_index] = sem_group - semantic_group_index += 1 + semantic_group_index = SemanticGroupIndex(semantic_group_index + 1) - self._flag_roles = {} - self.__dict__["flag_roles"] = self.__class__.flag_roles + self._flag_roles:Mapping[FlagIndex, FlagRole] = {} + self.flag_roles:Mapping[FlagName, FlagRole] = self.__class__.flag_roles for flag in self.__class__.flag_roles: role = self.__class__.flag_roles[flag] if isinstance(role, str): role = FlagRole[role] self._flag_roles[self._flags[flag]] = role - self.__dict__["flags_required_for_flag_condition"] = self.__class__.flags_required_for_flag_condition + self.flags_required_for_flag_condition:Mapping['lowlevelil.LowLevelILFlagCondition', List[FlagName]] = self.__class__.flags_required_for_flag_condition - self._flags_required_by_semantic_flag_group = {} - self.__dict__["flags_required_for_semantic_flag_group"] = self.__class__.flags_required_for_semantic_flag_group + self._flags_required_by_semantic_flag_group:Mapping[SemanticGroupIndex, List[FlagIndex]] = {} + self.flags_required_for_semantic_flag_group:Mapping[SemanticGroupName, List[FlagName]] = self.__class__.flags_required_for_semantic_flag_group for group in self.__class__.flags_required_for_semantic_flag_group: - flags = [] + flags:List[FlagIndex] = [] for flag in self.__class__.flags_required_for_semantic_flag_group[group]: flags.append(self._flags[flag]) self._flags_required_by_semantic_flag_group[self._semantic_flag_groups[group]] = flags self._flag_conditions_for_semantic_flag_group = {} - self.__dict__["flag_conditions_for_semantic_flag_group"] = self.__class__.flag_conditions_for_semantic_flag_group + self.flag_conditions_for_semantic_flag_group = self.__class__.flag_conditions_for_semantic_flag_group for group in self.__class__.flag_conditions_for_semantic_flag_group: class_cond = {} for sem_class in self.__class__.flag_conditions_for_semantic_flag_group[group]: @@ -340,7 +595,7 @@ class Architecture(with_metaclass(_ArchitectureMetaClass, object)): self._flag_conditions_for_semantic_flag_group[self._semantic_flag_groups[group]] = class_cond self._flags_written_by_flag_write_type = {} - self.__dict__["flags_written_by_flag_write_type"] = self.__class__.flags_written_by_flag_write_type + self.flags_written_by_flag_write_type = self.__class__.flags_written_by_flag_write_type for write_type in self.__class__.flags_written_by_flag_write_type: flags = [] for flag in self.__class__.flags_written_by_flag_write_type[write_type]: @@ -348,7 +603,7 @@ class Architecture(with_metaclass(_ArchitectureMetaClass, object)): self._flags_written_by_flag_write_type[self._flag_write_types[write_type]] = flags self._semantic_class_for_flag_write_type = {} - self.__dict__["semantic_class_for_flag_write_type"] = self.__class__.semantic_class_for_flag_write_type + self.semantic_class_for_flag_write_type = self.__class__.semantic_class_for_flag_write_type for write_type in self.__class__.semantic_class_for_flag_write_type: sem_class = self.__class__.semantic_class_for_flag_write_type[write_type] if sem_class in self._semantic_flag_classes: @@ -357,25 +612,25 @@ class Architecture(with_metaclass(_ArchitectureMetaClass, object)): sem_class_index = 0 self._semantic_class_for_flag_write_type[self._flag_write_types[write_type]] = sem_class_index - self.__dict__["global_regs"] = self.__class__.global_regs - self.__dict__["system_regs"] = self.__class__.system_regs + self.global_regs = self.__class__.global_regs + self.system_regs = self.__class__.system_regs - self._intrinsics = {} - self._intrinsics_by_index = {} - self.__dict__["intrinsics"] = self.__class__.intrinsics - intrinsic_index = 0 + self._intrinsics:Mapping[IntrinsicName, IntrinsicIndex] = {} + self._intrinsics_by_index:Mapping[IntrinsicIndex, Tuple[IntrinsicName, IntrinsicInfo]] = {} + self.intrinsics = self.__class__.intrinsics + intrinsic_index = IntrinsicIndex(0) for intrinsic in self.__class__.intrinsics.keys(): if intrinsic not in self._intrinsics: info = self.__class__.intrinsics[intrinsic] for i in range(0, len(info.inputs)): if isinstance(info.inputs[i], types.Type): - info.inputs[i] = binaryninja.function.IntrinsicInput(info.inputs[i]) + info.inputs[i] = IntrinsicInput(info.inputs[i]) elif isinstance(info.inputs[i], tuple): - info.inputs[i] = binaryninja.function.IntrinsicInput(info.inputs[i][0], info.inputs[i][1]) + info.inputs[i] = IntrinsicInput(info.inputs[i][0], info.inputs[i][1]) info.index = intrinsic_index self._intrinsics[intrinsic] = intrinsic_index self._intrinsics_by_index[intrinsic_index] = (intrinsic, info) - intrinsic_index += 1 + intrinsic_index = IntrinsicIndex(intrinsic_index + 1) self._pending_reg_lists = {} self._pending_token_lists = {} @@ -399,27 +654,32 @@ class Architecture(with_metaclass(_ArchitectureMetaClass, object)): def __hash__(self): return hash(ctypes.addressof(self.handle.contents)) - def __setattr__(self, name, value): - if ((name == "name") or (name == "endianness") or (name == "address_size") or - (name == "default_int_size") or (name == "regs") or (name == "get_max_instruction_length") or - (name == "get_instruction_alignment")): - raise AttributeError("attribute '%s' is read only" % name) - else: - try: - object.__setattr__(self, name, value) - except AttributeError: - raise AttributeError("attribute '%s' is read only" % name) + # def __setattr__(self, name, value): + # if ((name == "name") or (name == "endianness") or (name == "address_size") or + # (name == "default_int_size") or (name == "regs") or (name == "get_max_instruction_length") or + # (name == "get_instruction_alignment")): + # raise AttributeError("attribute '%s' is read only" % name) + # else: + # try: + # object.__setattr__(self, name, value) + # except AttributeError: + # raise AttributeError("attribute '%s' is read only" % name) - @property - def list(self): - """Allow tab completion to discover metaclass list property""" - pass + @classmethod + def register(cls) -> None: + binaryninja._init_plugins() + if cls.name is None: + raise ValueError("architecture 'name' is not defined") + arch = cls() + cls._registered_cb = arch._cb + arch.handle = core.BNRegisterArchitecture(cls.name, arch._cb) @property - def full_width_regs(self): + def full_width_regs(self) -> List[RegisterName]: """List of full width register strings (read-only)""" count = ctypes.c_ulonglong() regs = core.BNGetFullWidthArchitectureRegisters(self.handle, count) + assert regs is not None, "core.BNGetFullWidthArchitectureRegisters returned None" result = [] for i in range(0, count.value): result.append(core.BNGetArchitectureRegisterName(self.handle, regs[i])) @@ -427,10 +687,11 @@ class Architecture(with_metaclass(_ArchitectureMetaClass, object)): return result @property - def calling_conventions(self): + def calling_conventions(self) -> Mapping[str, 'callingconvention.CallingConvention']: """Dict of CallingConvention objects (read-only)""" count = ctypes.c_ulonglong() cc = core.BNGetArchitectureCallingConventions(self.handle, count) + assert cc is not None, "core.BNGetArchitectureCallingConventions returned None" result = {} for i in range(0, count.value): obj = callingconvention.CallingConvention(handle=core.BNNewCallingConventionReference(cc[i])) @@ -439,24 +700,25 @@ class Architecture(with_metaclass(_ArchitectureMetaClass, object)): return result @property - def standalone_platform(self): + def standalone_platform(self) -> 'platform.Platform': """Architecture standalone platform (read-only)""" pl = core.BNGetArchitectureStandalonePlatform(self.handle) return platform.Platform(self, pl) @property - def type_libraries(self): + def type_libraries(self) -> List['typelibrary.TypeLibrary']: """Architecture type libraries""" count = ctypes.c_ulonglong(0) result = [] handles = core.BNGetArchitectureTypeLibraries(self.handle, count) + assert handles is not None, "core.BNGetArchitectureTypeLibraries returned None" for i in range(0, count.value): - result.append(binaryninja.typelibrary.TypeLibrary(core.BNNewTypeLibraryReference(handles[i]))) + result.append(typelibrary.TypeLibrary(core.BNNewTypeLibraryReference(handles[i]))) core.BNFreeTypeLibraryList(handles, count.value) return result @property - def can_assemble(self): + def can_assemble(self) -> bool: """returns if the architecture can assemble instructions (read-only)""" return core.BNCanArchitectureAssemble(self.handle) @@ -550,7 +812,7 @@ class Architecture(with_metaclass(_ArchitectureMetaClass, object)): tokens = info[0] length[0] = info[1] count[0] = len(tokens) - token_buf = binaryninja.function.InstructionTextToken.get_instruction_lines(tokens) + token_buf = function.InstructionTextToken._get_core_struct(tokens) result[0] = token_buf ptr = ctypes.cast(token_buf, ctypes.c_void_p) self._pending_token_lists[ptr.value] = (ptr.value, token_buf) @@ -600,7 +862,7 @@ class Architecture(with_metaclass(_ArchitectureMetaClass, object)): log.log_error(traceback.format_exc()) return core.BNAllocString("") - def _get_flag_write_type_name(self, ctxt, write_type): + def _get_flag_write_type_name(self, ctxt, write_type:FlagWriteTypeIndex): try: if write_type in self._flag_write_types_by_index: return core.BNAllocString(self._flag_write_types_by_index[write_type]) @@ -717,16 +979,13 @@ class Architecture(with_metaclass(_ArchitectureMetaClass, object)): count[0] = 0 return None - def _get_flag_role(self, ctxt, flag, sem_class): - try: - if sem_class in self._semantic_flag_classes_by_index: - sem_class = self._semantic_flag_classes_by_index[sem_class] - else: - sem_class = None - return self.get_flag_role(flag, sem_class) - except KeyError: - log.log_error(traceback.format_exc()) - return FlagRole.SpecialFlagRole + def _get_flag_role(self, ctxt, flag:FlagIndex, sem_class:Optional[SemanticClassName]=None): + if sem_class in self._semantic_flag_classes: + assert sem_class is not None + _sem_class = self._semantic_flag_classes[sem_class] + else: + _sem_class = None + return self.get_flag_role(flag, _sem_class) def _get_flags_required_for_flag_condition(self, ctxt, cond, sem_class, count): try: @@ -903,6 +1162,8 @@ class Architecture(with_metaclass(_ArchitectureMetaClass, object)): result[0].extend = ImplicitRegisterExtend.NoExtend def _get_stack_pointer_register(self, ctxt): + if self.stack_pointer is None: + return None try: return self._all_regs[self.stack_pointer] except KeyError: @@ -1214,234 +1475,10 @@ class Architecture(with_metaclass(_ArchitectureMetaClass, object)): log.log_error(traceback.format_exc()) return False - def perform_get_associated_arch_by_address(self, addr): - """ - Deprecated method provided for compatibility. Architecture plugins should override :func:`get_associated_arch_by_address`. - """ + def get_associated_arch_by_address(self, addr:int) -> Tuple['Architecture', int]: return self, addr - @abc.abstractmethod - def perform_get_instruction_info(self, data, addr): - """ - Deprecated method provided for compatibility. Architecture plugins should override :func:`get_instruction_info`. - - :param str data: bytes to decode - :param int addr: virtual address of the byte to be decoded - :return: a :py:class:`InstructionInfo` object containing the length and branch types for the given instruction - :rtype: InstructionInfo - """ - raise NotImplementedError - - @abc.abstractmethod - def perform_get_instruction_text(self, data, addr): - """ - Deprecated method provided for compatibility. Architecture plugins should override :func:`get_instruction_text`. - - :param str data: bytes to decode - :param int addr: virtual address of the byte to be decoded - :return: a tuple of list(InstructionTextToken) and length of instruction decoded - :rtype: tuple(list(InstructionTextToken), int) - """ - raise NotImplementedError - - @abc.abstractmethod - def perform_get_instruction_low_level_il(self, data, addr, il): - """ - Deprecated method provided for compatibility. Architecture plugins should override :func:`get_instruction_low_level_il`. - - :param str data: bytes to be interpreted as low-level IL instructions - :param int addr: virtual address of start of ``data`` - :param LowLevelILFunction il: LowLevelILFunction object to append LowLevelILExpr objects to - :rtype: length of bytes read on success, None on failure - """ - raise NotImplementedError - - @abc.abstractmethod - def perform_get_flag_write_low_level_il(self, op, size, write_type, flag, operands, il): - """ - Deprecated method provided for compatibility. Architecture plugins should override :func:`get_flag_write_low_level_il`. - - :param LowLevelILOperation op: - :param int size: - :param int write_type: - :param int flag: - :param operands: - :type operands: list(str) or list(int) - :param LowLevelILFunction il: - :rtype: LowLevelILExpr - """ - flag = self.get_flag_index(flag) - if flag not in self._flag_roles: - return il.unimplemented() - return self.get_default_flag_write_low_level_il(op, size, self._flag_roles[flag], operands, il) - - @abc.abstractmethod - def perform_get_flag_condition_low_level_il(self, cond, sem_class, il): - """ - Deprecated method provided for compatibility. Architecture plugins should override :func:`get_flag_condition_low_level_il`. - - :param LowLevelILFlagCondition cond: Flag condition to be computed - :param str sem_class: Semantic class to be used (None for default semantics) - :param LowLevelILFunction il: LowLevelILFunction object to append LowLevelILExpr objects to - :rtype: LowLevelILExpr - """ - return self.get_default_flag_condition_low_level_il(cond, sem_class, il) - - @abc.abstractmethod - def perform_get_semantic_flag_group_low_level_il(self, sem_group, il): - """ - Deprecated method provided for compatibility. Architecture plugins should override :func:`get_semantic_flag_group_low_level_il`. - - :param str sem_group: Semantic group to be computed - :param LowLevelILFunction il: LowLevelILFunction object to append LowLevelILExpr objects to - :rtype: LowLevelILExpr - """ - return il.unimplemented() - - @abc.abstractmethod - def perform_assemble(self, code, addr): - """ - Deprecated method provided for compatibility. Architecture plugins should override :func:`assemble`. - - :param str code: string representation of the instructions to be assembled - :param int addr: virtual address that the instructions will be loaded at - :return: the bytes for the assembled instructions or error string - :rtype: (a tuple of instructions and empty string) or (or None and error string) - """ - raise NotImplementedError("Architecture does not implement an assembler.\n") - - @abc.abstractmethod - def perform_is_never_branch_patch_available(self, data, addr): - """ - Deprecated method provided for compatibility. Architecture plugins should override :func:`is_never_branch_patch_available`. - - .. note:: Architecture subclasses should implement this method. - - .. warning:: This method should never be called directly. - - :param str data: bytes to be checked - :param int addr: the virtual address of the instruction to be patched - :return: True if the instruction can be patched, False otherwise - :rtype: bool - """ - return False - - @abc.abstractmethod - def perform_is_always_branch_patch_available(self, data, addr): - """ - Deprecated method provided for compatibility. Architecture plugins should override :func:`is_always_branch_patch_available`. - - :param str data: bytes to be checked - :param int addr: the virtual address of the instruction to be patched - :return: True if the instruction can be patched, False otherwise - :rtype: bool - """ - return False - - @abc.abstractmethod - def perform_is_invert_branch_patch_available(self, data, addr): - """ - Deprecated method provided for compatibility. Architecture plugins should override :func:`is_invert_branch_patch_available`. - - :param int addr: the virtual address of the instruction to be patched - :return: True if the instruction can be patched, False otherwise - :rtype: bool - """ - return False - - @abc.abstractmethod - def perform_is_skip_and_return_zero_patch_available(self, data, addr): - """ - Deprecated method provided for compatibility. Architecture plugins should override :func:`is_skip_and_return_zero_patch_available`. - - :param str data: bytes to be checked - :param int addr: the virtual address of the instruction to be patched - :return: True if the instruction can be patched, False otherwise - :rtype: bool - """ - return False - - @abc.abstractmethod - def perform_is_skip_and_return_value_patch_available(self, data, addr): - """ - Deprecated method provided for compatibility. Architecture plugins should override :func:`is_skip_and_return_value_patch_available`. - - :param str data: bytes to be checked - :param int addr: the virtual address of the instruction to be patched - :return: True if the instruction can be patched, False otherwise - :rtype: bool - """ - return False - - @abc.abstractmethod - def perform_convert_to_nop(self, data, addr): - """ - Deprecated method provided for compatibility. Architecture plugins should override :func:`convert_to_nop`. - - :param str data: bytes at virtual address ``addr`` - :param int addr: the virtual address of the instruction to be patched - :return: nop sequence of same length as ``data`` or None - :rtype: str or None - """ - return None - - @abc.abstractmethod - def perform_always_branch(self, data, addr): - """ - Deprecated method provided for compatibility. Architecture plugins should override :func:`always_branch`. - - :param str data: bytes to be checked - :param int addr: the virtual address of the instruction to be patched - :return: The bytes of the replacement unconditional branch instruction - :rtype: str - """ - return None - - @abc.abstractmethod - def perform_invert_branch(self, data, addr): - """ - Deprecated method provided for compatibility. Architecture plugins should override :func:`invert_branch`. - - :param str data: bytes to be checked - :param int addr: the virtual address of the instruction to be patched - :return: The bytes of the replacement unconditional branch instruction - :rtype: str - """ - return None - - @abc.abstractmethod - def perform_skip_and_return_value(self, data, addr, value): - """ - Deprecated method provided for compatibility. Architecture plugins should override :func:`skip_and_return_value`. - - :param str data: bytes to be checked - :param int addr: the virtual address of the instruction to be patched - :param int value: value to be returned - :return: The bytes of the replacement unconditional branch instruction - :rtype: str - """ - return None - - def perform_get_flag_role(self, flag, sem_class): - """ - Deprecated method provided for compatibility. Architecture plugins should override :func:`get_flag_role`. - """ - if flag in self._flag_roles: - return self._flag_roles[flag] - return FlagRole.SpecialFlagRole - - def perform_get_flags_required_for_flag_condition(self, cond, sem_class): - """ - Deprecated method provided for compatibility. Architecture plugins should override :func:`get_flags_required_for_flag_condition`. - """ - if cond in self.flags_required_for_flag_condition: - return self.flags_required_for_flag_condition[cond] - return [] - - def get_associated_arch_by_address(self, addr): - return self.perform_get_associated_arch_by_address(addr) - - def get_instruction_info(self, data, addr): + def get_instruction_info(self, data:bytes, addr:int) -> Optional[InstructionInfo]: """ ``get_instruction_info`` returns an InstructionInfo object for the instruction at the given virtual address ``addr`` with data ``data``. @@ -1471,9 +1508,9 @@ class Architecture(with_metaclass(_ArchitectureMetaClass, object)): :return: the InstructionInfo for the current instruction :rtype: InstructionInfo """ - return self.perform_get_instruction_info(data, addr) + raise NotImplementedError - def get_instruction_text(self, data, addr): + def get_instruction_text(self, data:bytes, addr:int) -> Tuple[List['function.InstructionTextToken'], int]: """ ``get_instruction_text`` returns a tuple containing a list of decoded InstructionTextToken objects and the bytes used at the given virtual address ``addr`` with data ``data``. @@ -1485,15 +1522,15 @@ class Architecture(with_metaclass(_ArchitectureMetaClass, object)): :return: a tuple containing the InstructionTextToken list and length of bytes decoded :rtype: tuple(list(InstructionTextToken), int) """ - return self.perform_get_instruction_text(data, addr) + raise NotImplementedError - def get_instruction_low_level_il_instruction(self, bv, addr): + def get_instruction_low_level_il_instruction(self, bv:'binaryview.BinaryView', addr:int) -> 'lowlevelil.LowLevelILInstruction': il = lowlevelil.LowLevelILFunction(self) data = bv.read(addr, self.max_instr_length) self.get_instruction_low_level_il(data, addr, il) return il[0] - def get_instruction_low_level_il(self, data, addr, il): + def get_instruction_low_level_il(self, data:bytes, addr:int, il:'lowlevelil.LowLevelILFunction') -> int: """ ``get_instruction_low_level_il`` appends LowLevelILExpr objects to ``il`` for the instruction at the given virtual address ``addr`` with data ``data``. @@ -1509,94 +1546,108 @@ class Architecture(with_metaclass(_ArchitectureMetaClass, object)): :return: the length of the current instruction :rtype: int """ - return self.perform_get_instruction_low_level_il(data, addr, il) + raise NotImplementedError - def get_low_level_il_from_bytes(self, data, addr): + def get_low_level_il_from_bytes(self, data:bytes, addr:int) -> Generator['lowlevelil.LowLevelILInstruction', None, None]: """ ``get_low_level_il_from_bytes`` converts the instruction in bytes to ``il`` at the given virtual address :param str data: the bytes of the instruction :param int addr: virtual address of bytes in ``data`` - :return: the instruction - :rtype: LowLevelILInstruction + :return: a list of low level il instructions + :rtype: List[LowLevelILInstruction] :Example: - >>> arch.get_low_level_il_from_bytes(b'\\xeb\\xfe', 0x40DEAD) - <il: jump(0x40dead)> + >>> list(arch.get_low_level_il_from_bytes(b'\\xeb\\xfe', 0x40DEAD)) + [<il: jump(0x40dead)>] >>> """ func = lowlevelil.LowLevelILFunction(self) self.get_instruction_low_level_il(data, addr, func) - return func[0] + return func.instructions - def get_reg_name(self, reg): + def get_reg_name(self, reg:RegisterIndex) -> RegisterName: """ - ``get_reg_name`` gets a register name from a register number. + ``get_reg_name`` gets a register name from a register index. - :param int reg: register number - :return: the corresponding register string - :rtype: str + :param RegisterIndex reg: register index + :return: the corresponding register name + :rtype: RegisterName """ - return core.BNGetArchitectureRegisterName(self.handle, reg) + return RegisterName(core.BNGetArchitectureRegisterName(self.handle, reg)) - def get_reg_stack_name(self, reg_stack): + def get_reg_stack_name(self, reg_stack:RegisterStackIndex) -> RegisterStackName: """ ``get_reg_stack_name`` gets a register stack name from a register stack number. :param int reg_stack: register stack number :return: the corresponding register string - :rtype: str + :rtype: RegisterStackName """ - return core.BNGetArchitectureRegisterStackName(self.handle, reg_stack) + return RegisterStackName(core.BNGetArchitectureRegisterStackName(self.handle, reg_stack)) - def get_reg_stack_for_reg(self, reg): - reg = self.get_reg_index(reg) - result = core.BNGetArchitectureRegisterStackForRegister(self.handle, reg) + def get_reg_stack_for_reg(self, reg:RegisterName) -> Optional[RegisterStackName]: + _reg = self.get_reg_index(reg) + result = core.BNGetArchitectureRegisterStackForRegister(self.handle, _reg) if result == 0xffffffff: return None return self.get_reg_stack_name(result) - def get_flag_name(self, flag): + def get_flag_name(self, flag:FlagIndex) -> FlagName: """ - ``get_flag_name`` gets a flag name from a flag number. + ``get_flag_name`` gets a flag name from a flag index. - :param int reg: register number - :return: the corresponding register string - :rtype: str + :param int flag: flag index + :return: the corresponding flag name string + :rtype: FlagName """ - return core.BNGetArchitectureFlagName(self.handle, flag) + return FlagName(core.BNGetArchitectureFlagName(self.handle, flag)) - def get_reg_index(self, reg): + def get_reg_index(self, reg:RegisterType) -> RegisterIndex: if isinstance(reg, str): - return self.regs[reg].index + try: + index = self.regs[reg].index + assert index is not None + return index + except KeyError: + log.log_error(f"Failed to map string {reg} to register index: ") + log.log_error(traceback.format_exc()) elif isinstance(reg, lowlevelil.ILRegister): return reg.index - return reg + elif isinstance(reg, RegisterIndex): + return reg + raise Exception("Attempting to get register index of non-existant register") - def get_reg_stack_index(self, reg_stack): + def get_reg_stack_index(self, reg_stack:RegisterStackType) -> RegisterStackIndex: if isinstance(reg_stack, str): - return self.reg_stacks[reg_stack].index + reg_stack_info = self.reg_stacks[reg_stack] + if reg_stack_info is not None and reg_stack_info.index is not None: + return reg_stack_info.index elif isinstance(reg_stack, lowlevelil.ILRegisterStack): return reg_stack.index - return reg_stack + elif isinstance(reg_stack, RegisterStackIndex): + return reg_stack + raise Exception("reg_stack is not convertable to index") - def get_flag_index(self, flag): + def get_flag_index(self, flag:FlagType) -> FlagIndex: if isinstance(flag, str): return self._flags[flag] elif isinstance(flag, lowlevelil.ILFlag): return flag.index - return flag + elif isinstance(flag, FlagIndex): + return flag + raise Exception("flag is not convertable to index") - def get_semantic_flag_class_index(self, sem_class): - if sem_class is None: - return 0 - elif isinstance(sem_class, str): + def get_semantic_flag_class_index(self, sem_class:SemanticClassType) -> SemanticClassIndex: + if isinstance(sem_class, SemanticClassName): return self._semantic_flag_classes[sem_class] elif isinstance(sem_class, lowlevelil.ILSemanticFlagClass): return sem_class.index - return sem_class + elif isinstance(sem_class, SemanticClassIndex): + return sem_class + raise Exception("sem_class is not convertable to index") - def get_semantic_flag_class_name(self, class_index): + def get_semantic_flag_class_name(self, class_index:SemanticClassIndex) -> SemanticClassName: """ ``get_semantic_flag_class_name`` gets the name of a semantic flag class from the index. @@ -1604,21 +1655,21 @@ class Architecture(with_metaclass(_ArchitectureMetaClass, object)): :return: the name of the semantic flag class :rtype: str """ - if not isinstance(class_index, numbers.Integral): + if not isinstance(class_index, int): raise ValueError("argument 'class_index' must be an integer") try: return self._semantic_flag_classes_by_index[class_index] except KeyError: raise AttributeError("argument class_index is not a valid class index") - def get_semantic_flag_group_index(self, sem_group): + def get_semantic_flag_group_index(self, sem_group:SemanticGroupType) -> SemanticGroupIndex: if isinstance(sem_group, str): return self._semantic_flag_groups[sem_group] elif isinstance(sem_group, lowlevelil.ILSemanticFlagGroup): return sem_group.index return sem_group - def get_semantic_flag_group_name(self, group_index): + def get_semantic_flag_group_name(self, group_index:SemanticGroupIndex) -> SemanticGroupName: """ ``get_semantic_flag_group_name`` gets the name of a semantic flag group from the index. @@ -1626,61 +1677,70 @@ class Architecture(with_metaclass(_ArchitectureMetaClass, object)): :return: the name of the semantic flag group :rtype: str """ - if not isinstance(group_index, numbers.Integral): + if not isinstance(group_index, int): raise ValueError("argument 'group_index' must be an integer") try: return self._semantic_flag_groups_by_index[group_index] except KeyError: raise AttributeError("argument group_index is not a valid group index") - def get_intrinsic_name(self, intrinsic): + def get_intrinsic_name(self, intrinsic:IntrinsicIndex) -> IntrinsicName: """ ``get_intrinsic_name`` gets an intrinsic name from an intrinsic number. :param int intrinsic: intrinsic number :return: the corresponding intrinsic string - :rtype: str + :rtype: IntrinsicName + """ + return IntrinsicName(core.BNGetArchitectureIntrinsicName(self.handle, intrinsic)) + + def get_intrinsic_index(self, intrinsic:IntrinsicType) -> IntrinsicIndex: """ - return core.BNGetArchitectureIntrinsicName(self.handle, intrinsic) + ``get_intrinsic_index`` gets an intrinsic index given an IntrinsicType. - def get_intrinsic_index(self, intrinsic): - if isinstance(intrinsic, str): + :param IntrinsicType intrinsic: intrinsic number + :return: the corresponding intrinsic string + :rtype: IntrinsicIndex + """ + if isinstance(intrinsic, IntrinsicName): return self._intrinsics[intrinsic] elif isinstance(intrinsic, lowlevelil.ILIntrinsic): return intrinsic.index - return intrinsic + elif isinstance(intrinsic, IntrinsicIndex): + return intrinsic + raise Exception("intrinsic is not convertable to index") - def get_flag_write_type_name(self, write_type): + def get_flag_write_type_name(self, write_type:FlagWriteTypeIndex) -> FlagWriteTypeName: """ ``get_flag_write_type_name`` gets the flag write type name for the given flag. - :param int write_type: flag + :param FlagWriteTypeIndex write_type: flag :return: flag write type name - :rtype: str + :rtype: FlagWriteTypeName """ - return core.BNGetArchitectureFlagWriteTypeName(self.handle, write_type) + return FlagWriteTypeName(core.BNGetArchitectureFlagWriteTypeName(self.handle, write_type)) - def get_flag_by_name(self, flag): + def get_flag_by_name(self, flag:FlagName) -> FlagIndex: """ ``get_flag_by_name`` get flag name for flag index. - :param int flag: flag index - :return: flag name for flag index - :rtype: str + :param FlagName flag: flag name + :return: flag index for flag name + :rtype: FlagIndex """ return self._flags[flag] - def get_flag_write_type_by_name(self, write_type): + def get_flag_write_type_by_name(self, write_type:FlagWriteTypeName) -> FlagWriteTypeIndex: """ ``get_flag_write_type_by_name`` gets the flag write type name for the flag write type. - :param int write_type: flag write type + :param str write_type: flag write type :return: flag write type - :rtype: str + :rtype: int """ return self._flag_write_types[write_type] - def get_semantic_flag_class_by_name(self, sem_class): + def get_semantic_flag_class_by_name(self, sem_class:SemanticClassName) -> SemanticClassIndex: """ ``get_semantic_flag_class_by_name`` gets the semantic flag class index by name. @@ -1690,17 +1750,17 @@ class Architecture(with_metaclass(_ArchitectureMetaClass, object)): """ return self._semantic_flag_classes[sem_class] - def get_semantic_flag_group_by_name(self, sem_group): + def get_semantic_flag_group_by_name(self, sem_group:SemanticGroupName) -> SemanticGroupIndex: """ ``get_semantic_flag_group_by_name`` gets the semantic flag group index by name. - :param int sem_group: semantic flag group + :param SemanticGroupName sem_group: semantic flag group name :return: semantic flag group index - :rtype: str + :rtype: int """ return self._semantic_flag_groups[sem_group] - def get_flag_role(self, flag, sem_class = None): + def get_flag_role(self, flag:FlagIndex, sem_class:Optional[SemanticClassIndex]= None) -> FlagRole: """ ``get_flag_role`` gets the role of a given flag. @@ -1709,9 +1769,12 @@ class Architecture(with_metaclass(_ArchitectureMetaClass, object)): :return: flag role :rtype: FlagRole """ - return self.perform_get_flag_role(flag, sem_class) + if flag in self._flag_roles: + return self._flag_roles[flag] + return FlagRole.SpecialFlagRole - def get_flag_write_low_level_il(self, op, size, write_type, flag, operands, il): + def get_flag_write_low_level_il(self, op:LowLevelILOperation, size:int, write_type:Optional[FlagWriteTypeName], flag:FlagType, + operands:List['lowlevelil.ILRegisterType'], il:'lowlevelil.LowLevelILFunction') -> 'lowlevelil.LowLevelILExpr': """ :param LowLevelILOperation op: :param int size: @@ -1721,9 +1784,13 @@ class Architecture(with_metaclass(_ArchitectureMetaClass, object)): :param LowLevelILFunction il: :rtype: LowLevelILExpr """ - return self.perform_get_flag_write_low_level_il(op, size, write_type, flag, operands, il) + flag = self.get_flag_index(flag) + if flag not in self._flag_roles: + return il.unimplemented() + return self.get_default_flag_write_low_level_il(op, size, self._flag_roles[flag], operands, il) - def get_default_flag_write_low_level_il(self, op, size, role, operands, il): + def get_default_flag_write_low_level_il(self, op:'lowlevelil.LowLevelILOperation', size:int, role:FlagRole, + operands:List['lowlevelil.ILRegisterType'], il:'lowlevelil.LowLevelILFunction') -> 'lowlevelil.LowLevelILExpr': """ :param LowLevelILOperation op: :param int size: @@ -1735,49 +1802,59 @@ class Architecture(with_metaclass(_ArchitectureMetaClass, object)): """ operand_list = (core.BNRegisterOrConstant * len(operands))() for i in range(len(operands)): - if isinstance(operands[i], str): + operand = operands[i] + if isinstance(operand, RegisterName): operand_list[i].constant = False - operand_list[i].reg = self.regs[operands[i]].index - elif isinstance(operands[i], lowlevelil.ILRegister): + operand_list[i].reg = self.regs[operand].index + elif isinstance(operand, lowlevelil.ILRegister): operand_list[i].constant = False - operand_list[i].reg = operands[i].index + operand_list[i].reg = operand.index else: operand_list[i].constant = True - operand_list[i].value = operands[i] + operand_list[i].value = operand return lowlevelil.LowLevelILExpr(core.BNGetDefaultArchitectureFlagWriteLowLevelIL(self.handle, op, size, role, operand_list, len(operand_list), il.handle)) - def get_flag_condition_low_level_il(self, cond, sem_class, il): + def get_flag_condition_low_level_il(self, cond:'lowlevelil.LowLevelILFlagCondition', sem_class:Optional[SemanticClassType], + il:'lowlevelil.LowLevelILFunction') -> 'lowlevelil.LowLevelILExpr': """ :param LowLevelILFlagCondition cond: Flag condition to be computed - :param str sem_class: Semantic class to be used (None for default semantics) + :param SemanticClassType sem_class: Semantic class to be used (None for default semantics) :param LowLevelILFunction il: LowLevelILFunction object to append LowLevelILExpr objects to :rtype: LowLevelILExpr """ - return self.perform_get_flag_condition_low_level_il(cond, sem_class, il) + return self.get_default_flag_condition_low_level_il(cond, sem_class, il) - def get_default_flag_condition_low_level_il(self, cond, sem_class, il): + def get_default_flag_condition_low_level_il(self, cond:'lowlevelil.LowLevelILFlagCondition', + sem_class:Optional[SemanticClassType], il:'lowlevelil.LowLevelILFunction') -> 'lowlevelil.LowLevelILExpr': """ :param LowLevelILFlagCondition cond: + :param SemanticClassType sem_class: :param LowLevelILFunction il: - :param str sem_class: :rtype: LowLevelILExpr """ - class_index = self.get_semantic_flag_class_index(sem_class) - return lowlevelil.LowLevelILExpr(core.BNGetDefaultArchitectureFlagConditionLowLevelIL(self.handle, cond, class_index, il.handle)) + _class_index = None + if sem_class is not None: + _class_index = self.get_semantic_flag_class_index(sem_class) + return lowlevelil.LowLevelILExpr(core.BNGetDefaultArchitectureFlagConditionLowLevelIL(self.handle, cond, + _class_index, il.handle)) - def get_semantic_flag_group_low_level_il(self, sem_group, il): + def get_semantic_flag_group_low_level_il(self, sem_group:Optional[SemanticGroupType], + il:'lowlevelil.LowLevelILFunction') -> 'lowlevelil.LowLevelILExpr': """ - :param str sem_group: + :param Optional[SemanticGroupType] sem_group: :param LowLevelILFunction il: :rtype: LowLevelILExpr """ - return self.perform_get_semantic_flag_group_low_level_il(sem_group, il) + return il.unimplemented() - def get_flags_required_for_flag_condition(self, cond, sem_class = None): - return self.perform_get_flags_required_for_flag_condition(cond, sem_class) + def get_flags_required_for_flag_condition(self, cond:'lowlevelil.LowLevelILFlagCondition', + sem_class:Optional[SemanticClassType]=None): + if cond in self.flags_required_for_flag_condition: + return self.flags_required_for_flag_condition[cond] + return [] - def get_modified_regs_on_write(self, reg): + def get_modified_regs_on_write(self, reg:RegisterName) -> List[RegisterName]: """ ``get_modified_regs_on_write`` returns a list of register names that are modified when ``reg`` is written. @@ -1788,13 +1865,14 @@ class Architecture(with_metaclass(_ArchitectureMetaClass, object)): reg = core.BNGetArchitectureRegisterByName(self.handle, str(reg)) count = ctypes.c_ulonglong() regs = core.BNGetModifiedArchitectureRegistersOnWrite(self.handle, reg, count) + assert regs is not None, "core.BNGetModifiedArchitectureRegistersOnWrite is not None" result = [] for i in range(0, count.value): result.append(core.BNGetArchitectureRegisterName(self.handle, regs[i])) core.BNFreeRegisterList(regs) return result - def assemble(self, code, addr=0): + def assemble(self, code:str, addr:int=0) -> bytes: """ ``assemble`` converts the string of assembly instructions ``code`` loaded at virtual address ``addr`` to the byte representation of those instructions. @@ -1819,9 +1897,9 @@ class Architecture(with_metaclass(_ArchitectureMetaClass, object)): b'\\x0f\\x84\\x04\\x00\\x00\\x00' >>> """ - return self.perform_assemble(code, addr) + return NotImplemented - def is_never_branch_patch_available(self, data, addr): + def is_never_branch_patch_available(self, data:bytes, addr:int=0) -> bool: """ ``is_never_branch_patch_available`` determines if the instruction ``data`` at ``addr`` can be made to **never branch**. @@ -1839,9 +1917,9 @@ class Architecture(with_metaclass(_ArchitectureMetaClass, object)): False >>> """ - return self.perform_is_never_branch_patch_available(data, addr) + return NotImplemented - def is_always_branch_patch_available(self, data, addr): + def is_always_branch_patch_available(self, data:bytes, addr:int=0) -> bool: """ ``is_always_branch_patch_available`` determines if the instruction ``data`` at ``addr`` can be made to **always branch**. @@ -1860,9 +1938,9 @@ class Architecture(with_metaclass(_ArchitectureMetaClass, object)): False >>> """ - return self.perform_is_always_branch_patch_available(data, addr) + return NotImplemented - def is_invert_branch_patch_available(self, data, addr): + def is_invert_branch_patch_available(self, data:bytes, addr:int=0) -> bool: """ ``is_always_branch_patch_available`` determines if the instruction ``data`` at ``addr`` can be inverted. @@ -1880,9 +1958,9 @@ class Architecture(with_metaclass(_ArchitectureMetaClass, object)): False >>> """ - return self.perform_is_invert_branch_patch_available(data, addr) + return NotImplemented - def is_skip_and_return_zero_patch_available(self, data, addr): + def is_skip_and_return_zero_patch_available(self, data:bytes, addr:int=0) -> bool: """ ``is_skip_and_return_zero_patch_available`` determines if the instruction ``data`` at ``addr`` is a *call-like* instruction that can be made into an instruction *returns zero*. @@ -1903,9 +1981,9 @@ class Architecture(with_metaclass(_ArchitectureMetaClass, object)): False >>> """ - return self.perform_is_skip_and_return_zero_patch_available(data, addr) + return NotImplemented - def is_skip_and_return_value_patch_available(self, data, addr): + def is_skip_and_return_value_patch_available(self, data:bytes, addr:int=0) -> bool: """ ``is_skip_and_return_value_patch_available`` determines if the instruction ``data`` at ``addr`` is a *call-like* instruction that can be made into an instruction *returns a value*. @@ -1924,9 +2002,9 @@ class Architecture(with_metaclass(_ArchitectureMetaClass, object)): False >>> """ - return self.perform_is_skip_and_return_value_patch_available(data, addr) + return NotImplemented - def convert_to_nop(self, data, addr): + def convert_to_nop(self, data:bytes, addr:int=0) -> Optional[bytes]: """ ``convert_to_nop`` reads the instruction(s) in ``data`` at virtual address ``addr`` and returns a string of nop instructions of the same length as data. @@ -1943,9 +2021,9 @@ class Architecture(with_metaclass(_ArchitectureMetaClass, object)): b'\\x90\\x90' >>> """ - return self.perform_convert_to_nop(data, addr) + return NotImplemented - def always_branch(self, data, addr): + def always_branch(self, data:bytes, addr:int=0) -> Optional[bytes]: """ ``always_branch`` reads the instruction(s) in ``data`` at virtual address ``addr`` and returns a string of bytes of the same length which always branches. @@ -1965,9 +2043,9 @@ class Architecture(with_metaclass(_ArchitectureMetaClass, object)): (['jmp', ' ', '0x9'], 5) >>> """ - return self.perform_always_branch(data, addr) + return NotImplemented - def invert_branch(self, data, addr): + def invert_branch(self, data:bytes, addr:int=0) -> Optional[bytes]: """ ``invert_branch`` reads the instruction(s) in ``data`` at virtual address ``addr`` and returns a string of bytes of the same length which inverts the branch of provided instruction. @@ -1988,9 +2066,9 @@ class Architecture(with_metaclass(_ArchitectureMetaClass, object)): (['jl', ' ', '0xa'], 6) >>> """ - return self.perform_invert_branch(data, addr) + return NotImplemented - def skip_and_return_value(self, data, addr, value): + def skip_and_return_value(self, data:bytes, addr:int, value:int) -> Optional[bytes]: """ ``skip_and_return_value`` reads the instruction(s) in ``data`` at virtual address ``addr`` and returns a string of bytes of the same length which doesn't call and instead *return a value*. @@ -2007,9 +2085,9 @@ class Architecture(with_metaclass(_ArchitectureMetaClass, object)): (['mov', ' ', 'eax', ', ', '0x0'], 5) >>> """ - return self.perform_skip_and_return_value(data, addr, value) + return NotImplemented - def is_view_type_constant_defined(self, type_name, const_name): + def is_view_type_constant_defined(self, type_name:str, const_name:str) -> bool: """ :param str type_name: the BinaryView type name of the constant to query @@ -2027,7 +2105,7 @@ class Architecture(with_metaclass(_ArchitectureMetaClass, object)): """ return core.BNIsBinaryViewTypeArchitectureConstantDefined(self.handle, type_name, const_name) - def get_view_type_constant(self, type_name, const_name, default_value=0): + def get_view_type_constant(self, type_name:str, const_name:str, default_value:int=0) -> int: """ ``get_view_type_constant`` retrieves the view type constant for the given type_name and const_name. @@ -2047,7 +2125,7 @@ class Architecture(with_metaclass(_ArchitectureMetaClass, object)): """ return core.BNGetBinaryViewTypeArchitectureConstant(self.handle, type_name, const_name, default_value) - def set_view_type_constant(self, type_name, const_name, value): + def set_view_type_constant(self, type_name:str, const_name:str, value:int) -> None: """ ``set_view_type_constant`` creates a new binaryview type constant. @@ -2063,7 +2141,7 @@ class Architecture(with_metaclass(_ArchitectureMetaClass, object)): """ core.BNSetBinaryViewTypeArchitectureConstant(self.handle, type_name, const_name, value) - def register_calling_convention(self, cc): + def register_calling_convention(self, cc:'callingconvention.CallingConvention') -> None: """ ``register_calling_convention`` registers a new calling convention for the Architecture. @@ -2075,54 +2153,57 @@ class Architecture(with_metaclass(_ArchitectureMetaClass, object)): _architecture_cache = {} class CoreArchitecture(Architecture): - def __init__(self, handle): + def __init__(self, handle:core.BNArchitecture): super(CoreArchitecture, self).__init__() self.handle = core.handle_of_type(handle, core.BNArchitecture) - self.__dict__["name"] = core.BNGetArchitectureName(self.handle) - self.__dict__["endianness"] = Endianness(core.BNGetArchitectureEndianness(self.handle)) - self.__dict__["address_size"] = core.BNGetArchitectureAddressSize(self.handle) - self.__dict__["default_int_size"] = core.BNGetArchitectureDefaultIntegerSize(self.handle) - self.__dict__["instr_alignment"] = core.BNGetArchitectureInstructionAlignment(self.handle) - self.__dict__["max_instr_length"] = core.BNGetArchitectureMaxInstructionLength(self.handle) - self.__dict__["opcode_display_length"] = core.BNGetArchitectureOpcodeDisplayLength(self.handle) - self.__dict__["stack_pointer"] = core.BNGetArchitectureRegisterName(self.handle, + self.name = core.BNGetArchitectureName(self.handle) + self.endianness = Endianness(core.BNGetArchitectureEndianness(self.handle)) + self.address_size = core.BNGetArchitectureAddressSize(self.handle) + self.default_int_size = core.BNGetArchitectureDefaultIntegerSize(self.handle) + self.instr_alignment = core.BNGetArchitectureInstructionAlignment(self.handle) + self.max_instr_length = core.BNGetArchitectureMaxInstructionLength(self.handle) + self.opcode_display_length = core.BNGetArchitectureOpcodeDisplayLength(self.handle) + self.stack_pointer:str = core.BNGetArchitectureRegisterName(self.handle, core.BNGetArchitectureStackPointerRegister(self.handle)) link_reg = core.BNGetArchitectureLinkRegister(self.handle) if link_reg == 0xffffffff: - self.__dict__["link_reg"] = None + self.link_reg = None else: - self.__dict__["link_reg"] = core.BNGetArchitectureRegisterName(self.handle, link_reg) + self.link_reg = core.BNGetArchitectureRegisterName(self.handle, link_reg) count = ctypes.c_ulonglong() regs = core.BNGetAllArchitectureRegisters(self.handle, count) + assert regs is not None, "core.BNGetAllArchitectureRegisters returned None" self._all_regs = {} self._regs_by_index = {} self._full_width_regs = {} - self.__dict__["regs"] = {} + self.regs = {} for i in range(0, count.value): - name = core.BNGetArchitectureRegisterName(self.handle, regs[i]) + name = RegisterName(core.BNGetArchitectureRegisterName(self.handle, regs[i])) + assert name is not None, "" info = core.BNGetArchitectureRegisterInfo(self.handle, regs[i]) - full_width_reg = core.BNGetArchitectureRegisterName(self.handle, info.fullWidthRegister) - self.regs[name] = binaryninja.function.RegisterInfo(full_width_reg, info.size, info.offset, + full_width_reg = RegisterName(core.BNGetArchitectureRegisterName(self.handle, info.fullWidthRegister)) + self.regs[name] = RegisterInfo(full_width_reg, info.size, info.offset, ImplicitRegisterExtend(info.extend), regs[i]) self._all_regs[name] = regs[i] self._regs_by_index[regs[i]] = name for i in range(0, count.value): info = core.BNGetArchitectureRegisterInfo(self.handle, regs[i]) - full_width_reg = core.BNGetArchitectureRegisterName(self.handle, info.fullWidthRegister) + full_width_reg = RegisterName(core.BNGetArchitectureRegisterName(self.handle, info.fullWidthRegister)) if full_width_reg not in self._full_width_regs: self._full_width_regs[full_width_reg] = self._all_regs[full_width_reg] core.BNFreeRegisterList(regs) count = ctypes.c_ulonglong() flags = core.BNGetAllArchitectureFlags(self.handle, count) + assert flags is not None, "core.BNGetAllArchitectureFlags returned None" self._flags = {} self._flags_by_index = {} - self.__dict__["flags"] = [] + self.flags = [] for i in range(0, count.value): - name = core.BNGetArchitectureFlagName(self.handle, flags[i]) + name = FlagName(core.BNGetArchitectureFlagName(self.handle, flags[i])) self._flags[name] = flags[i] self._flags_by_index[flags[i]] = name self.flags.append(name) @@ -2130,11 +2211,12 @@ class CoreArchitecture(Architecture): count = ctypes.c_ulonglong() write_types = core.BNGetAllArchitectureFlagWriteTypes(self.handle, count) - self._flag_write_types = {} + assert write_types is not None, "core.BNGetAllArchitectureFlagWriteTypes returned None" + self._flag_write_types:Mapping[str, FlagWriteTypeIndex] = {} self._flag_write_types_by_index = {} - self.__dict__["flag_write_types"] = [] + self.flag_write_types = [] for i in range(0, count.value): - name = core.BNGetArchitectureFlagWriteTypeName(self.handle, write_types[i]) + name = FlagWriteTypeName(core.BNGetArchitectureFlagWriteTypeName(self.handle, write_types[i])) self._flag_write_types[name] = write_types[i] self._flag_write_types_by_index[write_types[i]] = name self.flag_write_types.append(name) @@ -2142,11 +2224,12 @@ class CoreArchitecture(Architecture): count = ctypes.c_ulonglong() sem_classes = core.BNGetAllArchitectureSemanticFlagClasses(self.handle, count) + assert sem_classes is not None,"core.BNGetAllArchitectureSemanticFlagClasses returned None" self._semantic_flag_classes = {} self._semantic_flag_classes_by_index = {} - self.__dict__["semantic_flag_classes"] = [] + self.semantic_flag_classes = [] for i in range(0, count.value): - name = core.BNGetArchitectureSemanticFlagClassName(self.handle, sem_classes[i]) + name = SemanticClassName(core.BNGetArchitectureSemanticFlagClassName(self.handle, sem_classes[i])) self._semantic_flag_classes[name] = sem_classes[i] self._semantic_flag_classes_by_index[sem_classes[i]] = name self.semantic_flag_classes.append(name) @@ -2154,39 +2237,42 @@ class CoreArchitecture(Architecture): count = ctypes.c_ulonglong() sem_groups = core.BNGetAllArchitectureSemanticFlagGroups(self.handle, count) + assert sem_groups is not None,"core.BNGetAllArchitectureSemanticFlagGroups returned Non" self._semantic_flag_groups = {} self._semantic_flag_groups_by_index = {} - self.__dict__["semantic_flag_groups"] = [] + self.semantic_flag_groups = [] for i in range(0, count.value): - name = core.BNGetArchitectureSemanticFlagGroupName(self.handle, sem_groups[i]) + name = SemanticGroupName(core.BNGetArchitectureSemanticFlagGroupName(self.handle, sem_groups[i])) self._semantic_flag_groups[name] = sem_groups[i] self._semantic_flag_groups_by_index[sem_groups[i]] = name self.semantic_flag_groups.append(name) core.BNFreeRegisterList(sem_groups) self._flag_roles = {} - self.__dict__["flag_roles"] = {} - for flag in self.__dict__["flags"]: + self.flag_roles = {} + for flag in self.flags: role = FlagRole(core.BNGetArchitectureFlagRole(self.handle, self._flags[flag], 0)) - self.__dict__["flag_roles"][flag] = role + self.flag_roles[flag] = role self._flag_roles[self._flags[flag]] = role - self.__dict__["flags_required_for_flag_condition"] = {} + self.flags_required_for_flag_condition:Mapping[LowLevelILFlagCondition, List[FlagName]] = {} for cond in LowLevelILFlagCondition: count = ctypes.c_ulonglong() flags = core.BNGetArchitectureFlagsRequiredForFlagCondition(self.handle, cond, 0, count) + assert flags is not None, "core.BNGetArchitectureFlagsRequiredForFlagCondition returned None" flag_names = [] for i in range(0, count.value): flag_names.append(self._flags_by_index[flags[i]]) core.BNFreeRegisterList(flags) - self.__dict__["flags_required_for_flag_condition"][cond] = flag_names + self.flags_required_for_flag_condition[cond] = flag_names self._flags_required_by_semantic_flag_group = {} - self.__dict__["flags_required_for_semantic_flag_group"] = {} + self.flags_required_for_semantic_flag_group = {} for group in self.semantic_flag_groups: count = ctypes.c_ulonglong() flags = core.BNGetArchitectureFlagsRequiredForSemanticFlagGroup(self.handle, self._semantic_flag_groups[group], count) + assert flags is not None, "core.BNGetArchitectureFlagsRequiredForSemanticFlagGroup returned None" flag_indexes = [] flag_names = [] for i in range(0, count.value): @@ -2194,14 +2280,15 @@ class CoreArchitecture(Architecture): flag_names.append(self._flags_by_index[flags[i]]) core.BNFreeRegisterList(flags) self._flags_required_by_semantic_flag_group[self._semantic_flag_groups[group]] = flag_indexes - self.__dict__["flags_required_for_semantic_flag_group"][cond] = flag_names + self.flags_required_for_semantic_flag_group[group] = flag_names self._flag_conditions_for_semantic_flag_group = {} - self.__dict__["flag_conditions_for_semantic_flag_group"] = {} + self.flag_conditions_for_semantic_flag_group = {} for group in self.semantic_flag_groups: count = ctypes.c_ulonglong() conditions = core.BNGetArchitectureFlagConditionsForSemanticFlagGroup(self.handle, self._semantic_flag_groups[group], count) + assert conditions is not None, "core.BNGetArchitectureFlagConditionsForSemanticFlagGroup returned None" class_index_cond = {} class_cond = {} for i in range(0, count.value): @@ -2212,14 +2299,15 @@ class CoreArchitecture(Architecture): class_cond[self._semantic_flag_classes_by_index[conditions[i].semanticClass]] = conditions[i].condition core.BNFreeFlagConditionsForSemanticFlagGroup(conditions) self._flag_conditions_for_semantic_flag_group[self._semantic_flag_groups[group]] = class_index_cond - self.__dict__["flag_conditions_for_semantic_flag_group"][group] = class_cond + self.flag_conditions_for_semantic_flag_group[group] = class_cond self._flags_written_by_flag_write_type = {} - self.__dict__["flags_written_by_flag_write_type"] = {} + self.flags_written_by_flag_write_type = {} for write_type in self.flag_write_types: count = ctypes.c_ulonglong() flags = core.BNGetArchitectureFlagsWrittenByFlagWriteType(self.handle, self._flag_write_types[write_type], count) + assert flags is not None, "core.BNGetArchitectureFlagsWrittenByFlagWriteType returned None" flag_indexes = [] flag_names = [] for i in range(0, count.value): @@ -2227,10 +2315,10 @@ class CoreArchitecture(Architecture): flag_names.append(self._flags_by_index[flags[i]]) core.BNFreeRegisterList(flags) self._flags_written_by_flag_write_type[self._flag_write_types[write_type]] = flag_indexes - self.__dict__["flags_written_by_flag_write_type"][write_type] = flag_names + self.flags_written_by_flag_write_type[write_type] = flag_names self._semantic_class_for_flag_write_type = {} - self.__dict__["semantic_class_for_flag_write_type"] = {} + self.semantic_class_for_flag_write_type = {} for write_type in self.flag_write_types: sem_class = core.BNGetArchitectureSemanticClassForFlagWriteType(self.handle, self._flag_write_types[write_type]) @@ -2239,83 +2327,90 @@ class CoreArchitecture(Architecture): else: sem_class_name = self._semantic_flag_classes_by_index[sem_class] self._semantic_class_for_flag_write_type[self._flag_write_types[write_type]] = sem_class - self.__dict__["semantic_class_for_flag_write_type"][write_type] = sem_class_name + self.semantic_class_for_flag_write_type[write_type] = sem_class_name count = ctypes.c_ulonglong() regs = core.BNGetArchitectureGlobalRegisters(self.handle, count) - self.__dict__["global_regs"] = [] + assert regs is not None, "core.BNGetArchitectureGlobalRegisters returned None" + self.global_regs:List[RegisterName] = [] for i in range(0, count.value): - self.global_regs.append(core.BNGetArchitectureRegisterName(self.handle, regs[i])) + self.global_regs.append(RegisterName(core.BNGetArchitectureRegisterName(self.handle, regs[i]))) core.BNFreeRegisterList(regs) count = ctypes.c_ulonglong() regs = core.BNGetArchitectureSystemRegisters(self.handle, count) - self.__dict__["system_regs"] = [] + self.system_regs:List[RegisterName] = [] for i in range(0, count.value): - self.system_regs.append(core.BNGetArchitectureRegisterName(self.handle, regs[i])) + assert regs is not None, "core.BNGetArchitectureSystemRegisters returned None" + self.system_regs.append(RegisterName(core.BNGetArchitectureRegisterName(self.handle, regs[i]))) core.BNFreeRegisterList(regs) count = ctypes.c_ulonglong() regs = core.BNGetAllArchitectureRegisterStacks(self.handle, count) + assert regs is not None, "core.BNGetAllArchitectureRegisterStacks returned None" self._all_reg_stacks = {} self._reg_stacks_by_index = {} - self.__dict__["reg_stacks"] = {} + self.reg_stacks = {} for i in range(0, count.value): - name = core.BNGetArchitectureRegisterStackName(self.handle, regs[i]) + name = RegisterStackName(core.BNGetArchitectureRegisterStackName(self.handle, regs[i])) info = core.BNGetArchitectureRegisterStackInfo(self.handle, regs[i]) storage = [] for j in range(0, info.storageCount): storage.append(core.BNGetArchitectureRegisterName(self.handle, info.firstStorageReg + j)) - top_rel = [] + top_rel:List[RegisterName] = [] for j in range(0, info.topRelativeCount): - top_rel.append(core.BNGetArchitectureRegisterName(self.handle, info.firstTopRelativeReg + j)) + reg_name = RegisterName(core.BNGetArchitectureRegisterName(self.handle, info.firstTopRelativeReg + j)) + top_rel.append(reg_name) top = core.BNGetArchitectureRegisterName(self.handle, info.stackTopReg) - self.reg_stacks[name] = binaryninja.function.RegisterStackInfo(storage, top_rel, top, regs[i]) + self.reg_stacks[name] = RegisterStackInfo(storage, top_rel, RegisterName(top), regs[i]) self._all_reg_stacks[name] = regs[i] self._reg_stacks_by_index[regs[i]] = name core.BNFreeRegisterList(regs) count = ctypes.c_ulonglong() intrinsics = core.BNGetAllArchitectureIntrinsics(self.handle, count) - self._intrinsics = {} - self._intrinsics_by_index = {} - self.__dict__["intrinsics"] = {} - for i in range(0, count.value): - name = core.BNGetArchitectureIntrinsicName(self.handle, intrinsics[i]) + assert intrinsics is not None, "core.BNGetAllArchitectureIntrinsics returned None" + self._intrinsics:Mapping[IntrinsicName, IntrinsicIndex] = {} + self._intrinsics_by_index:Mapping[IntrinsicIndex, Tuple[IntrinsicName, IntrinsicInfo]] = {} + self._intrinsics_info:Mapping[IntrinsicName, IntrinsicInfo] = {} + for i in range(count.value): + name = IntrinsicName(core.BNGetArchitectureIntrinsicName(self.handle, intrinsics[i])) input_count = ctypes.c_ulonglong() inputs = core.BNGetArchitectureIntrinsicInputs(self.handle, intrinsics[i], input_count) + assert inputs is not None, "core.BNGetArchitectureIntrinsicInputs returned None" input_list = [] for j in range(0, input_count.value): input_name = inputs[j].name type_obj = types.Type(core.BNNewTypeReference(inputs[j].type), confidence = inputs[j].typeConfidence) - input_list.append(binaryninja.function.IntrinsicInput(type_obj, input_name)) + input_list.append(IntrinsicInput(type_obj, input_name)) core.BNFreeNameAndTypeList(inputs, input_count.value) output_count = ctypes.c_ulonglong() outputs = core.BNGetArchitectureIntrinsicOutputs(self.handle, intrinsics[i], output_count) + assert outputs is not None, "core.BNGetArchitectureIntrinsicOutputs returned None" output_list = [] - for j in range(0, output_count.value): + for j in range(output_count.value): output_list.append(types.Type(core.BNNewTypeReference(outputs[j].type), confidence = outputs[j].confidence)) core.BNFreeOutputTypeList(outputs, output_count.value) - self.intrinsics[name] = binaryninja.function.IntrinsicInfo(input_list, output_list) + self._intrinsics_info[name] = IntrinsicInfo(input_list, output_list) self._intrinsics[name] = intrinsics[i] - self._intrinsics_by_index[intrinsics[i]] = (name, self.intrinsics[name]) + self._intrinsics_by_index[intrinsics[i]] = (name, self._intrinsics_info[name]) core.BNFreeRegisterList(intrinsics) if type(self) is CoreArchitecture: global _architecture_cache _architecture_cache[ctypes.addressof(handle.contents)] = self @classmethod - def _from_cache(cls, handle): + def _from_cache(cls, handle) -> 'Architecture': global _architecture_cache return _architecture_cache.get(ctypes.addressof(handle.contents)) or cls(handle) - def get_associated_arch_by_address(self, addr): + def get_associated_arch_by_address(self, addr:int) -> Tuple['Architecture', int]: new_addr = ctypes.c_ulonglong() new_addr.value = addr result = core.BNGetAssociatedArchitectureByAddress(self.handle, new_addr) return CoreArchitecture._from_cache(handle = result), new_addr.value - def get_instruction_info(self, data, addr): + def get_instruction_info(self, data:Union[bytes, str], addr:int) -> Optional[InstructionInfo]: """ ``get_instruction_info`` returns an InstructionInfo object for the instruction at the given virtual address ``addr`` with data ``data``. @@ -2328,12 +2423,17 @@ class CoreArchitecture(Architecture): :return: the InstructionInfo for the current instruction :rtype: InstructionInfo """ + if not isinstance(data, bytes): + if isinstance(data, str): + data=data.encode() + else: + raise TypeError("Must be bytes or str") info = core.BNInstructionInfo() buf = (ctypes.c_ubyte * len(data))() ctypes.memmove(buf, data, len(data)) if not core.BNGetInstructionInfo(self.handle, buf, addr, len(data), info): return None - result = binaryninja.function.InstructionInfo() + result = InstructionInfo() result.length = info.length result.arch_transition_by_target_addr = info.archTransitionByTargetAddr result.branch_delay = info.branchDelay @@ -2346,7 +2446,7 @@ class CoreArchitecture(Architecture): result.add_branch(BranchType(info.branchType[i]), target, arch) return result - def get_instruction_text(self, data, addr): + def get_instruction_text(self, data:bytes, addr:int) -> Tuple[List['function.InstructionTextToken'], int]: """ ``get_instruction_text`` returns a list of InstructionTextToken objects for the instruction at the given virtual address ``addr`` with data ``data``. @@ -2367,13 +2467,15 @@ class CoreArchitecture(Architecture): buf = (ctypes.c_ubyte * len(data))() ctypes.memmove(buf, data, len(data)) tokens = ctypes.POINTER(core.BNInstructionTextToken)() - if not core.BNGetInstructionText(self.handle, buf, addr, length, tokens, count): - return None, 0 - result = binaryninja.function.InstructionTextToken.get_instruction_lines(tokens, count.value) - core.BNFreeInstructionText(tokens, count.value) - return result, length.value + result = [] + result_length = 0 + if core.BNGetInstructionText(self.handle, buf, addr, length, tokens, count): + result = function.InstructionTextToken._from_core_struct(tokens, count.value) + result_length = length.value + core.BNFreeInstructionText(tokens, count.value) + return result, result_length - def get_instruction_low_level_il(self, data, addr, il): + def get_instruction_low_level_il(self, data:Union[bytes, str], addr:int, il:lowlevelil.LowLevelILFunction) -> int: """ ``get_instruction_low_level_il`` appends LowLevelILExpr objects to ``il`` for the instruction at the given virtual address ``addr`` with data ``data``. @@ -2381,20 +2483,27 @@ class CoreArchitecture(Architecture): This is used to analyze arbitrary data at an address, if you are working with an existing binary, you likely want to be using :func:`Function.get_low_level_il_at`. - :param str data: max_instruction_length bytes from the binary at virtual address ``addr`` + :param str|bytes data: max_instruction_length bytes from the binary at virtual address ``addr`` :param int addr: virtual address of bytes in ``data`` :param LowLevelILFunction il: The function the current instruction belongs to :return: the length of the current instruction :rtype: int """ + if not isinstance(data, bytes): + if isinstance(data, str): + data=data.encode() + else: + raise TypeError("Must be bytes or str") length = ctypes.c_ulonglong() length.value = len(data) buf = (ctypes.c_ubyte * len(data))() ctypes.memmove(buf, data, len(data)) - core.BNGetInstructionLowLevelIL(self.handle, buf, addr, length, il.handle) + if not core.BNGetInstructionLowLevelIL(self.handle, buf, addr, length, il.handle): + raise Exception("BNGetInstructionLowLevelIL failed") return length.value - def get_flag_write_low_level_il(self, op, size, write_type, flag, operands, il): + def get_flag_write_low_level_il(self, op:LowLevelILOperation, size:int, write_type:FlagWriteTypeName, + flag:FlagType, operands:List['lowlevelil.ILRegisterType'], il:'lowlevelil.LowLevelILFunction') -> 'lowlevelil.LowLevelILExpr': """ :param LowLevelILOperation op: :param int size: @@ -2407,19 +2516,21 @@ class CoreArchitecture(Architecture): flag = self.get_flag_index(flag) operand_list = (core.BNRegisterOrConstant * len(operands))() for i in range(len(operands)): - if isinstance(operands[i], str): + operand = operands[i] + if isinstance(operand, RegisterName): operand_list[i].constant = False - operand_list[i].reg = self.regs[operands[i]].index - elif isinstance(operands[i], lowlevelil.ILRegister): + operand_list[i].reg = self.regs[operand].index + elif isinstance(operand, lowlevelil.ILRegister): operand_list[i].constant = False - operand_list[i].reg = operands[i].index + operand_list[i].reg = operand.index else: operand_list[i].constant = True - operand_list[i].value = operands[i] + operand_list[i].value = operand return lowlevelil.LowLevelILExpr(core.BNGetArchitectureFlagWriteLowLevelIL(self.handle, op, size, self._flag_write_types[write_type], flag, operand_list, len(operand_list), il.handle)) - def get_flag_condition_low_level_il(self, cond, sem_class, il): + def get_flag_condition_low_level_il(self, cond:LowLevelILFlagCondition, sem_class:SemanticClassType, + il:'lowlevelil.LowLevelILFunction') -> 'lowlevelil.LowLevelILExpr': """ :param LowLevelILFlagCondition cond: Flag condition to be computed :param str sem_class: Semantic class to be used (None for default semantics) @@ -2430,7 +2541,8 @@ class CoreArchitecture(Architecture): return lowlevelil.LowLevelILExpr(core.BNGetArchitectureFlagConditionLowLevelIL(self.handle, cond, class_index, il.handle)) - def get_semantic_flag_group_low_level_il(self, sem_group, il): + def get_semantic_flag_group_low_level_il(self, sem_group:SemanticGroupName, + il:'lowlevelil.LowLevelILFunction') -> 'lowlevelil.LowLevelILExpr': """ :param str sem_group: :param LowLevelILFunction il: @@ -2439,7 +2551,7 @@ class CoreArchitecture(Architecture): group_index = self.get_semantic_flag_group_index(sem_group) return lowlevelil.LowLevelILExpr(core.BNGetArchitectureSemanticFlagGroupLowLevelIL(self.handle, group_index, il.handle)) - def assemble(self, code, addr=0): + def assemble(self, code:str, addr:int=0) -> bytes: """ ``assemble`` converts the string of assembly instructions ``code`` loaded at virtual address ``addr`` to the byte representation of those instructions. @@ -2460,12 +2572,9 @@ class CoreArchitecture(Architecture): error_str = errors.value core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) raise ValueError("Could not assemble: %s" % error_str) - if isinstance(str(result), bytes): - return str(result) - else: - return bytes(result) + return bytes(result) - def is_never_branch_patch_available(self, data, addr): + def is_never_branch_patch_available(self, data:bytes, addr:int=0) -> bool: """ ``is_never_branch_patch_available`` determines if the instruction ``data`` at ``addr`` can be made to **never branch**. @@ -2485,7 +2594,7 @@ class CoreArchitecture(Architecture): ctypes.memmove(buf, data, len(data)) return core.BNIsArchitectureNeverBranchPatchAvailable(self.handle, buf, addr, len(data)) - def is_always_branch_patch_available(self, data, addr): + def is_always_branch_patch_available(self, data:bytes, addr:int=0) -> bool: """ ``is_always_branch_patch_available`` determines if the instruction ``data`` at ``addr`` can be made to **always branch**. @@ -2506,7 +2615,7 @@ class CoreArchitecture(Architecture): ctypes.memmove(buf, data, len(data)) return core.BNIsArchitectureAlwaysBranchPatchAvailable(self.handle, buf, addr, len(data)) - def is_invert_branch_patch_available(self, data, addr): + def is_invert_branch_patch_available(self, data:bytes, addr:int=0) -> bool: """ ``is_always_branch_patch_available`` determines if the instruction ``data`` at ``addr`` can be inverted. @@ -2526,7 +2635,7 @@ class CoreArchitecture(Architecture): ctypes.memmove(buf, data, len(data)) return core.BNIsArchitectureInvertBranchPatchAvailable(self.handle, buf, addr, len(data)) - def is_skip_and_return_zero_patch_available(self, data, addr): + def is_skip_and_return_zero_patch_available(self, data:bytes, addr:int=0) -> bool: """ ``is_skip_and_return_zero_patch_available`` determines if the instruction ``data`` at ``addr`` is a *call-like* instruction that can be made into an instruction *returns zero*. @@ -2549,7 +2658,7 @@ class CoreArchitecture(Architecture): ctypes.memmove(buf, data, len(data)) return core.BNIsArchitectureSkipAndReturnZeroPatchAvailable(self.handle, buf, addr, len(data)) - def is_skip_and_return_value_patch_available(self, data, addr): + def is_skip_and_return_value_patch_available(self, data:bytes, addr:int=0) -> bool: """ ``is_skip_and_return_value_patch_available`` determines if the instruction ``data`` at ``addr`` is a *call-like* instruction that can be made into an instruction *returns a value*. @@ -2570,7 +2679,7 @@ class CoreArchitecture(Architecture): ctypes.memmove(buf, data, len(data)) return core.BNIsArchitectureSkipAndReturnValuePatchAvailable(self.handle, buf, addr, len(data)) - def convert_to_nop(self, data, addr): + def convert_to_nop(self, data:bytes, addr:int=0) -> Optional[bytes]: """ ``convert_to_nop`` reads the instruction(s) in ``data`` at virtual address ``addr`` and returns a string of nop instructions of the same length as data. @@ -2593,7 +2702,7 @@ class CoreArchitecture(Architecture): ctypes.memmove(result, buf, len(data)) return result.raw - def always_branch(self, data, addr): + def always_branch(self, data:bytes, addr:int=0) -> Optional[bytes]: """ ``always_branch`` reads the instruction(s) in ``data`` at virtual address ``addr`` and returns a string of bytes of the same length which always branches. @@ -2619,7 +2728,7 @@ class CoreArchitecture(Architecture): ctypes.memmove(result, buf, len(data)) return result.raw - def invert_branch(self, data, addr): + def invert_branch(self, data:bytes, addr:int=0) -> Optional[bytes]: """ ``invert_branch`` reads the instruction(s) in ``data`` at virtual address ``addr`` and returns a string of bytes of the same length which inverts the branch of provided instruction. @@ -2646,7 +2755,7 @@ class CoreArchitecture(Architecture): ctypes.memmove(result, buf, len(data)) return result.raw - def skip_and_return_value(self, data, addr, value): + def skip_and_return_value(self, data:bytes, addr:int, value:int) -> Optional[bytes]: """ ``skip_and_return_value`` reads the instruction(s) in ``data`` at virtual address ``addr`` and returns a string of bytes of the same length which doesn't call and instead *return a value*. @@ -2669,7 +2778,7 @@ class CoreArchitecture(Architecture): ctypes.memmove(result, buf, len(data)) return result.raw - def get_flag_role(self, flag, sem_class = None): + def get_flag_role(self, flag:FlagIndex, sem_class:Optional[SemanticClassIndex]= None) -> FlagRole: """ ``get_flag_role`` gets the role of a given flag. @@ -2679,13 +2788,19 @@ class CoreArchitecture(Architecture): :rtype: FlagRole """ flag = self.get_flag_index(flag) - sem_class = self.get_semantic_flag_class_index(sem_class) - return FlagRole(core.BNGetArchitectureFlagRole(self.handle, flag, sem_class)) + _sem_class = None + if sem_class is not None: + _sem_class = self.get_semantic_flag_class_index(sem_class) + return FlagRole(core.BNGetArchitectureFlagRole(self.handle, flag, _sem_class)) - def get_flags_required_for_flag_condition(self, cond, sem_class = None): - sem_class = self.get_semantic_flag_class_index(sem_class) + def get_flags_required_for_flag_condition(self, cond:LowLevelILFlagCondition, + sem_class:Optional[SemanticClassType]=None) -> List[FlagName]: + _sem_class = None + if sem_class is not None: + _sem_class = self.get_semantic_flag_class_index(sem_class) count = ctypes.c_ulonglong() - flags = core.BNGetArchitectureFlagsRequiredForFlagCondition(self.handle, cond, sem_class, count) + flags = core.BNGetArchitectureFlagsRequiredForFlagCondition(self.handle, cond, _sem_class, count) + assert flags is not None, "core.BNGetArchitectureFlagsRequiredForFlagCondition returned None" flag_names = [] for i in range(0, count.value): flag_names.append(self._flags_by_index[flags[i]]) @@ -2694,7 +2809,7 @@ class CoreArchitecture(Architecture): class ArchitectureHook(CoreArchitecture): - def __init__(self, base_arch): + def __init__(self, base_arch:'Architecture'): self._base_arch = base_arch super(ArchitectureHook, self).__init__(base_arch.handle) @@ -2728,198 +2843,9 @@ class ArchitectureHook(CoreArchitecture): core.BNFinalizeArchitectureHook(self._base_arch.handle) @property - def base_arch(self): - """ """ + def base_arch(self) -> 'Architecture': return self._base_arch @base_arch.setter - def base_arch(self, value): + def base_arch(self, value:'Architecture') -> None: self._base_arch = value - - -class ReferenceSource(object): - def __init__(self, func, arch, addr): - self._function = func - self._arch = arch - self._address = addr - - def __repr__(self): - if self._arch: - return "<ref: %s@%#x>" % (self._arch.name, self._address) - else: - return "<ref: %#x>" % self._address - - def __eq__(self, other): - if not isinstance(other, self.__class__): - return NotImplemented - return (self.function, self.arch, self.address) == (other.function, other.arch, other.address) - - 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 self.address < other.address - - def __gt__(self, other): - if not isinstance(other, self.__class__): - return NotImplemented - return self.address > other.address - - def __ge__(self, other): - if not isinstance(other, self.__class__): - return NotImplemented - return self.address >= other.address - - def __le__(self, other): - if not isinstance(other, self.__class__): - return NotImplemented - return self.address <= other.address - - def __hash__(self): - return hash((self._function, self._arch, self._address)) - - @property - def function(self): - """ """ - return self._function - - @function.setter - def function(self, value): - self._function = value - - @property - def arch(self): - """ """ - return self._arch - - @arch.setter - def arch(self, value): - self._arch = value - - @property - def address(self): - """ """ - return self._address - - @address.setter - def address(self, value): - self._address = value - - -class TypeFieldReference(object): - def __init__(self, func, arch, addr, size, t): - self._function = func - self._arch = arch - self._address = addr - self._size = size - self._incomingType = t - - def __repr__(self): - if self._arch: - return "<ref: %s@%#x, size: %#x, type: %s>" % (self._arch.name, self._address, self._size,\ - self._incomingType) - else: - return "<ref: %#x, size: %#x, type: %s>" % (self._address, self._size, self._incomingType) - - def __eq__(self, other): - if not isinstance(other, self.__class__): - return NotImplemented - return (self.function, self.arch, self.address, self._size, self._incomingType) ==\ - (other.function, other.arch, other.address, other.size, other.incomingType) - - 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.address < other.address: - return True - elif self.address > other.address: - return False - else: - return self.size < other.size - - def __gt__(self, other): - if not isinstance(other, self.__class__): - return NotImplemented - if self.address > other.address: - return True - elif self.address < other.address: - return False - else: - return self.size > other.size - - def __ge__(self, other): - if not isinstance(other, self.__class__): - return NotImplemented - if self.address > other.address: - return True - elif self.address < other.address: - return False - else: - return self.size >= other.size - - def __le__(self, other): - if not isinstance(other, self.__class__): - return NotImplemented - if self.address < other.address: - return True - elif self.address > other.address: - return False - else: - return self.size <= other.size - - def __hash__(self): - return hash((self._function, self._arch, self._address, self._size, self._incomingType)) - - @property - def function(self): - """ """ - return self._function - - @function.setter - def function(self, value): - self._function = value - - @property - def arch(self): - """ """ - return self._arch - - @arch.setter - def arch(self, value): - self._arch = value - - @property - def address(self): - """ """ - return self._address - - @address.setter - def address(self, value): - self._address = value - - @property - def size(self): - """ """ - return self._size - - @size.setter - def size(self, value): - self._size = value - - @property - def incomingType(self): - """ """ - return self._incomingType - - @size.setter - def incomingType(self, value): - self._incomingType = value diff --git a/python/associateddatastore.py b/python/associateddatastore.py index 7c572ebf..fe09d9c0 100644 --- a/python/associateddatastore.py +++ b/python/associateddatastore.py @@ -19,16 +19,16 @@ # IN THE SOFTWARE. import copy - +from typing import Any class _AssociatedDataStore(dict): _defaults = {} @classmethod - def set_default(cls, name, value): + def set_default(cls, name:str, value:Any): cls._defaults[name] = value - def __getattr__(self, name): + def __getattr__(self, name:str) -> Any: if name in self.__dict__: return self.__dict__[name] if name not in self: @@ -38,8 +38,8 @@ class _AssociatedDataStore(dict): return result return self.__getitem__(name) - def __setattr__(self, name, value): + def __setattr__(self, name:str, value:Any): self.__setitem__(name, value) - def __delattr__(self, name): + def __delattr__(self, name:str): self.__delitem__(name) diff --git a/python/basicblock.py b/python/basicblock.py index 2394f25f..ed6bc9e3 100644 --- a/python/basicblock.py +++ b/python/basicblock.py @@ -19,19 +19,19 @@ # IN THE SOFTWARE. import ctypes +from typing import Generator, Optional, List, Tuple # Binary Ninja components import binaryninja -from binaryninja import highlight -from binaryninja import _binaryninjacore as core -from binaryninja.enums import BranchType, HighlightColorStyle, HighlightStandardColor, InstructionTextTokenType - -# 2-3 compatibility -from binaryninja import range - +from . import _binaryninjacore as core +from .enums import BranchType, HighlightStandardColor +# from . import highlight +# from . import function as function_module +# from . import binaryview +# from . import architecture class BasicBlockEdge(object): - def __init__(self, branch_type, source, target, back_edge, fall_through): + def __init__(self, branch_type:BranchType, source:'BasicBlock', target:'BasicBlock', back_edge:bool, fall_through:bool): self._type = branch_type self._source = source self._target = target @@ -61,60 +61,55 @@ class BasicBlockEdge(object): return hash((self._type, self._source, self._target, self.back_edge, self.fall_through)) @property - def type(self): - """ """ + def type(self) -> BranchType: return self._type @type.setter - def type(self, value): + def type(self, value:BranchType) -> None: self._type = value @property - def source(self): - """ """ + def source(self) -> 'BasicBlock': return self._source @source.setter - def source(self, value): + def source(self, value:'BasicBlock') -> None: self._source = value @property - def target(self): - """ """ + def target(self) -> 'BasicBlock': return self._target @target.setter - def target(self, value): + def target(self, value:'BasicBlock') -> None: self._target = value @property - def back_edge(self): - """ """ + def back_edge(self) -> bool: return self._back_edge @back_edge.setter - def back_edge(self, value): + def back_edge(self, value:bool) -> None: self._back_edge = value @property - def fall_through(self): - """ """ + def fall_through(self) -> bool: return self._fall_through @fall_through.setter - def fall_through(self, value): + def fall_through(self, value:bool) -> None: self._fall_through = value class BasicBlock(object): - def __init__(self, handle, view = None): + def __init__(self, handle:core.BNBasicBlock, view:Optional['binaryninja.binaryview.BinaryView']=None): self._view = view self.handle = core.handle_of_type(handle, core.BNBasicBlock) self._arch = None self._func = None - self._instStarts = None - self._instLengths = None + self._instStarts:Optional[List[int]] = None + self._instLengths:Optional[List[int]] = None def __del__(self): core.BNFreeBasicBlock(self.handle) @@ -140,7 +135,7 @@ class BasicBlock(object): return not (self == other) def __hash__(self): - return hash((self.start, self.end, self.arch.name)) + return hash((self.start, self.end, self.arch)) def __setattr__(self, name, value): try: @@ -148,38 +143,52 @@ class BasicBlock(object): except AttributeError: raise AttributeError("attribute '%s' is read only" % name) - def __iter__(self): + def __iter__(self) -> Generator[Tuple[List['binaryninja.function.InstructionTextToken'], int], None, None]: + if self.arch is None: + raise Exception("Attempting to iterate a BasicBlock object with no Architecture set") + if self.view is None: + raise Exception("Attempting to iterate a Basic Block with no BinaryView") if self._instStarts is None: # don't add instruction start cache--the object is likely ephemeral idx = self.start while idx < self.end: - data = self._view.read(idx, min(self.arch.max_instr_length, self.end - idx)) + data = self.view.read(idx, min(self.arch.max_instr_length, self.end - idx)) inst_text = self.arch.get_instruction_text(data, idx) if inst_text[1] == 0: break yield inst_text idx += inst_text[1] else: + assert self._instLengths is not None for start, length in zip(self._instStarts, self._instLengths): - inst_text = self.arch.get_instruction_text(self._view.read(start, length), start) + inst_text = self.arch.get_instruction_text(self.view.read(start, length), start) if inst_text[1] == 0: break yield inst_text def __getitem__(self, i): self._buildStartCache() + assert self._instStarts is not None + assert self._instLengths is not None + if self.arch is None: + raise Exception("Attempting to iterate a BasicBlock object with no Architecture set") + if self.view is None: + raise Exception("Attempting to iterate a Basic Block with no BinaryView") + if isinstance(i, slice): return [self[index] for index in range(*i.indices(len(self._instStarts)))] start = self._instStarts[i] length = self._instLengths[i] - data = self._view.read(start, length) + data = self.view.read(start, length) return self.arch.get_instruction_text(data, start) - def _buildStartCache(self): + def _buildStartCache(self) -> None: if self._instStarts is None: # build the instruction start cache - self._instLengths = [] + if self.view is None: + raise Exception("Attempting to buildStartCache when BinaryView for BasicBlock is None") self._instStarts = [] + self._instLengths = [] start = self.start while start < self.end: length = self.view.get_instruction_length(start) @@ -189,36 +198,39 @@ class BasicBlock(object): self._instStarts.append(start) start += length - def _create_instance(self, handle, view): + def _create_instance(self, handle:core.BNBasicBlock, view:'binaryninja.binaryview.BinaryView') -> 'BasicBlock': """Internal method used to instantiate child instances""" return BasicBlock(handle, view) @property - def instruction_count(self): + def instruction_count(self) -> int: self._buildStartCache() + assert self._instStarts is not None return len(self._instStarts) @property - def function(self): + def function(self) -> Optional['binaryninja.function.Function']: """Basic block function (read-only)""" if self._func is not None: return self._func func = core.BNGetBasicBlockFunction(self.handle) if func is None: return None - self._func =binaryninja.function.Function(self._view, func) + self._func = binaryninja.function.Function(self._view, func) return self._func @property - def view(self): - """Binary view that contains the basic block (read-only)""" + def view(self) -> Optional['binaryninja.binaryview.BinaryView']: + """BinaryView that contains the basic block (read-only)""" if self._view is not None: return self._view + if self.function is None: + return None self._view = self.function.view return self._view @property - def arch(self): + def arch(self) -> Optional['binaryninja.architecture.Architecture']: """Basic block architecture (read-only)""" # The arch for a BasicBlock isn't going to change so just cache # it the first time we need it @@ -231,7 +243,7 @@ class BasicBlock(object): return self._arch @property - def source_block(self): + def source_block(self) -> Optional['BasicBlock']: """Basic block source block (read-only)""" block = core.BNGetBasicBlockSource(self.handle) if block is None: @@ -239,65 +251,70 @@ class BasicBlock(object): return BasicBlock(block, self._view) @property - def start(self): + def start(self) -> int: """Basic block start (read-only)""" return core.BNGetBasicBlockStart(self.handle) @property - def end(self): + def end(self) -> int: """Basic block end (read-only)""" return core.BNGetBasicBlockEnd(self.handle) @property - def length(self): + def length(self) -> int: """Basic block length (read-only)""" return core.BNGetBasicBlockLength(self.handle) @property - def index(self): + def index(self) -> int: """Basic block index in list of blocks for the function (read-only)""" return core.BNGetBasicBlockIndex(self.handle) @property - def outgoing_edges(self): + def outgoing_edges(self) -> List[BasicBlockEdge]: """List of basic block outgoing edges (read-only)""" + if self.view is None: + raise Exception("Attempting to call BasicBlock.outgoing_edges when BinaryView is None") count = ctypes.c_ulonglong(0) edges = core.BNGetBasicBlockOutgoingEdges(self.handle, count) + assert edges is not None, "core.BNGetBasicBlockOutgoingEdges returned None" result = [] for i in range(0, count.value): branch_type = BranchType(edges[i].type) - if edges[i].target: - target = self._create_instance(core.BNNewBasicBlockReference(edges[i].target), self.view) - else: - target = None + handle = core.BNNewBasicBlockReference(edges[i].target) + assert handle is not None + target = self._create_instance(handle, self.view) result.append(BasicBlockEdge(branch_type, self, target, edges[i].backEdge, edges[i].fallThrough)) core.BNFreeBasicBlockEdgeList(edges, count.value) return result @property - def incoming_edges(self): + def incoming_edges(self) -> List[BasicBlockEdge]: """List of basic block incoming edges (read-only)""" count = ctypes.c_ulonglong(0) + if self.view is None: + raise Exception("Attempting to buildStartCache when BinaryView for BasicBlock is None") + edges = core.BNGetBasicBlockIncomingEdges(self.handle, count) + assert edges is not None, "core.BNGetBasicBlockIncomingEdges returned None" result = [] for i in range(0, count.value): branch_type = BranchType(edges[i].type) - if edges[i].target: - target = self._create_instance(core.BNNewBasicBlockReference(edges[i].target), self.view) - else: - target = None + handle = core.BNNewBasicBlockReference(edges[i].target) + assert handle is not None + target = self._create_instance(handle, self.view) result.append(BasicBlockEdge(branch_type, target, self, edges[i].backEdge, edges[i].fallThrough)) core.BNFreeBasicBlockEdgeList(edges, count.value) return result @property - def has_undetermined_outgoing_edges(self): + def has_undetermined_outgoing_edges(self) -> bool: """Whether basic block has undetermined outgoing edges (read-only)""" return core.BNBasicBlockHasUndeterminedOutgoingEdges(self.handle) @property - def can_exit(self): - """Whether basic block can return or is tagged as 'No Return'""" + def can_exit(self) -> bool: + """Whether basic block can return or is tagged as 'No Return' (read-only)""" return core.BNBasicBlockCanExit(self.handle) @can_exit.setter @@ -306,112 +323,160 @@ class BasicBlock(object): BNBasicBlockSetCanExit(self.handle, value) @property - def has_invalid_instructions(self): + def has_invalid_instructions(self) -> bool: """Whether basic block has any invalid instructions (read-only)""" return core.BNBasicBlockHasInvalidInstructions(self.handle) @property - def dominators(self): + def dominators(self) -> List['BasicBlock']: """List of dominators for this basic block (read-only)""" + if self.view is None: + raise Exception("Attempting to call BasicBlock.dominators when BinaryView is None") count = ctypes.c_ulonglong() blocks = core.BNGetBasicBlockDominators(self.handle, count, False) + assert blocks is not None, "core.BNGetBasicBlockDominators returned None" result = [] for i in range(0, count.value): - result.append(self._create_instance(core.BNNewBasicBlockReference(blocks[i]), self.view)) + handle = core.BNNewBasicBlockReference(blocks[i]) + assert handle is not None + result.append(self._create_instance(handle, self.view)) core.BNFreeBasicBlockList(blocks, count.value) return result @property - def post_dominators(self): + def post_dominators(self) -> List['BasicBlock']: """List of dominators for this basic block (read-only)""" + if self.view is None: + raise Exception("Attempting to call BasicBlock.post_dominators when BinaryView is None") count = ctypes.c_ulonglong() blocks = core.BNGetBasicBlockDominators(self.handle, count, True) + assert blocks is not None, "core.BNGetBasicBlockDominators returned None" result = [] for i in range(0, count.value): - result.append(self._create_instance(core.BNNewBasicBlockReference(blocks[i]), self.view)) + handle = core.BNNewBasicBlockReference(blocks[i]) + assert handle is not None, "core.BNNewBasicBlockReference returned None" + result.append(self._create_instance(handle, self.view)) core.BNFreeBasicBlockList(blocks, count.value) return result @property - def strict_dominators(self): + def strict_dominators(self) -> List['BasicBlock']: """List of strict dominators for this basic block (read-only)""" + if self.view is None: + raise Exception("Attempting to call BasicBlock.strict_dominators when BinaryView is None") count = ctypes.c_ulonglong() blocks = core.BNGetBasicBlockStrictDominators(self.handle, count, False) + assert blocks is not None, "core.BNGetBasicBlockStrictDominators returned None" result = [] for i in range(0, count.value): - result.append(self._create_instance(core.BNNewBasicBlockReference(blocks[i]), self.view)) + handle = core.BNNewBasicBlockReference(blocks[i]) + assert handle is not None + result.append(self._create_instance(handle, self.view)) core.BNFreeBasicBlockList(blocks, count.value) return result @property - def immediate_dominator(self): + def immediate_dominator(self) -> Optional['BasicBlock']: """Immediate dominator of this basic block (read-only)""" + if self.view is None: + raise Exception("Attempting to call BasicBlock.immediate_dominator when BinaryView is None") + result = core.BNGetBasicBlockImmediateDominator(self.handle, False) if not result: return None return self._create_instance(result, self.view) @property - def immediate_post_dominator(self): + def immediate_post_dominator(self) -> Optional['BasicBlock']: """Immediate dominator of this basic block (read-only)""" + if self.view is None: + raise Exception("Attempting to call BasicBlock.immediate_post_dominator when BinaryView is None") + result = core.BNGetBasicBlockImmediateDominator(self.handle, True) if not result: return None return self._create_instance(result, self.view) @property - def dominator_tree_children(self): + def dominator_tree_children(self) -> List['BasicBlock']: """List of child blocks in the dominator tree for this basic block (read-only)""" + if self.view is None: + raise Exception("Attempting to call BasicBlock.dominator_tree_children when BinaryView is None") + count = ctypes.c_ulonglong() blocks = core.BNGetBasicBlockDominatorTreeChildren(self.handle, count, False) + assert blocks is not None, "core.BNGetBasicBlockDominatorTreeChildren returned None" result = [] for i in range(0, count.value): - result.append(self._create_instance(core.BNNewBasicBlockReference(blocks[i]), self.view)) + handle = core.BNNewBasicBlockReference(blocks[i]) + assert handle is not None + result.append(self._create_instance(handle, self.view)) core.BNFreeBasicBlockList(blocks, count.value) return result @property - def post_dominator_tree_children(self): + def post_dominator_tree_children(self) -> List['BasicBlock']: """List of child blocks in the post dominator tree for this basic block (read-only)""" + if self.view is None: + raise Exception("Attempting to call BasicBlock.post_dominator_tree_children when BinaryView is None") + count = ctypes.c_ulonglong() blocks = core.BNGetBasicBlockDominatorTreeChildren(self.handle, count, True) + assert blocks is not None, "core.BNGetBasicBlockDominatorTreeChildren returned None" result = [] for i in range(0, count.value): - result.append(self._create_instance(core.BNNewBasicBlockReference(blocks[i]), self.view)) + handle = core.BNNewBasicBlockReference(blocks[i]) + assert handle is not None + result.append(self._create_instance(handle, self.view)) core.BNFreeBasicBlockList(blocks, count.value) return result @property - def dominance_frontier(self): + def dominance_frontier(self) -> List['BasicBlock']: """Dominance frontier for this basic block (read-only)""" + if self.view is None: + raise Exception("Attempting to call BasicBlock.dominance_frontier when BinaryView is None") + count = ctypes.c_ulonglong() blocks = core.BNGetBasicBlockDominanceFrontier(self.handle, count, False) + assert blocks is not None, "core.BNGetBasicBlockDominanceFrontier returned None" result = [] for i in range(0, count.value): - result.append(self._create_instance(core.BNNewBasicBlockReference(blocks[i]), self.view)) + handle = core.BNNewBasicBlockReference(blocks[i]) + assert handle is not None + result.append(self._create_instance(handle, self.view)) core.BNFreeBasicBlockList(blocks, count.value) return result @property - def post_dominance_frontier(self): + def post_dominance_frontier(self) -> List['BasicBlock']: """Post dominance frontier for this basic block (read-only)""" + if self.view is None: + raise Exception("Attempting to call BasicBlock.post_dominance_frontier when BinaryView is None") count = ctypes.c_ulonglong() blocks = core.BNGetBasicBlockDominanceFrontier(self.handle, count, True) + assert blocks is not None, "core.BNGetBasicBlockDominanceFrontier returned None" result = [] for i in range(0, count.value): - result.append(self._create_instance(core.BNNewBasicBlockReference(blocks[i]), self.view)) + handle = core.BNNewBasicBlockReference(blocks[i]) + assert handle is not None + result.append(self._create_instance(handle, self.view)) core.BNFreeBasicBlockList(blocks, count.value) return result @property - def annotations(self): + def annotations(self) -> List[List['binaryninja.function.InstructionTextToken']]: """List of automatic annotations for the start of this block (read-only)""" + assert self.arch is not None, "attempting to get annotation from BasicBlock without architecture" + if self.function is None: + raise Exception("Attempting to call BasicBlock.annotations when BinaryView is None") + return self.function.get_block_annotations(self.start, self.arch) @property - def disassembly_text(self): + def disassembly_text(self) -> List['binaryninja.function.DisassemblyTextLine']: """ - ``disassembly_text`` property which returns a list of binaryninja.function.DisassemblyTextLine objects for the current basic block. + ``disassembly_text`` property which returns a list of function.DisassemblyTextLine objects for the current basic block. :Example: >>> current_basic_block.disassembly_text @@ -420,7 +485,7 @@ class BasicBlock(object): return self.get_disassembly_text() @property - def highlight(self): + def highlight(self) -> 'binaryninja.highlightHighlightColor': """Gets or sets the highlight color for basic block :Example: @@ -429,48 +494,51 @@ class BasicBlock(object): >>> current_basic_block.highlight <color: blue> """ - return highlight.HighlightColor._from_core_struct(core.BNGetBasicBlockHighlight(self.handle)) + return binaryninja.highlightHighlightColor._from_core_struct(core.BNGetBasicBlockHighlight(self.handle)) @highlight.setter - def highlight(self, value): + def highlight(self, value:'binaryninja.highlightHighlightColor') -> None: self.set_user_highlight(value) @property - def is_il(self): + def is_il(self) -> bool: """Whether the basic block contains IL""" return core.BNIsILBasicBlock(self.handle) @property - def is_low_level_il(self): + def is_low_level_il(self) -> bool: """Whether the basic block contains Low Level IL""" return core.BNIsLowLevelILBasicBlock(self.handle) @property - def is_medium_level_il(self): + def is_medium_level_il(self) -> bool: """Whether the basic block contains Medium Level IL""" return core.BNIsMediumLevelILBasicBlock(self.handle) - @classmethod - def get_iterated_dominance_frontier(self, blocks): + @staticmethod + def get_iterated_dominance_frontier(blocks:List['BasicBlock']) -> List['BasicBlock']: if len(blocks) == 0: return [] - block_set = (ctypes.POINTER(core.BNBasicBlock) * len(blocks))() + block_set = (ctypes.POINTER(core.BNBasicBlock) * len(blocks))() # type: ignore for i in range(len(blocks)): block_set[i] = blocks[i].handle count = ctypes.c_ulonglong() out_blocks = core.BNGetBasicBlockIteratedDominanceFrontier(block_set, len(blocks), count) + assert out_blocks is not None, "core.BNGetBasicBlockIteratedDominanceFrontier returned None" result = [] for i in range(0, count.value): - result.append(BasicBlock(core.BNNewBasicBlockReference(out_blocks[i]), blocks[0].view)) + handle = core.BNNewBasicBlockReference(out_blocks[i]) + assert handle is not None + result.append(BasicBlock(handle, blocks[0].view)) core.BNFreeBasicBlockList(out_blocks, count.value) return result - def mark_recent_use(self): + def mark_recent_use(self) -> None: core.BNMarkBasicBlockAsRecentlyUsed(self.handle) - def get_disassembly_text(self, settings=None): + def get_disassembly_text(self, settings:'binaryninja.function.DisassemblySettings'=None) -> List['binaryninja.function.DisassemblyTextLine']: """ - ``get_disassembly_text`` returns a list of binaryninja.function.DisassemblyTextLine objects for the current basic block. + ``get_disassembly_text`` returns a list of DisassemblyTextLine objects for the current basic block. :param DisassemblySettings settings: (optional) DisassemblySettings object :Example: @@ -484,52 +552,53 @@ class BasicBlock(object): count = ctypes.c_ulonglong() lines = core.BNGetBasicBlockDisassemblyText(self.handle, settings_obj, count) + assert lines is not None, "core.BNGetBasicBlockDisassemblyText returned None" result = [] for i in range(0, count.value): addr = lines[i].addr if (lines[i].instrIndex != 0xffffffffffffffff) and hasattr(self, 'il_function'): - il_instr = self.il_function[lines[i].instrIndex] # pylint: disable=no-member + il_instr = self.il_function[lines[i].instrIndex] # type: ignore else: il_instr = None - color = highlight.HighlightColor._from_core_struct(lines[i].highlight) - tokens = binaryninja.function.InstructionTextToken.get_instruction_lines(lines[i].tokens, lines[i].count) + color = binaryninja.highlightHighlightColor._from_core_struct(lines[i].highlight) + tokens = binaryninja.function.InstructionTextToken._from_core_struct(lines[i].tokens, lines[i].count) result.append(binaryninja.function.DisassemblyTextLine(tokens, addr, il_instr, color)) core.BNFreeDisassemblyTextLines(lines, count.value) return result - def set_auto_highlight(self, color): + def set_auto_highlight(self, color:'binaryninja.highlightHighlightColor') -> None: """ ``set_auto_highlight`` highlights the current BasicBlock with the supplied color. .. warning:: Use only in analysis plugins. Do not use in regular plugins, as colors won't be saved to the database. - :param HighlightStandardColor or highlight.HighlightColor color: Color value to use for highlighting + :param HighlightStandardColor or HighlightColor color: Color value to use for highlighting """ - 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, HighlightColor): + raise ValueError("Specified color is not one of HighlightStandardColor, HighlightColor") if isinstance(color, HighlightStandardColor): - color = highlight.HighlightColor(color) + color = binaryninja.highlightHighlightColor(color) core.BNSetAutoBasicBlockHighlight(self.handle, color._get_core_struct()) - def set_user_highlight(self, color): + def set_user_highlight(self, color:'binaryninja.highlightHighlightColor') -> None: """ ``set_user_highlight`` highlights the current BasicBlock with the supplied color - :param HighlightStandardColor or highlight.HighlightColor color: Color value to use for highlighting + :param HighlightStandardColor or HighlightColor color: Color value to use for highlighting :Example: - >>> current_basic_block.set_user_highlight(highlight.HighlightColor(red=0xff, blue=0xff, green=0)) + >>> current_basic_block.set_user_highlight(HighlightColor(red=0xff, blue=0xff, green=0)) >>> current_basic_block.set_user_highlight(HighlightStandardColor.BlueHighlightColor) """ - 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, binaryninja.highlightHighlightColor): + raise ValueError("Specified color is not one of HighlightStandardColor, HighlightColor") if isinstance(color, HighlightStandardColor): - color = highlight.HighlightColor(color) + color = binaryninja.highlightHighlightColor(color) core.BNSetUserBasicBlockHighlight(self.handle, color._get_core_struct()) - def get_instruction_containing_address(self, addr): + def get_instruction_containing_address(self, addr:int) -> Tuple[bool, int]: start = ctypes.c_uint64() - ret = core.BNGetBasicBlockInstructionContainingAddress(self.handle, addr, start) + ret:bool = core.BNGetBasicBlockInstructionContainingAddress(self.handle, addr, start) return ret, start.value @property diff --git a/python/binaryview.py b/python/binaryview.py index e2bae443..7aa1ad53 100644 --- a/python/binaryview.py +++ b/python/binaryview.py @@ -25,175 +25,240 @@ import queue import traceback import ctypes import abc -import numbers import json import inspect -from typing import Union +import os +from pathlib import Path +from typing import Callable, Generator, Optional, Union, Tuple, List, Mapping from collections import defaultdict, OrderedDict + # Binary Ninja components -from binaryninja import _binaryninjacore as core -from binaryninja.enums import (AnalysisState, SymbolType, InstructionTextTokenType, - Endianness, ModificationStatus, StringType, SegmentFlag, SectionSemantics, FindFlag, - TypeClass, SaveOption, BinaryViewEventType, FunctionGraphType) import binaryninja -from binaryninja import associateddatastore # required for _BinaryViewAssociatedDataStore -from binaryninja import log -from binaryninja import types -from binaryninja import typelibrary -from binaryninja import fileaccessor -from binaryninja import databuffer -from binaryninja import basicblock -from binaryninja import lineardisassembly -from binaryninja import metadata -from binaryninja import highlight -from binaryninja import function -from binaryninja.function import AddressRange -from binaryninja import settings -from binaryninja import workflow -from binaryninja import pyNativeStr +from . import _binaryninjacore as core +from .enums import (AnalysisState, SymbolType, + Endianness, ModificationStatus, StringType, SegmentFlag, SectionSemantics, FindFlag, + TypeClass, BinaryViewEventType, FunctionGraphType, TagReferenceType, TagTypeType, TypeFieldReference) +from . import associateddatastore # required for _BinaryViewAssociatedDataStore +from . import log +from . import typelibrary +from . import fileaccessor +from . import databuffer +from . import basicblock +from . import lineardisassembly +from . import metadata +from . import highlight +from . import settings +from . import variable +from . import architecture +from . import filemetadata +from . import lowlevelil +from . import mediumlevelil +from . import highlevelil +from . import workflow +# The following are imported as such to allow the linter disambiguate the module name +# from properties and methods of the same name +from . import function as _function +from . import types as _types +from . import platform as _platform + + +PathType = Union[str, os.PathLike] +InstructionsType = Generator[Tuple[List['_function.InstructionTextToken'], int], None, None] +NotificationType = Mapping['BinaryDataNotification', 'BinaryDataNotificationCallbacks'] + +class ReferenceSource(object): + def __init__(self, func:Optional['_function.Function'], arch:Optional['architecture.Architecture'], + addr:int): + self._function = func + self._arch = arch + self._address = addr + + def __repr__(self): + if self._arch: + return f"<ref: {self._arch.name}@{self._address:#x}>" + else: + return f"<ref: {self._address:#x}>" + + def __eq__(self, other): + if not isinstance(other, self.__class__): + return NotImplemented + return (self.function, self.arch, self.address) == (other.function, other.arch, other.address) + + 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 self.address < other.address + + def __gt__(self, other): + if not isinstance(other, self.__class__): + return NotImplemented + return self.address > other.address + + def __ge__(self, other): + if not isinstance(other, self.__class__): + return NotImplemented + return self.address >= other.address + + def __le__(self, other): + if not isinstance(other, self.__class__): + return NotImplemented + return self.address <= other.address + + def __hash__(self): + return hash((self._function, self._arch, self._address)) + + @property + def function(self): + return self._function + + @property + def arch(self): + return self._arch + + @property + def address(self): + return self._address -# 2-3 compatibility -from binaryninja import range -from binaryninja import with_metaclass -from binaryninja import cstr class BinaryDataNotification(object): def __init__(self): pass - def data_written(self, view, offset, length): + def data_written(self, view:'BinaryView', offset:int, length:int) -> None: pass - def data_inserted(self, view, offset, length): + def data_inserted(self, view:'BinaryView', offset:int, length:int) -> None: pass - def data_removed(self, view, offset, length): + def data_removed(self, view:'BinaryView', offset:int, length:int) -> None: pass - def function_added(self, view, func): + def function_added(self, view:'BinaryView', func:'_function.Function') -> None: pass - def function_removed(self, view, func): + def function_removed(self, view:'BinaryView', func:'_function.Function') -> None: pass - def function_updated(self, view, func): + def function_updated(self, view:'BinaryView', func:'_function.Function') -> None: pass - def function_update_requested(self, view, func): + def function_update_requested(self, view:'BinaryView', func:'_function.Function') -> None: pass - def data_var_added(self, view, var): + def data_var_added(self, view:'BinaryView', var:'DataVariable') -> None: pass - def data_var_removed(self, view, var): + def data_var_removed(self, view:'BinaryView', var:'DataVariable') -> None: pass - def data_var_updated(self, view, var): + def data_var_updated(self, view:'BinaryView', var:'DataVariable') -> None: pass - def data_metadata_updated(self, view, offset): + def data_metadata_updated(self, view:'BinaryView', offset:int) -> None: pass - def tag_type_updated(self, view, tag_type): + def tag_type_updated(self, view:'BinaryView', tag_type) -> None: pass - def tag_added(self, view, tag, ref_type, auto_defined, arch, func, addr): + def tag_added(self, view:'BinaryView', tag:'Tag', ref_type:TagReferenceType, auto_defined:bool, + arch:Optional['architecture.Architecture'], func:Optional[_function.Function], addr:int) -> None: pass - def tag_updated(self, view, tag, ref_type, auto_defined, arch, func, addr): + def tag_updated(self, view:'BinaryView', tag:'Tag', ref_type:TagReferenceType, auto_defined:bool, + arch:Optional['architecture.Architecture'], func:Optional[_function.Function], addr:int) -> None: pass - def tag_removed(self, view, tag, ref_type, auto_defined, arch, func, addr): + def tag_removed(self, view:'BinaryView', tag:'Tag', ref_type:TagReferenceType, auto_defined:bool, + arch:Optional['architecture.Architecture'], func:Optional[_function.Function], addr:int) -> None: pass - def symbol_added(self, view, sym): + def symbol_added(self, view:'BinaryView', sym:'_types.Symbol') -> None: pass - def symbol_updated(self, view, sym): + def symbol_updated(self, view:'BinaryView', sym:'_types.Symbol') -> None: pass - def symbol_removed(self, view, sym): + def symbol_removed(self, view:'BinaryView', sym:'_types.Symbol') -> None: pass - def string_found(self, view, string_type, offset, length): + def string_found(self, view:'BinaryView', string_type:StringType, offset:int, length:int) -> None: pass - def string_removed(self, view, string_type, offset, length): + def string_removed(self, view:'BinaryView', string_type:StringType, offset:int, length:int) -> None: pass - def type_defined(self, view, name, type): + def type_defined(self, view:'BinaryView', name:'_types.QualifiedName', type:'_types.Type') -> None: pass - def type_undefined(self, view, name, type): + def type_undefined(self, view:'BinaryView', name:'_types.QualifiedName', type:'_types.Type') -> None: pass - def type_ref_changed(self, view, name, type): + def type_ref_changed(self, view:'BinaryView', name:'_types.QualifiedName', type:'_types.Type') -> None: pass -_decodings = { - StringType.AsciiString: "ascii", - StringType.Utf8String: "utf-8", - StringType.Utf16String: "utf-16", - StringType.Utf32String: "utf-32", -} - class StringReference(object): - def __init__(self, bv, string_type, start, length): + _decodings = { + StringType.AsciiString: "ascii", + StringType.Utf8String: "utf-8", + StringType.Utf16String: "utf-16", + StringType.Utf32String: "utf-32", + } + def __init__(self, bv:'BinaryView', string_type:StringType, start:int, length:int): self._type = string_type self._start = start self._length = length self._view = bv def __repr__(self): - return "<%s: %#x, len %#x>" % (self._type, self._start, self._length) + return f"<{self._type}: {self._start:#x}, len {self._length:#x}>" def __str__(self): - return pyNativeStr(self.raw) + return self.value def __len__(self): return self._length @property - def value(self): - return self._view.read(self._start, self._length).decode(_decodings[self._type]) + def value(self) -> str: + return self._view.read(self._start, self._length).decode(self._decodings[self._type]) @property - def raw(self): + def raw(self) -> bytes: return self._view.read(self._start, self._length) @property - def type(self): - """ """ + def type(self) -> StringType: return self._type @type.setter - def type(self, value): + def type(self, value:StringType): self._type = value @property - def start(self): - """ """ + def start(self) -> int: return self._start @start.setter - def start(self, value): + def start(self, value:int): self._start = value @property - def length(self): - """ """ + def length(self) -> int: return self._length @property - def view(self): - """ """ + def view(self) -> 'BinaryView': return self._view -_pending_analysis_completion_events = {} class AnalysisCompletionEvent(object): """ The ``AnalysisCompletionEvent`` object provides an asynchronous mechanism for receiving @@ -208,38 +273,36 @@ class AnalysisCompletionEvent(object): >>> evt = AnalysisCompletionEvent(bv, on_complete) >>> """ - def __init__(self, view, callback): + _pending_analysis_completion_events = {} + def __init__(self, view:'BinaryView', callback:Union[Callable[['AnalysisCompletionEvent'], None], Callable[[], None]]): self._view = view self.callback = callback self._cb = ctypes.CFUNCTYPE(None, ctypes.c_void_p)(self._notify) self.handle = core.BNAddAnalysisCompletionEvent(self._view.handle, None, self._cb) - global _pending_analysis_completion_events - _pending_analysis_completion_events[id(self)] = self + self.__class__._pending_analysis_completion_events[id(self)] = self def __del__(self): - global _pending_analysis_completion_events - if id(self) in _pending_analysis_completion_events: - del _pending_analysis_completion_events[id(self)] + if id(self) in self.__class__._pending_analysis_completion_events: + del self.__class__._pending_analysis_completion_events[id(self)] core.BNFreeAnalysisCompletionEvent(self.handle) def _notify(self, ctxt): - global _pending_analysis_completion_events - if id(self) in _pending_analysis_completion_events: - del _pending_analysis_completion_events[id(self)] + if id(self) in self.__class__._pending_analysis_completion_events: + del self.__class__._pending_analysis_completion_events[id(self)] try: arg_offset = inspect.ismethod(self.callback) callback_spec = inspect.getargspec(self.callback) if len(callback_spec.args) > arg_offset: - self.callback(self) + self.callback(self) # type: ignore else: - self.callback() + self.callback() # type: ignore except: log.log_error(traceback.format_exc()) def _empty_callback(self): pass - def cancel(self): + def cancel(self) -> None: """ The ``cancel`` method will cancel analysis for an :class:`AnalysisCompletionEvent`. @@ -248,23 +311,18 @@ class AnalysisCompletionEvent(object): """ self.callback = self._empty_callback core.BNCancelAnalysisCompletionEvent(self.handle) - global _pending_analysis_completion_events - if id(self) in _pending_analysis_completion_events: - del _pending_analysis_completion_events[id(self)] + if id(self) in self.__class__._pending_analysis_completion_events: + del self.__class__._pending_analysis_completion_events[id(self)] @property - def view(self): - """ """ + def view(self) -> 'BinaryView': return self._view @view.setter - def view(self, value): + def view(self, value:'BinaryView'): self._view = value -# This has no functional purposes; -# we just need it to stop Python from prematurely freeing the object -_binaryview_events = {} class BinaryViewEvent(object): """ The ``BinaryViewEvent`` object provides a mechanism for receiving callbacks when a BinaryView @@ -286,24 +344,28 @@ class BinaryViewEvent(object): ... >>> BinaryViewType.add_binaryview_finalized_event(callback) """ + BinaryViewEventCallback = Callable[['BinaryView'], None] + # This has no functional purposes; + # we just need it to stop Python from prematurely freeing the object + _binaryview_events = {} + @classmethod - def register(cls, event_type, callback): + def register(cls, event_type:BinaryViewEventType, callback:BinaryViewEventCallback) -> None: callback_obj = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.POINTER(core.BNBinaryView))(lambda ctxt, view: cls._notify(view, callback)) core.BNRegisterBinaryViewEvent(event_type, callback_obj, None) - global _binaryview_events - _binaryview_events[len(_binaryview_events)] = callback_obj + cls._binaryview_events[len(cls._binaryview_events)] = callback_obj - @classmethod - def _notify(cls, view, callback): + @staticmethod + def _notify(view:core.BNBinaryView, callback:BinaryViewEventCallback) -> None: try: - file_metadata = binaryninja.filemetadata.FileMetadata(handle = core.BNGetFileForView(view)) - view_obj = binaryninja.binaryview.BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) + file_metadata = filemetadata.FileMetadata(handle = core.BNGetFileForView(view)) + view_obj = BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) callback(view_obj) except: - binaryninja.log.log_error(traceback.format_exc()) + log.log_error(traceback.format_exc()) class ActiveAnalysisInfo(object): - def __init__(self, func, analysis_time, update_count, submit_count): + def __init__(self, func:'_function.Function', analysis_time:int, update_count:int, submit_count:int): self._func = func self._analysis_time = analysis_time self._update_count = update_count @@ -313,81 +375,46 @@ class ActiveAnalysisInfo(object): return "<ActiveAnalysisInfo %s, analysis_time %d, update_count %d, submit_count %d>" % (self._func, self._analysis_time, self._update_count, self._submit_count) @property - def func(self): - """ """ + def func(self) -> '_function.Function': return self._func - @func.setter - def func(self, value): - self._func = value - @property - def analysis_time(self): - """ """ + def analysis_time(self) -> int: return self._analysis_time - @analysis_time.setter - def analysis_time(self, value): - self._analysis_time = value - @property - def update_count(self): - """ """ + def update_count(self) -> int: return self._update_count - @update_count.setter - def update_count(self, value): - self._update_count = value - @property - def submit_count(self): - """ """ + def submit_count(self) -> int: return self._submit_count - @submit_count.setter - def submit_count(self, value): - self._submit_count = value - class AnalysisInfo(object): - def __init__(self, state, analysis_time, active_info): + def __init__(self, state:AnalysisState, analysis_time:int, active_info:List[ActiveAnalysisInfo]): self._state = AnalysisState(state) self._analysis_time = analysis_time self._active_info = active_info def __repr__(self): - return "<AnalysisInfo %s, analysis_time %d, active_info %s>" % (self._state, self._analysis_time, self._active_info) + return f"<AnalysisInfo {self._state}, analysis_time {self._analysis_time}, active_info {len(self._active_info)}>" @property - def state(self): - """ """ + def state(self) -> AnalysisState: return self._state - @state.setter - def state(self, value): - self._state = value - @property - def analysis_time(self): - """ """ + def analysis_time(self) -> int: return self._analysis_time - @analysis_time.setter - def analysis_time(self, value): - self._analysis_time = value - @property - def active_info(self): - """ """ + def active_info(self) -> List[ActiveAnalysisInfo]: return self._active_info - @active_info.setter - def active_info(self, value): - self._active_info = value - class AnalysisProgress(object): - def __init__(self, state, count, total): + def __init__(self, state:AnalysisState, count:int, total:int): self._state = state self._count = count self._total = total @@ -409,109 +436,20 @@ class AnalysisProgress(object): return "<progress: %s>" % str(self) @property - def state(self): - """ """ + def state(self) -> AnalysisState: return self._state - @state.setter - def state(self, value): - self._state = value - @property - def count(self): - """ """ + def count(self) -> int: return self._count - @count.setter - def count(self, value): - self._count = value - @property - def total(self): - """ """ + def total(self) -> int: return self._total - @total.setter - def total(self, value): - self._total = value - - -class DataVariable(object): - def __init__(self, addr, var_type, auto_discovered, view=None): - self._address = addr - self._type = var_type - self._auto_discovered = auto_discovered - self._view = view - - @property - def data_refs_from(self): - """data cross references from this data variable (read-only)""" - return self._view.get_data_refs_from(self._address, max(1, len(self))) - - @property - def data_refs(self): - """data cross references to this data variable (read-only)""" - return self._view.get_data_refs(self._address, max(1, len(self))) - - @property - def code_refs(self): - """code references to this data variable (read-only)""" - return self._view.get_code_refs(self._address, max(1, len(self))) - - def __len__(self): - return len(self._type) - - def __repr__(self): - return "<var 0x%x: %s>" % (self._address, str(self._type)) - - @property - def address(self): - """ """ - return self._address - - @address.setter - def address(self, value): - self._address = value - - @property - def type(self): - """ """ - return self._type - - @type.setter - def type(self, value): - self._type = value - - @property - def auto_discovered(self): - """ """ - return self._auto_discovered - - @auto_discovered.setter - def auto_discovered(self, value): - self._auto_discovered = value - - @property - def view(self): - """ """ - return self._view - - @view.setter - def view(self, value): - self._view = value - - -class DataVariableAndName(DataVariable): - def __init__(self, addr: int, var_type: types.Type, var_name: str, auto_discovered: bool, view: "BinaryView" = None) -> None: - super(DataVariableAndName, self).__init__(addr, var_type, auto_discovered, view) - self.name = var_name - - def __repr__(self) -> str: - return "<var 0x%x: %s %s>" % (self.address, str(self.type), self.name) - class BinaryDataNotificationCallbacks(object): - def __init__(self, view, notify): + def __init__(self, view:'BinaryView', notify:'BinaryDataNotification'): self._view = view self._notify = notify self._cb = core.BNBinaryDataNotification() @@ -540,238 +478,225 @@ class BinaryDataNotificationCallbacks(object): self._cb.typeUndefined = self._cb.typeUndefined.__class__(self._type_undefined) self._cb.typeReferenceChanged = self._cb.typeReferenceChanged.__class__(self._type_ref_changed) - def _register(self): + def _register(self) -> None: core.BNRegisterDataNotification(self._view.handle, self._cb) - def _unregister(self): + def _unregister(self) -> None: core.BNUnregisterDataNotification(self._view.handle, self._cb) - def _data_written(self, ctxt, view, offset, length): + def _data_written(self, ctxt, view:core.BNBinaryView, offset:int, length:int) -> None: try: self._notify.data_written(self._view, offset, length) except OSError: log.log_error(traceback.format_exc()) - def _data_inserted(self, ctxt, view, offset, length): + def _data_inserted(self, ctxt, view:core.BNBinaryView, offset:int, length:int) -> None: try: self._notify.data_inserted(self._view, offset, length) except: log.log_error(traceback.format_exc()) - def _data_removed(self, ctxt, view, offset, length): + def _data_removed(self, ctxt, view:core.BNBinaryView, offset:int, length:int) -> None: try: self._notify.data_removed(self._view, offset, length) except: log.log_error(traceback.format_exc()) - def _function_added(self, ctxt, view, func): + def _function_added(self, ctxt, view:core.BNBinaryView, func:core.BNFunction) -> None: try: - self._notify.function_added(self._view, binaryninja.function.Function(self._view, core.BNNewFunctionReference(func))) + self._notify.function_added(self._view, _function.Function(self._view, core.BNNewFunctionReference(func))) except: log.log_error(traceback.format_exc()) - def _function_removed(self, ctxt, view, func): + def _function_removed(self, ctxt, view:core.BNBinaryView, func:core.BNFunction) -> None: try: - self._notify.function_removed(self._view, binaryninja.function.Function(self._view, core.BNNewFunctionReference(func))) + self._notify.function_removed(self._view, _function.Function(self._view, core.BNNewFunctionReference(func))) except: log.log_error(traceback.format_exc()) - def _function_updated(self, ctxt, view, func): + def _function_updated(self, ctxt, view:core.BNBinaryView, func:core.BNFunction) -> None: try: - self._notify.function_updated(self._view, binaryninja.function.Function(self._view, core.BNNewFunctionReference(func))) + self._notify.function_updated(self._view, _function.Function(self._view, core.BNNewFunctionReference(func))) except: log.log_error(traceback.format_exc()) - def _function_update_requested(self, ctxt, view, func): + def _function_update_requested(self, ctxt, view:core.BNBinaryView, func:core.BNFunction) -> None: try: - self._notify.function_update_requested(self._view, binaryninja.function.Function(self._view, core.BNNewFunctionReference(func))) + self._notify.function_update_requested(self._view, _function.Function(self._view, core.BNNewFunctionReference(func))) except: log.log_error(traceback.format_exc()) - def _data_var_added(self, ctxt, view, var): + def _data_var_added(self, ctxt, view:core.BNBinaryView, var:core.BNVariable) -> None: try: address = var[0].address - var_type = types.Type(core.BNNewTypeReference(var[0].type), platform = self._view.platform, confidence = var[0].typeConfidence) + var_type = _types.Type(core.BNNewTypeReference(var[0].type), platform = self._view.platform, confidence = var[0].typeConfidence) auto_discovered = var[0].autoDiscovered self._notify.data_var_added(self._view, DataVariable(address, var_type, auto_discovered, self._view)) except: log.log_error(traceback.format_exc()) - def _data_var_removed(self, ctxt, view, var): + def _data_var_removed(self, ctxt, view:core.BNBinaryView, var:core.BNVariable) -> None: try: address = var[0].address - var_type = types.Type(core.BNNewTypeReference(var[0].type), platform = self._view.platform, confidence = var[0].typeConfidence) + var_type = _types.Type(core.BNNewTypeReference(var[0].type), platform = self._view.platform, confidence = var[0].typeConfidence) auto_discovered = var[0].autoDiscovered self._notify.data_var_removed(self._view, DataVariable(address, var_type, auto_discovered, self._view)) except: log.log_error(traceback.format_exc()) - def _data_var_updated(self, ctxt, view, var): + def _data_var_updated(self, ctxt, view:core.BNBinaryView, var:core.BNVariable) -> None: try: address = var[0].address - var_type = types.Type(core.BNNewTypeReference(var[0].type), platform = self._view.platform, confidence = var[0].typeConfidence) + var_type = _types.Type(core.BNNewTypeReference(var[0].type), platform = self._view.platform, confidence = var[0].typeConfidence) auto_discovered = var[0].autoDiscovered self._notify.data_var_updated(self._view, DataVariable(address, var_type, auto_discovered, self._view)) except: log.log_error(traceback.format_exc()) - def _data_metadata_updated(self, ctxt, view, offset): + def _data_metadata_updated(self, ctxt, view:core.BNBinaryView, offset:int) -> None: try: self._notify.data_metadata_updated(self._view, offset) except: log.log_error(traceback.format_exc()) - def _tag_type_updated(self, ctxt, view, tag_type): + def _tag_type_updated(self, ctxt, view:core.BNBinaryView, tag_type:core.BNTagType) -> None: try: - self._notify.tag_type_updated(self._view, TagType(core.BNNewTagTypeReference(tag_type))) + core_tag_type = core.BNNewTagTypeReference(tag_type) + assert core_tag_type is not None, "core.BNNewTagTypeReference returned None" + self._notify.tag_type_updated(self._view, TagType(core_tag_type)) except: log.log_error(traceback.format_exc()) - def _tag_added(self, ctxt, view, tag_ref): + def _tag_added(self, ctxt, view:core.BNBinaryView, tag_ref:core.BNTagReference) -> None: try: ref_type = tag_ref[0].refType auto_defined = tag_ref[0].autoDefined - tag = Tag(core.BNNewTagReference(tag_ref[0].tag)) + core_tag = core.BNNewTagReference(tag_ref[0].tag) + assert core_tag is not None, "core.BNNewTagReference returned None" + tag = Tag(core_tag) # Null for data tags (not in any arch or function) if ctypes.cast(tag_ref[0].arch, ctypes.c_void_p).value is None: arch = None else: - arch = binaryninja.architecture.CoreArchitecture._from_cache(tag_ref[0].arch) + arch = architecture.CoreArchitecture._from_cache(tag_ref[0].arch) if ctypes.cast(tag_ref[0].func, ctypes.c_void_p).value is None: func = None else: - func = binaryninja.function.Function(self._view, core.BNNewFunctionReference(tag_ref[0].func)) + func = _function.Function(self._view, core.BNNewFunctionReference(tag_ref[0].func)) addr = tag_ref[0].addr self._notify.tag_added(self._view, tag, ref_type, auto_defined, arch, func, addr) except: log.log_error(traceback.format_exc()) - def _tag_updated(self, ctxt, view, tag_ref): + def _tag_updated(self, ctxt, view:core.BNBinaryView, tag_ref:core.BNTagReference) -> None: try: ref_type = tag_ref[0].refType auto_defined = tag_ref[0].autoDefined - tag = Tag(core.BNNewTagReference(tag_ref[0].tag)) + core_tag = core.BNNewTagReference(tag_ref[0].tag) + assert core_tag is not None + tag = Tag(core_tag) # Null for data tags (not in any arch or function) if ctypes.cast(tag_ref[0].arch, ctypes.c_void_p).value is None: arch = None else: - arch = binaryninja.architecture.CoreArchitecture._from_cache(tag_ref[0].arch) + arch = architecture.CoreArchitecture._from_cache(tag_ref[0].arch) if ctypes.cast(tag_ref[0].func, ctypes.c_void_p).value is None: func = None else: - func = binaryninja.function.Function(self._view, core.BNNewFunctionReference(tag_ref[0].func)) + func = _function.Function(self._view, core.BNNewFunctionReference(tag_ref[0].func)) addr = tag_ref[0].addr self._notify.tag_updated(self._view, tag, ref_type, auto_defined, arch, func, addr) except: log.log_error(traceback.format_exc()) - def _tag_removed(self, ctxt, view, tag_ref): + def _tag_removed(self, ctxt, view:core.BNBinaryView, tag_ref:core.BNTagReference) -> None: try: ref_type = tag_ref[0].refType auto_defined = tag_ref[0].autoDefined - tag = Tag(core.BNNewTagReference(tag_ref[0].tag)) + core_tag = core.BNNewTagReference(tag_ref[0].tag) + assert core_tag is not None, "core.BNNewTagReference returned None" + tag = Tag(core_tag) # Null for data tags (not in any arch or function) if ctypes.cast(tag_ref[0].arch, ctypes.c_void_p).value is None: arch = None else: - arch = binaryninja.architecture.CoreArchitecture._from_cache(tag_ref[0].arch) + arch = architecture.CoreArchitecture._from_cache(tag_ref[0].arch) if ctypes.cast(tag_ref[0].func, ctypes.c_void_p).value is None: func = None else: - func = binaryninja.function.Function(self._view, core.BNNewFunctionReference(tag_ref[0].func)) + func = _function.Function(self._view, core.BNNewFunctionReference(tag_ref[0].func)) addr = tag_ref[0].addr self._notify.tag_removed(self._view, tag, ref_type, auto_defined, arch, func, addr) except: log.log_error(traceback.format_exc()) - def _symbol_added(self, ctxt, view, sym): + def _symbol_added(self, ctxt, view:core.BNBinaryView, sym:core.BNSymbol) -> None: try: - self._notify.symbol_added(self._view, types.Symbol(None, None, None, handle = core.BNNewSymbolReference(sym))) + self._notify.symbol_added(self._view, _types.Symbol(None, None, None, handle = core.BNNewSymbolReference(sym))) except: log.log_error(traceback.format_exc()) - def _symbol_updated(self, ctxt, view, sym): + def _symbol_updated(self, ctxt, view:core.BNBinaryView, sym:core.BNSymbol) -> None: try: - self._notify.symbol_updated(self._view, types.Symbol(None, None, None, handle = core.BNNewSymbolReference(sym))) + self._notify.symbol_updated(self._view, _types.Symbol(None, None, None, handle = core.BNNewSymbolReference(sym))) except: log.log_error(traceback.format_exc()) - def _symbol_removed(self, ctxt, view, sym): + def _symbol_removed(self, ctxt, view:core.BNBinaryView, sym:core.BNSymbol) -> None: try: - self._notify.symbol_removed(self._view, types.Symbol(None, None, None, handle = core.BNNewSymbolReference(sym))) + self._notify.symbol_removed(self._view, _types.Symbol(None, None, None, handle = core.BNNewSymbolReference(sym))) except: log.log_error(traceback.format_exc()) - def _string_found(self, ctxt, view, string_type, offset, length): + def _string_found(self, ctxt, view:core.BNBinaryView, string_type:int, offset:int, length:int) -> None: try: self._notify.string_found(self._view, StringType(string_type), offset, length) except: log.log_error(traceback.format_exc()) - def _string_removed(self, ctxt, view, string_type, offset, length): + def _string_removed(self, ctxt, view:core.BNBinaryView, string_type:int, offset:int, length:int) -> None: try: self._notify.string_removed(self._view, StringType(string_type), offset, length) except: log.log_error(traceback.format_exc()) - def _type_defined(self, ctxt, view, name, type_obj): + def _type_defined(self, ctxt, view:core.BNBinaryView, name:str, type_obj:'_types.Type') -> None: try: - qualified_name = types.QualifiedName._from_core_struct(name[0]) - self._notify.type_defined(self._view, qualified_name, types.Type(core.BNNewTypeReference(type_obj), platform = self._view.platform)) + qualified_name = _types.QualifiedName._from_core_struct(name[0]) + self._notify.type_defined(self._view, qualified_name, _types.Type(core.BNNewTypeReference(type_obj), platform = self._view.platform)) except: log.log_error(traceback.format_exc()) - def _type_undefined(self, ctxt, view, name, type_obj): + def _type_undefined(self, ctxt, view:core.BNBinaryView, name:str, type_obj:'_types.Type') -> None: try: - qualified_name = types.QualifiedName._from_core_struct(name[0]) - self._notify.type_undefined(self._view, qualified_name, types.Type(core.BNNewTypeReference(type_obj), platform = self._view.platform)) + qualified_name = _types.QualifiedName._from_core_struct(name[0]) + self._notify.type_undefined(self._view, qualified_name, _types.Type(core.BNNewTypeReference(type_obj), platform = self._view.platform)) except: log.log_error(traceback.format_exc()) - def _type_ref_changed(self, ctxt, view, name, type_obj): + def _type_ref_changed(self, ctxt, view:core.BNBinaryView, name:str, type_obj:'_types.Type') -> None: try: - qualified_name = types.QualifiedName._from_core_struct(name[0]) - self._notify.type_ref_changed(self._view, qualified_name, types.Type(core.BNNewTypeReference(type_obj), platform = self._view.platform)) + qualified_name = _types.QualifiedName._from_core_struct(name[0]) + self._notify.type_ref_changed(self._view, qualified_name, _types.Type(core.BNNewTypeReference(type_obj), platform = self._view.platform)) except: log.log_error(traceback.format_exc()) @property - def view(self): - """ """ + def view(self) -> 'BinaryView': return self._view - @view.setter - def view(self, value): - self._view = value - @property - def notify(self): - """ """ + def notify(self) -> 'BinaryDataNotification': return self._notify - @notify.setter - def notify(self, value): - self._notify = value - class _BinaryViewTypeMetaclass(type): - - @property - def list(self): - """List all BinaryView types (read-only)""" - binaryninja._init_plugins() - count = ctypes.c_ulonglong() - types = core.BNGetBinaryViewTypes(count) - result = [] - for i in range(0, count.value): - result.append(BinaryViewType(types[i])) - core.BNFreeBinaryViewTypeList(types) - return result - def __iter__(self): binaryninja._init_plugins() count = ctypes.c_ulonglong() types = core.BNGetBinaryViewTypes(count) + if types is None: + return try: for i in range(0, count.value): yield BinaryViewType(types[i]) @@ -786,17 +711,14 @@ class _BinaryViewTypeMetaclass(type): return BinaryViewType(view_type) - -# Used to force Python callback objects to not get garbage collected -_platform_recognizers = {} - -class BinaryViewType(with_metaclass(_BinaryViewTypeMetaclass, object)): - - def __init__(self, handle): +class BinaryViewType(metaclass=_BinaryViewTypeMetaclass): + def __init__(self, handle:core.BNBinaryViewType): + # Used to force Python callback objects to not get garbage collected + _platform_recognizers = {} self.handle = core.handle_of_type(handle, core.BNBinaryViewType) def __repr__(self): - return "<view type: '%s'>" % self.name + return f"<view type: '{self.name}'>" def __eq__(self, other): if not isinstance(other, self.__class__): @@ -812,32 +734,27 @@ class BinaryViewType(with_metaclass(_BinaryViewTypeMetaclass, object)): return hash(ctypes.addressof(self.handle.contents)) @property - def list(self): - """Allow tab completion to discover metaclass list property""" - pass - - @property - def name(self): + def name(self) -> str: """BinaryView name (read-only)""" return core.BNGetBinaryViewTypeName(self.handle) @property - def long_name(self): + def long_name(self) -> str: """BinaryView long name (read-only)""" return core.BNGetBinaryViewTypeLongName(self.handle) @property - def is_deprecated(self): + def is_deprecated(self) -> bool: """returns if the BinaryViewType is deprecated (read-only)""" return core.BNIsBinaryViewTypeDeprecated(self.handle) - def create(self, data): + def create(self, data:'BinaryView') -> Optional['BinaryView']: view = core.BNCreateBinaryViewOfType(self.handle, data.handle) if view is None: return None return BinaryView(file_metadata=data.file, handle=view) - def open(self, src, file_metadata=None): + def open(self, src:PathType, file_metadata:'filemetadata.FileMetadata'=None) -> Optional['BinaryView']: """ ``open`` opens an instance of a particular BinaryViewType and returns it, or None if not possible. @@ -846,14 +763,13 @@ class BinaryViewType(with_metaclass(_BinaryViewTypeMetaclass, object)): :return: returns a :py:class:`BinaryView` object for the given filename :rtype: :py:class:`BinaryView` or ``None`` """ - data = BinaryView.open(src, file_metadata) if data is None: return None return self.create(data) @classmethod - def get_view_of_file(cls, filename, update_analysis=True, progress_func=None): + def get_view_of_file(cls, filename:PathType, update_analysis:bool=True, progress_func:Callable[[int, int], bool]=None): """ ``get_view_of_file`` opens and returns the first available :py:class:`BinaryView`, excluding a Raw :py:class:`BinaryViewType` unless no other view is available @@ -873,7 +789,7 @@ class BinaryViewType(with_metaclass(_BinaryViewTypeMetaclass, object)): if f is None or f.read(len(sqlite)) != sqlite: return None f.close() - view = binaryninja.filemetadata.FileMetadata().open_existing_database(filename, progress_func) + view = filemetadata.FileMetadata().open_existing_database(filename, progress_func) else: view = BinaryView.open(filename) @@ -890,7 +806,7 @@ class BinaryViewType(with_metaclass(_BinaryViewTypeMetaclass, object)): if isDatabase: bv = view.get_view_of_type("Raw") else: - bv = cls["Raw"].open(filename) + bv = cls["Raw"].open(filename) # type: ignore if bv is not None and update_analysis: bv.update_analysis_and_wait() @@ -940,7 +856,7 @@ class BinaryViewType(with_metaclass(_BinaryViewTypeMetaclass, object)): if f is None or f.read(len(sqlite)) != sqlite: return None f.close() - view = binaryninja.filemetadata.FileMetadata().open_database_for_configuration(filename) + view = filemetadata.FileMetadata().open_database_for_configuration(filename) else: view = BinaryView.open(filename) @@ -956,7 +872,7 @@ class BinaryViewType(with_metaclass(_BinaryViewTypeMetaclass, object)): bvt = available if bvt is None: - bvt = cls["Mapped"] + bvt = cls["Mapped"] # type: ignore default_settings = settings.Settings(bvt.name + "_settings") default_settings.deserialize_schema(settings.Settings().serialize_schema()) @@ -969,18 +885,19 @@ class BinaryViewType(with_metaclass(_BinaryViewTypeMetaclass, object)): # FIXME: if universal_bvt is not None and "files.universal.architecturePreference" in options: load_settings = universal_bvt.get_load_settings_for_data(view) + if load_settings is None: + raise Exception(f"Could not load {options['files.universal.architecturePreference'][0]} from Universal image. Entry not found!") arch_list = json.loads(load_settings.get_string('loader.universal.architectures')) arch_entry = [entry for entry in arch_list if entry['architecture'] == options['files.universal.architecturePreference'][0]] if not arch_entry: - log.log_error(f"Could not load {options['files.universal.architecturePreference'][0]} from Universal image. Entry not found!") - return None + raise Exception(f"Could not load {options['files.universal.architecturePreference'][0]} from Universal image. Entry not found!") + load_settings = settings.Settings(core.BNGetUniqueIdentifierString()) load_settings.deserialize_schema(arch_entry[0]['loadSchema']) else: load_settings = bvt.get_load_settings_for_data(view) if load_settings is None: - log.log_error(f"Could not get load settings for binary view of type `{bvt.name}`") - return view + raise Exception(f"Could not get load settings for binary view of type `{bvt.name}`") load_settings.set_resource_id(bvt.name) view.set_load_settings(bvt.name, load_settings) @@ -996,6 +913,8 @@ class BinaryViewType(with_metaclass(_BinaryViewTypeMetaclass, object)): if isDatabase: view = view.file.open_existing_database(filename, progress_func) + if view is None: + raise Exception(f"Unable to open_existing_database with filename {filename}") bv = view.get_view_of_type(bvt.name) else: bv = bvt.create(view) @@ -1006,22 +925,22 @@ class BinaryViewType(with_metaclass(_BinaryViewTypeMetaclass, object)): bv.update_analysis_and_wait() return bv - def parse(self, data): + def parse(self, data:'BinaryView') -> Optional['BinaryView']: view = core.BNParseBinaryViewOfType(self.handle, data.handle) if view is None: return None return BinaryView(file_metadata=data.file, handle=view) - def is_valid_for_data(self, data): + def is_valid_for_data(self, data:'BinaryView') -> bool: return core.BNIsBinaryViewTypeValidForData(self.handle, data.handle) - def get_default_load_settings_for_data(self, data): + def get_default_load_settings_for_data(self, data:'BinaryView') -> Optional['settings.Settings']: load_settings = core.BNGetBinaryViewDefaultLoadSettingsForData(self.handle, data.handle) if load_settings is None: return None return settings.Settings(handle=load_settings) - def get_load_settings_for_data(self, data): + def get_load_settings_for_data(self, data:'BinaryView') -> Optional['settings.Settings']: view_handle = None if data is not None: view_handle = data.handle @@ -1030,19 +949,19 @@ class BinaryViewType(with_metaclass(_BinaryViewTypeMetaclass, object)): return None return settings.Settings(handle=load_settings) - def register_arch(self, ident, endian, arch): + def register_arch(self, ident:int, endian:Endianness, arch:'architecture.Architecture') -> None: core.BNRegisterArchitectureForViewType(self.handle, ident, endian, arch.handle) - def get_arch(self, ident, endian): + def get_arch(self, ident:int, endian:Endianness) -> Optional['architecture.Architecture']: arch = core.BNGetArchitectureForViewType(self.handle, ident, endian) if arch is None: return None - return binaryninja.architecture.CoreArchitecture._from_cache(arch) + return architecture.CoreArchitecture._from_cache(arch) - def register_platform(self, ident, arch, plat): + def register_platform(self, ident:int, arch:'architecture.Architecture', plat:'_platform.Platform') -> None: core.BNRegisterPlatformForViewType(self.handle, ident, arch.handle, plat.handle) - def register_default_platform(self, arch, plat): + def register_default_platform(self, arch:'architecture.Architecture', plat:'_platform.Platform') -> None: core.BNRegisterDefaultPlatformForViewType(self.handle, arch.handle, plat.handle) def register_platform_recognizer(self, ident, endian, cb): @@ -1060,14 +979,14 @@ class BinaryViewType(with_metaclass(_BinaryViewTypeMetaclass, object)): callback_obj = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p, ctypes.POINTER(core.BNBinaryView), ctypes.POINTER(core.BNMetadata))(lambda ctxt, view, meta: callback(cb, view, meta)) core.BNRegisterPlatformRecognizerForViewType(self.handle, ident, endian, callback_obj, None) - global _platform_recognizers - _platform_recognizers[len(_platform_recognizers)] = callback_obj + self.__class__._platform_recognizers[len(self.__class__._platform_recognizers)] = callback_obj + - def get_platform(self, ident, arch): + def get_platform(self, ident:int, arch:'architecture.Architecture') -> Optional['_platform.Platform']: plat = core.BNGetPlatformForViewType(self.handle, ident, arch.handle) if plat is None: return None - return binaryninja.platform.Platform(handle = plat) + return _platform.Platform(handle = plat) def recognize_platform(self, ident, endian, view, metadata): plat = core.BNRecognizePlatformForViewType(self.handle, ident, endian, view.handle, metadata.handle) @@ -1076,7 +995,7 @@ class BinaryViewType(with_metaclass(_BinaryViewTypeMetaclass, object)): return binaryninja.platform.Platform(handle = plat) @staticmethod - def add_binaryview_finalized_event(callback): + def add_binaryview_finalized_event(callback:BinaryViewEvent.BinaryViewEventCallback) -> None: """ `add_binaryview_finalized_event` adds a callback that gets executed when new binaryview is finalized. @@ -1096,17 +1015,17 @@ class BinaryViewType(with_metaclass(_BinaryViewTypeMetaclass, object)): class Segment(object): - def __init__(self, handle): + def __init__(self, handle:core.BNSegment): self.handle = handle def __del__(self): core.BNFreeSegment(self.handle) def __repr__(self): - return "<segment: %#x-%#x, %s%s%s>" % (self.start, self.end, - "r" if self.readable else "-", - "w" if self.writable else "-", - "x" if self.executable else "-") + r ="r" if self.readable else "-" + w ="w" if self.writable else "-" + x ="x" if self.executable else "-" + return f"<segment: {self.start:#x}-{self.end:#x}, {r}{w}{x}>" def __len__(self): return core.BNSegmentGetLength(self.handle) @@ -1125,79 +1044,82 @@ class Segment(object): return hash(ctypes.addressof(self.handle.contents)) @property - def start(self): + def start(self) -> int: return core.BNSegmentGetStart(self.handle) @property - def end(self): + def end(self) -> int: return core.BNSegmentGetEnd(self.handle) @property - def executable(self): + def executable(self) -> bool: return (core.BNSegmentGetFlags(self.handle) & SegmentFlag.SegmentExecutable) != 0 @property - def writable(self): + def writable(self) -> bool: return (core.BNSegmentGetFlags(self.handle) & SegmentFlag.SegmentWritable) != 0 @property - def readable(self): + def readable(self) -> bool: return (core.BNSegmentGetFlags(self.handle) & SegmentFlag.SegmentReadable) != 0 @property - def data_length(self): + def data_length(self) -> int: return core.BNSegmentGetDataLength(self.handle) @property - def data_offset(self): + def data_offset(self) -> int: return core.BNSegmentGetDataOffset(self.handle) @property - def data_end(self): + def data_end(self) -> int: return core.BNSegmentGetDataEnd(self.handle) @property - def relocation_count(self): + def relocation_count(self) -> int: return core.BNSegmentGetRelocationsCount(self.handle) @property - def auto_defined(self): + def auto_defined(self) -> bool: return core.BNSegmentIsAutoDefined(self.handle) @property - def relocation_ranges(self): + def relocation_ranges(self) -> Generator[Tuple[int, int], None, None]: """List of relocation range tuples (read-only)""" count = ctypes.c_ulonglong() ranges = core.BNSegmentGetRelocationRanges(self.handle, count) - result = [] - for i in range(0, count.value): - result.append((ranges[i].start, ranges[i].end)) - core.BNFreeRelocationRanges(ranges, count) - return result + assert ranges is not None, "core.BNSegmentGetRelocationRanges returned None" - def relocation_ranges_at(self, addr): + try: + for i in range(0, count.value): + yield (ranges[i].start, ranges[i].end) + finally: + core.BNFreeRelocationRanges(ranges, count) + + def relocation_ranges_at(self, addr:int) -> Generator[Tuple[int, int], None, None]: """List of relocation range tuples (read-only)""" count = ctypes.c_ulonglong() ranges = core.BNSegmentGetRelocationRangesAtAddress(self.handle, addr, count) - result = [] - for i in range(0, count.value): - result.append((ranges[i].start, ranges[i].end)) - core.BNFreeRelocationRanges(ranges, count) - return result + assert ranges is not None, "core.BNSegmentGetRelocationRangesAtAddress returned None" + try: + for i in range(0, count.value): + yield (ranges[i].start, ranges[i].end) + finally: + core.BNFreeRelocationRanges(ranges, count) class Section(object): - def __init__(self, handle): + def __init__(self, handle:core.BNSection): self.handle = core.handle_of_type(handle, core.BNSection) def __del__(self): core.BNFreeSection(self.handle) def __repr__(self): - return "<section %s: %#x-%#x>" % (self.name, self.start, self.end) + return "<section {self.name}: {self.start:#x}-{self.end:#x}>" def __len__(self): return core.BNSectionGetLength(self.handle) @@ -1216,59 +1138,59 @@ class Section(object): return hash(ctypes.addressof(self.handle.contents)) @property - def name(self): + def name(self) -> str: return core.BNSectionGetName(self.handle) @property - def type(self): + def type(self) -> str: return core.BNSectionGetType(self.handle) @property - def start(self): + def start(self) -> int: return core.BNSectionGetStart(self.handle) @property - def linked_section(self): + def linked_section(self) -> str: return core.BNSectionGetLinkedSection(self.handle) @property - def info_section(self): + def info_section(self) -> str: return core.BNSectionGetInfoSection(self.handle) @property - def info_data(self): + def info_data(self) -> int: return core.BNSectionGetInfoData(self.handle) @property - def align(self): + def align(self) -> int: return core.BNSectionGetAlign(self.handle) @property - def entry_size(self): + def entry_size(self) -> int: return core.BNSectionGetEntrySize(self.handle) @property - def semantics(self): + def semantics(self) -> SectionSemantics: return SectionSemantics(core.BNSectionGetSemantics(self.handle)) @property - def auto_defined(self): + def auto_defined(self) -> bool: return core.BNSectionIsAutoDefined(self.handle) @property - def end(self): + def end(self) -> int: return self.start + len(self) class TagType(object): - def __init__(self, handle): + def __init__(self, handle:core.BNTagType): self.handle = core.handle_of_type(handle, core.BNTagType) def __del__(self): core.BNFreeTagType(self.handle) def __repr__(self): - return "<tag type %s: %s>" % (self.name, self.icon) + return f"<tag type {self.name}: {self.icon}>" def __eq__(self, other): if not isinstance(other, self.__class__): @@ -1284,57 +1206,56 @@ class TagType(object): return hash(ctypes.addressof(self.handle.contents)) @property - def id(self): + def id(self) -> str: """Unique id of the TagType""" return core.BNTagTypeGetId(self.handle) @property - def name(self): + def name(self) -> str: """Name of the TagType""" return core.BNTagTypeGetName(self.handle) @name.setter - def name(self, value): + def name(self, value:str) -> None: core.BNTagTypeSetName(self.handle, value) @property - def icon(self): + def icon(self) -> str: """Unicode str containing an emoji to be used as an icon""" return core.BNTagTypeGetIcon(self.handle) @icon.setter - def icon(self, value): + def icon(self, value:str) -> None: core.BNTagTypeSetIcon(self.handle, value) @property - def visible(self): + def visible(self) -> bool: """Boolean for whether the tags of this type are visible""" return core.BNTagTypeGetVisible(self.handle) @visible.setter - def visible(self, value): + def visible(self, value:bool) -> None: core.BNTagTypeSetVisible(self.handle, value) @property - def type(self): + def type(self) -> TagTypeType: """Type from enums.TagTypeType""" return core.BNTagTypeGetType(self.handle) @type.setter - def type(self, value): + def type(self, value:TagTypeType) -> None: core.BNTagTypeSetType(self.handle, value) - class Tag(object): - def __init__(self, handle): + def __init__(self, handle:core.BNTag): self.handle = core.handle_of_type(handle, core.BNTag) def __del__(self): core.BNFreeTag(self.handle) def __repr__(self): - return "<tag %s %s: %s>" % (self.type.icon, self.type.name, self.data) + return "<tag {self.type.icon} {self.type.name}: {self.data}>" def __eq__(self, other): if not isinstance(other, self.__class__): @@ -1350,19 +1271,21 @@ class Tag(object): return hash(ctypes.addressof(self.handle.contents)) @property - def id(self): + def id(self) -> str: return core.BNTagGetId(self.handle) @property - def type(self): - return TagType(core.BNTagGetType(self.handle)) + def type(self) -> TagType: + core_tag_type = core.BNTagGetType(self.handle) + assert core_tag_type is not None, "core.BNTagGetType returned None" + return TagType(core_tag_type) @property - def data(self): + def data(self) -> str: return core.BNTagGetData(self.handle) @data.setter - def data(self, value): + def data(self, value:str) -> None: core.BNTagSetData(self.handle, value) @@ -1417,28 +1340,28 @@ class BinaryView(object): database (e.g. :func:`remove_user_function` rather than :func:`remove_function`). Thus use ``_user_`` methods if saving \ to the database is desired. """ - name = None - long_name = None + name: Optional[str] = None + long_name: Optional[str] = None _registered = False _registered_cb = None registered_view_type = None - _next_address = 0 _associated_data = {} _registered_instances = [] - - def __init__(self, file_metadata=None, parent_view=None, handle=None): + def __init__(self, file_metadata:'filemetadata.FileMetadata'=None, parent_view:'BinaryView'=None, handle:'ctypes.pointer[core.BNBinaryView]'=None): if handle is not None: self.handle = core.handle_of_type(handle, core.BNBinaryView) + assert self.handle is not None, "core.handle_of_type returned None" if file_metadata is None: - self._file = binaryninja.filemetadata.FileMetadata(handle=core.BNGetFileForView(handle)) + self._file = filemetadata.FileMetadata(handle=core.BNGetFileForView(handle)) else: self._file = file_metadata elif self.__class__ is BinaryView: binaryninja._init_plugins() if file_metadata is None: - file_metadata = binaryninja.filemetadata.FileMetadata() + file_metadata = filemetadata.FileMetadata() self.handle = core.BNCreateBinaryDataView(file_metadata.handle) - self._file = binaryninja.filemetadata.FileMetadata(handle=core.BNNewFileReference(file_metadata.handle)) + assert self.handle is not None, "core.BNCreateBinaryDataView returned None" + self._file = filemetadata.FileMetadata(handle=core.BNNewFileReference(file_metadata.handle)) else: binaryninja._init_plugins() if not self.__class__._registered: @@ -1466,12 +1389,17 @@ class BinaryView(object): self._cb.isRelocatable = self._cb.isRelocatable.__class__(self._is_relocatable) self._cb.getAddressSize = self._cb.getAddressSize.__class__(self._get_address_size) self._cb.save = self._cb.save.__class__(self._save) + if file_metadata is None: + raise Exception("Attempting to create a BinaryView with FileMetadata which is None") self._file = file_metadata + _parent_view = None if parent_view is not None: - parent_view = parent_view.handle - self.handle = core.BNCreateCustomBinaryView(self.__class__.name, file_metadata.handle, parent_view, self._cb) + _parent_view = parent_view.handle + self.handle = core.BNCreateCustomBinaryView(self.__class__.name, file_metadata.handle, _parent_view, self._cb) + assert self.handle is not None, "core.BNCreateCustomBinaryView returned None" + assert self.handle is not None self._notifications = {} - self._next_address = None # Do NOT try to access view before init() is called, use placeholder + self._parse_only = False def __enter__(self): return self @@ -1480,7 +1408,7 @@ class BinaryView(object): self.file.close() def __del__(self): - for i in self.notifications.values(): + for i in self._notifications.values(): i._unregister() core.BNFreeBinaryView(self.handle) @@ -1488,13 +1416,13 @@ class BinaryView(object): start = self.start length = len(self) if start != 0: - size = "start %#x, len %#x" % (start, length) + size = f"start {start:#x}, len {length:#x}" else: - size = "len %#x" % length + size = f"len {length:#x}" filename = self._file.filename if len(filename) > 0: - return "<BinaryView: '%s', %s>" % (filename, size) - return "<BinaryView: %s>" % (size) + return f"<BinaryView: '{filename}', {size}>" + return f"<BinaryView: {size}>" def __len__(self): return int(core.BNGetViewLength(self.handle)) @@ -1502,6 +1430,8 @@ class BinaryView(object): def __eq__(self, other): if not isinstance(other, self.__class__): return NotImplemented + assert self.handle is not None + assert other.handle is not None return ctypes.addressof(self.handle.contents) == ctypes.addressof(other.handle.contents) def __ne__(self, other): @@ -1510,14 +1440,16 @@ class BinaryView(object): return not (self == other) def __hash__(self): + assert self.handle is not None return hash(ctypes.addressof(self.handle.contents)) - def __iter__(self): + def __iter__(self) -> Generator['_function.Function', None, None]: count = ctypes.c_ulonglong(0) funcs = core.BNGetAnalysisFunctionList(self.handle, count) + assert funcs is not None, "core.BNGetAnalysisFunctionList returned None" try: for i in range(0, count.value): - yield binaryninja.function.Function(self, core.BNNewFunctionReference(funcs[i])) + yield _function.Function(self, core.BNNewFunctionReference(funcs[i])) finally: core.BNFreeFunctionList(funcs, count.value) @@ -1594,33 +1526,45 @@ class BinaryView(object): cls._registered_cb.parse = cls._registered_cb.parse.__class__(cls._parse) cls._registered_cb.isValidForData = cls._registered_cb.isValidForData.__class__(cls._is_valid_for_data) cls._registered_cb.getLoadSettingsForData = cls._registered_cb.getLoadSettingsForData.__class__(cls._get_load_settings_for_data) - cls.registered_view_type = BinaryViewType(core.BNRegisterBinaryViewType(cls.name, cls.long_name, cls._registered_cb)) + view_handle = core.BNRegisterBinaryViewType(cls.name, cls.long_name, cls._registered_cb) + assert view_handle is not None, "core.BNRegisterBinaryViewType returned None" + cls.registered_view_type = BinaryViewType(view_handle) cls._registered = True + @property + def parse_only(self) -> bool: + return self._parse_only + + @parse_only.setter + def parse_only(self, value:bool) -> None: + self._parse_only = value + @classmethod - def _create(cls, ctxt, data): + def _create(cls, ctxt, data:core.BNBinaryView): try: - file_metadata = binaryninja.filemetadata.FileMetadata(handle=core.BNGetFileForView(data)) - view = cls(BinaryView(file_metadata=file_metadata, handle=core.BNNewViewReference(data))) + file_metadata = filemetadata.FileMetadata(handle=core.BNGetFileForView(data)) + view = cls(BinaryView(file_metadata=file_metadata, handle=core.BNNewViewReference(data))) # type: ignore if view is None: return None - # FIXME: There is probably a better way to convey this information... - view.__dict__.update({'parse_only' : False}) - return ctypes.cast(core.BNNewViewReference(view.handle), ctypes.c_void_p).value + view.parse_only = False + view_handle = core.BNNewViewReference(view.handle) + assert view_handle is not None, "core.BNNewViewReference returned None" + return ctypes.cast(view_handle, ctypes.c_void_p).value except: log.log_error(traceback.format_exc()) return None @classmethod - def _parse(cls, ctxt, data): + def _parse(cls, ctxt, data:core.BNBinaryView): try: - file_metadata = binaryninja.filemetadata.FileMetadata(handle=core.BNGetFileForView(data)) - view = cls(BinaryView(file_metadata=file_metadata, handle=core.BNNewViewReference(data))) + file_metadata = filemetadata.FileMetadata(handle=core.BNGetFileForView(data)) + view = cls(BinaryView(file_metadata=file_metadata, handle=core.BNNewViewReference(data))) # type: ignore if view is None: return None - # FIXME: There is probably a better way to convey this information... - view.__dict__.update({'parse_only' : True}) - return ctypes.cast(core.BNNewViewReference(view.handle), ctypes.c_void_p).value + view.parse_only = True + view_handle = core.BNNewViewReference(view.handle) + assert view_handle is not None, "core.BNNewViewReference returned None" + return ctypes.cast(view_handle, ctypes.c_void_p).value except: log.log_error(traceback.format_exc()) return None @@ -1628,7 +1572,8 @@ class BinaryView(object): @classmethod def _is_valid_for_data(cls, ctxt, data): try: - return cls.is_valid_for_data(BinaryView(handle=core.BNNewViewReference(data))) + # I'm not sure whats going on here even so I've suppressed the linter warning + return cls.is_valid_for_data(BinaryView(handle=core.BNNewViewReference(data))) # type: ignore except: log.log_error(traceback.format_exc()) return False @@ -1638,35 +1583,36 @@ class BinaryView(object): try: attr = getattr(cls, "get_load_settings_for_data", None) if callable(attr): - result = cls.get_load_settings_for_data(BinaryView(handle=core.BNNewViewReference(data))) - return ctypes.cast(core.BNNewSettingsReference(result.handle), ctypes.c_void_p).value + result = cls.get_load_settings_for_data(BinaryView(handle=core.BNNewViewReference(data))) # type: ignore + settings_handle = core.BNNewSettingsReference(result.handle) + assert settings_handle is not None, "core.BNNewSettingsReference returned None" + return ctypes.cast(settings_handle, ctypes.c_void_p).value else: return None except: log.log_error(traceback.format_exc()) return None - @classmethod - def open(cls, src, file_metadata=None): + @staticmethod + def open(src, file_metadata=None) -> Optional['BinaryView']: binaryninja._init_plugins() if isinstance(src, fileaccessor.FileAccessor): if file_metadata is None: - file_metadata = binaryninja.filemetadata.FileMetadata() + file_metadata = filemetadata.FileMetadata() view = core.BNCreateBinaryDataViewFromFile(file_metadata.handle, src._cb) else: if file_metadata is None: - file_metadata = binaryninja.filemetadata.FileMetadata(str(src)) + file_metadata = filemetadata.FileMetadata(str(src)) view = core.BNCreateBinaryDataViewFromFilename(file_metadata.handle, str(src)) if view is None: return None - result = BinaryView(file_metadata=file_metadata, handle=view) - return result + return BinaryView(file_metadata=file_metadata, handle=view) - @classmethod - def new(cls, data=None, file_metadata=None): + @staticmethod + def new(data:Optional['BinaryView']=None, file_metadata:Optional['filemetadata.FileMetadata']=None) -> Optional['BinaryView']: binaryninja._init_plugins() if file_metadata is None: - file_metadata = binaryninja.filemetadata.FileMetadata() + file_metadata = filemetadata.FileMetadata() if data is None: view = core.BNCreateBinaryDataView(file_metadata.handle) else: @@ -1674,17 +1620,16 @@ class BinaryView(object): view = core.BNCreateBinaryDataViewFromBuffer(file_metadata.handle, buf.handle) if view is None: return None - result = BinaryView(file_metadata=file_metadata, handle=view) - return result + return BinaryView(file_metadata=file_metadata, handle=view) @classmethod - def _unregister(cls, view): + def _unregister(cls, view:core.BNBinaryView) -> None: handle = ctypes.cast(view, ctypes.c_void_p) if handle.value in cls._associated_data: del cls._associated_data[handle.value] - @classmethod - def set_default_session_data(cls, name, value): + @staticmethod + def set_default_session_data(name:str, value:str) -> None: """ ``set_default_session_data`` saves a variable to the BinaryView. @@ -1699,35 +1644,35 @@ class BinaryView(object): _BinaryViewAssociatedDataStore.set_default(name, value) @property - def basic_blocks(self): + def basic_blocks(self) -> Generator['basicblock.BasicBlock', None, None]: """A generator of all BasicBlock objects in the BinaryView""" for func in self: for block in func.basic_blocks: yield block @property - def llil_basic_blocks(self): + def llil_basic_blocks(self) -> 'lowlevelil.LLILBasicBlocksType': """A generator of all LowLevelILBasicBlock objects in the BinaryView""" for func in self: for il_block in func.low_level_il.basic_blocks: yield il_block @property - def mlil_basic_blocks(self): + def mlil_basic_blocks(self) -> 'mediumlevelil.MLILBasicBlocksType': """A generator of all MediumLevelILBasicBlock objects in the BinaryView""" for func in self: for il_block in func.mlil.basic_blocks: yield il_block @property - def hlil_basic_blocks(self): + def hlil_basic_blocks(self) -> 'highlevelil.HLILBasicBlocksType': """A generator of all HighLevelILBasicBlock objects in the BinaryView""" for func in self: for il_block in func.hlil.basic_blocks: yield il_block @property - def instructions(self): + def instructions(self) -> InstructionsType: """A generator of instruction tokens and their start addresses""" for block in self.basic_blocks: start = block.start @@ -1736,28 +1681,28 @@ class BinaryView(object): start += i[1] @property - def llil_instructions(self): + def llil_instructions(self) -> 'lowlevelil.LLILInstructionsType': """A generator of llil instructions""" for block in self.llil_basic_blocks: for i in block: yield i @property - def mlil_instructions(self): + def mlil_instructions(self) -> 'mediumlevelil.MLILInstructionsType': """A generator of mlil instructions""" for block in self.mlil_basic_blocks: for i in block: yield i @property - def hlil_instructions(self): + def hlil_instructions(self) -> 'highlevelil.HLILInstructionsType': """A generator of hlil instructions""" for block in self.hlil_basic_blocks: for i in block: yield i @property - def parent_view(self): + def parent_view(self) -> Optional['BinaryView']: """View that contains the raw data used by this view (read-only)""" result = core.BNGetParentView(self.handle) if result is None: @@ -1765,236 +1710,227 @@ class BinaryView(object): return BinaryView(handle=result) @property - def modified(self): + def modified(self) -> bool: """boolean modification state of the BinaryView (read/write)""" return self._file.modified @modified.setter - def modified(self, value): + def modified(self, value:bool) -> None: self._file.modified = value @property - def analysis_changed(self): + def analysis_changed(self) -> bool: """boolean analysis state changed of the currently running analysis (read-only)""" return self._file.analysis_changed @property - def has_database(self): + def has_database(self) -> bool: """boolean has a database been written to disk (read-only)""" return self._file.has_database @property - def view(self): + def view(self) -> str: return self._file.view @view.setter - def view(self, value): + def view(self, value:str): self._file.view = value @property - def offset(self): + def offset(self) -> int: return self._file.offset @offset.setter - def offset(self, value): + def offset(self, value:int) -> None: self._file.offset = value @property - def file(self): + def file(self) -> 'filemetadata.FileMetadata': """:py:class:`FileMetadata` backing the BinaryView """ return self._file - @file.setter - def file(self, value): - self._file = value - @property - def notifications(self): - return self._notifications - - @notifications.setter - def notifications(self, value): - self._notifications = value - - @property - def next_address(self): - """:py:class:`FileMetadata` backing the BinaryView """ - return self._next_address - - @next_address.setter - def next_address(self, value): - self._next_address = value - - @property - def start(self): + def start(self) -> int: """Start offset of the binary (read-only)""" return core.BNGetStartOffset(self.handle) @property - def end(self): + def end(self) -> int: """End offset of the binary (read-only)""" return core.BNGetEndOffset(self.handle) @property - def entry_point(self): + def entry_point(self) -> int: """Entry point of the binary (read-only)""" return core.BNGetEntryPoint(self.handle) @property - def arch(self): + def arch(self) -> Optional['architecture.Architecture']: """The architecture associated with the current :py:class:`BinaryView` (read/write)""" arch = core.BNGetDefaultArchitecture(self.handle) if arch is None: return None - return binaryninja.architecture.CoreArchitecture._from_cache(handle=arch) + return architecture.CoreArchitecture._from_cache(handle=arch) @arch.setter - def arch(self, value): + def arch(self, value:'architecture.Architecture') -> None: if value is None: core.BNSetDefaultArchitecture(self.handle, None) else: core.BNSetDefaultArchitecture(self.handle, value.handle) @property - def platform(self): + def platform(self) -> Optional['_platform.Platform']: """The platform associated with the current BinaryView (read/write)""" plat = core.BNGetDefaultPlatform(self.handle) if plat is None: return None - return binaryninja.platform.Platform(self.arch, handle=plat) + return _platform.Platform(self.arch, handle=plat) @platform.setter - def platform(self, value): + def platform(self, value:Optional['_platform.Platform']) -> None: if value is None: core.BNSetDefaultPlatform(self.handle, None) else: core.BNSetDefaultPlatform(self.handle, value.handle) @property - def endianness(self): + def endianness(self) -> Endianness: """Endianness of the binary (read-only)""" return Endianness(core.BNGetDefaultEndianness(self.handle)) @property - def relocatable(self): + def relocatable(self) -> bool: """Boolean - is the binary relocatable (read-only)""" return core.BNIsRelocatable(self.handle) @property - def address_size(self): + def address_size(self) -> int: """Address size of the binary (read-only)""" return core.BNGetViewAddressSize(self.handle) @property - def executable(self): + def executable(self) -> bool: """Whether the binary is an executable (read-only)""" return core.BNIsExecutableView(self.handle) @property - def functions(self): + def functions(self) -> Generator['_function.Function', None, None]: """List of functions (read-only)""" count = ctypes.c_ulonglong(0) funcs = core.BNGetAnalysisFunctionList(self.handle, count) - result = [] - for i in range(0, count.value): - result.append(binaryninja.function.Function(self, core.BNNewFunctionReference(funcs[i]))) - core.BNFreeFunctionList(funcs, count.value) - return result + assert funcs is not None, "core.BNGetAnalysisFunctionList returned None" + try: + for i in range(0, count.value): + yield _function.Function(self, core.BNNewFunctionReference(funcs[i])) + finally: + core.BNFreeFunctionList(funcs, count.value) @property - def has_functions(self): + def has_functions(self) -> bool: """Boolean whether the binary has functions (read-only)""" return core.BNHasFunctions(self.handle) @property - def has_symbols(self): + def has_symbols(self) -> bool: """Boolean whether the binary has symbols (read-only)""" return core.BNHasSymbols(self.handle) @property - def has_data_variables(self): + def has_data_variables(self) -> bool: """Boolean whether the binary has data variables (read-only)""" return core.BNHasDataVariables(self.handle) @property - def entry_function(self): + def entry_function(self) -> Optional['_function.Function']: """Entry function (read-only)""" func = core.BNGetAnalysisEntryPoint(self.handle) if func is None: return None - return binaryninja.function.Function(self, func) + return _function.Function(self, func) @property - def symbols(self): - """Dict of symbols (read-only)""" + def symbols(self) -> Mapping[str, List['_types.Symbol']]: + """ + Deprecated: Dict of symbols (read-only) + This API is deprecated and will be removed in future versions. + + .. warning:: This method **should not** be used in any applications where speed is important as it \ + copies all symbols from the binaryview each time it is invoked. + """ + count = ctypes.c_ulonglong(0) syms = core.BNGetSymbols(self.handle, count, None) + assert syms is not None, "core.BNGetSymbols returned None" result = defaultdict(list) for i in range(0, count.value): - sym = types.Symbol(None, None, None, handle=core.BNNewSymbolReference(syms[i])) + sym = _types.Symbol(None, None, None, handle=core.BNNewSymbolReference(syms[i])) result[sym.raw_name].append(sym) core.BNFreeSymbolList(syms, count.value) return result - @classmethod - def internal_namespace(self): + @staticmethod + def internal_namespace() -> '_types.NameSpace': """Internal namespace for the current BinaryView""" ns = core.BNGetInternalNameSpace() - result = types.NameSpace._from_core_struct(ns) + result = _types.NameSpace._from_core_struct(ns) core.BNFreeNameSpace(ns) return result - @classmethod - def external_namespace(self): + @staticmethod + def external_namespace() -> '_types.NameSpace': """External namespace for the current BinaryView""" ns = core.BNGetExternalNameSpace() - result = types.NameSpace._from_core_struct(ns) + result = _types.NameSpace._from_core_struct(ns) core.BNFreeNameSpace(ns) return result @property - def namespaces(self): + def namespaces(self) -> List['_types.NameSpace']: """Returns a list of namespaces for the current BinaryView""" count = ctypes.c_ulonglong(0) nameSpaceList = core.BNGetNameSpaces(self.handle, count) + assert nameSpaceList is not None, "core.BNGetNameSpaces returned None" result = [] for i in range(count.value): - result.append(types.NameSpace._from_core_struct(nameSpaceList[i])) + result.append(_types.NameSpace._from_core_struct(nameSpaceList[i])) core.BNFreeNameSpaceList(nameSpaceList, count.value) return result @property - def view_type(self): + def view_type(self) -> str: """View type (read-only)""" return core.BNGetViewType(self.handle) @property - def available_view_types(self): + def available_view_types(self) -> List[BinaryViewType]: """Available view types (read-only)""" count = ctypes.c_ulonglong(0) types = core.BNGetBinaryViewTypesForData(self.handle, count) result = [] + if types is None: + return result for i in range(0, count.value): result.append(BinaryViewType(types[i])) core.BNFreeBinaryViewTypeList(types) return result @property - def strings(self): + def strings(self) -> List[str]: """List of strings (read-only)""" return self.get_strings() @property - def saved(self): + def saved(self) -> bool: """boolean state of whether or not the file has been saved (read/write)""" return self._file.saved @saved.setter - def saved(self, value): + def saved(self, value:bool) -> None: self._file.saved = value @property - def analysis_info(self): + def analysis_info(self) -> AnalysisInfo: """Provides instantaneous analysis state information and a list of current functions under analysis (read-only). All times are given in units of milliseconds (ms). Per-function `analysis_time` is the aggregation of time spent performing incremental updates and is reset on a full function update. Per-function `update_count` tracks the @@ -2005,10 +1941,11 @@ class BinaryView(object): """ info_ref = core.BNGetAnalysisInfo(self.handle) + assert info_ref is not None, "core.BNGetAnalysisInfo returned None" info = info_ref[0] - active_info_list = [] + active_info_list:List[ActiveAnalysisInfo] = [] for i in range(0, info.count): - func = binaryninja.function.Function(self, core.BNNewFunctionReference(info.activeInfo[i].func)) + func = _function.Function(self, core.BNNewFunctionReference(info.activeInfo[i].func)) active_info = ActiveAnalysisInfo(func, info.activeInfo[i].analysisTime, info.activeInfo[i].updateCount, info.activeInfo[i].submitCount) active_info_list.append(active_info) result = AnalysisInfo(info.state, info.analysisTime, active_info_list) @@ -2016,7 +1953,7 @@ class BinaryView(object): return result @property - def analysis_progress(self): + def analysis_progress(self) -> AnalysisProgress: """Status of current analysis (read-only)""" result = core.BNGetAnalysisProgress(self.handle) return AnalysisProgress(result.state, result.count, result.total) @@ -2031,10 +1968,11 @@ class BinaryView(object): """List of data variables (read-only)""" count = ctypes.c_ulonglong(0) var_list = core.BNGetDataVariables(self.handle, count) + assert var_list is not None, "core.BNGetDataVariables returned None" result = {} for i in range(0, count.value): addr = var_list[i].address - var_type = types.Type(core.BNNewTypeReference(var_list[i].type), platform = self.platform, confidence = var_list[i].typeConfidence) + var_type = _types.Type(core.BNNewTypeReference(var_list[i].type), platform = self.platform, confidence = var_list[i].typeConfidence) auto_discovered = var_list[i].autoDiscovered result[addr] = DataVariable(addr, var_type, auto_discovered, self) core.BNFreeDataVariables(var_list, count.value) @@ -2045,10 +1983,11 @@ class BinaryView(object): """List of defined types (read-only)""" count = ctypes.c_ulonglong(0) type_list = core.BNGetAnalysisTypeList(self.handle, count) + assert type_list is not None, "core.BNGetAnalysisTypeList returned None" result = {} for i in range(0, count.value): - name = types.QualifiedName._from_core_struct(type_list[i].name) - result[name] = types.Type(core.BNNewTypeReference(type_list[i].type), platform = self.platform) + name = _types.QualifiedName._from_core_struct(type_list[i].name) + result[name] = _types.Type(core.BNNewTypeReference(type_list[i].type), platform = self.platform) core.BNFreeTypeList(type_list, count.value) return result @@ -2057,9 +1996,10 @@ class BinaryView(object): """List of defined type names (read-only)""" count = ctypes.c_ulonglong(0) name_list = core.BNGetAnalysisTypeNames(self.handle, count, "") + assert name_list is not None, "core.BNGetAnalysisTypeNames returned None" result = [] for i in range(0, count.value): - result.append(types.QualifiedName._from_core_struct(name_list[i])) + result.append(_types.QualifiedName._from_core_struct(name_list[i])) core.BNFreeTypeNameList(name_list, count.value) return result @@ -2069,6 +2009,7 @@ class BinaryView(object): """List of imported type libraries (read-only)""" count = ctypes.c_ulonglong(0) libraries = core.BNGetBinaryViewTypeLibraries(self.handle, count) + assert libraries is not None, "core.BNGetBinaryViewTypeLibraries returned None" result = [] for i in range(0, count.value): result.append(typelibrary.TypeLibrary(core.BNNewTypeLibraryReference(libraries[i]))) @@ -2081,9 +2022,12 @@ class BinaryView(object): """List of segments (read-only)""" count = ctypes.c_ulonglong(0) segment_list = core.BNGetSegments(self.handle, count) + assert segment_list is not None, "core.BNGetSegments returned None" result = [] for i in range(0, count.value): - result.append(Segment(core.BNNewSegmentReference(segment_list[i]))) + segment_handle = core.BNNewSegmentReference(segment_list[i]) + assert segment_handle is not None, "core.BNNewSegmentReference returned None" + result.append(Segment(segment_handle)) core.BNFreeSegmentList(segment_list, count.value) return result @@ -2092,9 +2036,12 @@ class BinaryView(object): """Dictionary of sections (read-only)""" count = ctypes.c_ulonglong(0) section_list = core.BNGetSections(self.handle, count) + assert section_list is not None, "core.BNGetSections returned None" result = {} for i in range(0, count.value): - result[core.BNSectionGetName(section_list[i])] = Section(core.BNNewSectionReference(section_list[i])) + section_handle = core.BNNewSectionReference(section_list[i]) + assert section_handle is not None, "core.BNNewSectionReference returned None" + result[core.BNSectionGetName(section_list[i])] = Section(section_handle) core.BNFreeSectionList(section_list, count.value) return result @@ -2103,15 +2050,18 @@ class BinaryView(object): """List of valid address ranges for this view (read-only)""" count = ctypes.c_ulonglong(0) range_list = core.BNGetAllocatedRanges(self.handle, count) + assert range_list is not None, "core.BNGetAllocatedRanges returned None" result = [] for i in range(0, count.value): - result.append(AddressRange(range_list[i].start, range_list[i].end)) + result.append(variable.AddressRange(range_list[i].start, range_list[i].end)) core.BNFreeAddressRanges(range_list) return result @property def session_data(self): """Dictionary object where plugins can store arbitrary data associated with the view""" + # TODO: + # assert isinstance(self.handle, int), "session_data called with no BinaryView.handle" handle = ctypes.cast(self.handle, ctypes.c_void_p) if handle.value not in BinaryView._associated_data: obj = _BinaryViewAssociatedDataStore() @@ -2124,7 +2074,7 @@ class BinaryView(object): def global_pointer_value(self): """Discovered value of the global pointer register, if the binary uses one (read-only)""" result = core.BNGetGlobalPointerValue(self.handle) - return binaryninja.function.RegisterValue(self.arch, result.value, confidence = result.confidence) + return variable.RegisterValue(self.arch, result.value, confidence = result.confidence) @property def parameters_for_analysis(self): @@ -2149,6 +2099,7 @@ class BinaryView(object): count = ctypes.c_ulonglong() ranges = core.BNGetRelocationRanges(self.handle, count) + assert ranges is not None, "core.BNGetRelocationRanges returned None" result = [] for i in range(0, count.value): result.append((ranges[i].start, ranges[i].end)) @@ -2160,6 +2111,7 @@ class BinaryView(object): count = ctypes.c_ulonglong() ranges = core.BNGetRelocationRangesAtAddress(self.handle, addr, count) + assert ranges is not None, "core.BNGetRelocationRangesAtAddress returned None" result = [] for i in range(0, count.value): result.append((ranges[i].start, ranges[i].end)) @@ -2199,7 +2151,6 @@ class BinaryView(object): data = self.perform_read(offset, length) if data is None: return 0 - data = cstr(data) if len(data) > length: data = data[0:length] ctypes.memmove(dest, data, len(data)) @@ -2334,57 +2285,46 @@ class BinaryView(object): def init(self): return True - def get_disassembly(self, addr, arch=None): - """ - ``get_disassembly`` simple helper function for printing disassembly of a given address - - :param int addr: virtual address of instruction - :param Architecture arch: optional Architecture, ``self.arch`` is used if this parameter is None - :return: a str representation of the instruction at virtual address ``addr`` or None - :rtype: str or None - :Example: - - >>> bv.get_disassembly(bv.entry_point) - 'push ebp' - >>> - """ + def disassembly_tokens(self, addr:int, arch:Optional['architecture.Architecture']=None) -> Generator[Tuple[List['_function.InstructionTextToken'], int], None, None]: if arch is None: + if self.arch is None: + raise Exception("Can not call method disassembly with no Architecture specified") arch = self.arch - txt, size = arch.get_instruction_text(self.read(addr, arch.max_instr_length), addr) - self.next_address = addr + size - if txt is None: - return None - return ''.join(str(a) for a in txt).strip() - def get_next_disassembly(self, arch=None): + size = 1 + while size != 0: + tokens, size = arch.get_instruction_text(self.read(addr, arch.max_instr_length), addr) + addr += size + if size == 0 or tokens is None: + break + yield (tokens, size) + + def disassembly_text(self, addr:int, arch:Optional['architecture.Architecture']=None) -> Generator[Tuple[str, int], None, None]: """ - ``get_next_disassembly`` simple helper function for printing disassembly of the next instruction. - The internal state of the instruction to be printed is stored in the :attr:`next_address` attribute + ``disassembly_text`` helper function for getting disassembly of a given address + :param int addr: virtual address of instruction :param Architecture arch: optional Architecture, ``self.arch`` is used if this parameter is None - :return: a str representation of the instruction at virtual address :attr:`next_address` + :return: a str representation of the instruction at virtual address ``addr`` or None :rtype: str or None :Example: - >>> bv.get_next_disassembly() - 'push ebp' - >>> bv.get_next_disassembly() - 'mov ebp, esp' - >>> #Now reset the starting point back to the entry point - >>> bv.next_address = bv.entry_point - >>> bv.get_next_disassembly() - 'push ebp' + >>> next(bv.disassembly_text(bv.entry_point)) + 'push ebp', 1 >>> """ if arch is None: + if self.arch is None: + raise Exception("Can not call method disassembly with no Architecture specified") arch = self.arch - if self.next_address is None: - self.next_address = self.entry_point - txt, size = arch.get_instruction_text(self.read(self.next_address, arch.max_instr_length), self.next_address) - self.next_address += size - if txt is None: - return None - return ''.join(str(a) for a in txt).strip() + + size = 1 + while size != 0: + tokens, size = arch.get_instruction_text(self.read(addr, arch.max_instr_length), addr) + addr += size + if size == 0 or tokens is None: + break + yield (''.join(str(a) for a in tokens).strip(), size) def perform_save(self, accessor): if self.parent_view is not None: @@ -2410,7 +2350,7 @@ class BinaryView(object): """ return 0 - def perform_read(self, addr, length): + def perform_read(self, addr:int, length:int) -> bytes: """ ``perform_read`` implements a mapping between a virtual address and an absolute file offset, reading ``length`` bytes from the rebased address ``addr``. @@ -2423,9 +2363,9 @@ class BinaryView(object): :param int addr: a virtual address to attempt to read from :param int length: the number of bytes to be read :return: length bytes read from addr, should return empty string on error - :rtype: str + :rtype: bytes """ - return "" + return b"" def perform_write(self, addr, data): """ @@ -2783,12 +2723,12 @@ class BinaryView(object): :Example: >>> import random - >>> bv.navigate(bv.view, random.choice(bv.functions).start) + >>> bv.navigate(bv.view, random.choice(list(bv.functions)).start) True """ return self._file.navigate(view, offset) - def read(self, addr, length): + def read(self, addr:int, length:int) -> bytes: """ ``read`` returns the data reads at most ``length`` bytes from virtual address ``addr``. @@ -2798,7 +2738,7 @@ class BinaryView(object): :param int addr: virtual address to read from. :param int length: number of bytes to read. :return: at most ``length`` bytes from the virtual address ``addr``, empty string on error or no data. - :rtype: python2 - str; python3 - bytes + :rtype: bytes :Example: >>> #Opening a x86_64 Mach-O binary @@ -2811,12 +2751,12 @@ class BinaryView(object): buf = databuffer.DataBuffer(handle=core.BNReadViewBuffer(self.handle, addr, length)) return bytes(buf) - def write(self, addr, data): + def write(self, addr:int, data:bytes) -> int: """ ``write`` writes the bytes in ``data`` to the virtual address ``addr``. :param int addr: virtual address to write to. - :param Union[bytes, bytearray, str] data: data to be written at addr. + :param bytes data: data to be written at addr. :return: number of bytes written to virtual address ``addr`` :rtype: int :Example: @@ -2824,7 +2764,7 @@ class BinaryView(object): >>> bv.read(0,4) 'BBBB' >>> bv.write(0, "AAAA") - 4L + 4 >>> bv.read(0,4) 'AAAA' """ @@ -2834,18 +2774,18 @@ class BinaryView(object): buf = databuffer.DataBuffer(data) return core.BNWriteViewBuffer(self.handle, addr, buf.handle) - def insert(self, addr, data): + def insert(self, addr:int, data:bytes) -> int: """ ``insert`` inserts the bytes in ``data`` to the virtual address ``addr``. :param int addr: virtual address to write to. - :param Union[bytes, bytearray, str] data: data to be inserted at addr. + :param bytes data: data to be inserted at addr. :return: number of bytes inserted to virtual address ``addr`` :rtype: int :Example: >>> bv.insert(0,"BBBB") - 4L + 4 >>> bv.read(0,8) 'BBBBAAAA' """ @@ -2855,7 +2795,7 @@ class BinaryView(object): buf = databuffer.DataBuffer(data) return core.BNInsertViewBuffer(self.handle, addr, buf.handle) - def remove(self, addr, length): + def remove(self, addr:int, length:int) -> int: """ ``remove`` removes at most ``length`` bytes from virtual address ``addr``. @@ -2868,13 +2808,13 @@ class BinaryView(object): >>> bv.read(0,8) 'BBBBAAAA' >>> bv.remove(0,4) - 4L + 4 >>> bv.read(0,4) 'AAAA' """ return core.BNRemoveViewData(self.handle, addr, length) - def get_entropy(self, addr, length, block_size=0): + def get_entropy(self, addr:int, length:int, block_size:int=0) -> List[float]: """ ``get_entropy`` returns the shannon entropy given the start ``addr``, ``length`` in bytes, and optionally in ``block_size`` chunks. @@ -2997,7 +2937,7 @@ class BinaryView(object): return core.BNSaveToFile(self.handle, dest._cb) return core.BNSaveToFilename(self.handle, str(dest)) - def register_notification(self, notify): + def register_notification(self, notify:BinaryDataNotification): """ `register_notification` provides a mechanism for receiving callbacks for various analysis events. A full list of callbacks can be seen in :py:Class:`BinaryDataNotification`. @@ -3007,9 +2947,9 @@ class BinaryView(object): """ cb = BinaryDataNotificationCallbacks(self, notify) cb._register() - self.notifications[notify] = cb + self._notifications[notify] = cb - def unregister_notification(self, notify): + def unregister_notification(self, notify:BinaryDataNotification): """ `unregister_notification` unregisters the :py:Class:`BinaryDataNotification` object passed to `register_notification` @@ -3017,9 +2957,9 @@ class BinaryView(object): :param BinaryDataNotification notify: notify is a subclassed instance of :py:Class:`BinaryDataNotification`. :rtype: None """ - if notify in self.notifications: - self.notifications[notify]._unregister() - del self.notifications[notify] + if notify in self._notifications: + self._notifications[notify]._unregister() + del self._notifications[notify] def add_function(self, addr, plat=None): """ @@ -3039,8 +2979,8 @@ class BinaryView(object): raise Exception("Default platform not set in BinaryView") if plat is None: plat = self.platform - if not isinstance(plat, binaryninja.platform.Platform): - raise AttributeError("Provided platform is not of type `binaryninja.platform.Platform`") + if not isinstance(plat, _platform.Platform): + raise AttributeError("Provided platform is not of type `Platform`") core.BNAddFunctionForAnalysis(self.handle, plat.handle, addr) def add_entry_point(self, addr, plat=None): @@ -3058,8 +2998,8 @@ class BinaryView(object): raise Exception("Default platform not set in BinaryView") if plat is None: plat = self.platform - if not isinstance(plat, binaryninja.platform.Platform): - raise AttributeError("Provided platform is not of type `binaryninja.platform.Platform`") + if not isinstance(plat, _platform.Platform): + raise AttributeError("Provided platform is not of type `Platform`") core.BNAddEntryPointForAnalysis(self.handle, plat.handle, addr) def remove_function(self, func): @@ -3074,7 +3014,7 @@ class BinaryView(object): >>> bv.functions [<func: x86_64@0x1>] - >>> bv.remove_function(bv.functions[0]) + >>> bv.remove_function(next(bv.functions)) >>> bv.functions [] """ @@ -3096,9 +3036,9 @@ class BinaryView(object): """ if plat is None: if self.platform is None: - raise ValueError("Can't create user function with no platform specified.") - plat, addr = self.platform.get_associated_platform_by_address(addr) - return binaryninja.function.Function(self, core.BNCreateUserFunction(self.handle, plat.handle, addr)) + raise Exception("Attempting to call create_user_function with no specified platform") + plat = self.platform + return _function.Function(self, core.BNCreateUserFunction(self.handle, plat.handle, addr)) def remove_user_function(self, func): """ @@ -3112,7 +3052,7 @@ class BinaryView(object): >>> bv.functions [<func: x86_64@0x1>] - >>> bv.remove_user_function(bv.functions[0]) + >>> bv.remove_user_function(next(bv.functions)) >>> bv.functions [] """ @@ -3265,7 +3205,7 @@ class BinaryView(object): var = core.BNDataVariable() if not core.BNGetDataVariableAtAddress(self.handle, addr, var): return None - return DataVariable(var.address, types.Type(var.type, platform = self.platform, confidence = var.typeConfidence), var.autoDiscovered, self) + return DataVariable(var.address, _types.Type(var.type, platform = self.platform, confidence = var.typeConfidence), var.autoDiscovered, self) def get_functions_containing(self, addr, plat=None): """ @@ -3276,11 +3216,12 @@ class BinaryView(object): """ count = ctypes.c_ulonglong(0) funcs = core.BNGetAnalysisFunctionsContainingAddress(self.handle, addr, count) + assert funcs is not None, "core.BNGetAnalysisFunctionsContainingAddress returned None" result = [] for i in range(0, count.value): - result.append(binaryninja.function.Function(self, core.BNNewFunctionReference(funcs[i]))) + result.append(_function.Function(self, core.BNNewFunctionReference(funcs[i]))) core.BNFreeFunctionList(funcs, count.value) - if not plat is None: + if plat is not None: result = [func for func in result if func.platform == plat] return result @@ -3330,7 +3271,7 @@ class BinaryView(object): func = core.BNGetAnalysisFunction(self.handle, plat.handle, addr) if func is None: return None - return binaryninja.function.Function(self, func) + return _function.Function(self, func) def get_functions_at(self, addr): """ @@ -3349,9 +3290,10 @@ class BinaryView(object): """ count = ctypes.c_ulonglong(0) funcs = core.BNGetAnalysisFunctionsForAddress(self.handle, addr, count) + assert funcs is not None, "core.BNGetAnalysisFunctionsForAddress returned None" result = [] for i in range(0, count.value): - result.append(binaryninja.function.Function(self, core.BNNewFunctionReference(funcs[i]))) + result.append(_function.Function(self, core.BNNewFunctionReference(funcs[i]))) core.BNFreeFunctionList(funcs, count.value) return result @@ -3359,7 +3301,7 @@ class BinaryView(object): func = core.BNGetRecentAnalysisFunctionForAddress(self.handle, addr) if func is None: return None - return binaryninja.function.Function(self, func) + return _function.Function(self, func) def get_basic_blocks_at(self, addr): """ @@ -3371,9 +3313,12 @@ class BinaryView(object): """ count = ctypes.c_ulonglong(0) blocks = core.BNGetBasicBlocksForAddress(self.handle, addr, count) + assert blocks is not None, "core.BNGetBasicBlocksForAddress returned None" result = [] for i in range(0, count.value): - result.append(basicblock.BasicBlock(core.BNNewBasicBlockReference(blocks[i]), self)) + block_handle = core.BNNewBasicBlockReference(blocks[i]) + assert block_handle is not None, "core.BNNewBasicBlockReference is None" + result.append(basicblock.BasicBlock(block_handle, self)) core.BNFreeBasicBlockList(blocks, count.value) return result @@ -3387,9 +3332,12 @@ class BinaryView(object): """ count = ctypes.c_ulonglong(0) blocks = core.BNGetBasicBlocksStartingAtAddress(self.handle, addr, count) + assert blocks is not None, "core.BNGetBasicBlocksStartingAtAddress returned None" result = [] for i in range(0, count.value): - result.append(basicblock.BasicBlock(core.BNNewBasicBlockReference(blocks[i]), self)) + block_handle = core.BNNewBasicBlockReference(blocks[i]) + assert block_handle is not None, "core.BNNewBasicBlockReference returned None" + result.append(basicblock.BasicBlock(block_handle, self)) core.BNFreeBasicBlockList(blocks, count.value) return result @@ -3399,16 +3347,16 @@ class BinaryView(object): return None return basicblock.BasicBlock(block, self) - def get_code_refs(self, addr, length=None): + def get_code_refs(self, addr:int, length:int=None) -> Generator['ReferenceSource', None, None]: """ - ``get_code_refs`` returns a list of ReferenceSource objects (xrefs or cross-references) that point to the provided virtual address. + ``get_code_refs`` returns a Generator[ReferenceSource] objects (xrefs or cross-references) that point to the provided virtual address. This function returns both autoanalysis ("auto") and user-specified ("user") xrefs. - To add a user-specified reference, see :func:`~binaryninja.function.Function.add_user_code_ref`. + To add a user-specified reference, see :func:`~Function.add_user_code_ref`. :param int addr: virtual address to query for references :param int length: optional length of query - :return: List of References for the given virtual address - :rtype: list(ReferenceSource) + :return: Generator[References] for the given virtual address + :rtype: Generator[ReferenceSource, None, None] :Example: >>> bv.get_code_refs(here) @@ -3419,22 +3367,25 @@ class BinaryView(object): count = ctypes.c_ulonglong(0) if length is None: refs = core.BNGetCodeReferences(self.handle, addr, count) + assert refs is not None, "core.BNGetCodeReferences returned None" else: refs = core.BNGetCodeReferencesInRange(self.handle, addr, length, 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 + assert refs is not None, "core.BNGetCodeReferencesInRange returned None" + + try: + for i in range(0, count.value): + if refs[i].func: + func = _function.Function(self, core.BNNewFunctionReference(refs[i].func)) + else: + func = None + if refs[i].arch: + arch = architecture.CoreArchitecture._from_cache(refs[i].arch) + else: + arch = None + + yield ReferenceSource(func, arch, refs[i].addr) + finally: + core.BNFreeCodeReferences(refs, count.value) def get_code_refs_from(self, addr, func=None, arch=None, length=None): """ @@ -3443,7 +3394,7 @@ class BinaryView(object): all functions and containing the address will be returned. If no architecture is specified, the architecture of the function will be used. This function returns both autoanalysis ("auto") and user-specified ("user") xrefs. - To add a user-specified reference, see :func:`~binaryninja.function.Function.add_user_code_ref`. + To add a user-specified reference, see :func:`~Function.add_user_code_ref`. :param int addr: virtual address to query for references :param int length: optional length of query @@ -3462,14 +3413,16 @@ class BinaryView(object): count = ctypes.c_ulonglong(0) if length is None: refs = core.BNGetCodeReferencesFrom(self.handle, ref_src, count) + assert refs is not None, "core.BNGetCodeReferencesFrom returned None" else: refs = core.BNGetCodeReferencesFromInRange(self.handle, ref_src, length, count) + assert refs is not None, "core.BNGetCodeReferencesFromInRange returned None" for i in range(0, count.value): result.append(refs[i]) core.BNFreeAddressList(refs) return result - def get_data_refs(self, addr, length=None): + def get_data_refs(self, addr:int, length:int=None) -> Generator[int, None, None]: """ ``get_data_refs`` returns a list of virtual addresses of data which references ``addr``. Optionally specifying a length. When ``length`` is set ``get_data_refs`` returns the data which references in the range ``addr``-``addr``+``length``. @@ -3489,16 +3442,18 @@ class BinaryView(object): count = ctypes.c_ulonglong(0) if length is None: refs = core.BNGetDataReferences(self.handle, addr, count) + assert refs is not None, "core.BNGetDataReferences returned None" else: refs = core.BNGetDataReferencesInRange(self.handle, addr, length, count) + assert refs is not None, "core.BNGetDataReferencesInRange returned None" - result = [] - for i in range(0, count.value): - result.append(refs[i]) - core.BNFreeDataReferences(refs, count.value) - return result + try: + for i in range(0, count.value): + yield refs[i] + finally: + core.BNFreeDataReferences(refs, count.value) - def get_data_refs_from(self, addr, length=None): + def get_data_refs_from(self, addr:int, length:int=None) -> Generator[int, None, None]: """ ``get_data_refs_from`` returns a list of virtual addresses referenced by the address ``addr``. Optionally specifying a length. When ``length`` is set ``get_data_refs_from`` returns the data referenced in the range ``addr``-``addr``+``length``. @@ -3518,19 +3473,20 @@ class BinaryView(object): count = ctypes.c_ulonglong(0) if length is None: refs = core.BNGetDataReferencesFrom(self.handle, addr, count) + assert refs is not None, "core.BNGetDataReferencesFrom returned None" else: refs = core.BNGetDataReferencesFromInRange(self.handle, addr, length, count) + assert refs is not None, "core.BNGetDataReferencesFromInRange returned None" - result = [] - for i in range(0, count.value): - result.append(refs[i]) - core.BNFreeDataReferences(refs, count.value) - return result - + try: + for i in range(0, count.value): + yield refs[i] + finally: + core.BNFreeDataReferences(refs, count.value) - def get_code_refs_for_type(self, name): + def get_code_refs_for_type(self, name:str) -> Generator[ReferenceSource, None, None]: """ - ``get_code_refs_for_type`` returns a list of ReferenceSource objects (xrefs or cross-references) that reference the provided QualifiedName. + ``get_code_refs_for_type`` returns a Generator[ReferenceSource] objects (xrefs or cross-references) that reference the provided QualifiedName. :param QualifiedName name: name of type to query for references :return: List of References for the given type @@ -3543,33 +3499,34 @@ class BinaryView(object): """ count = ctypes.c_ulonglong(0) - name = types.QualifiedName(name)._get_core_struct() - refs = core.BNGetCodeReferencesForType(self.handle, name, count) + _name = _types.QualifiedName(name)._get_core_struct() + refs = core.BNGetCodeReferencesForType(self.handle, _name, count) + assert refs is not None, "core.BNGetCodeReferencesForType returned None" - 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 + try: + for i in range(0, count.value): + if refs[i].func: + func = _function.Function(self, core.BNNewFunctionReference(refs[i].func)) + else: + func = None + if refs[i].arch: + arch = architecture.CoreArchitecture._from_cache(refs[i].arch) + else: + arch = None + + yield ReferenceSource(func, arch, refs[i].addr) + finally: + core.BNFreeCodeReferences(refs, count.value) - def get_code_refs_for_type_field(self, name, offset): + def get_code_refs_for_type_field(self, name:str, offset:int) -> Generator['_types.TypeFieldReference', None, None]: """ - ``get_code_refs_for_type`` returns a list of TypeFieldReference objects (xrefs or cross-references) that reference the provided type field. + ``get_code_refs_for_type`` returns a Generator[TypeFieldReference] objects (xrefs or cross-references) that reference the provided type field. :param QualifiedName name: name of type to query for references :param int offset: offset of the field, relative to the type - :return: List of References for the given type - :rtype: list(TypeFieldReference) + :return: Generator of References for the given type + :rtype: Generator[TypeFieldReference] :Example: >>> bv.get_code_refs_for_type_field('A', 0x8) @@ -3578,31 +3535,31 @@ class BinaryView(object): """ count = ctypes.c_ulonglong(0) - name = types.QualifiedName(name)._get_core_struct() - refs = core.BNGetCodeReferencesForTypeField(self.handle, name, offset, 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 - size = refs[i].size - typeObj = None - if refs[i].incomingType.type: - typeObj = types.Type(core.BNNewTypeReference(refs[i].incomingType.type),\ - confidence = refs[i].incomingType.confidence) - result.append(binaryninja.architecture.TypeFieldReference(func, arch, addr, size, typeObj)) - core.BNFreeTypeFieldReferences(refs, count.value) - return result + _name = _types.QualifiedName(name)._get_core_struct() + refs = core.BNGetCodeReferencesForTypeField(self.handle, _name, offset, count) + assert refs is not None, "core.BNGetCodeReferencesForTypeField returned None" + try: + for i in range(0, count.value): + if refs[i].func: + func = _function.Function(self, 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 + size = refs[i].size + typeObj = None + if refs[i].incomingType.type: + typeObj = types.Type(core.BNNewTypeReference(refs[i].incomingType.type),\ + confidence = refs[i].incomingType.confidence) + yield TypeFieldReference(func, arch, addr, size, typeObj) + finally: + core.BNFreeTypeFieldReferences(refs, count.value) - def get_data_refs_for_type(self, name): + def get_data_refs_for_type(self, name:str) -> Generator[int, None, None]: """ ``get_data_refs_for_type`` returns a list of virtual addresses of data which references the type ``name``. Note, the returned addresses are the actual start of the queried type. For example, suppose there is a DataVariable @@ -3619,14 +3576,15 @@ class BinaryView(object): >>> """ count = ctypes.c_ulonglong(0) - name = types.QualifiedName(name)._get_core_struct() - refs = core.BNGetDataReferencesForType(self.handle, name, count) + _name = _types.QualifiedName(name)._get_core_struct() + refs = core.BNGetDataReferencesForType(self.handle, _name, count) + assert refs is not None, "core.BNGetDataReferencesForType returned None" - result = [] - for i in range(0, count.value): - result.append(refs[i]) - core.BNFreeDataReferences(refs, count.value) - return result + try: + for i in range(0, count.value): + yield refs[i] + finally: + core.BNFreeDataReferences(refs, count.value) def get_data_refs_for_type_field(self, name, offset): @@ -3647,8 +3605,9 @@ class BinaryView(object): >>> """ count = ctypes.c_ulonglong(0) - name = types.QualifiedName(name)._get_core_struct() + name = _types.QualifiedName(name)._get_core_struct() refs = core.BNGetDataReferencesForTypeField(self.handle, name, offset, count) + assert refs is not None, "core.BNGetDataReferencesForTypeField returned None" result = [] for i in range(0, count.value): @@ -3672,12 +3631,13 @@ class BinaryView(object): """ count = ctypes.c_ulonglong(0) - name = types.QualifiedName(name)._get_core_struct() + name = _types.QualifiedName(name)._get_core_struct() refs = core.BNGetTypeReferencesForType(self.handle, name, count) + assert refs is not None, "core.BNGetTypeReferencesForType returned None" result = [] for i in range(0, count.value): - type_field = types.TypeReferenceSource(types.QualifiedName._from_core_struct(refs[i].name), refs[i].offset, refs[i].type) + type_field = _types.TypeReferenceSource(_types.QualifiedName._from_core_struct(refs[i].name), refs[i].offset, refs[i].type) result.append(type_field) core.BNFreeTypeReferences(refs, count.value) return result @@ -3699,12 +3659,13 @@ class BinaryView(object): """ count = ctypes.c_ulonglong(0) - name = types.QualifiedName(name)._get_core_struct() + name = _types.QualifiedName(name)._get_core_struct() refs = core.BNGetTypeReferencesForTypeField(self.handle, name, offset, count) + assert refs is not None, "core.BNGetTypeReferencesForTypeField returned None" result = [] for i in range(0, count.value): - type_field = types.TypeReferenceSource(types.QualifiedName._from_core_struct(refs[i].name), refs[i].offset, refs[i].type) + type_field = _types.TypeReferenceSource(_types.QualifiedName._from_core_struct(refs[i].name), refs[i].offset, refs[i].type) result.append(type_field) core.BNFreeTypeReferences(refs, count.value) return result @@ -3731,10 +3692,12 @@ class BinaryView(object): count = ctypes.c_ulonglong(0) if length is None: refs = core.BNGetCodeReferencesForTypeFrom(self.handle, ref_src, count) + assert refs is not None, "core.BNGetCodeReferencesForTypeFrom returned None" else: refs = core.BNGetCodeReferencesForTypeFromInRange(self.handle, ref_src, length, count) + assert refs is not None, "core.BNGetCodeReferencesForTypeFromInRange returned None" for i in range(0, count.value): - type_field = types.TypeReferenceSource(types.QualifiedName._from_core_struct(refs[i].name), refs[i].offset, refs[i].type) + type_field = _types.TypeReferenceSource(_types.QualifiedName._from_core_struct(refs[i].name), refs[i].offset, refs[i].type) result.append(type_field) core.BNFreeTypeReferences(refs, count.value) return result @@ -3761,10 +3724,12 @@ class BinaryView(object): count = ctypes.c_ulonglong(0) if length is None: refs = core.BNGetCodeReferencesForTypeFieldsFrom(self.handle, ref_src, count) + assert refs is not None, "core.BNGetCodeReferencesForTypeFieldsFrom returned None" else: refs = core.BNGetCodeReferencesForTypeFieldsFromInRange(self.handle, ref_src, length, count) + assert refs is not None, "core.BNGetCodeReferencesForTypeFieldsFromInRange returned None" for i in range(0, count.value): - type_field = types.TypeReferenceSource(types.QualifiedName._from_core_struct(refs[i].name), refs[i].offset, refs[i].type) + type_field = _types.TypeReferenceSource(_types.QualifiedName._from_core_struct(refs[i].name), refs[i].offset, refs[i].type) result.append(type_field) core.BNFreeTypeReferences(refs, count.value) return result @@ -3810,8 +3775,9 @@ class BinaryView(object): """ count = ctypes.c_ulonglong(0) - name = types.QualifiedName(name)._get_core_struct() + name = _types.QualifiedName(name)._get_core_struct() refs = core.BNGetAllFieldsReferenced(self.handle, name, count) + assert refs is not None, "core.BNGetAllFieldsReferenced returned None" result = [] for i in range(0, count.value): @@ -3836,8 +3802,9 @@ class BinaryView(object): """ count = ctypes.c_ulonglong(0) - name = types.QualifiedName(name)._get_core_struct() + name = _types.QualifiedName(name)._get_core_struct() refs = core.BNGetAllSizesReferenced(self.handle, name, count) + assert refs is not None, "core.BNGetAllSizesReferenced returned None" result = {} for i in range(0, count.value): @@ -3865,14 +3832,15 @@ class BinaryView(object): """ count = ctypes.c_ulonglong(0) - name = types.QualifiedName(name)._get_core_struct() + name = _types.QualifiedName(name)._get_core_struct() refs = core.BNGetAllTypesReferenced(self.handle, name, count) + assert refs is not None, "core.BNGetAllTypesReferenced returned None" result = {} for i in range(0, count.value): result[refs[i].offset] = [] for j in range(0, refs[i].count): - typeObj = types.Type(core.BNNewTypeReference(refs[i].types[j].type),\ + typeObj = _types.Type(core.BNNewTypeReference(refs[i].types[j].type),\ confidence = refs[i].types[j].confidence) result[refs[i].offset].append(typeObj) @@ -3895,8 +3863,9 @@ class BinaryView(object): """ count = ctypes.c_ulonglong(0) - name = types.QualifiedName(name)._get_core_struct() + name = _types.QualifiedName(name)._get_core_struct() refs = core.BNGetSizesReferenced(self.handle, name, offset, count) + assert refs is not None, "core.BNGetSizesReferenced returned None" result = [] for i in range(0, count.value): @@ -3920,12 +3889,13 @@ class BinaryView(object): >>> """ count = ctypes.c_ulonglong(0) - name = types.QualifiedName(name)._get_core_struct() + name = _types.QualifiedName(name)._get_core_struct() refs = core.BNGetTypesReferenced(self.handle, name, offset, count) + assert refs is not None, "core.BNGetTypesReferenced returned None" result = [] for i in range(0, count.value): - typeObj = types.Type(core.BNNewTypeReference(refs[i].type),\ + typeObj = _types.Type(core.BNNewTypeReference(refs[i].type),\ confidence = refs[i].confidence) result.append(typeObj) @@ -3934,20 +3904,19 @@ class BinaryView(object): def create_structure_from_offset_access(self, name): newMemberAdded = ctypes.c_bool(False) - name = types.QualifiedName(name)._get_core_struct() + name = _types.QualifiedName(name)._get_core_struct() struct = core.BNCreateStructureFromOffsetAccess(self.handle, name, newMemberAdded) if struct is None: return None - return types.Structure(struct) + return _types.Structure(struct) - @classmethod def create_structure_member_from_access(self, name, offset): - name = types.QualifiedName(name)._get_core_struct() + name = _types.QualifiedName(name)._get_core_struct() result = core.BNCreateStructureMemberFromAccess(self.handle, name, offset) if not result.type: return None - return types.Type(core.BNNewTypeReference(result.type),\ + return _types.Type(core.BNNewTypeReference(result.type),\ confidence = result.confidence) def get_callers(self, addr): @@ -3967,20 +3936,21 @@ class BinaryView(object): """ count = ctypes.c_ulonglong(0) refs = core.BNGetCallers(self.handle, addr, 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 + assert refs is not None, "core.BNGetCallers returned None" + try: + for i in range(0, count.value): + if refs[i].func: + func = _function.Function(self, core.BNNewFunctionReference(refs[i].func)) + else: + func = None + if refs[i].arch: + arch = architecture.CoreArchitecture._from_cache(refs[i].arch) + else: + arch = None + + yield ReferenceSource(func, arch, refs[i].addr) + finally: + core.BNFreeCodeReferences(refs, count.value) def get_callees(self, addr, func=None, arch=None): """ @@ -4005,6 +3975,7 @@ class BinaryView(object): ref_src = core.BNReferenceSource(src_func.handle, src_arch.handle, addr) count = ctypes.c_ulonglong(0) refs = core.BNGetCallees(self.handle, ref_src, count) + assert refs is not None, "core.BNGetCallees returned None" for i in range(0, count.value): result.append(refs[i]) core.BNFreeAddressList(refs) @@ -4026,14 +3997,14 @@ class BinaryView(object): >>> """ if isinstance(namespace, str): - namespace = types.NameSpace(namespace) - if isinstance(namespace, types.NameSpace): + namespace = _types.NameSpace(namespace) + if isinstance(namespace, _types.NameSpace): namespace = namespace._get_core_struct() sym = core.BNGetSymbolByAddress(self.handle, addr, namespace) if sym is None: return None - return types.Symbol(None, None, None, handle = sym) + return _types.Symbol(None, None, None, handle = sym) def get_symbol_by_raw_name(self, name, namespace=None): """ @@ -4050,13 +4021,13 @@ class BinaryView(object): >>> """ if isinstance(namespace, str): - namespace = types.NameSpace(namespace) - if isinstance(namespace, types.NameSpace): + namespace = _types.NameSpace(namespace) + if isinstance(namespace, _types.NameSpace): namespace = namespace._get_core_struct() sym = core.BNGetSymbolByRawName(self.handle, name, namespace) if sym is None: return None - return types.Symbol(None, None, None, handle = sym) + return _types.Symbol(None, None, None, handle = sym) def get_symbols_by_name(self, name, namespace=None, ordered_filter=[SymbolType.FunctionSymbol, SymbolType.ImportedFunctionSymbol, SymbolType.LibraryFunctionSymbol, SymbolType.DataSymbol, SymbolType.ImportedDataSymbol, SymbolType.ImportAddressSymbol, SymbolType.ExternalSymbol]): """ @@ -4075,14 +4046,15 @@ class BinaryView(object): >>> """ if isinstance(namespace, str): - namespace = types.NameSpace(namespace) - if isinstance(namespace, types.NameSpace): + namespace = _types.NameSpace(namespace) + if isinstance(namespace, _types.NameSpace): namespace = namespace._get_core_struct() count = ctypes.c_ulonglong(0) syms = core.BNGetSymbolsByName(self.handle, name, count, namespace) + assert syms is not None, "core.BNGetSymbolsByName returned None" result = [] for i in range(0, count.value): - result.append(types.Symbol(None, None, None, handle = core.BNNewSymbolReference(syms[i]))) + result.append(_types.Symbol(None, None, None, handle = core.BNNewSymbolReference(syms[i]))) core.BNFreeSymbolList(syms, count.value) result = sorted(filter(lambda sym: sym.type in ordered_filter, result), key=lambda sym: ordered_filter.index(sym.type)) return result @@ -4103,16 +4075,18 @@ class BinaryView(object): """ count = ctypes.c_ulonglong(0) if isinstance(namespace, str): - namespace = types.NameSpace(namespace) - if isinstance(namespace, types.NameSpace): + namespace = _types.NameSpace(namespace) + if isinstance(namespace, _types.NameSpace): namespace = namespace._get_core_struct() if start is None: syms = core.BNGetSymbols(self.handle, count, namespace) + assert syms is not None, "core.BNGetSymbols returned None" else: syms = core.BNGetSymbolsInRange(self.handle, start, length, count, namespace) + assert syms is not None, "core.BNGetSymbolsInRange returned None" result = [] for i in range(0, count.value): - result.append(types.Symbol(None, None, None, handle = core.BNNewSymbolReference(syms[i]))) + result.append(_types.Symbol(None, None, None, handle = core.BNNewSymbolReference(syms[i]))) core.BNFreeSymbolList(syms, count.value) return result @@ -4135,17 +4109,19 @@ class BinaryView(object): if isinstance(sym_type, str): sym_type = SymbolType[sym_type] if isinstance(namespace, str): - namespace = types.NameSpace(namespace) - if isinstance(namespace, types.NameSpace): + namespace = _types.NameSpace(namespace) + if isinstance(namespace, _types.NameSpace): namespace = namespace._get_core_struct() count = ctypes.c_ulonglong(0) if start is None: syms = core.BNGetSymbolsOfType(self.handle, sym_type, count, namespace) + assert syms is not None, "core.BNGetSymbolsOfType returned None" else: syms = core.BNGetSymbolsOfTypeInRange(self.handle, sym_type, start, length, count) + assert syms is not None, "core.BNGetSymbolsOfTypeInRange returned None" result = [] for i in range(0, count.value): - result.append(types.Symbol(None, None, None, handle = core.BNNewSymbolReference(syms[i]))) + result.append(_types.Symbol(None, None, None, handle = core.BNNewSymbolReference(syms[i]))) core.BNFreeSymbolList(syms, count.value) return result @@ -4173,11 +4149,13 @@ class BinaryView(object): :rtype: None """ if plat is None: + if self.platform is None: + raise Exception("Attempting to call define_auto_symbol_and_var_or_function without Platform specified") plat = self.platform - elif not isinstance(plat, binaryninja.platform.Platform): - raise AttributeError("Provided platform is not of type `binaryninja.platform.Platform`") + elif not isinstance(plat, _platform.Platform): + raise AttributeError("Provided platform is not of type `Platform`") - if isinstance(sym_type, binaryninja.Type): + if isinstance(sym_type, _types.Type): sym_type = sym_type.handle elif sym_type is not None: raise AttributeError("Provided sym_type is not of type `binaryninja.Type`") @@ -4185,7 +4163,7 @@ class BinaryView(object): sym = core.BNDefineAutoSymbolAndVariableOrFunction(self.handle, plat.handle, sym.handle, sym_type) if sym is None: return None - return types.Symbol(None, None, None, handle = sym) + return _types.Symbol(None, None, None, handle = sym) def undefine_auto_symbol(self, sym): """ @@ -4240,7 +4218,9 @@ class BinaryView(object): >>> bv.create_user_data_tag(here, tt, "Get Crabbed") >>> """ - tag_type = TagType(core.BNCreateTagType(self.handle, name, icon)) + tag_handle = core.BNCreateTagType(self.handle, name, icon) + assert tag_handle is not None, "core.BNCreateTagType returned None" + tag_type = TagType(tag_handle) tag_type.name = name tag_type.icon = icon core.BNAddTagType(self.handle, tag_type.handle) @@ -4265,9 +4245,12 @@ class BinaryView(object): """ count = ctypes.c_ulonglong(0) types = core.BNGetTagTypes(self.handle, count) + assert types is not None, "core.BNGetTagTypes returned None" result = {} for i in range(0, count.value): - tag = TagType(core.BNNewTagTypeReference(types[i])) + tag_handle = core.BNNewTagTypeReference(types[i]) + assert tag_handle is not None, "core.BNNewTagTypeReference returned None" + tag = TagType(tag_handle) if tag.name in result: if type(result[tag.name]) == list: result[tag.name].append(tag) @@ -4317,7 +4300,7 @@ class BinaryView(object): def create_auto_tag(self, type, data): return self.create_tag(type, data, False) - def create_tag(self, type, data, user=True): + def create_tag(self, type:TagType, data:str, user:bool=True) -> Tag: """ ``create_tag`` creates a new Tag object but does not add it anywhere. Use :py:meth:`create_user_data_tag` to create and add in one step. @@ -4334,7 +4317,9 @@ class BinaryView(object): >>> bv.add_user_data_tag(here, tag) >>> """ - tag = Tag(core.BNCreateTag(type.handle, data)) + tag_handle = core.BNCreateTag(type.handle, data) + assert tag_handle is not None, "core.BNCreateTag returned None" + tag = Tag(tag_handle) core.BNAddTag(self.handle, tag.handle, user) return tag @@ -4359,12 +4344,15 @@ class BinaryView(object): :type: list(int, Tag) """ count = ctypes.c_ulonglong() - refs = core.BNGetDataTagReferences(self.handle, count) + tags = core.BNGetDataTagReferences(self.handle, count) + assert tags is not None, "core.BNGetDataTagReferences returned None" result = [] for i in range(0, count.value): - tag = Tag(core.BNNewTagReference(refs[i].tag)) - result.append((refs[i].addr, tag)) - core.BNFreeTagReferences(refs, count.value) + tag_handle = core.BNNewTagReference(tags[i].tag) + assert tag_handle is not None, "core.BNNewTagReference is not None" + tag = Tag(tag_handle) + result.append((tags[i].addr, tag)) + core.BNFreeTagReferences(tags, count.value) return result @property @@ -4411,9 +4399,12 @@ class BinaryView(object): """ count = ctypes.c_ulonglong() tags = core.BNGetDataTags(self.handle, addr, count) + assert tags is not None, "core.BNGetDataTags returned None" result = [] for i in range(0, count.value): - result.append(Tag(core.BNNewTagReference(tags[i]))) + tag_handle = core.BNNewTagReference(tags[i]) + assert tag_handle is not None, "core.BNNewTagReference is not None" + result.append(Tag(tag_handle)) core.BNFreeTagList(tags, count.value) return result @@ -4477,9 +4468,12 @@ class BinaryView(object): """ count = ctypes.c_ulonglong() tags = core.BNGetAutoDataTagsOfType(self.handle, addr, tag_type.handle, count) + assert tags is not None, "core.BNGetAutoDataTagsOfType returned None" result = [] for i in range(0, count.value): - result.append(Tag(core.BNNewTagReference(tags[i]))) + tag_handle = core.BNNewTagReference(tags[i]) + assert tag_handle is not None, "core.BNNewTagReference returned None" + result.append(Tag(tag_handle)) core.BNFreeTagList(tags, count.value) return result @@ -4574,8 +4568,8 @@ class BinaryView(object): undo buffer. This API is appropriate for generic data tags, for functions, - consider using :meth:`create_user_function_tag <binaryninja.function.Function.create_user_function_tag>` or for - specific addresses inside of functions: :meth:`create_user_address_tag <binaryninja.function.Function.create_user_address_tag>`. + consider using :meth:`create_user_function_tag <Function.create_user_function_tag>` or for + specific addresses inside of functions: :meth:`create_user_address_tag <Function.create_user_address_tag>`. :param int addr: Address at which to add the tag :param TagType type: Tag Type for the Tag that is created @@ -4689,6 +4683,8 @@ class BinaryView(object): >>> """ if arch is None: + if self.arch is None: + raise Exception("Attempting to call can_assemble without an Architecture specified") arch = self.arch return core.BNCanAssemble(self.handle, arch.handle) @@ -4715,6 +4711,8 @@ class BinaryView(object): >>> """ if arch is None: + if self.arch is None: + raise Exception("Attempting to call can_assemble without an Architecture specified") arch = self.arch return core.BNIsNeverBranchPatchAvailable(self.handle, arch.handle, addr) @@ -4741,6 +4739,8 @@ class BinaryView(object): >>> """ if arch is None: + if self.arch is None: + raise Exception("Attempting to call can_assemble without an Architecture specified") arch = self.arch return core.BNIsAlwaysBranchPatchAvailable(self.handle, arch.handle, addr) @@ -4768,6 +4768,8 @@ class BinaryView(object): """ if arch is None: + if self.arch is None: + raise Exception("Attempting to call can_assemble without an Architecture specified") arch = self.arch return core.BNIsInvertBranchPatchAvailable(self.handle, arch.handle, addr) @@ -4795,6 +4797,8 @@ class BinaryView(object): >>> """ if arch is None: + if self.arch is None: + raise Exception("Attempting to call can_assemble without an Architecture specified") arch = self.arch return core.BNIsSkipAndReturnZeroPatchAvailable(self.handle, arch.handle, addr) @@ -4822,6 +4826,8 @@ class BinaryView(object): >>> """ if arch is None: + if self.arch is None: + raise Exception("Attempting to call can_assemble without an Architecture specified") arch = self.arch return core.BNIsSkipAndReturnValuePatchAvailable(self.handle, arch.handle, addr) @@ -4858,6 +4864,8 @@ class BinaryView(object): 'mov byte [ebp-0x1c], al' """ if arch is None: + if self.arch is None: + raise Exception("Attempting to call can_assemble without an Architecture specified") arch = self.arch return core.BNConvertToNop(self.handle, arch.handle, addr) @@ -4884,6 +4892,8 @@ class BinaryView(object): >>> """ if arch is None: + if self.arch is None: + raise Exception("Attempting to call can_assemble without an Architecture specified") arch = self.arch return core.BNAlwaysBranch(self.handle, arch.handle, addr) @@ -4910,6 +4920,8 @@ class BinaryView(object): >>> """ if arch is None: + if self.arch is None: + raise Exception("Attempting to call can_assemble without an Architecture specified") arch = self.arch return core.BNConvertToNop(self.handle, arch.handle, addr) @@ -4937,6 +4949,8 @@ class BinaryView(object): >>> """ if arch is None: + if self.arch is None: + raise Exception("Attempting to call can_assemble without an Architecture specified") arch = self.arch return core.BNInvertBranch(self.handle, arch.handle, addr) @@ -4962,6 +4976,8 @@ class BinaryView(object): >>> """ if arch is None: + if self.arch is None: + raise Exception("Attempting to call can_assemble without an Architecture specified") arch = self.arch return core.BNSkipAndReturnValue(self.handle, arch.handle, addr, value) @@ -4983,6 +4999,8 @@ class BinaryView(object): >>> """ if arch is None: + if self.arch is None: + raise Exception("Attempting to call can_assemble without an Architecture specified") arch = self.arch return core.BNGetInstructionLength(self.handle, arch.handle, addr) @@ -5015,10 +5033,12 @@ class BinaryView(object): count = ctypes.c_ulonglong(0) if start is None: strings = core.BNGetStrings(self.handle, count) + assert strings is not None, "core.BNGetStrings returned None" else: if length is None: length = self.end - start strings = core.BNGetStringsInRange(self.handle, start, length, count) + assert strings is not None, "core.BNGetStringsInRange returned None" result = [] for i in range(0, count.value): result.append(StringReference(self, StringType(strings[i].type), strings[i].start, strings[i].length)) @@ -5076,7 +5096,7 @@ class BinaryView(object): >>> s2.value 'WAVAUATUSH' """ - if not isinstance(addr, numbers.Integral): + if not isinstance(addr, int): raise AttributeError("Input address (" + str(addr) + ") is not a number.") if addr < self.start or addr >= self.end: return None @@ -5199,7 +5219,7 @@ class BinaryView(object): addr = var.address + core.BNGetTypeWidth(var.type) continue break - return DataVariable(var.address, types.Type(var.type, platform = self.platform, confidence = var.typeConfidence), var.autoDiscovered, self) + return DataVariable(var.address, _types.Type(var.type, platform = self.platform, confidence = var.typeConfidence), var.autoDiscovered, self) def get_next_data_var_start_after(self, addr): """ @@ -5309,7 +5329,7 @@ class BinaryView(object): var = core.BNDataVariable() if not core.BNGetDataVariableAtAddress(self.handle, prev_data_var_start, var): return None - return DataVariable(var.address, types.Type(var.type, platform = self.platform, confidence = var.typeConfidence), var.autoDiscovered, self) + return DataVariable(var.address, _types.Type(var.type, platform = self.platform, confidence = var.typeConfidence), var.autoDiscovered, self) def get_previous_data_var_start_before(self, addr): """ @@ -5437,7 +5457,6 @@ class BinaryView(object): @property def view(self): - """ """ return self._view @view.setter @@ -5446,7 +5465,6 @@ class BinaryView(object): @property def settings(self): - """ """ return self._settings @settings.setter @@ -5470,18 +5488,19 @@ class BinaryView(object): (<type: int32_t>, 'foo') >>> """ - if not (isinstance(text, str) or isinstance(text, unicode)): + if not isinstance(text, str): raise AttributeError("Source must be a string") result = core.BNQualifiedNameAndType() errors = ctypes.c_char_p() type_list = core.BNQualifiedNameList() type_list.count = 0 if not core.BNParseTypeString(self.handle, text, result, errors, type_list): + assert errors.value is not None, "core.BNParseTypeString returned 'errors' set to None" error_str = errors.value.decode("utf-8") core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) raise SyntaxError(error_str) - type_obj = types.Type(core.BNNewTypeReference(result.type), platform = self.platform) - name = types.QualifiedName._from_core_struct(result.name) + type_obj = _types.Type(core.BNNewTypeReference(result.type), platform = self.platform) + name = _types.QualifiedName._from_core_struct(result.name) core.BNFreeQualifiedNameAndType(result) return type_obj, name @@ -5501,7 +5520,7 @@ class BinaryView(object): <type: int32_t(int32_t x)>}}, '') >>> """ - if not (isinstance(text, str) or isinstance(text, unicode)): + if not isinstance(text, str): raise AttributeError("Source must be a string") parse = core.BNTypeParserResult() @@ -5509,6 +5528,7 @@ class BinaryView(object): type_list = core.BNQualifiedNameList() type_list.count = 0 if not core.BNParseTypesString(self.handle, text, parse, errors, type_list): + assert errors.value is not None, "core.BNParseTypesString returned errors set to None" error_str = errors.value.decode("utf-8") core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) raise SyntaxError(error_str) @@ -5517,16 +5537,16 @@ class BinaryView(object): variables = {} functions = {} for i in range(0, parse.typeCount): - name = types.QualifiedName._from_core_struct(parse.types[i].name) - type_dict[name] = types.Type(core.BNNewTypeReference(parse.types[i].type), platform = self.platform) + name = _types.QualifiedName._from_core_struct(parse.types[i].name) + type_dict[name] = _types.Type(core.BNNewTypeReference(parse.types[i].type), platform = self.platform) for i in range(0, parse.variableCount): - name = types.QualifiedName._from_core_struct(parse.variables[i].name) - variables[name] = types.Type(core.BNNewTypeReference(parse.variables[i].type), platform = self.platform) + name = _types.QualifiedName._from_core_struct(parse.variables[i].name) + variables[name] = _types.Type(core.BNNewTypeReference(parse.variables[i].type), platform = self.platform) for i in range(0, parse.functionCount): - name = types.QualifiedName._from_core_struct(parse.functions[i].name) - functions[name] = types.Type(core.BNNewTypeReference(parse.functions[i].type), platform = self.platform) + name = _types.QualifiedName._from_core_struct(parse.functions[i].name) + functions[name] = _types.Type(core.BNNewTypeReference(parse.functions[i].type), platform = self.platform) core.BNFreeTypeParserResult(parse) - return types.TypeParserResult(type_dict, variables, functions) + return _types.TypeParserResult(type_dict, variables, functions) def parse_possiblevalueset(self, value, state, here=0): """ @@ -5566,13 +5586,14 @@ class BinaryView(object): value = '' if not core.BNParsePossibleValueSet(self.handle, value, state, result, here, errors): if errors: + assert errors.value is not None, "core.BNParseTypesString returned errors set to None" error_str = errors.value.decode("utf-8") else: error_str = "Error parsing specified PossibleValueSet" core.BNFreePossibleValueSet(result) core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) raise ValueError(error_str) - return function.PossibleValueSet(self.arch, result) + return variable.PossibleValueSet(self.arch, result) def get_type_by_name(self, name): """ @@ -5589,11 +5610,11 @@ class BinaryView(object): <type: int32_t> >>> """ - name = types.QualifiedName(name)._get_core_struct() + name = _types.QualifiedName(name)._get_core_struct() obj = core.BNGetAnalysisTypeByName(self.handle, name) if not obj: return None - return types.Type(obj, platform = self.platform) + return _types.Type(obj, platform = self.platform) def get_type_by_id(self, id): """ @@ -5614,7 +5635,7 @@ class BinaryView(object): obj = core.BNGetAnalysisTypeById(self.handle, id) if not obj: return None - return types.Type(obj, platform = self.platform) + return _types.Type(obj, platform = self.platform) def get_type_name_by_id(self, id): """ @@ -5634,7 +5655,7 @@ class BinaryView(object): >>> """ name = core.BNGetAnalysisTypeNameById(self.handle, id) - result = types.QualifiedName._from_core_struct(name) + result = _types.QualifiedName._from_core_struct(name) core.BNFreeQualifiedName(name) if len(result) == 0: return None @@ -5657,7 +5678,7 @@ class BinaryView(object): True >>> """ - name = types.QualifiedName(name)._get_core_struct() + name = _types.QualifiedName(name)._get_core_struct() return core.BNGetAnalysisTypeId(self.handle, name) def add_type_library(self, lib): @@ -5701,7 +5722,7 @@ class BinaryView(object): False >>> """ - name = types.QualifiedName(name)._get_core_struct() + name = _types.QualifiedName(name)._get_core_struct() return core.BNIsAnalysisTypeAutoDefined(self.handle, name) def define_type(self, type_id, default_name, type_obj): @@ -5721,9 +5742,9 @@ class BinaryView(object): >>> bv.get_type_by_name(registered_name) <type: int32_t> """ - name = types.QualifiedName(default_name)._get_core_struct() + name = _types.QualifiedName(default_name)._get_core_struct() reg_name = core.BNDefineAnalysisType(self.handle, type_id, name, type_obj.handle) - result = types.QualifiedName._from_core_struct(reg_name) + result = _types.QualifiedName._from_core_struct(reg_name) core.BNFreeQualifiedName(reg_name) return result @@ -5742,7 +5763,7 @@ class BinaryView(object): >>> bv.get_type_by_name(name) <type: int32_t> """ - name = types.QualifiedName(name)._get_core_struct() + name = _types.QualifiedName(name)._get_core_struct() core.BNDefineUserAnalysisType(self.handle, name, type_obj.handle) def undefine_type(self, type_id): @@ -5781,7 +5802,7 @@ class BinaryView(object): >>> bv.get_type_by_name(name) >>> """ - name = types.QualifiedName(name)._get_core_struct() + name = _types.QualifiedName(name)._get_core_struct() core.BNUndefineUserAnalysisType(self.handle, name) def rename_type(self, old_name, new_name): @@ -5802,8 +5823,8 @@ class BinaryView(object): <type: int32_t> >>> """ - old_name = types.QualifiedName(old_name)._get_core_struct() - new_name = types.QualifiedName(new_name)._get_core_struct() + old_name = _types.QualifiedName(old_name)._get_core_struct() + new_name = _types.QualifiedName(new_name)._get_core_struct() core.BNRenameAnalysisType(self.handle, old_name, new_name) def import_library_type(self, name, lib = None): @@ -5824,12 +5845,12 @@ class BinaryView(object): :return: a `NamedTypeReference` to the type, taking into account any renaming performed :rtype: Type """ - if not isinstance(name, types.QualifiedName): - name = types.QualifiedName(name) + if not isinstance(name, _types.QualifiedName): + name = _types.QualifiedName(name) handle = core.BNBinaryViewImportTypeLibraryType(self.handle, None if lib is None else lib.handle, name._get_core_struct()) if handle is None: return None - return types.Type(handle, platform = self.platform) + return _types.Type(handle, platform = self.platform) def import_library_object(self, name, lib = None): """ @@ -5845,12 +5866,12 @@ class BinaryView(object): :return: the object type, with any interior `NamedTypeReferences` renamed as necessary to be appropriate for the current view :rtype: Type """ - if not isinstance(name, types.QualifiedName): - name = types.QualifiedName(name) + if not isinstance(name, _types.QualifiedName): + name = _types.QualifiedName(name) handle = core.BNBinaryViewImportTypeLibraryObject(self.handle, None if lib is None else lib.handle, name._get_core_struct()) if handle is None: return None - return types.Type(handle, platform = self.platform) + return _types.Type(handle, platform = self.platform) def export_type_to_library(self, lib, name, type_obj): """ @@ -5864,11 +5885,11 @@ class BinaryView(object): :param Type type_obj: :rtype: None """ - if not isinstance(name, types.QualifiedName): - name = types.QualifiedName(name) + if not isinstance(name, _types.QualifiedName): + name = _types.QualifiedName(name) if not isinstance(lib, typelibrary.TypeLibrary): raise ValueError("lib must be a TypeLibrary object") - if not isinstance(type_obj, types.Type): + if not isinstance(type_obj, _types.Type): raise ValueError("type_obj must be a Type object") core.BNBinaryViewExportTypeToTypeLibrary(self.handle, lib.handle, name._get_core_struct(), type_obj.handle) @@ -5884,11 +5905,11 @@ class BinaryView(object): :param Type type_obj: :rtype: None """ - if not isinstance(name, types.QualifiedName): - name = types.QualifiedName(name) + if not isinstance(name, _types.QualifiedName): + name = _types.QualifiedName(name) if not isinstance(lib, typelibrary.TypeLibrary): raise ValueError("lib must be a TypeLibrary object") - if not isinstance(type_obj, types.Type): + if not isinstance(type_obj, _types.Type): raise ValueError("type_obj must be a Type object") core.BNBinaryViewExportObjectToTypeLibrary(self.handle, lib.handle, name._get_core_struct(), type_obj.handle) @@ -5950,13 +5971,13 @@ class BinaryView(object): FindCaseSensitive Case-sensitive search FindCaseInsensitive Case-insensitive search ==================== ============================ - :param FunctionGraphType graph_type: the IL to search wihtin + :param FunctionGraphType graph_type: the IL to search within """ if not isinstance(text, str): raise TypeError("text parameter is not str type") if settings is None: - settings = function.DisassemblySettings() - if not isinstance(settings, function.DisassemblySettings): + settings = _function.DisassemblySettings() + if not isinstance(settings, _function.DisassemblySettings): raise TypeError("settings parameter is not DisassemblySettings type") result = ctypes.c_ulonglong() @@ -5974,13 +5995,13 @@ class BinaryView(object): :param int start: virtual address to start searching from. :param int constant: constant to search for :param DisassemblySettings settings: disassembly settings - :param FunctionGraphType graph_type: the IL to search wihtin + :param FunctionGraphType graph_type: the IL to search within """ - if not isinstance(constant, numbers.Integral): + if not isinstance(constant, int): raise TypeError("constant parameter is not integral type") if settings is None: - settings = function.DisassemblySettings() - if not isinstance(settings, function.DisassemblySettings): + settings = _function.DisassemblySettings() + if not isinstance(settings, _function.DisassemblySettings): raise TypeError("settings parameter is not DisassemblySettings type") result = ctypes.c_ulonglong() @@ -6025,12 +6046,12 @@ class BinaryView(object): ==================== ============================ :param callback progress_func: optional function to be called with the current progress and total count. This function should return a boolean value that decides whether the - search should conitnue or stop + search should continue or stop :param callback match_callback: function that gets called when a match is found. The callback takes two parameters, i.e., the address of the match, and the actual DataBuffer that satisfies the search. If this parameter is None, this function becomes a generator and yields a tuple of the matching address and the matched DataBuffer. This function - can return a boolean value that decides whether the search should conitnue or stop + can return a boolean value that decides whether the search should continue or stop :rtype bool: whether any (one or more) match is found for the search """ if not (isinstance(data, bytes) or isinstance(data, bytearray) or isinstance(data, str)): @@ -6074,14 +6095,16 @@ class BinaryView(object): block = None line = lines[0] if line.function: - func = binaryninja.function.Function(self, core.BNNewFunctionReference(line.function)) + func = _function.Function(self, core.BNNewFunctionReference(line.function)) if line.block: - block = binaryninja.basicblock.BasicBlock(core.BNNewBasicBlockReference(line.block), self) + block_handle = core.BNNewBasicBlockReference(line.block) + assert block_handle is not None, "core.BNNewBasicBlockReference returned None" + block = basicblock.BasicBlock(block_handle, self) color = highlight.HighlightColor._from_core_struct(line.contents.highlight) addr = line.contents.addr - tokens = binaryninja.function.InstructionTextToken.get_instruction_lines(line.contents.tokens, line.contents.count) - contents = binaryninja.function.DisassemblyTextLine(tokens, addr, color = color) - return binaryninja.lineardisassembly.LinearDisassemblyLine(line.type, func, block, contents) + tokens = _function.InstructionTextToken._from_core_struct(line.contents.tokens, line.contents.count) + contents = _function.DisassemblyTextLine(tokens, addr, color = color) + return lineardisassembly.LinearDisassemblyLine(line.type, func, block, contents) def find_all_text(self, start, end, text, settings = None, flags = FindFlag.FindCaseSensitive, @@ -6105,24 +6128,24 @@ class BinaryView(object): FindCaseSensitive Case-sensitive search FindCaseInsensitive Case-insensitive search ==================== ============================ - :param FunctionGraphType graph_type: the IL to search wihtin + :param FunctionGraphType graph_type: the IL to search within :param callback progress_func: optional function to be called with the current progress and total count. This function should return a boolean value that decides whether the - search should conitnue or stop + search should continue or stop :param callback match_callback: function that gets called when a match is found. The callback takes three parameters, i.e., the address of the match, and the actual string that satisfies the search, and the LinearDisassemblyLine that contains the matching line. If this parameter is None, this function becomes a generator and yields a tuple of the matching address, the matched string, and the matching LinearDisassemblyLine. This function can return a boolean value that decides whether - the search should conitnue or stop + the search should continue or stop :rtype bool: whether any (one or more) match is found for the search """ if not isinstance(text, str): raise TypeError("text parameter is not str type") if settings is None: - settings = function.DisassemblySettings() - if not isinstance(settings, function.DisassemblySettings): + settings = _function.DisassemblySettings() + if not isinstance(settings, _function.DisassemblySettings): raise TypeError("settings parameter is not DisassemblySettings type") if not isinstance(flags, FindFlag): raise TypeError('flag parameter must have type FindFlag') @@ -6175,23 +6198,23 @@ class BinaryView(object): :param int constant: constant to search for :param DisassemblySettings settings: DisassemblySettings object used to render the text to be searched - :param FunctionGraphType graph_type: the IL to search wihtin + :param FunctionGraphType graph_type: the IL to search within :param callback progress_func: optional function to be called with the current progress and total count. This function should return a boolean value that decides whether the - search should conitnue or stop + search should continue or stop :param callback match_callback: function that gets called when a match is found. The callback takes two parameters, i.e., the address of the match, and the LinearDisassemblyLine that contains the matching line. If this parameter is None, this function becomes a generator and yields the the matching address and the matching LinearDisassemblyLine. This function can return a boolean value that - decides whether the search should conitnue or stop + decides whether the search should continue or stop :rtype bool: whether any (one or more) match is found for the search """ - if not isinstance(constant, numbers.Integral): + if not isinstance(constant, int): raise TypeError("constant parameter is not integral type") if settings is None: - settings = function.DisassemblySettings() - if not isinstance(settings, function.DisassemblySettings): + settings = _function.DisassemblySettings() + if not isinstance(settings, _function.DisassemblySettings): raise TypeError("settings parameter is not DisassemblySettings type") if progress_func: @@ -6360,7 +6383,9 @@ class BinaryView(object): seg = core.BNGetSegmentAt(self.handle, addr) if not seg: return None - return Segment(core.BNNewSegmentReference(seg)) + segment_handle = core.BNNewSegmentReference(seg) + assert segment_handle is not None, "core.BNNewSegmentReference returned None" + return Segment(segment_handle) def get_address_for_data_offset(self, offset): """ @@ -6410,24 +6435,30 @@ class BinaryView(object): def get_sections_at(self, addr): count = ctypes.c_ulonglong(0) section_list = core.BNGetSectionsAt(self.handle, addr, count) + assert section_list is not None, "core.BNGetSectionsAt returned None" result = [] for i in range(0, count.value): - result.append(Section(core.BNNewSectionReference(section_list[i]))) + section_handle = core.BNNewSectionReference(section_list[i]) + assert section_handle is not None, "core.BNNewSectionReference returned None" + result.append(Section(section_handle)) core.BNFreeSectionList(section_list, count.value) return result def get_section_by_name(self, name): section = core.BNGetSectionByName(self.handle, name) - if not section: + if section is None: return None - result = Section(core.BNNewSectionReference(section)) + section_handle = core.BNNewSectionReference(section) + assert section_handle is not None, "core.BNNewSectionReference returned None" + result = Section(section_handle) return result def get_unique_section_names(self, name_list): incoming_names = (ctypes.c_char_p * len(name_list))() for i in range(0, len(name_list)): - incoming_names[i] = binaryninja.cstr(name_list[i]) + incoming_names[i] = name_list[i].decode("utf-8") outgoing_names = core.BNGetUniqueSectionNames(self.handle, incoming_names, len(name_list)) + assert outgoing_names is not None, "core.BNGetUniqueSectionNames returned None" result = [] for i in range(0, len(name_list)): result.append(str(outgoing_names[i])) @@ -6439,14 +6470,15 @@ class BinaryView(object): """ Returns a read-only dict of the address comments attached to this BinaryView - Note that these are different from function-level comments which are specific to each function. + Note that these are different from function-level comments which are specific to each _function. For annotating code, it is recommended to use comments attached to functions rather than address comments attached to the BinaryView. On the other hand, BinaryView comments can be attached to data whereas function comments cannot. - To create a function-level comment, use :func:`~binaryninja.function.Function.set_comment_at`. + To create a function-level comment, use :func:`~Function.set_comment_at`. """ count = ctypes.c_ulonglong() addrs = core.BNGetGlobalCommentedAddresses(self.handle, count) + assert addrs is not None, "core.BNGetGlobalCommentedAddresses returned None" result = {} for i in range(0, count.value): result[addrs[i]] = self.get_comment_at(addrs[i]) @@ -6456,7 +6488,7 @@ class BinaryView(object): def get_comment_at(self, addr): """ ``get_comment_at`` returns the address-based comment attached to the given address in this BinaryView - Note that address-based comments are different from function-level comments which are specific to each function. + Note that address-based comments are different from function-level comments which are specific to each _function. For more information, see :func:`address_comments`. :param int addr: virtual address within the current BinaryView to apply the comment to :rtype: str @@ -6468,7 +6500,7 @@ class BinaryView(object): """ ``set_comment_at`` sets a comment for the BinaryView at the address specified - Note that these are different from function-level comments which are specific to each function. \ + Note that these are different from function-level comments which are specific to each _function. \ For more information, see :func:`address_comments`. :param int addr: virtual address within the current BinaryView to apply the comment to @@ -6534,7 +6566,7 @@ class BinaryView(object): :param Varies md: object to store. :param bool isAuto: whether the metadata is an auto metadata. Most metadata should keep this as False. Only those automatically generated metadata should have this set - to True. Auto metadata is not saved into the database and is presumably re-genereated + to True. Auto metadata is not saved into the database and is presumably re-generated when re-opening the database. :rtype: None :Example: @@ -6577,6 +6609,7 @@ class BinaryView(object): result = [] count = ctypes.c_ulonglong(0) names = core.BNBinaryViewGetLoadSettingsTypeNames(self.handle, count) + assert names is not None, "core.BNBinaryViewGetLoadSettingsTypeNames returned None" for i in range(count.value): result.append(names[i]) core.BNFreeStringList(names, count) @@ -6640,7 +6673,7 @@ class BinaryView(object): - ``[<expression>].d`` - read the dword (4 bytes) at ``<expression>`` - ``[<expression>].q`` - read the quadword (8 bytes) at ``<expression>`` - - The ``$here`` (or more succintly: ``$``) keyword can be used in calculations and is defined as the ``here`` parameter, or the currently selected addresss + - The ``$here`` (or more succinctly: ``$``) keyword can be used in calculations and is defined as the ``here`` parameter, or the currently selected address - The ``$start``/``$end`` keyword represents the address of the first/last bytes in the file respectively :param str expression: Arithmetic expression to be evaluated @@ -6650,6 +6683,7 @@ class BinaryView(object): offset = ctypes.c_ulonglong() errors = ctypes.c_char_p() if not core.BNParseExpression(self.handle, expression, offset, here, errors): + assert errors.value is not None, "core.BNParseExpression returned errors set to None" error_str = errors.value.decode("utf-8") core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) raise ValueError(error_str) @@ -6683,20 +6717,24 @@ class BinaryReader(object): '0xcffaedfeL' >>> """ - def __init__(self, view, endian = None): - self.handle = core.BNCreateBinaryReader(view.handle) + def __init__(self, view:'BinaryView', endian:Optional[Endianness]=None): + self._handle = core.BNCreateBinaryReader(view.handle) + assert self._handle is not None, "core.BNCreateBinaryReader returned None" if endian is None: - core.BNSetBinaryReaderEndianness(self.handle, view.endianness) + core.BNSetBinaryReaderEndianness(self._handle, view.endianness) else: - core.BNSetBinaryReaderEndianness(self.handle, endian) + core.BNSetBinaryReaderEndianness(self._handle, endian) + print(self._handle) def __del__(self): - core.BNFreeBinaryReader(self.handle) + core.BNFreeBinaryReader(self._handle) def __eq__(self, other): if not isinstance(other, self.__class__): return NotImplemented - return ctypes.addressof(self.handle.contents) == ctypes.addressof(other.handle.contents) + assert self._handle is not None + assert other._handle is not None + return ctypes.addressof(self._handle.contents) == ctypes.addressof(other._handle.contents) def __ne__(self, other): if not isinstance(other, self.__class__): @@ -6704,10 +6742,11 @@ class BinaryReader(object): return not (self == other) def __hash__(self): - return hash(ctypes.addressof(self.handle.contents)) + assert self._handle is not None + return hash(ctypes.addressof(self._handle.contents)) @property - def endianness(self): + def endianness(self) -> Endianness: """ The Endianness to read data. (read/write) @@ -6715,11 +6754,11 @@ class BinaryReader(object): :setter: sets the endianness of the reader (BigEndian or LittleEndian) :type: Endianness """ - return core.BNGetBinaryReaderEndianness(self.handle) + return core.BNGetBinaryReaderEndianness(self._handle) @endianness.setter def endianness(self, value): - core.BNSetBinaryReaderEndianness(self.handle, value) + core.BNSetBinaryReaderEndianness(self._handle, value) @property def offset(self): @@ -6730,11 +6769,11 @@ class BinaryReader(object): :setter: sets the internal offset :type: int """ - return core.BNGetReaderPosition(self.handle) + return core.BNGetReaderPosition(self._handle) @offset.setter def offset(self, value): - core.BNSeekBinaryReader(self.handle, value) + core.BNSeekBinaryReader(self._handle, value) @property def eof(self): @@ -6744,7 +6783,7 @@ class BinaryReader(object): :getter: returns boolean, true if end of file, false otherwise :type: bool """ - return core.BNIsEndOfFile(self.handle) + return core.BNIsEndOfFile(self._handle) def read(self, length): """ @@ -6760,7 +6799,7 @@ class BinaryReader(object): >>> """ dest = ctypes.create_string_buffer(length) - if not core.BNReadData(self.handle, dest, length): + if not core.BNReadData(self._handle, dest, length): return None return dest.raw @@ -6778,7 +6817,7 @@ class BinaryReader(object): >>> """ result = ctypes.c_ubyte() - if not core.BNRead8(self.handle, result): + if not core.BNRead8(self._handle, result): return None return result.value @@ -6796,7 +6835,7 @@ class BinaryReader(object): >>> """ result = ctypes.c_ushort() - if not core.BNRead16(self.handle, result): + if not core.BNRead16(self._handle, result): return None return result.value @@ -6814,7 +6853,7 @@ class BinaryReader(object): >>> """ result = ctypes.c_uint() - if not core.BNRead32(self.handle, result): + if not core.BNRead32(self._handle, result): return None return result.value @@ -6832,7 +6871,7 @@ class BinaryReader(object): >>> """ result = ctypes.c_ulonglong() - if not core.BNRead64(self.handle, result): + if not core.BNRead64(self._handle, result): return None return result.value @@ -6958,7 +6997,7 @@ class BinaryReader(object): '0x100000000L' >>> """ - core.BNSeekBinaryReader(self.handle, offset) + core.BNSeekBinaryReader(self._handle, offset) def seek_relative(self, offset): """ @@ -6975,13 +7014,7 @@ class BinaryReader(object): '0x100000000L' >>> """ - core.BNSeekBinaryReaderRelative(self.handle, offset) - - def __setattr__(self, name, value): - try: - object.__setattr__(self, name, value) - except AttributeError: - raise AttributeError("attribute '%s' is read only" % name) + core.BNSeekBinaryReaderRelative(self._handle, offset) class BinaryWriter(object): @@ -7007,19 +7040,22 @@ class BinaryWriter(object): >>> """ def __init__(self, view, endian = None): - self.handle = core.BNCreateBinaryWriter(view.handle) + self._handle = core.BNCreateBinaryWriter(view.handle) + assert self._handle is not None, "core.BNCreateBinaryWriter returned None" if endian is None: - core.BNSetBinaryWriterEndianness(self.handle, view.endianness) + core.BNSetBinaryWriterEndianness(self._handle, view.endianness) else: - core.BNSetBinaryWriterEndianness(self.handle, endian) + core.BNSetBinaryWriterEndianness(self._handle, endian) def __del__(self): - core.BNFreeBinaryWriter(self.handle) + core.BNFreeBinaryWriter(self._handle) def __eq__(self, other): if not isinstance(other, self.__class__): return NotImplemented - return ctypes.addressof(self.handle.contents) == ctypes.addressof(other.handle.contents) + assert self._handle is not None + assert other._handle is not None + return ctypes.addressof(self._handle.contents) == ctypes.addressof(other._handle.contents) def __ne__(self, other): if not isinstance(other, self.__class__): @@ -7027,7 +7063,8 @@ class BinaryWriter(object): return not (self == other) def __hash__(self): - return hash(ctypes.addressof(self.handle.contents)) + assert self._handle is not None + return hash(ctypes.addressof(self._handle.contents)) @property def endianness(self): @@ -7038,11 +7075,11 @@ class BinaryWriter(object): :setter: sets the endianness of the reader (BigEndian or LittleEndian) :type: Endianness """ - return core.BNGetBinaryWriterEndianness(self.handle) + return core.BNGetBinaryWriterEndianness(self._handle) @endianness.setter def endianness(self, value): - core.BNSetBinaryWriterEndianness(self.handle, value) + core.BNSetBinaryWriterEndianness(self._handle, value) @property def offset(self): @@ -7053,11 +7090,11 @@ class BinaryWriter(object): :setter: sets the internal offset :type: int """ - return core.BNGetWriterPosition(self.handle) + return core.BNGetWriterPosition(self._handle) @offset.setter def offset(self, value): - core.BNSeekBinaryWriter(self.handle, value) + core.BNSeekBinaryWriter(self._handle, value) def write(self, value): """ @@ -7075,10 +7112,10 @@ class BinaryWriter(object): >>> """ - value = cstr(value) + value = value.decode("utf-8") buf = ctypes.create_string_buffer(len(value)) ctypes.memmove(buf, value, len(value)) - return core.BNWriteData(self.handle, buf, len(value)) + return core.BNWriteData(self._handle, buf, len(value)) def write8(self, value): """ @@ -7095,7 +7132,7 @@ class BinaryWriter(object): 'B' >>> """ - return core.BNWrite8(self.handle, value) + return core.BNWrite8(self._handle, value) def write16(self, value): """ @@ -7105,7 +7142,7 @@ class BinaryWriter(object): :return: boolean True on success, False on failure. :rtype: bool """ - return core.BNWrite16(self.handle, value) + return core.BNWrite16(self._handle, value) def write32(self, value): """ @@ -7115,7 +7152,7 @@ class BinaryWriter(object): :return: boolean True on success, False on failure. :rtype: bool """ - return core.BNWrite32(self.handle, value) + return core.BNWrite32(self._handle, value) def write64(self, value): """ @@ -7125,7 +7162,7 @@ class BinaryWriter(object): :return: boolean True on success, False on failure. :rtype: bool """ - return core.BNWrite64(self.handle, value) + return core.BNWrite64(self._handle, value) def write16le(self, value): """ @@ -7208,7 +7245,7 @@ class BinaryWriter(object): '0x100000000L' >>> """ - core.BNSeekBinaryWriter(self.handle, offset) + core.BNSeekBinaryWriter(self._handle, offset) def seek_relative(self, offset): """ @@ -7225,7 +7262,7 @@ class BinaryWriter(object): '0x100000000L' >>> """ - core.BNSeekBinaryWriterRelative(self.handle, offset) + core.BNSeekBinaryWriterRelative(self._handle, offset) class StructuredDataValue(object): def __init__(self, type, address, value, endian): @@ -7317,21 +7354,26 @@ class StructuredDataView(object): _address = 0 _bv = None - def __init__(self, bv, structure_name, address): + def __init__(self, bv:'BinaryView', structure_name:str, address:int, endian:Optional[Endianness]=None): self._bv = bv + if endian is None: + if bv.arch is not None: + endian = bv.arch.endianness + else: + raise Exception("Can not instantiate StructuredDataView without specifying and Endianness") self._structure_name = structure_name self._address = address self._members = OrderedDict() - self._endian = bv.arch.endianness - + self._endian = endian self._lookup_structure() self._define_members() def __repr__(self): - return "<StructuredDataView type:{} size:{:#x} address:{:#x}>".format(self._structure_name, - self._structure.width, self._address) + return "<StructuredDataView type:{self._structure_name} " + \ + "size:{self._structure.width:#x} address:{self._address:#x}>" def __len__(self): + assert self._structure is not None, "self._structure is None" return self._structure.width def __getattr__(self, key): @@ -7350,9 +7392,19 @@ class StructuredDataView(object): offset = m.offset width = ty.width - value = self._bv.read(self._address + offset, width) + value = self.view.read(self._address + offset, width) return StructuredDataValue(ty, self._address + offset, value, self._endian) + @property + def view(self) -> 'BinaryView': + assert self._bv is not None + return self._bv + + @property + def structure(self) -> '_types.Structure': + assert self._structure is not None + return self._structure + def __str__(self): rv = "struct {name} 0x{addr:x} {{\n".format(name=self._structure_name, addr=self._address) for k in self._members: @@ -7365,6 +7417,7 @@ class StructuredDataView(object): formatted_type = "{:s} {:s}".format(str(ty), k) value = self[k] + assert value is not None if value.width in (1, 2, 4, 8): formatted_value = str.zfill("{:x}".format(value.int), value.width * 2) else: @@ -7377,15 +7430,91 @@ class StructuredDataView(object): return rv def _lookup_structure(self): - s = self._bv.get_type_by_name(self._structure_name) + s = self.view.get_type_by_name(self._structure_name) if s is None: - raise Exception("Could not find structure with name: {}".format(self._structure_name)) + raise Exception(f"Could not find structure with name: {self._structure_name}") if s.type_class != TypeClass.StructureTypeClass: - raise Exception("{} is not a StructureTypeClass, got: {}".format(self._structure_name, s._type_class)) + raise Exception(f"{self._structure_name} is not a StructureTypeClass, got: {s.type_class}") self._structure = s.structure def _define_members(self): - for m in self._structure.members: + for m in self.structure.members: self._members[m.name] = m + + + +class DataVariable(object): + def __init__(self, addr:int, var_type:'_types.Type', auto_discovered:bool, view:Optional['BinaryView']=None): + self._address = addr + self._type = var_type + self._auto_discovered = auto_discovered + self._view = view + + @property + def data_refs_from(self) -> Optional[Generator[int, None, None]]: + """data cross references from this data variable (read-only)""" + if self._view is None: + return None + return self._view.get_data_refs_from(self._address, max(1, len(self))) + + @property + def data_refs(self) -> Optional[Generator[int, None, None]]: + """data cross references to this data variable (read-only)""" + if self._view is None: + return None + return self._view.get_data_refs(self._address, max(1, len(self))) + + @property + def code_refs(self): + """code references to this data variable (read-only)""" + if self._view is None: + return None + return self._view.get_code_refs(self._address, max(1, len(self))) + + def __len__(self): + return len(self._type) + + def __repr__(self): + return "<var 0x%x: %s>" % (self._address, str(self._type)) + + @property + def address(self): + return self._address + + @address.setter + def address(self, value): + self._address = value + + @property + def type(self): + return self._type + + @type.setter + def type(self, value): + self._type = value + + @property + def auto_discovered(self): + return self._auto_discovered + + @auto_discovered.setter + def auto_discovered(self, value): + self._auto_discovered = value + + @property + def view(self): + return self._view + + @view.setter + def view(self, value): + self._view = value + +class DataVariableAndName(DataVariable): + def __init__(self, addr: int, var_type: types.Type, var_name: str, auto_discovered: bool, view: "BinaryView" = None) -> None: + super(DataVariableAndName, self).__init__(addr, var_type, auto_discovered, view) + self.name = var_name + + def __repr__(self) -> str: + return "<var 0x%x: %s %s>" % (self.address, str(self.type), self.name) diff --git a/python/bncompleter.py b/python/bncompleter.py index 6f349ee1..62641a2a 100644 --- a/python/bncompleter.py +++ b/python/bncompleter.py @@ -44,6 +44,7 @@ import atexit import __main__ import inspect import sys +from typing import Optional __all__ = ["Completer"] @@ -92,7 +93,7 @@ class Completer: self.use_main_ns = 0 self.namespace = namespace - def complete(self, text, state): + def complete(self, text:str, state) -> Optional[str]: """Return the next possible completion for 'text'. This is called successively with state == 0, 1, 2, ... until it diff --git a/python/callingconvention.py b/python/callingconvention.py index c29404aa..096a6b73 100644 --- a/python/callingconvention.py +++ b/python/callingconvention.py @@ -22,13 +22,12 @@ import traceback import ctypes # Binary Ninja components -import binaryninja -from binaryninja import _binaryninjacore as core -from binaryninja import log -from binaryninja.enums import VariableSourceType - -# 2-3 compatibility -from binaryninja import range +from . import _binaryninjacore as core +from .enums import VariableSourceType +from . import log +from . import variable +from . import function +from . import architecture class CallingConvention(object): @@ -50,7 +49,7 @@ class CallingConvention(object): _registered_calling_conventions = [] - def __init__(self, arch=None, name=None, handle=None, confidence=binaryninja.types.max_confidence): + def __init__(self, arch=None, name=None, handle=None, confidence=core.max_confidence): if handle is None: if arch is None or name is None: self.handle = None @@ -82,7 +81,7 @@ class CallingConvention(object): self.__class__._registered_calling_conventions.append(self) else: self.handle = handle - self.arch = binaryninja.architecture.CoreArchitecture._from_cache(core.BNGetCallingConventionArchitecture(self.handle)) + self.arch = architecture.CoreArchitecture._from_cache(core.BNGetCallingConventionArchitecture(self.handle)) self.__dict__["name"] = core.BNGetCallingConventionName(self.handle) self.__dict__["arg_regs_share_index"] = core.BNAreArgumentRegistersSharedIndex(self.handle) self.__dict__["arg_regs_for_varargs"] = core.BNAreArgumentRegistersUsedForVarArgs(self.handle) @@ -92,6 +91,7 @@ class CallingConvention(object): count = ctypes.c_ulonglong() regs = core.BNGetCallerSavedRegisters(self.handle, count) + assert regs is not None, "core.BNGetCallerSavedRegisters returned None" result = [] arch = self.arch for i in range(0, count.value): @@ -101,6 +101,7 @@ class CallingConvention(object): count = ctypes.c_ulonglong() regs = core.BNGetCalleeSavedRegisters(self.handle, count) + assert regs is not None, "core.BNGetCalleeSavedRegisters returned None" result = [] arch = self.arch for i in range(0, count.value): @@ -110,6 +111,7 @@ class CallingConvention(object): count = ctypes.c_ulonglong() regs = core.BNGetIntegerArgumentRegisters(self.handle, count) + assert regs is not None, "core.BNGetIntegerArgumentRegisters returned None" result = [] arch = self.arch for i in range(0, count.value): @@ -119,6 +121,7 @@ class CallingConvention(object): count = ctypes.c_ulonglong() regs = core.BNGetFloatArgumentRegisters(self.handle, count) + assert regs is not None, "core.BNGetFloatArgumentRegisters returned None" result = [] arch = self.arch for i in range(0, count.value): @@ -152,6 +155,7 @@ class CallingConvention(object): count = ctypes.c_ulonglong() regs = core.BNGetImplicitlyDefinedRegisters(self.handle, count) + assert regs is not None, "core.BNGetImplicitlyDefinedRegisters returned None" result = [] arch = self.arch for i in range(0, count.value): @@ -174,6 +178,8 @@ class CallingConvention(object): def __eq__(self, other): if not isinstance(other, self.__class__): return NotImplemented + assert self.handle is not None + assert other.handle is not None return ctypes.addressof(self.handle.contents) == ctypes.addressof(other.handle.contents) def __ne__(self, other): @@ -182,6 +188,7 @@ class CallingConvention(object): return not (self == other) def __hash__(self): + assert self.handle is not None return hash(ctypes.addressof(self.handle.contents)) def _get_caller_saved_regs(self, ctxt, count): @@ -308,7 +315,7 @@ class CallingConvention(object): try: if self.__class__.float_return_reg is None: return 0xffffffff - return self.arch.regs[self.__class__.float_int_return_reg].index + return self.arch.regs[self.__class__.float_return_reg].index except: log.log_error(traceback.format_exc()) return False @@ -339,23 +346,23 @@ class CallingConvention(object): def _get_incoming_reg_value(self, ctxt, reg, func, result): try: - func_obj = binaryninja.function.Function(handle = core.BNNewFunctionReference(func)) + func_obj = function.Function(handle = core.BNNewFunctionReference(func)) reg_name = self.arch.get_reg_name(reg) api_obj = self.perform_get_incoming_reg_value(reg_name, func_obj)._to_api_object() except: log.log_error(traceback.format_exc()) - api_obj = binaryninja.function.RegisterValue()._to_api_object() + api_obj = variable.RegisterValue()._to_api_object() result[0].state = api_obj.state result[0].value = api_obj.value def _get_incoming_flag_value(self, ctxt, reg, func, result): try: - func_obj = binaryninja.function.Function(handle = core.BNNewFunctionReference(func)) + func_obj = function.Function(handle = core.BNNewFunctionReference(func)) reg_name = self.arch.get_reg_name(reg) api_obj = self.perform_get_incoming_flag_value(reg_name, func_obj)._to_api_object() except: log.log_error(traceback.format_exc()) - api_obj = binaryninja.function.RegisterValue()._to_api_object() + api_obj = variable.RegisterValue()._to_api_object() result[0].state = api_obj.state result[0].value = api_obj.value @@ -364,8 +371,8 @@ class CallingConvention(object): if func is None: func_obj = None else: - func_obj = binaryninja.function.Function(handle = core.BNNewFunctionReference(func)) - in_var_obj = binaryninja.function.Variable(func_obj, in_var[0].type, in_var[0].index, in_var[0].storage) + func_obj = function.Function(handle = core.BNNewFunctionReference(func)) + in_var_obj = variable.Variable(func_obj, in_var[0].type, in_var[0].index, in_var[0].storage) out_var = self.perform_get_incoming_var_for_parameter_var(in_var_obj, func_obj) result[0].type = out_var.source_type result[0].index = out_var.index @@ -381,8 +388,8 @@ class CallingConvention(object): if func is None: func_obj = None else: - func_obj = binaryninja.function.Function(handle = core.BNNewFunctionReference(func)) - in_var_obj = binaryninja.function.Variable(func_obj, in_var[0].type, in_var[0].index, in_var[0].storage) + func_obj = function.Function(handle = core.BNNewFunctionReference(func)) + in_var_obj = variable.Variable(func_obj, in_var[0].type, in_var[0].index, in_var[0].storage) out_var = self.perform_get_parameter_var_for_incoming_var(in_var_obj, func_obj) result[0].type = out_var.source_type result[0].index = out_var.index @@ -397,11 +404,11 @@ class CallingConvention(object): reg_stack = self.arch.get_reg_stack_for_reg(reg) if reg_stack is not None: if reg == self.arch.reg_stacks[reg_stack].stack_top_reg: - return binaryninja.function.RegisterValue.constant(0) - return binaryninja.function.RegisterValue() + return variable.RegisterValue.constant(0) + return variable.RegisterValue() def perform_get_incoming_flag_value(self, reg, func): - return binaryninja.function.RegisterValue() + return variable.RegisterValue() def perform_get_incoming_var_for_parameter_var(self, in_var, func): in_buf = core.BNVariable() @@ -412,7 +419,7 @@ class CallingConvention(object): name = None if (func is not None) and (out_var.type == VariableSourceType.RegisterVariableSourceType): name = func.arch.get_reg_name(out_var.storage) - return binaryninja.function.Variable(func, out_var.type, out_var.index, out_var.storage, name) + return variable.Variable(func, out_var.type, out_var.index, out_var.storage, name) def perform_get_parameter_var_for_incoming_var(self, in_var, func): in_buf = core.BNVariable() @@ -420,7 +427,7 @@ class CallingConvention(object): in_buf.index = in_var.index in_buf.storage = in_var.storage out_var = core.BNGetDefaultParameterVariableForIncomingVariable(self.handle, in_buf) - return binaryninja.function.Variable(func, out_var.type, out_var.index, out_var.storage) + return variable.Variable(func, out_var.type, out_var.index, out_var.storage) def with_confidence(self, confidence): return CallingConvention(self.arch, handle = core.BNNewCallingConventionReference(self.handle), @@ -431,14 +438,14 @@ class CallingConvention(object): func_handle = None if func is not None: func_handle = func.handle - return binaryninja.function.RegisterValue(self.arch, core.BNGetIncomingRegisterValue(self.handle, reg_num, func_handle)) + return variable.RegisterValue(self.arch, core.BNGetIncomingRegisterValue(self.handle, reg_num, func_handle)) def get_incoming_flag_value(self, flag, func): reg_num = self.arch.get_flag_index(flag) func_handle = None if func is not None: func_handle = func.handle - return binaryninja.function.RegisterValue(self.arch, core.BNGetIncomingFlagValue(self.handle, reg_num, func_handle)) + return variable.RegisterValue(self.arch, core.BNGetIncomingFlagValue(self.handle, reg_num, func_handle)) def get_incoming_var_for_parameter_var(self, in_var, func): in_buf = core.BNVariable() @@ -453,7 +460,7 @@ class CallingConvention(object): name = None if (func is not None) and (out_var.type == VariableSourceType.RegisterVariableSourceType): name = func.arch.get_reg_name(out_var.storage) - return binaryninja.function.Variable(func, out_var.type, out_var.index, out_var.storage, name) + return variable.Variable(func, out_var.type, out_var.index, out_var.storage, name) def get_parameter_var_for_incoming_var(self, in_var, func): in_buf = core.BNVariable() @@ -465,11 +472,10 @@ class CallingConvention(object): else: func_obj = func.handle out_var = core.BNGetParameterVariableForIncomingVariable(self.handle, in_buf, func_obj) - return binaryninja.function.Variable(func, out_var.type, out_var.index, out_var.storage) + return variable.Variable(func, out_var.type, out_var.index, out_var.storage) @property def arch(self): - """ """ return self._arch @arch.setter diff --git a/python/compatibility.py b/python/compatibility.py index 472607c2..aa371ab0 100644 --- a/python/compatibility.py +++ b/python/compatibility.py @@ -18,75 +18,23 @@ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # IN THE SOFTWARE. -# 2-3 compatibility import sys -PY2 = sys.version_info[0] == 2 -PY3 = sys.version_info[0] == 3 -PY34 = sys.version_info[0:2] >= (3, 4) +def pyNativeStr(arg): + if isinstance(arg, str): + return arg + else: + return arg.decode('utf8') -if PY2: - def range(*args): - return xrange(*args) +def valid_import(mod_name): + if sys.version_info[0:2] >= (3, 4): + import importlib.util + return importlib.util.find_spec(mod_name) is not None + else: + import importlib + mod_loader = importlib.find_loader(mod_name) + found = mod_loader is not None + return found - def valid_import(mod_name): - import imp - try: - imp.find_module(mod_name) - found = True - except ImportError: - found = False - return found - def pyNativeStr(arg): - return arg - - -if PY3: - range = range # Range needs to explicitly be defined or it's an error - - def valid_import(mod_name): - import importlib - mod_loader = importlib.find_loader(mod_name) - found = mod_loader is not None - return found - - def pyNativeStr(arg): - if isinstance(arg, str): - return arg - else: - try: - return arg.decode('utf8') - except UnicodeDecodeError: - return arg.decode('charmap') - - -if PY34: - def valid_import(mod_name): - import importlib.util - return importlib.util.find_spec(mod_name) is not None - - -def with_metaclass(meta, *bases): - """Create a base class with a metaclass.""" - class metaclass(type): - def __new__(cls, name, this_bases, d): - return meta(name, bases, d) - - @classmethod - def __prepare__(cls, name, this_bases): - return meta.__prepare__(name, bases) - return type.__new__(metaclass, 'temporary_class', (), {}) - - -def cstr(arg): - if isinstance(arg, bytes) or arg is None: - return arg - elif isinstance(arg, bytearray): - return bytes(arg) - else: - try: - return arg.encode('charmap') - except UnicodeEncodeError: - return arg.encode('utf8') diff --git a/python/databuffer.py b/python/databuffer.py index c7582f4f..83e6018c 100644 --- a/python/databuffer.py +++ b/python/databuffer.py @@ -21,27 +21,17 @@ import ctypes # Binary Ninja components -from binaryninja import _binaryninjacore as core - -# 2-3 compatibility -from binaryninja import pyNativeStr -from binaryninja import cstr -import numbers - +from . import _binaryninjacore as core class DataBuffer(object): - def __init__(self, contents="", handle=None): + def __init__(self, contents:bytes=b"", handle=None): if handle is not None: self.handle = core.handle_of_type(handle, core.BNDataBuffer) - elif isinstance(contents, int) or isinstance(contents, numbers.Integral): + elif isinstance(contents, int) or isinstance(contents, int): self.handle = core.BNCreateDataBuffer(None, contents) elif isinstance(contents, DataBuffer): self.handle = core.BNDuplicateDataBuffer(contents.handle) else: - if isinstance(contents, bytes) or isinstance(contents, bytearray) or isinstance(contents, str): - contents = cstr(contents) - else: - raise TypeError("DataBuffer contents must be bytes, bytearray, or str") self.handle = core.BNCreateDataBuffer(contents, len(contents)) def __del__(self): @@ -65,7 +55,9 @@ class DataBuffer(object): if stop <= start: return "" buf = ctypes.create_string_buffer(stop - start) - ctypes.memmove(buf, core.BNGetDataBufferContentsAt(self.handle, start), stop - start) + data = core.BNGetDataBufferContentsAt(self.handle, start) + assert data is not None, "core.BNGetDataBufferContentsAt returned None" + ctypes.memmove(buf, data, stop - start) return buf.raw else: return bytes(self)[i] @@ -93,34 +85,44 @@ class DataBuffer(object): core.BNSetDataBufferContents(self.handle, data, len(data)) else: value = str(value) - buf = ctypes.create_string_buffer(value) - ctypes.memmove(core.BNGetDataBufferContentsAt(self.handle, start), buf, len(value)) + buf = ctypes.create_string_buffer(len(value)) + data = core.BNGetDataBufferContentsAt(self.handle, start) + assert data is not None, "core.BNGetDataBufferContentsAt returned None" + ctypes.memmove(data, buf, len(value)) elif i < 0: if i >= -len(self): if len(value) != 1: raise ValueError("expected single byte for assignment") value = str(value) - buf = ctypes.create_string_buffer(value) - ctypes.memmove(core.BNGetDataBufferContentsAt(self.handle, int(len(self) + i)), buf, 1) + buf = ctypes.create_string_buffer(len(value)) + data = core.BNGetDataBufferContentsAt(self.handle, int(len(self) + i)) + assert data is not None, "core.BNGetDataBufferContentsAt returned None" + ctypes.memmove(data, buf, 1) else: raise IndexError("index out of range") elif i < len(self): if len(value) != 1: raise ValueError("expected single byte for assignment") value = str(value) - buf = ctypes.create_string_buffer(value) - ctypes.memmove(core.BNGetDataBufferContentsAt(self.handle, int(i)), buf, 1) + buf = ctypes.create_string_buffer(len(value)) + data = core.BNGetDataBufferContentsAt(self.handle, int(i)) + assert data is not None, "core.BNGetDataBufferContentsAt returned None" + ctypes.memmove(data, buf, 1) else: raise IndexError("index out of range") def __str__(self): buf = ctypes.create_string_buffer(len(self)) - ctypes.memmove(buf, core.BNGetDataBufferContents(self.handle), len(self)) - return pyNativeStr(buf.raw) + data = core.BNGetDataBufferContents(self.handle) + assert data is not None, "core.BNGetDataBufferContents returned None" + ctypes.memmove(buf, data, len(self)) + return buf.raw.decode('utf8') def __bytes__(self): buf = ctypes.create_string_buffer(len(self)) - ctypes.memmove(buf, core.BNGetDataBufferContents(self.handle), len(self)) + data = core.BNGetDataBufferContents(self.handle) + assert data is not None, "core.BNGetDataBufferContents returned None" + ctypes.memmove(buf, data, len(self)) return buf.raw def escape(self): diff --git a/python/datarender.py b/python/datarender.py index e61c00ca..fbdebd0a 100644 --- a/python/datarender.py +++ b/python/datarender.py @@ -23,14 +23,14 @@ import traceback import ctypes import binaryninja -from binaryninja import _binaryninjacore as core -from binaryninja import filemetadata -from binaryninja import binaryview -from binaryninja import function -from binaryninja import enums -from binaryninja import log -from binaryninja import types -from binaryninja import highlight +from . import _binaryninjacore as core +from . import filemetadata +from . import binaryview +from . import function +from . import enums +from . import log +from . import types +from . import highlight class TypeContext(object): @@ -92,9 +92,9 @@ class DataRenderer(object): self._cb.getLinesForData = self._cb.getLinesForData.__class__(self._get_lines_for_data) self.handle = core.BNCreateDataRenderer(self._cb) - @classmethod - def is_type_of_struct_name(cls, type, name, context): - return (type.type_class == enums.TypeClass.StructureTypeClass and len(context) > 0 + @staticmethod + def is_type_of_struct_name(t, name, context): + return (t.type_class == enums.TypeClass.StructureTypeClass and len(context) > 0 and context[-1].type.type_class == enums.TypeClass.NamedTypeReferenceClass and context[-1].type.named_type_reference.name == name) @@ -131,7 +131,7 @@ class DataRenderer(object): view = binaryview.BinaryView(file_metadata=file_metadata, handle=core.BNNewViewReference(view)) type = types.Type(handle=core.BNNewTypeReference(type)) - prefixTokens = function.InstructionTextToken.get_instruction_lines(prefix, prefixCount) + prefixTokens = function.InstructionTextToken._from_core_struct(prefix, prefixCount) pycontext = [] for i in range(ctxCount): pycontext.append(TypeContext(types.Type(core.BNNewTypeReference(typeCtx[i].type)), typeCtx[i].offset)) @@ -161,7 +161,7 @@ class DataRenderer(object): line_buf[i].instrIndex = 0xffffffffffffffff line_buf[i].count = len(line.tokens) - line_buf[i].tokens = function.InstructionTextToken.get_instruction_lines(line.tokens) + line_buf[i].tokens = function.InstructionTextToken._get_core_struct(line.tokens) return ctypes.cast(line_buf, ctypes.c_void_p).value except: diff --git a/python/demangle.py b/python/demangle.py index 6f007a7b..db0bde1d 100644 --- a/python/demangle.py +++ b/python/demangle.py @@ -21,13 +21,9 @@ import ctypes # Binary Ninja components -from binaryninja import _binaryninjacore as core -from binaryninja.binaryview import BinaryView -from binaryninja import types - -# 2-3 compatibility -from binaryninja import range -from binaryninja import pyNativeStr +from . import _binaryninjacore as core +from . import binaryview +from . import types def get_qualified_name(names): @@ -68,11 +64,11 @@ def demangle_ms(arch, mangled_name, options = False): outName = ctypes.POINTER(ctypes.c_char_p)() outSize = ctypes.c_ulonglong() names = [] - if (isinstance(options, BinaryView) and core.BNDemangleMSWithOptions(arch.handle, mangled_name, ctypes.byref(handle), ctypes.byref(outName), ctypes.byref(outSize), options)) or \ + if (isinstance(options, binaryview.BinaryView) and core.BNDemangleMSWithOptions(arch.handle, mangled_name, ctypes.byref(handle), ctypes.byref(outName), ctypes.byref(outSize), options)) or \ (isinstance(options, bool) and core.BNDemangleMS(arch.handle, mangled_name, ctypes.byref(handle), ctypes.byref(outName), ctypes.byref(outSize), options)) or \ (options is None and core.BNDemangleMSWithOptions(arch.handle, mangled_name, ctypes.byref(handle), ctypes.byref(outName), ctypes.byref(outSize), None)): for i in range(outSize.value): - names.append(pyNativeStr(outName[i])) + names.append(outName[i].decode('utf8')) core.BNFreeDemangledName(ctypes.byref(outName), outSize.value) return (types.Type(handle), names) return (None, mangled_name) @@ -93,11 +89,11 @@ def demangle_gnu3(arch, mangled_name, options = None): outName = ctypes.POINTER(ctypes.c_char_p)() outSize = ctypes.c_ulonglong() names = [] - if (isinstance(options, BinaryView) and core.BNDemangleGNU3WithOptions(arch.handle, mangled_name, ctypes.byref(handle), ctypes.byref(outName), ctypes.byref(outSize), options)) or \ + if (isinstance(options, binaryview.BinaryView) and core.BNDemangleGNU3WithOptions(arch.handle, mangled_name, ctypes.byref(handle), ctypes.byref(outName), ctypes.byref(outSize), options)) or \ (isinstance(options, bool) and core.BNDemangleGNU3(arch.handle, mangled_name, ctypes.byref(handle), ctypes.byref(outName), ctypes.byref(outSize), options)) or \ (options is None and core.BNDemangleGNU3WithOptions(arch.handle, mangled_name, ctypes.byref(handle), ctypes.byref(outName), ctypes.byref(outSize), None)): for i in range(outSize.value): - names.append(pyNativeStr(outName[i])) + names.append(outName[i].decode('utf8')) core.BNFreeDemangledName(ctypes.byref(outName), outSize.value) if not handle: return (None, names) @@ -115,7 +111,7 @@ def simplify_name_to_string(input_name): :rtype: str :Example: - >>> bdemangle.simplify_name_to_string("std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") + >>> demangle.simplify_name_to_string("std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") 'std::string' >>> """ @@ -147,8 +143,10 @@ def simplify_name_to_qualified_name(input_name, simplify = True): result = None if isinstance(input_name, str): result = core.BNRustSimplifyStrToFQN(input_name, simplify) + assert result is not None, "core.BNRustSimplifyStrToFQN returned None" elif isinstance(input_name, types.QualifiedName): result = core.BNRustSimplifyStrToFQN(str(input_name), True) + assert result is not None, "core.BNRustSimplifyStrToFQN returned None" else: raise TypeError("Parameter must be of type `str` or `types.QualifiedName`") @@ -156,7 +154,7 @@ def simplify_name_to_qualified_name(input_name, simplify = True): for name in result: if name == b'': break - native_result.append(name) + native_result.append(name.decode("utf-8")) name_count = len(native_result) native_result = types.QualifiedName(native_result) diff --git a/python/downloadprovider.py b/python/downloadprovider.py index b45bda19..e5f3cb3c 100644 --- a/python/downloadprovider.py +++ b/python/downloadprovider.py @@ -21,27 +21,16 @@ import abc import ctypes -from json import loads, dumps +from json import dumps import sys import traceback - -if sys.version_info >= (3, 0, 0): - from urllib.parse import urlencode -else: - from urllib import urlencode +from urllib.parse import urlencode # Binary Ninja Components -import binaryninja._binaryninjacore as core - import binaryninja -from binaryninja import settings -from binaryninja import with_metaclass -from binaryninja import startup -from binaryninja import log - -# 2-3 compatibility -from binaryninja import pyNativeStr -from binaryninja import range +import binaryninja._binaryninjacore as core +from . import settings +from . import log def to_bytes(field): @@ -99,7 +88,7 @@ class DownloadInstance(object): def _perform_custom_request(self, ctxt, method, url, header_count, header_keys, header_values, response): # Cast response to an array of length 1 so ctypes can write to the pointer # out_response = ((BNDownloadInstanceResponse*)[1])response - out_response = (ctypes.POINTER(core.BNDownloadInstanceResponse) * 1).from_address(ctypes.addressof(response.contents)) + out_response = (ctypes.POINTER(core.BNDownloadInstanceResponse) * 1).from_address(ctypes.addressof(response.contents)) # type: ignore try: # Extract headers keys_ptr = ctypes.cast(header_keys, ctypes.POINTER(ctypes.c_char_p)) @@ -137,8 +126,8 @@ class DownloadInstance(object): self.bn_response.headerKeys = (ctypes.c_char_p * len(py_response.headers))() self.bn_response.headerValues = (ctypes.c_char_p * len(py_response.headers))() for i, (key, value) in enumerate(py_response.headers.items()): - self.bn_response.headerKeys[i] = core.BNAllocString(pyNativeStr(key)) - self.bn_response.headerValues[i] = core.BNAllocString(pyNativeStr(value)) + self.bn_response.headerKeys[i] = core.BNAllocString(key.decode('utf8')) + self.bn_response.headerValues[i] = core.BNAllocString(value.decode('utf8')) out_response[0] = ctypes.pointer(self.bn_response) else: @@ -258,18 +247,6 @@ class DownloadInstance(object): return self.request("POST", url, headers, data, json) class _DownloadProviderMetaclass(type): - @property - def list(self): - """List all DownloadProvider types (read-only)""" - binaryninja._init_plugins() - count = ctypes.c_ulonglong() - types = core.BNGetDownloadProviderList(count) - result = [] - for i in range(0, count.value): - result.append(DownloadProvider(types[i])) - core.BNFreeDownloadProviderList(types) - return result - def __iter__(self): binaryninja._init_plugins() count = ctypes.c_ulonglong() @@ -287,14 +264,8 @@ class _DownloadProviderMetaclass(type): raise KeyError("'%s' is not a valid download provider" % str(value)) return DownloadProvider(provider) - def __setattr__(self, name, value): - try: - type.__setattr__(self, name, value) - except AttributeError: - raise AttributeError("attribute '%s' is read only" % name) - -class DownloadProvider(with_metaclass(_DownloadProviderMetaclass, object)): +class DownloadProvider(metaclass=_DownloadProviderMetaclass): name = None instance_class = None _registered_providers = [] @@ -316,7 +287,9 @@ class DownloadProvider(with_metaclass(_DownloadProviderMetaclass, object)): result = self.__class__.instance_class(self) if result is None: return None - return ctypes.cast(core.BNNewDownloadInstanceReference(result.handle), ctypes.c_void_p).value + download_instance = core.BNNewDownloadInstanceReference(result.handle) + assert download_instance is not None, "core.BNNewDownloadInstanceReference returned None" + return ctypes.cast(download_instance, ctypes.c_void_p).value except: log.log_error(traceback.format_exc()) return None @@ -386,7 +359,7 @@ try: else: proxies = None - r = requests.get(pyNativeStr(url), proxies=proxies) + r = requests.get(url.decode('utf8'), proxies=proxies) if not r.ok: core.BNSetErrorForDownloadInstance(self.handle, "Received error from server") return -1 @@ -478,7 +451,7 @@ if not _loaded and (sys.platform != "win32") and (sys.version_info >= (2, 7, 9)) opener = build_opener(ProxyHandler({'https': proxy_setting})) install_opener(opener) - r = urlopen(pyNativeStr(url)) + r = urlopen(url.decode('utf8')) total_size = int(r.headers.get('content-length', 0)) bytes_sent = 0 while True: @@ -544,7 +517,7 @@ if not _loaded and (sys.platform != "win32") and (sys.version_info >= (2, 7, 9)) if b"Content-Length" in headers: del headers[b"Content-Length"] - req = PythonDownloadInstance.CustomRequest(pyNativeStr(url), data=data_generator, headers=headers, method=pyNativeStr(method)) + req = PythonDownloadInstance.CustomRequest(url.decode('utf8'), data=data_generator, headers=headers, method=method.decode('utf8')) result = urlopen(req) except HTTPError as he: result = he diff --git a/python/enum/__init__.py b/python/enum/__init__.py index 5045b4aa..8f1f4f5e 100644 --- a/python/enum/__init__.py +++ b/python/enum/__init__.py @@ -22,19 +22,6 @@ try: except ImportError: OrderedDict = None -try: - basestring -except NameError: - # In Python 2 basestring is the ancestor of both str and unicode - # in Python 3 it's just str, but was missing in 3.1 - basestring = str - -try: - unicode -except NameError: - # In Python 3 unicode no longer exists (it's just str) - unicode = str - class _RouteClassAttributeToGetattr(object): """Route attribute access on a class to __getattr__. diff --git a/python/examples/bin_info.py b/python/examples/bin_info.py index 1af59a31..850a97b8 100644 --- a/python/examples/bin_info.py +++ b/python/examples/bin_info.py @@ -28,9 +28,6 @@ import binaryninja.interaction as interaction from binaryninja.plugin import PluginCommand from binaryninja import Settings -# 2-3 compatibility -from binaryninja import range - def get_bininfo(bv): if bv is None: @@ -54,8 +51,9 @@ def get_bininfo(bv): contents += "| Start | Name |\n" contents += "|------:|:-------|\n" - for i in range(min(10, len(bv.functions))): - contents += "| 0x%x | %s |\n" % (bv.functions[i].start, bv.functions[i].symbol.full_name) + functions = list(bv.functions) + for i in range(min(10, len(functions))): + contents += "| 0x%x | %s |\n" % (functions[i].start, functions[i].symbol.full_name) contents += "### First 10 Strings ###\n" contents += "| Start | Length | String |\n" diff --git a/python/examples/mappedview.py b/python/examples/mappedview.py index 6d060235..bf7f2d11 100644 --- a/python/examples/mappedview.py +++ b/python/examples/mappedview.py @@ -43,8 +43,8 @@ class MappedView(BinaryView): def __init__(self, data): BinaryView.__init__(self, parent_view = data, file_metadata = data.file) - @classmethod - def is_valid_for_data(cls, data): + @staticmethod + def is_valid_for_data(data): # Insert code that looks for a magic identifier and return True if this BinaryViewType can handle/parse the binary return True @@ -64,12 +64,14 @@ class MappedView(BinaryView): # Optionally, perform light-weight parsing of the 'Raw' BinaryView to extract required information for load settings generation. # This allows finer control of the settings provided as well as their default values. # For example, the view.relocatable property could be used to control the read-only attribute of "loader.imageBase" - view = cls.registered_view_type.parse(data) - + registered_view = cls.registered_view_type + assert registered_view is not None + view = registered_view.parse(data) + assert view is not None # Populate settings container with default load settings # Note: `get_default_load_settings_for_data` automatically tries to parse the input if the `data` BinaryViewType name does not match the # cls BinaryViewType name. In this case a parsed view is already being passed. - load_settings = cls.registered_view_type.get_default_load_settings_for_data(view) + load_settings = registered_view.get_default_load_settings_for_data(view) # Specify default load settings that can be overridden (from the UI). overrides = ["loader.architecture", "loader.platform", "loader.entryPointOffset", "loader.imageBase", "loader.segments", "loader.sections"] @@ -113,8 +115,9 @@ class MappedView(BinaryView): load_settings = self.get_load_settings(self.name) if load_settings is None: if self.parse_only is True: - self.arch = Architecture['x86'] - self.platform = Architecture['x86'].standalone_platform + self.arch = Architecture['x86'] # type: ignore + self.platform = Architecture['x86'].standalone_platform # type: ignore + assert self.parent_view is not None self.add_auto_segment(0, len(self.parent_view), 0, len(self.parent_view), SegmentFlag.SegmentReadable) return True else: @@ -123,8 +126,8 @@ class MappedView(BinaryView): load_settings = self.__class__.get_load_settings_for_data(self.parent_view) arch = load_settings.get_string("loader.architecture", self) - self.arch = Architecture[arch] - self.platform = Architecture[arch].standalone_platform + self.arch = Architecture[arch] # type: ignore + self.platform = Architecture[arch].standalone_platform # type: ignore self.load_address = load_settings.get_integer("loader.imageBase", self) self.add_auto_segment(self.load_address, len(self.parent_view), 0, len(self.parent_view), SegmentFlag.SegmentReadable | SegmentFlag.SegmentExecutable) if load_settings.contains("loader.entryPointOffset"): diff --git a/python/examples/nds.py b/python/examples/nds.py index 4cf7c24d..18001d50 100644 --- a/python/examples/nds.py +++ b/python/examples/nds.py @@ -27,9 +27,6 @@ from binaryninja.log import log_error import struct import traceback -# 2-3 compatibility -from binaryninja import range - def crc16(data): crc = 0xffff @@ -48,8 +45,8 @@ class DSView(BinaryView): BinaryView.__init__(self, file_metadata = data.file, parent_view = data) self.raw = data - @classmethod - def is_valid_for_data(self, data): + @staticmethod + def is_valid_for_data(data): hdr = data.read(0, 0x160) if len(hdr) < 0x160: return False @@ -60,7 +57,7 @@ class DSView(BinaryView): return True def init_common(self): - self.platform = Architecture["armv7"].standalone_platform + self.platform = Architecture["armv7"].standalone_platform # type: ignore self.hdr = self.raw.read(0, 0x160) def init_arm9(self): @@ -72,7 +69,7 @@ class DSView(BinaryView): self.arm9_size = struct.unpack("<L", self.hdr[0x2C:0x30])[0] self.add_auto_segment(self.arm9_load_addr, self.arm9_size, self.arm9_offset, self.arm9_size, SegmentFlag.SegmentReadable | SegmentFlag.SegmentExecutable) - self.add_entry_point(Architecture['armv7'].standalone_platform, self.arm_entry_addr) + self.add_entry_point(Architecture['armv7'].standalone_platform, self.arm_entry_addr) # type: ignore return True except: log_error(traceback.format_exc()) @@ -87,7 +84,7 @@ class DSView(BinaryView): self.arm7_size = struct.unpack("<L", self.hdr[0x3C:0x40])[0] self.add_auto_segment(self.arm7_load_addr, self.arm7_size, self.arm7_offset, self.arm7_size, SegmentFlag.SegmentReadable | SegmentFlag.SegmentExecutable) - self.add_entry_point(Architecture['armv7'].standalone_platform, self.arm_entry_addr) + self.add_entry_point(Architecture['armv7'].standalone_platform, self.arm_entry_addr) # type: ignore return True except: log_error(traceback.format_exc()) diff --git a/python/examples/nes.py b/python/examples/nes.py index ba9a2965..013528a1 100644 --- a/python/examples/nes.py +++ b/python/examples/nes.py @@ -21,19 +21,17 @@ import struct import traceback import os +from typing import Callable, List, Any -from binaryninja.architecture import Architecture +from binaryninja.architecture import Architecture, InstructionInfo, RegisterInfo, RegisterName from binaryninja.lowlevelil import LowLevelILLabel, LLIL_TEMP -from binaryninja.function import RegisterInfo, InstructionInfo, InstructionTextToken +from binaryninja.function import InstructionTextToken from binaryninja.binaryview import BinaryView from binaryninja.types import Symbol from binaryninja.log import log_error from binaryninja.enums import (BranchType, InstructionTextTokenType, LowLevelILOperation, LowLevelILFlagCondition, FlagRole, SegmentFlag, SymbolType) -# 2-3 compatibility -from binaryninja import range - InstructionNames = [ "brk", "ora", None, None, None, "ora", "asl", None, # 0x00 @@ -145,14 +143,14 @@ OperandLengths = [ 1, # IND_Y_DEST 1, # REL 1, # ZERO - 1, # ZREO_DEST + 1, # ZERO_DEST 1, # ZERO_X 1, # ZERO_X_DEST 1, # ZERO_Y 1 # ZERO_Y_DEST ] -OperandTokens = [ +OperandTokens:List[Callable[[int], List[InstructionTextToken]]] = [ lambda value: [], # NONE lambda value: [InstructionTextToken(InstructionTextTokenType.PossibleAddressToken, "$%.4x" % value, value)], # ABS lambda value: [InstructionTextToken(InstructionTextTokenType.PossibleAddressToken, "$%.4x" % value, value)], # ABS_DEST @@ -248,7 +246,7 @@ OperandIL = [ def cond_branch(il, cond, dest): t = None if il[dest].operation == LowLevelILOperation.LLIL_CONST: - t = il.get_label_for_address(Architecture['6502'], il[dest].constant) + t = il.get_label_for_address(Architecture['6502'], il[dest].constant) # type: ignore if t is None: t = LowLevelILLabel() indirect = True @@ -266,7 +264,7 @@ def cond_branch(il, cond, dest): def jump(il, dest): label = None if il[dest].operation == LowLevelILOperation.LLIL_CONST: - label = il.get_label_for_address(Architecture['6502'], il[dest].constant) + label = il.get_label_for_address(Architecture['6502'], il[dest].constant) # type: ignore if label is None: il.append(il.jump(dest)) else: @@ -374,10 +372,10 @@ class M6502(Architecture): instr_alignment = 1 max_instr_length = 3 regs = { - "a": RegisterInfo("a", 1), - "x": RegisterInfo("x", 1), - "y": RegisterInfo("y", 1), - "s": RegisterInfo("s", 1) + "a": RegisterInfo(RegisterName("a"), 1), + "x": RegisterInfo(RegisterName("x"), 1), + "y": RegisterInfo(RegisterName("y"), 1), + "s": RegisterInfo(RegisterName("s"), 1) } stack_pointer = "s" flags = ["c", "z", "i", "d", "b", "v", "s"] @@ -451,7 +449,7 @@ class M6502(Architecture): def get_instruction_text(self, data, addr): instr, operand, length, value = self.decode_instruction(data, addr) - if instr is None: + if instr is None or operand is None or length is None or value is None: return None tokens = [] @@ -461,7 +459,7 @@ class M6502(Architecture): def get_instruction_low_level_il(self, data, addr, il): instr, operand, length, value = self.decode_instruction(data, addr) - if instr is None: + if instr is None or operand is None or length is None or value is None: return None operand = OperandIL[operand](il, value) @@ -518,26 +516,27 @@ class M6502(Architecture): def skip_and_return_value(self, data, addr, value): if (data[0:1] != b"\x20") or (len(data) != 3): return None - return b"\xa9" + chr(value & 0xff) + b"\xea" + return b"\xa9" + (value & 0xff).to_bytes(1, "little") + b"\xea" class NESView(BinaryView): name = "NES" long_name = "NES ROM" - + bank = None def __init__(self, data): BinaryView.__init__(self, parent_view = data, file_metadata = data.file) - self.platform = Architecture['6502'].standalone_platform + self.platform = Architecture['6502'].standalone_platform # type: ignore @classmethod - def is_valid_for_data(self, data): + def is_valid_for_data(cls, data): hdr = data.read(0, 16) if len(hdr) < 16: return False if hdr[0:4] != b"NES\x1a": return False rom_banks = struct.unpack("B", hdr[4:5])[0] - if rom_banks < (self.bank + 1): + assert cls.bank is not None + if rom_banks < (cls.bank + 1): return False return True @@ -558,6 +557,7 @@ class NESView(BinaryView): self.add_auto_segment(0, 0x8000, 0, 0, SegmentFlag.SegmentReadable | SegmentFlag.SegmentWritable | SegmentFlag.SegmentExecutable) # Add ROM mappings + assert self.__class__.bank is not None self.add_auto_segment(0x8000, 0x4000, self.rom_offset + (self.__class__.bank * 0x4000), 0x4000, SegmentFlag.SegmentReadable | SegmentFlag.SegmentExecutable) self.add_auto_segment(0xc000, 0x4000, self.rom_offset + self.rom_length - 0x4000, 0x4000, @@ -632,7 +632,7 @@ class NESView(BinaryView): return True def perform_get_entry_point(self): - return struct.unpack("<H", str(self.perform_read(0xfffc, 2)))[0] + return struct.unpack("<H", self.perform_read(0xfffc, 2))[0] banks = [] diff --git a/python/examples/nsf.py b/python/examples/nsf.py index 959dc060..45500cba 100644 --- a/python/examples/nsf.py +++ b/python/examples/nsf.py @@ -39,10 +39,10 @@ class NSFView(BinaryView): def __init__(self, data): BinaryView.__init__(self, parent_view=data, file_metadata=data.file) - self.platform = Architecture["6502"].standalone_platform + self.platform = Architecture["6502"].standalone_platform # type: ignore - @classmethod - def is_valid_for_data(self, data): + @staticmethod + def is_valid_for_data(data): hdr = data.read(0, 128) if len(hdr) < 128: return False @@ -57,25 +57,25 @@ class NSFView(BinaryView): def init(self): try: hdr = self.parent_view.read(0, 128) - self.version = struct.unpack("B", hdr[5])[0] - self.song_count = struct.unpack("B", hdr[6])[0] - self.starting_song = struct.unpack("B", hdr[7])[0] + self.version = int(hdr[5]) + self.song_count = int(hdr[6]) + self.starting_song = int(hdr[7]) self.load_address = struct.unpack("<H", hdr[8:10])[0] self.init_address = struct.unpack("<H", hdr[10:12])[0] self.play_address = struct.unpack("<H", hdr[12:14])[0] - self.song_name = hdr[15].split('\0')[0] - self.artist_name = hdr[46].split('\0')[0] - self.copyright_name = hdr[78].split('\0')[0] + self.song_name = int(hdr[15]) + self.artist_name = int(hdr[46]) + self.copyright_name = int(hdr[78]) self.play_speed_ntsc = struct.unpack("<H", hdr[110:112])[0] self.bank_switching = hdr[112:120] self.play_speed_pal = struct.unpack("<H", hdr[120:122])[0] - self.pal_ntsc_bits = struct.unpack("B", hdr[122])[0] + self.pal_ntsc_bits = int(hdr[122]) self.pal = True if (self.pal_ntsc_bits & 1) == 1 else False self.ntsc = not self.pal if self.pal_ntsc_bits & 2 == 2: self.pal = True self.ntsc = True - self.extra_sound_bits = struct.unpack("B", hdr[123])[0] + self.extra_sound_bits = int(hdr[123]) if self.bank_switching == "\0" * 8: # no bank switching @@ -139,7 +139,7 @@ class NSFView(BinaryView): return True def perform_get_entry_point(self): - return struct.unpack("<H", str(self.perform_read(0x0a, 2)))[0] + return struct.unpack("<H", self.perform_read(0x0a, 2))[0] NSFView.register() diff --git a/python/examples/snippets/__init__.py b/python/examples/snippets/__init__.py new file mode 100644 index 00000000..0f551a06 --- /dev/null +++ b/python/examples/snippets/__init__.py @@ -0,0 +1,444 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +import sys +import os +import re +import codecs +from PySide2.QtWidgets import (QLineEdit, QPushButton, QApplication, QTextEdit, QWidget, + QVBoxLayout, QHBoxLayout, QDialog, QFileSystemModel, QTreeView, QLabel, QSplitter, + QInputDialog, QMessageBox, QHeaderView, QMenu, QAction, QKeySequenceEdit, + QPlainTextEdit) +from PySide2.QtCore import (QDir, QObject, Qt, QFileInfo, QItemSelectionModel, QSettings, QUrl) +from PySide2.QtGui import (QFont, QFontMetrics, QDesktopServices, QKeySequence, QIcon) +from binaryninja import user_plugin_path +from binaryninja.plugin import PluginCommand, MainThreadActionHandler +from binaryninja.mainthread import execute_on_main_thread +from binaryninja.log import (log_error, log_debug) +from binaryninjaui import (getMonospaceFont, UIAction, UIActionHandler, Menu, DockHandler, + getThemeColor, ThemeColor) +import numbers +from .QCodeEditor import QCodeEditor, PythonHighlighter + +snippetPath = os.path.realpath(os.path.join(user_plugin_path(), "..", "snippets")) +try: + if not os.path.exists(snippetPath): + os.mkdir(snippetPath) +except IOError: + log_error("Unable to create %s" % snippetPath) + + +def includeWalk(dir, includeExt): + filePaths = [] + for (root, dirs, files) in os.walk(dir): + for f in files: + if os.path.splitext(f)[1] in includeExt: + filePaths.append(os.path.join(root, f)) + return filePaths + + +def loadSnippetFromFile(snippetPath): + try: + snippetText = codecs.open(snippetPath, 'r', "utf-8").readlines() + except: + return ("", "", "") + if (len(snippetText) < 3): + return ("", "", "") + else: + qKeySequence = QKeySequence(snippetText[1].strip()[1:]) + if qKeySequence.isEmpty(): + qKeySequence = None + return (snippetText[0].strip()[1:], + qKeySequence, + ''.join(snippetText[2:]) + ) + + +def actionFromSnippet(snippetName, snippetDescription): + if not snippetDescription: + shortName = os.path.basename(snippetName) + if shortName.endswith('.py'): + shortName = shortName[:-3] + return "Snippets\\" + shortName + else: + return "Snippets\\" + snippetDescription + + +def executeSnippet(code, context): + snippetGlobals = {} + if context.binaryView == None: + dock = DockHandler.getActiveDockHandler() + if not dock: + log_error("Snippet triggered with no context and no dock handler. This should not happen. Please report reproduction steps if possible.") + return + viewFrame = dock.getViewFrame() + if not viewFrame: + log_error("Snippet triggered with no context and no view frame. Snippets require at least one open binary.") + return + viewInterface = viewFrame.getCurrentViewInterface() + context.binaryView = viewInterface.getData() + snippetGlobals['current_view'] = context.binaryView + snippetGlobals['bv'] = context.binaryView + if not context.function: + if not context.lowLevelILFunction: + if not context.mediumLevelILFunction: + snippetGlobals['current_mlil'] = None + snippetGlobals['current_function'] = None + snippetGlobals['current_llil'] = None + else: + snippetGlobals['current_mlil'] = context.mediumLevelILFunction + snippetGlobals['current_function'] = context.mediumLevelILFunction.source_function + snippetGlobals['current_llil'] = context.mediumLevelILFunction.source_function.llil + else: + snippetGlobals['current_llil'] = context.lowLevelILFunction + snippetGlobals['current_function'] = context.lowLevelILFunction.source_function + snippetGlobals['current_mlil'] = context.lowLevelILFunction.source_function.mlil + else: + snippetGlobals['current_function'] = context.function + snippetGlobals['current_mlil'] = context.function.mlil + snippetGlobals['current_llil'] = context.function.llil + snippetGlobals['current_token'] = context.function.llil + + if context.function is not None: + snippetGlobals['current_basic_block'] = context.function.get_basic_block_at(context.address) + else: + snippetGlobals['current_basic_block'] = None + snippetGlobals['current_address'] = context.address + snippetGlobals['here'] = context.address + if context.address is not None and isinstance(context.length, int): + snippetGlobals['current_selection'] = (context.address, context.address+context.length) + else: + snippetGlobals['current_selection'] = None + snippetGlobals['uicontext'] = context + + exec("from binaryninja import *", snippetGlobals) + exec(code, snippetGlobals) + if snippetGlobals['here'] != context.address: + context.binaryView.file.navigate(context.binaryView.file.view, snippetGlobals['here']) + if snippetGlobals['current_address'] != context.address: + context.binaryView.file.navigate(context.binaryView.file.view, snippetGlobals['current_address']) + + +def makeSnippetFunction(code): + return lambda context: executeSnippet(code, context) + +class Snippets(QDialog): + + def __init__(self, context, parent=None): + super(Snippets, self).__init__(parent) + # Create widgets + self.setWindowModality(Qt.ApplicationModal) + self.title = QLabel(self.tr("Snippet Editor")) + self.saveButton = QPushButton(self.tr("&Save")) + self.saveButton.setShortcut(QKeySequence(self.tr("Ctrl+S"))) + self.runButton = QPushButton(self.tr("&Run")) + self.runButton.setShortcut(QKeySequence(self.tr("Ctrl+R"))) + self.closeButton = QPushButton(self.tr("Close")) + self.clearHotkeyButton = QPushButton(self.tr("Clear Hotkey")) + self.setWindowTitle(self.title.text()) + #self.newFolderButton = QPushButton("New Folder") + self.browseButton = QPushButton("Browse Snippets") + self.browseButton.setIcon(QIcon.fromTheme("edit-undo")) + self.deleteSnippetButton = QPushButton("Delete") + self.newSnippetButton = QPushButton("New Snippet") + self.edit = QCodeEditor(HIGHLIGHT_CURRENT_LINE=False, SyntaxHighlighter=PythonHighlighter) + self.edit.setPlaceholderText("python code") + self.resetting = False + self.columns = 3 + self.context = context + + self.keySequenceEdit = QKeySequenceEdit(self) + self.currentHotkey = QKeySequence() + self.currentHotkeyLabel = QLabel("") + self.currentFileLabel = QLabel() + self.currentFile = "" + self.snippetDescription = QLineEdit() + self.snippetDescription.setPlaceholderText("optional description") + + #Set Editbox Size + font = getMonospaceFont(self) + self.edit.setFont(font) + font = QFontMetrics(font) + self.edit.setTabStopWidth(4 * font.width(' ')); #TODO, replace with settings API + + #Files + self.files = QFileSystemModel() + self.files.setRootPath(snippetPath) + self.files.setNameFilters(["*.py"]) + + #Tree + self.tree = QTreeView() + self.tree.setModel(self.files) + self.tree.setSortingEnabled(True) + self.tree.hideColumn(2) + self.tree.sortByColumn(0, Qt.AscendingOrder) + self.tree.setRootIndex(self.files.index(snippetPath)) + for x in range(self.columns): + #self.tree.resizeColumnToContents(x) + self.tree.header().setSectionResizeMode(x, QHeaderView.ResizeToContents) + treeLayout = QVBoxLayout() + treeLayout.addWidget(self.tree) + treeButtons = QHBoxLayout() + #treeButtons.addWidget(self.newFolderButton) + treeButtons.addWidget(self.browseButton) + treeButtons.addWidget(self.newSnippetButton) + treeButtons.addWidget(self.deleteSnippetButton) + treeLayout.addLayout(treeButtons) + treeWidget = QWidget() + treeWidget.setLayout(treeLayout) + + # Create layout and add widgets + buttons = QHBoxLayout() + buttons.addWidget(self.clearHotkeyButton) + buttons.addWidget(self.keySequenceEdit) + buttons.addWidget(self.currentHotkeyLabel) + buttons.addWidget(self.closeButton) + buttons.addWidget(self.runButton) + buttons.addWidget(self.saveButton) + + description = QHBoxLayout() + description.addWidget(QLabel(self.tr("Description: "))) + description.addWidget(self.snippetDescription) + + vlayoutWidget = QWidget() + vlayout = QVBoxLayout() + vlayout.addLayout(description) + vlayout.addWidget(self.edit) + vlayout.addLayout(buttons) + vlayoutWidget.setLayout(vlayout) + + hsplitter = QSplitter() + hsplitter.addWidget(treeWidget) + hsplitter.addWidget(vlayoutWidget) + + hlayout = QHBoxLayout() + hlayout.addWidget(hsplitter) + + self.showNormal() #Fixes bug that maximized windows are "stuck" + #Because you can't trust QT to do the right thing here + if (sys.platform == "darwin"): + self.settings = QSettings("Vector35", "Snippet Editor") + else: + self.settings = QSettings("Vector 35", "Snippet Editor") + if self.settings.contains("ui/snippeteditor/geometry"): + self.restoreGeometry(self.settings.value("ui/snippeteditor/geometry")) + else: + self.edit.setMinimumWidth(80 * font.averageCharWidth()) + self.edit.setMinimumHeight(30 * font.lineSpacing()) + + # Set dialog layout + self.setLayout(hlayout) + + # Add signals + self.saveButton.clicked.connect(self.save) + self.closeButton.clicked.connect(self.close) + self.runButton.clicked.connect(self.run) + self.clearHotkeyButton.clicked.connect(self.clearHotkey) + self.tree.selectionModel().selectionChanged.connect(self.selectFile) + self.newSnippetButton.clicked.connect(self.newFileDialog) + self.deleteSnippetButton.clicked.connect(self.deleteSnippet) + #self.newFolderButton.clicked.connect(self.newFolder) + self.browseButton.clicked.connect(self.browseSnippets) + + if self.settings.contains("ui/snippeteditor/selected"): + selectedName = self.settings.value("ui/snippeteditor/selected") + self.tree.selectionModel().select(self.files.index(selectedName), QItemSelectionModel.ClearAndSelect | QItemSelectionModel.Rows) + if self.tree.selectionModel().hasSelection(): + self.selectFile(self.tree.selectionModel().selection(), None) + self.edit.setFocus() + cursor = self.edit.textCursor() + cursor.setPosition(self.edit.document().characterCount()-1) + self.edit.setTextCursor(cursor) + else: + self.readOnly(True) + else: + self.readOnly(True) + + + @staticmethod + def registerAllSnippets(): + for action in list(filter(lambda x: x.startswith("Snippets\\"), UIAction.getAllRegisteredActions())): + if action == "Snippets\\Snippet Editor...": + continue + UIActionHandler.globalActions().unbindAction(action) + Menu.mainMenu("Tools").removeAction(action) + UIAction.unregisterAction(action) + + for snippet in includeWalk(snippetPath, ".py"): + snippetKeys = None + (snippetDescription, snippetKeys, snippetCode) = loadSnippetFromFile(snippet) + actionText = actionFromSnippet(snippet, snippetDescription) + if snippetCode: + if snippetKeys == None: + UIAction.registerAction(actionText) + else: + UIAction.registerAction(actionText, snippetKeys) + UIActionHandler.globalActions().bindAction(actionText, UIAction(makeSnippetFunction(snippetCode))) + Menu.mainMenu("Tools").addAction(actionText, "Snippets") + + def clearSelection(self): + self.keySequenceEdit.clear() + self.currentHotkey = QKeySequence() + self.currentHotkeyLabel.setText("") + self.currentFileLabel.setText("") + self.snippetDescription.setText("") + self.edit.clear() + self.tree.clearSelection() + self.currentFile = "" + + def reject(self): + self.settings.setValue("ui/snippeteditor/geometry", self.saveGeometry()) + + if self.snippetChanged(): + question = QMessageBox.question(self, self.tr("Discard"), self.tr("You have unsaved changes, quit anyway?")) + if question != QMessageBox.StandardButton.Yes: + return + self.accept() + + def browseSnippets(self): + url = QUrl.fromLocalFile(snippetPath) + QDesktopServices.openUrl(url); + + def newFolder(self): + (folderName, ok) = QInputDialog.getText(self, self.tr("Folder Name"), self.tr("Folder Name: ")) + if ok and folderName: + index = self.tree.selectionModel().currentIndex() + selection = self.files.filePath(index) + if QFileInfo(selection).isDir(): + QDir(selection).mkdir(folderName) + else: + QDir(snippetPath).mkdir(folderName) + + def selectFile(self, new, old): + if (self.resetting): + self.resetting = False + return + if len(new.indexes()) == 0: + self.clearSelection() + self.currentFile = "" + self.readOnly(True) + return + newSelection = self.files.filePath(new.indexes()[0]) + self.settings.setValue("ui/snippeteditor/selected", newSelection) + if QFileInfo(newSelection).isDir(): + self.readOnly(True) + self.clearSelection() + self.currentFile = "" + return + + if old and old.length() > 0: + oldSelection = self.files.filePath(old.indexes()[0]) + if not QFileInfo(oldSelection).isDir() and self.snippetChanged(): + question = QMessageBox.question(self, self.tr("Discard"), self.tr("Snippet changed. Discard changes?")) + if question != QMessageBox.StandardButton.Yes: + self.resetting = True + self.tree.selectionModel().select(old, QItemSelectionModel.ClearAndSelect | QItemSelectionModel.Rows) + return False + + self.currentFile = newSelection + self.loadSnippet() + + def loadSnippet(self): + self.currentFileLabel.setText(QFileInfo(self.currentFile).baseName()) + (snippetDescription, snippetKeys, snippetCode) = loadSnippetFromFile(self.currentFile) + self.snippetDescription.setText(snippetDescription) if snippetDescription else self.snippetDescription.setText("") + self.keySequenceEdit.setKeySequence(snippetKeys) if snippetKeys else self.keySequenceEdit.setKeySequence(QKeySequence("")) + self.edit.setPlainText(snippetCode) if snippetCode else self.edit.setPlainText("") + self.readOnly(False) + + def newFileDialog(self): + (snippetName, ok) = QInputDialog.getText(self, self.tr("Snippet Name"), self.tr("Snippet Name: ")) + if ok and snippetName: + if not snippetName.endswith(".py"): + snippetName += ".py" + index = self.tree.selectionModel().currentIndex() + selection = self.files.filePath(index) + if QFileInfo(selection).isDir(): + path = os.path.join(selection, snippetName) + else: + path = os.path.join(snippetPath, snippetName) + self.readOnly(False) + open(path, "w").close() + self.tree.setCurrentIndex(self.files.index(path)) + log_debug("Snippet %s created." % snippetName) + + def readOnly(self, flag): + self.keySequenceEdit.setEnabled(not flag) + self.snippetDescription.setReadOnly(flag) + self.edit.setReadOnly(flag) + if flag: + self.snippetDescription.setDisabled(True) + self.edit.setDisabled(True) + else: + self.snippetDescription.setEnabled(True) + self.edit.setEnabled(True) + + def deleteSnippet(self): + selection = self.tree.selectedIndexes()[::self.columns][0] #treeview returns each selected element in the row + snippetName = self.files.fileName(selection) + question = QMessageBox.question(self, self.tr("Confirm"), self.tr("Confirm deletion: ") + snippetName) + if (question == QMessageBox.StandardButton.Yes): + log_debug("Deleting snippet %s." % snippetName) + self.clearSelection() + self.files.remove(selection) + self.registerAllSnippets() + + def snippetChanged(self): + if (self.currentFile == "" or QFileInfo(self.currentFile).isDir()): + return False + (snippetDescription, snippetKeys, snippetCode) = loadSnippetFromFile(self.currentFile) + if snippetKeys == None and not self.keySequenceEdit.keySequence().isEmpty(): + return True + if snippetKeys != None and snippetKeys != self.keySequenceEdit.keySequence().toString(): + return True + return self.edit.toPlainText() != snippetCode or \ + self.snippetDescription.text() != snippetDescription + + def save(self): + log_debug("Saving snippet %s" % self.currentFile) + outputSnippet = codecs.open(self.currentFile, "w", "utf-8") + outputSnippet.write("#" + self.snippetDescription.text() + "\n") + outputSnippet.write("#" + self.keySequenceEdit.keySequence().toString() + "\n") + outputSnippet.write(self.edit.toPlainText()) + outputSnippet.close() + self.registerAllSnippets() + + def run(self): + if self.context == None: + log_warn("Cannot run snippets outside of the UI at this time.") + return + if self.snippetChanged(): + question = QMessageBox.question(self, self.tr("Confirm"), self.tr("You have unsaved changes, must save first. Save?")) + if (question == QMessageBox.StandardButton.No): + return + else: + self.save() + actionText = actionFromSnippet(self.currentFile, self.snippetDescription.text()) + UIActionHandler.globalActions().executeAction(actionText, self.context) + + log_debug("Saving snippet %s" % self.currentFile) + outputSnippet = codecs.open(self.currentFile, "w", "utf-8") + outputSnippet.write("#" + self.snippetDescription.text() + "\n") + outputSnippet.write("#" + self.keySequenceEdit.keySequence().toString() + "\n") + outputSnippet.write(self.edit.toPlainText()) + outputSnippet.close() + self.registerAllSnippets() + + def clearHotkey(self): + self.keySequenceEdit.clear() + + +def launchPlugin(context): + snippets = Snippets(context) + snippets.exec_() + + +if __name__ == '__main__': + app = QApplication(sys.argv) + snippets = Snippets(None) + snippets.show() + sys.exit(app.exec_()) +else: + Snippets.registerAllSnippets() + UIAction.registerAction("Snippets\\Snippet Editor...") + UIActionHandler.globalActions().bindAction("Snippets\\Snippet Editor...", UIAction(launchPlugin)) + Menu.mainMenu("Tools").addAction("Snippets\\Snippet Editor...", "Snippet") diff --git a/python/fileaccessor.py b/python/fileaccessor.py index 141cc008..f848cb22 100644 --- a/python/fileaccessor.py +++ b/python/fileaccessor.py @@ -22,8 +22,8 @@ import traceback import ctypes # Binary Ninja components -from binaryninja import _binaryninjacore as core -from binaryninja import log +from . import _binaryninjacore as core +from . import log class FileAccessor(object): def __init__(self): @@ -33,6 +33,15 @@ class FileAccessor(object): self._cb.read = self._cb.read.__class__(self._read) self._cb.write = self._cb.write.__class__(self._write) + def get_length(self): + return NotImplemented + + def read(self, offset, length): + return NotImplemented + + def write(self, offset:int, data:bytes): + return NotImplemented + def __len__(self): return self.get_length() @@ -83,5 +92,5 @@ class CoreFileAccessor(FileAccessor): def write(self, offset, value): value = str(value) - data = ctypes.create_string_buffer(value) + data = ctypes.create_string_buffer(len(value)) return self._cb.write(self._cb.context, offset, data, len(value)) diff --git a/python/filemetadata.py b/python/filemetadata.py index 903a36f6..ee8e8271 100644 --- a/python/filemetadata.py +++ b/python/filemetadata.py @@ -18,20 +18,23 @@ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # IN THE SOFTWARE. -from __future__ import absolute_import import traceback import ctypes +from typing import Any, Callable, Optional, List # Binary Ninja Components import binaryninja -from binaryninja import _binaryninjacore as core -from binaryninja import associateddatastore #required for _FileMetadataAssociatedDataStore -from binaryninja import log -from binaryninja.enums import SaveOption -from binaryninja import pyNativeStr +from . import _binaryninjacore as core +from .enums import SaveOption +from . import associateddatastore #required for _FileMetadataAssociatedDataStore +from . import log +from . import binaryview + +ProgressFuncType = Callable[[int, int], bool] +ViewName = str class NavigationHandler(object): - def _register(self, handle): + def _register(self, handle) -> None: self._cb = core.BNNavigationHandler() self._cb.context = 0 self._cb.getCurrentView = self._cb.getCurrentView.__class__(self._get_current_view) @@ -39,7 +42,7 @@ class NavigationHandler(object): self._cb.navigate = self._cb.navigate.__class__(self._navigate) core.BNSetFileMetadataNavigationHandler(handle, self._cb) - def _get_current_view(self, ctxt): + def _get_current_view(self, ctxt:Any): try: view = self.get_current_view() except: @@ -47,27 +50,36 @@ class NavigationHandler(object): view = "" return core.BNAllocString(view) - def _get_current_offset(self, ctxt): + def _get_current_offset(self, ctxt:Any) -> int: try: return self.get_current_offset() except: log.log_error(traceback.format_exc()) return 0 - def _navigate(self, ctxt, view, offset): + def _navigate(self, ctxt:Any, view:ViewName, offset:int) -> bool: try: return self.navigate(view, offset) except: log.log_error(traceback.format_exc()) return False + def get_current_view(self) -> str: + return NotImplemented + + def get_current_offset(self) -> int: + return NotImplemented + + def navigate(self, view:ViewName, offset:int) -> bool: + return NotImplemented + class SaveSettings(object): """ ``class SaveSettings`` is used to specify actions and options that apply to saving a database (.bndb). """ - def __init__(self, handle = None): + def __init__(self, handle=None): if handle is None: self.handle = core.BNCreateSaveSettings() else: @@ -76,12 +88,12 @@ class SaveSettings(object): def __del__(self): core.BNFreeSaveSettings(self.handle) - def is_option_set(self, option): + def is_option_set(self, option:SaveOption) -> bool: if isinstance(option, str): option = SaveOption[option] return core.BNIsSaveSettingsOptionSet(self.handle, option) - def set_option(self, option, state = True): + def set_option(self, option:SaveOption, state:bool=True): """ Set a SaveOption in this instance. @@ -122,7 +134,7 @@ class FileMetadata(object): self.handle = core.BNCreateFileMetadata() if filename is not None: core.BNSetFilename(self.handle, str(filename)) - self._nav = None + self._nav:Optional[NavigationHandler] = None def __repr__(self): return "<FileMetadata: %s>" % self.filename @@ -135,6 +147,8 @@ class FileMetadata(object): def __eq__(self, other): if not isinstance(other, self.__class__): return NotImplemented + assert self.handle is not None + assert other.handle is not None return ctypes.addressof(self.handle.contents) == ctypes.addressof(other.handle.contents) def __ne__(self, other): @@ -143,16 +157,15 @@ class FileMetadata(object): return not (self == other) def __hash__(self): + assert self.handle is not None return hash(ctypes.addressof(self.handle.contents)) @property - def nav(self): - """ - """ + def nav(self) -> Optional[NavigationHandler]: return self._nav @nav.setter - def nav(self, value): + def nav(self, value:NavigationHandler) -> None: self._nav = value @classmethod @@ -161,100 +174,102 @@ class FileMetadata(object): if handle.value in cls._associated_data: del cls._associated_data[handle.value] - @classmethod - def set_default_session_data(cls, name, value): + @staticmethod + def set_default_session_data(name:str, value:Any) -> None: _FileMetadataAssociatedDataStore.set_default(name, value) @property - def original_filename(self): + def original_filename(self) -> str: """The original name of the binary opened if a bndb, otherwise reads or sets the current filename (read/write)""" return core.BNGetOriginalFilename(self.handle) @original_filename.setter - def original_filename(self, value): + def original_filename(self, value:str) -> None: core.BNSetOriginalFilename(self.handle, str(value)) @property - def filename(self): + def filename(self) -> str: """The name of the open bndb or binary filename (read/write)""" return core.BNGetFilename(self.handle) @filename.setter - def filename(self, value): + def filename(self, value:str) -> None: core.BNSetFilename(self.handle, str(value)) @property - def modified(self): + def modified(self) -> bool: """Boolean result of whether the file is modified (Inverse of 'saved' property) (read/write)""" return core.BNIsFileModified(self.handle) @modified.setter - def modified(self, value): + def modified(self, value:bool) -> None: if value: core.BNMarkFileModified(self.handle) else: core.BNMarkFileSaved(self.handle) @property - def analysis_changed(self): + def analysis_changed(self) -> bool: """Boolean result of whether the auto-analysis results have changed (read-only)""" return core.BNIsAnalysisChanged(self.handle) @property - def has_database(self, binary_view_type = ""): + def has_database(self, binary_view_type:ViewName="") -> bool: """Whether the FileMetadata is backed by a database, or if specified, a specific BinaryViewType (read-only)""" return core.BNIsBackedByDatabase(self.handle, binary_view_type) @property - def view(self): + def view(self) -> ViewName: return core.BNGetCurrentView(self.handle) @view.setter - def view(self, value): + def view(self, value:ViewName) -> None: core.BNNavigate(self.handle, str(value), core.BNGetCurrentOffset(self.handle)) @property - def offset(self): + def offset(self) -> int: """The current offset into the file (read/write)""" return core.BNGetCurrentOffset(self.handle) @offset.setter - def offset(self, value): + def offset(self, value:int) -> None: core.BNNavigate(self.handle, core.BNGetCurrentView(self.handle), value) @property - def raw(self): + def raw(self) -> Optional['binaryview.BinaryView']: """Gets the "Raw" BinaryView of the file""" view = core.BNGetFileViewOfType(self.handle, "Raw") if view is None: return None - return binaryninja.binaryview.BinaryView(file_metadata = self, handle = view) + return binaryview.BinaryView(file_metadata = self, handle = view) @property - def saved(self): + def saved(self) -> bool: """Boolean result of whether the file has been saved (Inverse of 'modified' property) (read/write)""" return not core.BNIsFileModified(self.handle) @saved.setter - def saved(self, value): + def saved(self, value:bool) -> None: if value: core.BNMarkFileSaved(self.handle) else: core.BNMarkFileModified(self.handle) @property - def navigation(self): + def navigation(self) -> Optional[NavigationHandler]: """Alias for nav""" return self._nav @navigation.setter - def navigation(self, value): + def navigation(self, value:NavigationHandler) -> None: + assert self.handle is not None value._register(self.handle) self._nav = value @property - def session_data(self): + def session_data(self) -> Any: """Dictionary object where plugins can store arbitrary data associated with the file""" + assert self.handle is not None, "Attempting to set session_data when handle is None" handle = ctypes.cast(self.handle, ctypes.c_void_p) if handle.value not in FileMetadata._associated_data: obj = _FileMetadataAssociatedDataStore() @@ -264,17 +279,17 @@ class FileMetadata(object): return FileMetadata._associated_data[handle.value] @property - def snapshot_data_applied_without_error(self): + def snapshot_data_applied_without_error(self) -> bool: return core.BNIsSnapshotDataAppliedWithoutError(self.handle) - def close(self): + def close(self) -> None: """ Closes the underlying file handle. It is recommended that this is done in a `finally` clause to avoid handle leaks. """ core.BNCloseFile(self.handle) - def begin_undo_actions(self): + def begin_undo_actions(self) -> None: """ ``begin_undo_actions`` start recording actions taken so the can be undone at some point. @@ -296,7 +311,7 @@ class FileMetadata(object): """ core.BNBeginUndoActions(self.handle) - def commit_undo_actions(self): + def commit_undo_actions(self) -> None: """ ``commit_undo_actions`` commit the actions taken since the last commit to the undo database. @@ -318,7 +333,7 @@ class FileMetadata(object): """ core.BNCommitUndoActions(self.handle) - def undo(self): + def undo(self) -> None: """ ``undo`` undo the last committed action in the undo database. @@ -343,7 +358,7 @@ class FileMetadata(object): """ core.BNUndo(self.handle) - def redo(self): + def redo(self) -> None: """ ``redo`` redo the last committed action in the undo database. @@ -368,7 +383,7 @@ class FileMetadata(object): """ core.BNRedo(self.handle) - def navigate(self, view, offset): + def navigate(self, view:ViewName, offset:int) -> bool: """ ``navigate`` navigates the UI to the specified virtual address @@ -381,12 +396,12 @@ class FileMetadata(object): :Example: >>> import random - >>> bv.navigate(bv.view, random.choice(bv.functions).start) + >>> bv.navigate(bv.view, random.choice(list(bv.functions)).start) True """ return core.BNNavigate(self.handle, str(view), offset) - def create_database(self, filename, progress_func = None, settings = None): + def create_database(self, filename:str, progress_func:Optional[ProgressFuncType]= None, settings:SaveSettings=None): """ ``create_database`` writes the current database (.bndb) out to the specified file. @@ -400,17 +415,20 @@ class FileMetadata(object): >>> bv.file.create_database(f"{bv.file.filename}.bndb", None, settings) True """ + _settings = None if settings is not None: - settings = settings.handle + _settings = settings.handle + assert self.raw is not None, "BinaryView.create_database called when raw view is None" if progress_func is None: - return core.BNCreateDatabase(self.raw.handle, str(filename), settings) + return core.BNCreateDatabase(self.raw.handle, str(filename), _settings) else: + _progress_func = progress_func return core.BNCreateDatabaseWithProgress(self.raw.handle, str(filename), None, ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.c_ulonglong, ctypes.c_ulonglong)( - lambda ctxt, cur, total: progress_func(cur, total)), settings) + lambda ctxt, cur, total: _progress_func(cur, total)), settings) - def open_existing_database(self, filename, progress_func = None): + def open_existing_database(self, filename:str, progress_func:Callable[[int, int], bool]=None): if progress_func is None: view = core.BNOpenExistingDatabase(self.handle, str(filename)) else: @@ -419,56 +437,62 @@ class FileMetadata(object): lambda ctxt, cur, total: progress_func(cur, total))) if view is None: return None - return binaryninja.binaryview.BinaryView(file_metadata = self, handle = view) + return binaryview.BinaryView(file_metadata = self, handle = view) - def open_database_for_configuration(self, filename): + def open_database_for_configuration(self, filename:str) -> Optional['binaryview.BinaryView']: view = core.BNOpenDatabaseForConfiguration(self.handle, str(filename)) if view is None: return None - return binaryninja.binaryview.BinaryView(file_metadata = self, handle = view) + return binaryview.BinaryView(file_metadata = self, handle = view) - def save_auto_snapshot(self, progress_func = None, settings = None): + def save_auto_snapshot(self, progress_func:Optional[ProgressFuncType]=None, settings:SaveSettings=None): + _settings = None if settings is not None: - settings = settings.handle + _settings = settings.handle + assert self.raw is not None, "BinaryView.save_auto_snapshot called when raw view is None" if progress_func is None: - return core.BNSaveAutoSnapshot(self.raw.handle, settings) + return core.BNSaveAutoSnapshot(self.raw.handle, _settings) else: + _progress_func = progress_func return core.BNSaveAutoSnapshotWithProgress(self.raw.handle, None, ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.c_ulonglong, ctypes.c_ulonglong)( - lambda ctxt, cur, total: progress_func(cur, total)), settings) + lambda ctxt, cur, total: _progress_func(cur, total)), _settings) - def merge_user_analysis(self, path, progress_func = None): + def merge_user_analysis(self, path:str, progress_func:ProgressFuncType): return core.BNMergeUserAnalysis(self.handle, str(path), None, ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.c_ulonglong, ctypes.c_ulonglong)( lambda ctxt, cur, total: progress_func(cur, total))) - def get_view_of_type(self, name): + def get_view_of_type(self, name:str) -> Optional['binaryview.BinaryView']: view = core.BNGetFileViewOfType(self.handle, str(name)) if view is None: view_type = core.BNGetBinaryViewTypeByName(str(name)) if view_type is None: return None + + assert self.raw is not None, "BinaryView.save_auto_snapshot called when raw view is None" view = core.BNCreateBinaryViewOfType(view_type, self.raw.handle) if view is None: return None - return binaryninja.binaryview.BinaryView(file_metadata = self, handle = view) + return binaryview.BinaryView(file_metadata = self, handle = view) - def open_project(self): + def open_project(self) -> bool: return core.BNOpenProject(self.handle) - def close_project(self): + def close_project(self) -> None: core.BNCloseProject(self.handle) - def is_project_open(self): + def is_project_open(self) -> bool: return core.BNIsProjectOpen(self.handle) @property - def existing_views(self): + def existing_views(self) -> List[ViewName]: length = ctypes.c_ulonglong() result = core.BNGetExistingViews(self.handle, ctypes.byref(length)) + assert result is not None, "core.BNGetExistingViews returned None" views = [] for i in range(length.value): - views.append(pyNativeStr(result[i])) + views.append(result[i].decode("utf-8")) core.BNFreeStringList(result, length) return views
\ No newline at end of file diff --git a/python/flowgraph.py b/python/flowgraph.py index 0407bec0..47ed187c 100644 --- a/python/flowgraph.py +++ b/python/flowgraph.py @@ -24,20 +24,18 @@ import traceback # Binary Ninja components import binaryninja -from binaryninja.enums import (BranchType, InstructionTextTokenType, HighlightStandardColor, FlowGraphOption, +from . import _binaryninjacore as core +from .enums import (BranchType, InstructionTextTokenType, HighlightStandardColor, FlowGraphOption, EdgePenStyle, ThemeColor) -from binaryninja import _binaryninjacore as core -from binaryninja import function -from binaryninja import binaryview -from binaryninja import lowlevelil -from binaryninja import mediumlevelil -from binaryninja import highlevelil -from binaryninja import basicblock -from binaryninja import log -from binaryninja import highlight - -# 2-3 compatibility -from binaryninja import range +from . import function +from . import binaryview +from . import lowlevelil +from . import mediumlevelil +from . import highlevelil +from . import basicblock +from . import log +from . import highlight +from . import interaction class FlowGraphEdge(object): @@ -73,8 +71,8 @@ class EdgeStyle(object): result.color = self.color return result - @classmethod - def from_core_struct(cls, edge_style): + @staticmethod + def from_core_struct(edge_style): return EdgeStyle(edge_style.style, edge_style.width, edge_style.color) def __eq__(self, other): @@ -92,6 +90,7 @@ class FlowGraphNode(object): raise ValueError("flow graph node must be associated with a graph") handle = core.BNCreateFlowGraphNode(graph.handle) self.handle = handle + assert self.handle is not None, "core.BNCreateFlowGraphNode returned None" self._graph = graph if self._graph is None: self._graph = FlowGraph(handle = core.BNGetFlowGraphNodeOwner(self.handle)) @@ -113,6 +112,9 @@ class FlowGraphNode(object): def __eq__(self, other): if not isinstance(other, self.__class__): return NotImplemented + + assert self.handle is not None + assert other.handle is not None return ctypes.addressof(self.handle.contents) == ctypes.addressof(other.handle.contents) def __ne__(self, other): @@ -121,28 +123,29 @@ class FlowGraphNode(object): return not (self == other) def __hash__(self): + assert self.handle is not None return hash(ctypes.addressof(self.handle.contents)) def __iter__(self): count = ctypes.c_ulonglong() lines = core.BNGetFlowGraphNodeLines(self.handle, count) + assert lines is not None, "core.BNGetFlowGraphNodeLines returned None" block = self.basic_block try: for i in range(0, count.value): addr = lines[i].addr if (lines[i].instrIndex != 0xffffffffffffffff) and (block is not None) and hasattr(block, 'il_function'): - il_instr = block.il_function[lines[i].instrIndex] + il_instr = block.__dict__['il_function'][lines[i].instrIndex] else: il_instr = None - tokens = function.InstructionTextToken.get_instruction_lines(lines[i].tokens, lines[i].count) + tokens = function.InstructionTextToken._from_core_struct(lines[i].tokens, lines[i].count) yield function.DisassemblyTextLine(tokens, addr, il_instr) finally: core.BNFreeDisassemblyTextLines(lines, count.value) @property def graph(self): - """ """ - return self._graph + return self._graph @graph.setter def graph(self, value): @@ -166,8 +169,8 @@ class FlowGraphNode(object): block = lowlevelil.LowLevelILBasicBlock(view, block, lowlevelil.LowLevelILFunction(func.arch, core.BNGetBasicBlockLowLevelILFunction(block), func)) elif core.BNIsMediumLevelILBasicBlock(block): - block = mediumlevelil.MediumLevelILBasicBlock(view, block, - mediumlevelil.MediumLevelILFunction(func.arch, core.BNGetBasicBlockMediumLevelILFunction(block), func)) + mlil_func = mediumlevelil.MediumLevelILFunction(func.arch, core.BNGetBasicBlockMediumLevelILFunction(block), func) + block = mediumlevelil.MediumLevelILBasicBlock(block, mlil_func, view) else: block = basicblock.BasicBlock(block, view) return block @@ -204,16 +207,17 @@ class FlowGraphNode(object): """Flow graph block list of text lines""" count = ctypes.c_ulonglong() lines = core.BNGetFlowGraphNodeLines(self.handle, count) + assert lines is not None, "core.BNGetFlowGraphNodeLines returned None" block = self.basic_block result = [] for i in range(0, count.value): addr = lines[i].addr if (lines[i].instrIndex != 0xffffffffffffffff) and (block is not None) and hasattr(block, 'il_function'): - il_instr = block.il_function[lines[i].instrIndex] + il_instr = block.__dict__['il_function'][lines[i].instrIndex] else: il_instr = None color = highlight.HighlightColor._from_core_struct(lines[i].highlight) - tokens = function.InstructionTextToken.get_instruction_lines(lines[i].tokens, lines[i].count) + tokens = function.InstructionTextToken._from_core_struct(lines[i].tokens, lines[i].count) result.append(function.DisassemblyTextLine(tokens, addr, il_instr, color)) core.BNFreeDisassemblyTextLines(lines, count.value) return result @@ -247,7 +251,7 @@ class FlowGraphNode(object): color = highlight.HighlightColor(color) line_buf[i].highlight = color._get_core_struct() line_buf[i].count = len(line.tokens) - line_buf[i].tokens = function.InstructionTextToken.get_instruction_lines(line.tokens) + line_buf[i].tokens = function.InstructionTextToken._get_core_struct(line.tokens) core.BNSetFlowGraphNodeLines(self.handle, line_buf, len(lines)) @property @@ -255,6 +259,7 @@ class FlowGraphNode(object): """Flow graph block list of outgoing edges (read-only)""" count = ctypes.c_ulonglong() edges = core.BNGetFlowGraphNodeOutgoingEdges(self.handle, count) + assert edges is not None, "core.BNGetFlowGraphNodeOutgoingEdges returned None" result = [] for i in range(0, count.value): branch_type = BranchType(edges[i].type) @@ -273,6 +278,7 @@ class FlowGraphNode(object): """Flow graph block list of incoming edges (read-only)""" count = ctypes.c_ulonglong() edges = core.BNGetFlowGraphNodeIncomingEdges(self.handle, count) + assert edges is not None, "core.BNGetFlowGraphNodeIncomingEdges returned None" result = [] for i in range(0, count.value): branch_type = BranchType(edges[i].type) @@ -419,6 +425,8 @@ class FlowGraph(object): def __eq__(self, other): if not isinstance(other, self.__class__): return NotImplemented + assert self.handle is not None + assert other.handle is not None return ctypes.addressof(self.handle.contents) == ctypes.addressof(other.handle.contents) def __ne__(self, other): @@ -427,6 +435,7 @@ class FlowGraph(object): return not (self == other) def __hash__(self): + assert self.handle is not None return hash(ctypes.addressof(self.handle.contents)) def __setattr__(self, name, value): @@ -438,9 +447,12 @@ class FlowGraph(object): def __iter__(self): count = ctypes.c_ulonglong() nodes = core.BNGetFlowGraphNodes(self.handle, count) + assert nodes is not None, "core.BNGetFlowGraphNodes returned None" try: for i in range(0, count.value): - yield FlowGraphNode(self, core.BNNewFlowGraphNodeReference(nodes[i])) + fg_node = core.BNNewFlowGraphNodeReference(nodes[i]) + assert fg_node is not None, "core.BNNewFlowGraphNodeReference returned None" + yield FlowGraphNode(self, fg_node) finally: core.BNFreeFlowGraphNodeList(nodes, count.value) @@ -473,7 +485,9 @@ class FlowGraph(object): graph = self.update() if graph is NotImplemented: return None - return ctypes.cast(core.BNNewFlowGraphReference(graph.handle), ctypes.c_void_p).value + flow_graph = core.BNNewFlowGraphReference(graph.handle) + assert flow_graph is not None, "core.BNNewFlowGraphReference returned None" + return ctypes.cast(flow_graph, ctypes.c_void_p).value except: log.log_error(traceback.format_exc()) return None @@ -557,9 +571,12 @@ class FlowGraph(object): """List of nodes in graph (read-only)""" count = ctypes.c_ulonglong() blocks = core.BNGetFlowGraphNodes(self.handle, count) + assert blocks is not None, "core.BNGetFlowGraphNodes returned None" result = [] for i in range(0, count.value): - result.append(FlowGraphNode(self, core.BNNewFlowGraphNodeReference(blocks[i]))) + fg_node = core.BNNewFlowGraphNodeReference(blocks[i]) + assert fg_node is not None, "core.BNNewFlowGraphNodeReference returned None" + result.append(FlowGraphNode(self, fg_node)) core.BNFreeFlowGraphNodeList(blocks, count.value) return result @@ -747,9 +764,12 @@ class FlowGraph(object): def get_nodes_in_region(self, left, top, right, bottom): count = ctypes.c_ulonglong() nodes = core.BNGetFlowGraphNodesInRegion(self.handle, left, top, right, bottom, count) + assert nodes is not None, "core.BNGetFlowGraphNodesInRegion returned None" result = [] for i in range(0, count.value): - result.append(FlowGraphNode(self, core.BNNewFlowGraphNodeReference(nodes[i]))) + fg_node = core.BNNewFlowGraphNodeReference(nodes[i]) + assert fg_node is not None, "core.BNNewFlowGraphNodeReference returned None" + result.append(FlowGraphNode(self, fg_node)) core.BNFreeFlowGraphNodeList(nodes, count.value) return result @@ -769,7 +789,7 @@ class FlowGraph(object): :param str title: Title to show in the new tab """ - binaryninja.interaction.show_graph_report(title, self) + interaction.show_graph_report(title, self) def update(self): """ diff --git a/python/function.py b/python/function.py index 9bc83b09..64aabdbb 100644 --- a/python/function.py +++ b/python/function.py @@ -19,1062 +19,115 @@ # 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) +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 -# 2-3 compatibility -from binaryninja import range +# 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'] -class LookupTableEntry(object): - def __init__(self, from_values, to_value): - self._from_values = from_values - self._to_value = to_value - - def __repr__(self): - return "[%s] -> %#x" % (', '.join(["%#x" % i for i in self.from_values]), self.to_value) - - 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) - - def __ne__(self, other): - if not isinstance(other, self.__class__): - return NotImplemented - return not (self == other) - - def __hash__(self): - return hash((self._from_values, self._to_value)) - - @property - def from_values(self): - """ """ - return self._from_values - - @from_values.setter - def from_values(self, value): - """ """ - self._from_values = value - - @property - def to_value(self): - """ """ - return self._to_value - - @to_value.setter - def to_value(self, value): - """ """ - self._to_value = value - - -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 __repr__(self): - if self._type == RegisterValueType.EntryValue: - return "<entry %s>" % self._reg - if self._type == RegisterValueType.ConstantValue: - return "<const %#x>" % self._value - if self._type == RegisterValueType.ConstantPointerValue: - return "<const ptr %#x>" % self._value - if self._type == RegisterValueType.StackFrameOffset: - return "<stack frame offset %#x>" % self._offset - if self._type == RegisterValueType.ReturnAddressValue: - return "<return address>" - if self._type == RegisterValueType.ImportedAddressValue: - return "<imported address from entry %#x>" % self._value - return "<undetermined>" - - def __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) - - 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 - - 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 - - @property - def is_constant(self): - """Boolean for whether the RegisterValue is known to be constant (read-only)""" - return self._is_constant - - @property - def type(self): - """:class:`~enums.RegisterValueType` (read-only)""" - return self._type - - @property - def arch(self): - """Architecture where it exists, None otherwise (read-only)""" - return self._arch - - @property - def reg(self): - """Register where the Architecture exists, None otherwise (read-only)""" - return self._reg - - @property - def value(self): - """Value where it exists, None otherwise (read-only)""" - return self._value - - @property - def offset(self): - """Offset where it exists, None otherwise (read-only)""" - return self._offset - - @property - def confidence(self): - """Confidence where it exists, None otherwise (read-only)""" - return self._confidence - - -class ValueRange(object): - def __init__(self, start, end, step): - self._start = start - self._end = end - self._step = step - - def __repr__(self): - if self.step == 1: - return "<range: %#x to %#x>" % (self.start, self.end) - return "<range: %#x to %#x, step %#x>" % (self.start, self.end, self.step) - - 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 - - def __contains__(self, other): - if not isinstance(other, numbers.Integral): - return NotImplemented - return other in range(self._start, self._end, self._step) - - @property - def start(self): - """ """ - return self._start - - @start.setter - def start(self, value): - """ """ - self._start = value - - @property - def end(self): - """ """ - return self._end - - @end.setter - def end(self, value): - """ """ - self._end = value - - @property - def step(self): - """ """ - return self._step - - @step.setter - def step(self, value): - """ """ - self._step = value - - -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 __repr__(self): - if self._type == RegisterValueType.EntryValue: - return "<entry %s>" % self.reg - if self._type == RegisterValueType.ConstantValue: - return "<const %#x>" % self.value - if self._type == RegisterValueType.ConstantPointerValue: - return "<const ptr %#x>" % self.value - if self._type == RegisterValueType.StackFrameOffset: - return "<stack frame offset %#x>" % self._offset - if self._type == RegisterValueType.SignedRangeValue: - return "<signed ranges: %s>" % repr(self.ranges) - if self._type == RegisterValueType.UnsignedRangeValue: - return "<unsigned ranges: %s>" % repr(self.ranges) - if self._type == RegisterValueType.LookupTableValue: - return "<table: %s>" % ', '.join([repr(i) for i in self.table]) - if self._type == RegisterValueType.InSetOfValues: - return "<in set(%s)>" % '[{}]'.format(', '.join(hex(i) for i in sorted(self.values))) - if self._type == RegisterValueType.NotInSetOfValues: - return "<not in set(%s)>" % '[{}]'.format(', '.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, 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 - - def __eq__(self, other): - if self.type in [RegisterValueType.ConstantValue, RegisterValueType.ConstantPointerValue] and isinstance(other, numbers.Integral): - 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 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_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 - - @type.setter - def type(self, value): - """ """ - self._type = value - - @property - def reg(self): - """ """ - return self._reg - - @reg.setter - def reg(self, value): - """ """ - self._reg = value - - @property - def value(self): - """ """ - return self._value - - @value.setter - def value(self, value): - """ """ - self._value = value - - @property - def offset(self): - """ """ - return self._offset - - @offset.setter - def offset(self, value): - """ """ - self._offset = value - - @property - def ranges(self): - """ """ - return self._ranges - - @ranges.setter - def ranges(self, value): - """ """ - self._ranges = value - - @property - def table(self): - """ """ - return self._table - - @table.setter - def table(self, value): - """ """ - self._table = value - - @property - def mapping(self): - """ """ - return self._mapping - - @mapping.setter - def mapping(self, value): - """ """ - self._mapping = value - - @property - def values(self): - """ """ - return self._values - - @values.setter - def values(self, value): - """ """ - self._values = value - - @property - def count(self): - """ """ - return self._count - - @count.setter - def count(self, value): - self._count = value - - @classmethod - def undetermined(self): - """ - Create a PossibleValueSet object of type UndeterminedValue. - - :return: PossibleValueSet object of type UndeterminedValue - :rtype: PossibleValueSet - """ - return PossibleValueSet() - - @classmethod - def constant(self, value): - """ - 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 - - @classmethod - def constant_ptr(self, value): - """ - 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 - - @classmethod - def stack_frame_offset(self, offset): - """ - Create a PossibleValueSet object for a stack frame offset. - - :param int value: Integer value of the offset - :rtype: PossibleValueSet - """ - result = PossibleValueSet() - result.type = RegisterValueType.StackFrameOffset - result.offset = offset - return result - - @classmethod - def signed_range_value(self, ranges): - """ - 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 - - @classmethod - def unsigned_range_value(self, ranges): - """ - 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 - - @classmethod - def in_set_of_values(self, values): - """ - 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 - - @classmethod - def not_in_set_of_values(self, values): - """ - 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 - - @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 - """ - result = PossibleValueSet() - result.type = RegisterValueType.LookupTableValue - result.table = lookup_table - result.mapping = mapping - return result +def _function_name_(): + return inspect.stack()[1][0].f_code.co_name class ArchAndAddr(object): - def __init__(self, arch = None, addr = 0): - self._arch = binaryninja.architecture.CoreArchitecture._from_cache(arch) + 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 "archandaddr <%s @ %#x>" % (self._arch.name, self._addr) @property - def arch(self): + def arch(self) -> 'architecture.Architecture': return self._arch @property - def addr(self): + def addr(self) -> int: return self._addr -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 __repr__(self): - if self._source_operand is None: - if self._referenced_offset != self._var.storage: - return "<ref to %s%+#x>" % (self._name, self._referenced_offset - self._var.storage) - return "<ref to %s>" % self._name - if self._referenced_offset != self._var.storage: - return "<operand %d ref to %s%+#x>" % (self._source_operand, self._name, self._var.storage) - return "<operand %d ref to %s>" % (self._source_operand, self._name) - - 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 __ne__(self, other): - if not isinstance(other, self.__class__): - return NotImplemented - return not (self == other) - - def __hash__(self): - return hash((self._source_operand, self._type, self._name, self._var, self._referenced_offset, self._size)) - - @property - def source_operand(self): - """ """ - return self._source_operand - - @source_operand.setter - def source_operand(self, value): - self._source_operand = value - - @property - def type(self): - """ """ - return self._type - - @type.setter - def type(self, value): - self._type = value - - @property - def name(self): - """ """ - return self._name - - @name.setter - def name(self, value): - self._name = value - - @property - def var(self): - """ """ - return self._var - - @var.setter - def var(self, value): - self._var = value - - @property - def referenced_offset(self): - """ """ - return self._referenced_offset - - @referenced_offset.setter - def referenced_offset(self, value): - self._referenced_offset = value - - @property - def size(self): - """ """ - return self._size - - @size.setter - def size(self, value): - self._size = value - -@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 __repr__(self): - if self.type is None: - return f"<var unknown-type {self.name}>" - return f"<var {self.type.get_string_before_name()} {self.name}{self.type.get_string_after_name()}>" - - def __str__(self): - return self.name - - def __eq__(self, other): - if not isinstance(other, self.__class__): - return NotImplemented - return (self.identifier, self.function) == (other.identifier, other.function) - - def __ne__(self, other): - if not isinstance(other, self.__class__): - return NotImplemented - return not (self == other) - - def __hash__(self): - return hash((self.identifier, self.function)) - - @property - def function(self): - """Function where the variable is defined""" - return self._function - - @function.setter - def function(self, value): - self._function = value - - @property - def source_type(self): - """:class:`~enums.VariableSourceType`""" - if not isinstance(self._source_type, VariableSourceType): - self._source_type = VariableSourceType(self._source_type) - - return self._source_type - - @source_type.setter - def source_type(self, value): - self._source_type = value - - @property - def index(self): - """ """ - return self._index - - @index.setter - def index(self, value): - self._index = value - - @property - def storage(self): - """Stack offset for StackVariableSourceType, register index for RegisterVariableSourceType""" - return self._storage - - @storage.setter - def storage(self, value): - self._storage = value - - @property - def identifier(self): - """ """ - if self._identifier is None: - self._identifier = core.BNToVariableIdentifier(self.to_BNVariable()) - return self._identifier - @identifier.setter - def identifier(self, value): - self._identifier = 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 - - @name.setter - def name(self, value): - self._name = 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 - - @type.setter - def type(self, value): - self._type = value - - def to_BNVariable(self): - v = core.BNVariable() - v.type = self.source_type - v.index = self._index - v.storage = self._storage - return v - - @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 - - @dead_store_elimination.setter - def dead_store_elimination(self, value): - core.BNSetFunctionVariableDeadStoreElimination(self._function.handle, self.to_BNVariable(), value) - - @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) - -class ConstantReference(object): - def __init__(self, val, size, ptr, intermediate): - self._value = val - self._size = size - self._pointer = ptr - self._intermediate = intermediate - - 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) - - @property - def value(self): - """ """ - return self._value - - @value.setter - def value(self, value): - self._value = value - - @property - def size(self): - """ """ - return self._size +class _FunctionAssociatedDataStore(associateddatastore._AssociatedDataStore): + _defaults = {} - @size.setter - def size(self, value): - self._size = value - @property - def pointer(self): - """ """ - return self._pointer +class DisassemblySettings(object): + def __init__(self, handle:core.BNDisassemblySettings=None): + if handle is None: + self.handle = core.BNCreateDisassemblySettings() + else: + self.handle = handle - @pointer.setter - def pointer(self, value): - self._pointer = value + def __del__(self): + core.BNFreeDisassemblySettings(self.handle) @property - def intermediate(self): - """ """ - return self._intermediate - - @intermediate.setter - def intermediate(self, value): - self._intermediate = value - - -class UserVariableValueInfo(object): - def __init__(self, var, def_site, value): - self.var = var - self.def_site = def_site - self.value = value - - def __repr__(self): - return "<user value for %s @ %s:%#x -> %s>" % (self.var, self.def_site.arch.name, self.def_site.addr, self.value) - - -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 - - def __repr__(self): - return "<branch %s:%#x -> %s:%#x>" % (self.source_arch.name, self.source_addr, self.dest_arch.name, self.dest_addr) - - -class ParameterVariables(object): - def __init__(self, var_list, confidence = types.max_confidence, 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) - - def __iter__(self): - for var in self._vars: - yield var - - def __getitem__(self, idx): - return self._vars[idx] - - def __setitem__(self, idx, value): - self._vars[idx] = value - if self._func is not None: - self._func.parameter_vars = self + def width(self) -> int: + return core.BNGetDisassemblyWidth(self.handle) - def with_confidence(self, confidence): - return ParameterVariables(list(self._vars), confidence, self._func) + @width.setter + def width(self, value:int) -> None: + core.BNSetDisassemblyWidth(self.handle, value) @property - def vars(self): - """ """ - return self._vars + def max_symbol_width(self) -> int: + return core.BNGetDisassemblyMaximumSymbolWidth(self.handle) - @vars.setter - def vars(self, value): - self._vars = value + @max_symbol_width.setter + def max_symbol_width(self, value:int) -> None: + core.BNSetDisassemblyMaximumSymbolWidth(self.handle, value) - @property - def confidence(self): - """ """ - return self._confidence + def is_option_set(self, option:DisassemblyOption) -> bool: + if isinstance(option, str): + option = DisassemblyOption[option] + return core.BNIsDisassemblySettingsOptionSet(self.handle, option) - @confidence.setter - def confidence(self, value): - self._confidence = value + def set_option(self, option:DisassemblyOption, state:bool=True) -> None: + if isinstance(option, str): + option = DisassemblyOption[option] + core.BNSetDisassemblySettingsOption(self.handle, option, state) class ILReferenceSource(object): - def __init__(self, func, arch, addr, il_type, expr_id): + 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 - def get_il_name(self, il_type): + @staticmethod + def get_il_name(il_type): if il_type == FunctionGraphType.NormalFunctionGraph: return 'disassembly' if il_type == FunctionGraphType.LowLevelILFunctionGraph: @@ -1099,125 +152,68 @@ class ILReferenceSource(object): def __repr__(self): if self._arch: return "<ref: %s@%#x, %s@%d>" %\ - (self._arch.name, self._address, self.get_il_name(self._il_type), self.expr_id) + (self._arch.name, self._address, self.get_il_name(self._il_type), self._expr_id) else: return "<ref: %#x, %s@%d>" %\ - (self._address, self.get_il_name(self._il_type), self.expr_id) + (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) + 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 __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 - - 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 - - def __ge__(self, other): - if not isinstance(other, self.__class__): - return NotImplemented - return (self == other) or (self > other) - - def __le__(self, other): - if not isinstance(other, self.__class__): - return NotImplemented - return (self == other) or (self < other) - def __hash__(self): return hash((self._function, self._arch, self._address, self._il_type, self._expr_id)) @property - def function(self): - """ """ + def function(self) -> Optional['Function']: return self._function @function.setter - def function(self, value): + def function(self, value:'Function') -> None: self._function = value @property - def arch(self): - """ """ + def arch(self) -> Optional['architecture.Architecture']: return self._arch @arch.setter - def arch(self, value): + def arch(self, value) -> None: self._arch = value @property - def address(self): - """ """ + def address(self) -> int: return self._address @address.setter - def address(self, value): + def address(self, value:int) -> None: self._address = value @property - def il_type(self): - """ """ + def il_type(self) -> FunctionGraphType: return self._il_type @il_type.setter - def il_type(self, value): + def il_type(self, value:FunctionGraphType) -> None: self._il_type = value @property - def expr_id(self): - """ """ + def expr_id(self) -> ExpressionIndex: return self._expr_id @expr_id.setter - def expr_id(self, value): + def expr_id(self, value:ExpressionIndex) -> None: self._expr_id = value class VariableReferenceSource(object): - def __init__(self, var, src): + def __init__(self, var:'variable.Variable', src:ILReferenceSource): self._var = var self._src = src @@ -1234,120 +230,32 @@ class VariableReferenceSource(object): 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 - - 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 - - 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 __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 - @property - def var(self): + def var(self) -> 'variable.Variable': return self._var + @var.setter - def var(self, value): + def var(self, value:'variable.Variable') -> None: self._var = value @property - def src(self): + def src(self) -> ILReferenceSource: return self._src @src.setter - def src(self, value): + def src(self, value:ILReferenceSource) -> None: self._src = value -class _FunctionAssociatedDataStore(associateddatastore._AssociatedDataStore): - _defaults = {} - - -class AddressRange(object): - def __init__(self, start, end): - self._start = start - self._end = end - - def __repr__(self): - return "<%#x-%#x>" % (self._start, self._end) - - def __len__(self): - return self._end - self.start - - def __eq__(self, other): - if not isinstance(other, self.__class__): - return NotImplemented - return (self._start, self._end) == (other._start, other._end) - - 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)) - - @property - def length(self): - return self._end - self._start - - @property - def start(self): - """ """ - return self._start - - @start.setter - def start(self, value): - self._start = value - - @property - def end(self): - """ """ - return self._end - - @end.setter - def end(self, value): - self._end = value - - class Function(object): _associated_data = {} - def __init__(self, view = None, handle = None): + def __init__(self, view:Optional['binaryview.BinaryView']=None, handle:Optional[core.BNFunction]=None): self._advanced_analysis_requests = 0 - if handle is None: - self.handle = None - raise NotImplementedError("creation of standalone 'Function' objects is not implemented") + 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 = binaryninja.binaryview.BinaryView(handle = core.BNGetFunctionData(self.handle)) + self._view = binaryview.BinaryView(handle = core.BNGetFunctionData(self.handle)) else: self._view = view self._arch = None @@ -1366,60 +274,65 @@ class Function(object): else: return "<func: %#x>" % self.start - def __eq__(self, other): + def __eq__(self, other:'Function') -> bool: if not isinstance(other, self.__class__): return NotImplemented 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 __lt__(self, other): + def __lt__(self, other:'Function') -> bool: if not isinstance(other, self.__class__): return NotImplemented return self.start < other.start - def __gt__(self, other): + def __gt__(self, other:'Function') -> bool: if not isinstance(other, self.__class__): return NotImplemented return self.start > other.start - def __le__(self, other): + def __le__(self, other:'Function') -> bool: if not isinstance(other, self.__class__): return NotImplemented return self.start <= other.start - def __ge__(self, other): + def __ge__(self, other:'Function') -> bool: if not isinstance(other, self.__class__): return NotImplemented return self.start >= other.start def __hash__(self): - return hash((self.start, self.arch.name, self.platform.name)) + return hash((self.start, self.arch, self.platform)) - def __getitem__(self, i): + 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 < -len(self.basic_blocks) or i >= count.value: + if i < -count.value 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 + 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) - def __iter__(self): + 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): - yield binaryninja.basicblock.BasicBlock(core.BNNewBasicBlockReference(blocks[i]), self._view) + block = core.BNNewBasicBlockReference(blocks[i]) + assert block is not None + yield basicblock.BasicBlock(block, self._view) finally: core.BNFreeBasicBlockList(blocks, count.value) @@ -1430,48 +343,49 @@ class Function(object): return result @classmethod - def _unregister(cls, func): + 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] - @classmethod - def set_default_session_data(cls, name, value): + @staticmethod + def set_default_session_data(name:str, value) -> None: _FunctionAssociatedDataStore.set_default(name, value) @property - def name(self): + def name(self) -> str: """Symbol name for the function""" return self.symbol.name @name.setter - def name(self, value): + 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) - else: + 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 view(self): + def view(self) -> 'binaryview.BinaryView': """Function view (read-only)""" return self._view @property - def arch(self): + def arch(self) -> Optional['architecture.Architecture']: """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) + assert arch is not None + self._arch = architecture.CoreArchitecture._from_cache(arch) return self._arch @property - def platform(self): + def platform(self) -> Optional['_platform.Platform']: """Function platform (read-only)""" if self._platform: return self._platform @@ -1479,108 +393,122 @@ class Function(object): plat = core.BNGetFunctionPlatform(self.handle) if plat is None: return None - self._platform = binaryninja.platform.Platform(handle = plat) + self._platform = _platform.Platform(handle = plat) return self._platform @property - def start(self): + def start(self) -> int: """Function start address (read-only)""" return core.BNGetFunctionStart(self.handle) @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.""" + def total_bytes(self) -> int: + """ + 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)) @property - def highest_address(self): - """The highest virtual address contained in a function.""" + def highest_address(self) -> int: + """The highest (largest) virtual address contained in a function.""" return core.BNGetFunctionHighestAddress(self.handle) @property - def lowest_address(self): - """The lowest virtual address contained in a function.""" + def lowest_address(self) -> int: + """The lowest (smallest) virtual address contained in a function.""" return core.BNGetFunctionLowestAddress(self.handle) @property - def address_ranges(self): + 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(AddressRange(range_list[i].start, range_list[i].end)) + result.append(variable.AddressRange(range_list[i].start, range_list[i].end)) core.BNFreeAddressRanges(range_list) return result @property - def symbol(self): + def symbol(self) -> 'types.Symbol': """Function symbol(read-only)""" sym = core.BNGetFunctionSymbol(self.handle) - if sym is None: - return None + assert sym is not None, "core.BNGetFunctionSymbol returned None" return types.Symbol(None, None, None, handle = sym) @property - def auto(self): - """Whether function was automatically discovered (read-only)""" + def auto(self) -> bool: + """ + 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. + """ return core.BNWasFunctionAutomaticallyDiscovered(self.handle) @property - def can_return(self): + def can_return(self) -> 'types.BoolWithConfidence': """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): + 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 = types.max_confidence + bc.confidence = core.max_confidence core.BNSetUserFunctionCanReturn(self.handle, bc) @property - def explicitly_defined_type(self): + def explicitly_defined_type(self) -> bool: """Whether function has explicitly defined types (read-only)""" return core.BNFunctionHasExplicitlyDefinedType(self.handle) @property - def needs_update(self): + 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): - """List of basic blocks (read-only)""" + 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) - 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 + 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): + 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 - def create_user_tag(self, type, data): + 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, data): + 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, data, user=True): + def create_tag(self, type:'binaryview.TagType', data:str="", user:bool=True) -> 'binaryview.Tag': """ ``create_tag`` creates a new Tag object but does not add it anywhere. Use :py:meth:`create_user_address_tag` or @@ -1600,245 +528,56 @@ class Function(object): return self.view.create_tag(type, data, user) @property - def address_tags(self): + def address_tags(self) -> Generator[Tuple['architecture.Architecture', int, 'binaryview.Tag'], None, None]: """ ``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)) + :rtype: Generator((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 + 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) - def get_address_tags_at(self, addr, arch=None): + def get_address_tags_at(self, addr:int, arch:Optional['architecture.Architecture']=None) -> Generator['binaryview.Tag', None, None]: """ - ``get_address_tags_at`` gets a list of all Tags in the function at a given address. + ``get_address_tags_at`` gets a generator 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) + :param Architecture arch: Architecture for the block in which the Tag is added (optional) + :return: A Generator of Tags """ 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) - result = [] - for i in range(0, count.value): - result.append(binaryninja.binaryview.Tag(core.BNNewTagReference(tags[i]))) - core.BNFreeTagList(tags, count.value) - return result - - def get_auto_address_tags_at(self, addr, arch=None): - """ - ``get_auto_address_tags_at`` gets a list of all auto-defined 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) - """ - if arch is None: - arch = self.arch - count = ctypes.c_ulonglong() - tags = core.BNGetAutoAddressTags(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) - return result - - def get_user_address_tags_at(self, addr, arch=None): - """ - ``get_user_address_tags_at`` gets a list of all user 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) - """ - if arch is None: - arch = self.arch - count = ctypes.c_ulonglong() - tags = core.BNGetUserAddressTags(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) - return result - - def get_address_tags_of_type(self, addr, tag_type, arch=None): - """ - ``get_address_tags_of_type`` gets a list of all Tags in the function at a given address with a given type. - - :param int addr: Address to get tags at - :param TagType tag_type: TagType object to match in searching - :param Architecture arch: Architecture for the block in which the Tags are located (optional) - :return: A list of data Tags - :rtype: list(Tag) - """ - if arch is None: - arch = self.arch - count = ctypes.c_ulonglong() - tags = core.BNGetAddressTagsOfType(self.handle, arch.handle, addr, tag_type.handle, count) - result = [] - for i in range(0, count.value): - result.append(binaryninja.binaryview.Tag(core.BNNewTagReference(tags[i]))) - core.BNFreeTagList(tags, count.value) - return result - - def get_auto_address_tags_of_type(self, addr, tag_type, arch=None): - """ - ``get_auto_address_tags_of_type`` gets a list of all auto-defined Tags in the function at a given address with a given type. - - :param int addr: Address to get tags at - :param TagType tag_type: TagType object to match in searching - :param Architecture arch: Architecture for the block in which the Tags are located (optional) - :return: A list of data Tags - :rtype: list(Tag) - """ - if arch is None: - arch = self.arch - count = ctypes.c_ulonglong() - tags = core.BNGetAutoAddressTagsOfType(self.handle, arch.handle, addr, tag_type.handle, count) - result = [] - for i in range(0, count.value): - result.append(binaryninja.binaryview.Tag(core.BNNewTagReference(tags[i]))) - core.BNFreeTagList(tags, count.value) - return result - - def get_user_address_tags_of_type(self, addr, tag_type, arch=None): - """ - ``get_user_address_tags_of_type`` gets a list of all user Tags in the function at a given address with a given type. - - :param int addr: Address to get tags at - :param TagType tag_type: TagType object to match in searching - :param Architecture arch: Architecture for the block in which the Tags are located (optional) - :return: A list of data Tags - :rtype: list(Tag) - """ - if arch is None: - arch = self.arch - count = ctypes.c_ulonglong() - tags = core.BNGetUserAddressTagsOfType(self.handle, arch.handle, addr, tag_type.handle, count) - result = [] - for i in range(0, count.value): - result.append(binaryninja.binaryview.Tag(core.BNNewTagReference(tags[i]))) - core.BNFreeTagList(tags, count.value) - return result - - def get_address_tags_in_range(self, address_range, arch=None): - """ - ``get_address_tags_in_range`` gets a list of all Tags in the function at a given address. - Range is inclusive at the start, exclusive at the end. - - :param AddressRange address_range: Address range from which to get tags - :param Architecture arch: Architecture for the block in which the Tag is located (optional) - :return: A list of (arch, address, Tag) tuples - :rtype: list((Architecture, int, Tag)) - """ - if arch is None: - arch = self.arch - count = ctypes.c_ulonglong() - refs = core.BNGetAddressTagsInRange(self.handle, arch.handle, address_range.start, address_range.end, count) - result = [] - for i in range(0, count.value): - tag = binaryninja.binaryview.Tag(core.BNNewTagReference(refs[i].tag)) - result.append((arch, refs[i].addr, tag)) - core.BNFreeTagReferences(refs, count.value) - return result - - def get_auto_address_tags_in_range(self, address_range, arch=None): - """ - ``get_auto_address_tags_in_range`` gets a list of all auto-defined Tags in the function at a given address. - Range is inclusive at the start, exclusive at the end. - - :param AddressRange address_range: Address range from which to get tags - :param Architecture arch: Architecture for the block in which the Tag is located (optional) - :return: A list of (arch, address, Tag) tuples - :rtype: list((Architecture, int, Tag)) - """ - if arch is None: - arch = self.arch - count = ctypes.c_ulonglong() - refs = core.BNGetAutoAddressTagsInRange(self.handle, arch.handle, address_range.start, address_range.end, count) - result = [] - for i in range(0, count.value): - tag = binaryninja.binaryview.Tag(core.BNNewTagReference(refs[i].tag)) - result.append((arch, refs[i].addr, tag)) - core.BNFreeTagReferences(refs, count.value) - return result + 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) - def get_user_address_tags_in_range(self, address_range, arch=None): - """ - ``get_user_address_tags_in_range`` gets a list of all user Tags in the function at a given address. - Range is inclusive at the start, exclusive at the end. - :param AddressRange address_range: Address range from which to get tags - :param Architecture arch: Architecture for the block in which the Tag is located (optional) - :return: A list of (arch, address, Tag) tuples - :rtype: list((Architecture, int, Tag)) - """ - if arch is None: - arch = self.arch - count = ctypes.c_ulonglong() - refs = core.BNGetUserAddressTagsInRange(self.handle, arch.handle, address_range.start, address_range.end, count) - result = [] - for i in range(0, count.value): - tag = binaryninja.binaryview.Tag(core.BNNewTagReference(refs[i].tag)) - 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 add_user_address_tag(self, addr:int, tag:'binaryview.Tag', arch:Optional['architecture.Architecture']=None) -> None: """ ``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 <binaryninja.function.Function.create_user_address_tag>` + :meth:`create_user_address_tag <function.Function.create_user_address_tag>` :param int addr: Address at which to add the tag :param Tag tag: Tag object to be added @@ -1846,15 +585,18 @@ class Function(object): :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 create_user_address_tag(self, addr, type, data, unique=False, arch=None): + 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 <binaryninja.binaryview.BinaryView.create_user_data_tag>`. + inside of a function, use :py:meth:`create_user_data_tag <binaryview.BinaryView.create_user_data_tag>`. :param int addr: Address at which to add the tag :param TagType type: Tag Type for the Tag that is created @@ -1865,6 +607,8 @@ class Function(object): :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) @@ -1876,39 +620,27 @@ class Function(object): core.BNAddUserAddressTag(self.handle, arch.handle, addr, tag.handle) return tag - def remove_user_address_tag(self, addr, tag, arch=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. :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) + :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 remove_user_address_tags_of_type(self, addr, tag_type, arch=None): - """ - ``remove_user_address_tags_of_type`` removes all tags at the given address of the given type. - Since this removes user tags, it will be added to the current undo buffer. - - :param int addr: Address at which to remove the tag - :param Tag tag_type: TagType object to match for removing - :param Architecture arch: Architecture for the block in which the Tags is located (optional) - :rtype: None - """ - if arch is None: - arch = self.arch - core.BNRemoveUserAddressTagsOfType(self.handle, arch.handle, addr, tag_type.handle) - - def add_auto_address_tag(self, addr, tag, arch=None): + 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 <binaryninja.function.Function.create_auto_address_tag>` + :meth:`create_auto_address_tag <function.Function.create_auto_address_tag>` :param int addr: Address at which to add the tag :param Tag tag: Tag object to be added @@ -1916,10 +648,12 @@ class Function(object): :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 create_auto_address_tag(self, addr, type, data, unique=False, arch=None): + 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. @@ -1932,6 +666,8 @@ class Function(object): :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) @@ -1943,138 +679,37 @@ class Function(object): core.BNAddAutoAddressTag(self.handle, arch.handle, addr, tag.handle) return tag - def remove_auto_address_tag(self, addr, tag, arch=None): - """ - ``remove_auto_address_tag`` removes a Tag object at a given address. - - :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 located (optional) - :rtype: None - """ - if arch is None: - arch = self.arch - core.BNRemoveAutoAddressTag(self.handle, arch.handle, addr, tag.handle) - - def remove_auto_address_tags_of_type(self, addr, tag_type, arch=None): - """ - ``remove_auto_address_tags_of_type`` removes all tags at the given address of the given type. - - :param int addr: Address at which to remove the tags - :param Tag tag_type: TagType object to match for removing - :param Architecture arch: Architecture for the block in which the Tags is located (optional) - :rtype: None - """ - if arch is None: - arch = self.arch - core.BNRemoveAutoAddressTagsOfType(self.handle, arch.handle, addr, tag_type.handle) - @property - def function_tags(self): + def function_tags(self) -> Generator['binaryview.Tag', None, None]: """ ``function_tags`` gets a list of all function Tags for the function. - :rtype: list(Tag) + :rtype: Generator(Tag) """ count = ctypes.c_ulonglong() tags = core.BNGetFunctionTags(self.handle, count) - result = [] - for i in range(0, count.value): - result.append(binaryninja.binaryview.Tag(core.BNNewTagReference(tags[i]))) - core.BNFreeTagList(tags, count.value) - return result - - @property - def auto_function_tags(self): - """ - ``auto_function_tags`` gets a list of all auto-defined function Tags for the function. - - :rtype: list(Tag) - """ - count = ctypes.c_ulonglong() - tags = core.BNGetAutoFunctionTags(self.handle, count) - result = [] - for i in range(0, count.value): - result.append(binaryninja.binaryview.Tag(core.BNNewTagReference(tags[i]))) - core.BNFreeTagList(tags, count.value) - return result - - @property - def user_function_tags(self): - """ - ``user_function_tags`` gets a list of all user function Tags for the function. - - :rtype: list(Tag) - """ - count = ctypes.c_ulonglong() - tags = core.BNGetUserFunctionTags(self.handle, count) - result = [] - for i in range(0, count.value): - result.append(binaryninja.binaryview.Tag(core.BNNewTagReference(tags[i]))) - 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. - - :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) - result = [] - for i in range(0, count.value): - result.append(binaryninja.binaryview.Tag(core.BNNewTagReference(tags[i]))) - core.BNFreeTagList(tags, count.value) - return result - - def get_auto_function_tags_of_type(self, tag_type): - """ - ``get_auto_function_tags_of_type`` gets a list of all auto-defined 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.BNGetAutoFunctionTagsOfType(self.handle, tag_type.handle, count) - result = [] - for i in range(0, count.value): - result.append(binaryninja.binaryview.Tag(core.BNNewTagReference(tags[i]))) - core.BNFreeTagList(tags, count.value) - return result - - def get_user_function_tags_of_type(self, tag_type): - """ - ``get_user_function_tags_of_type`` gets a list of all user 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.BNGetUserFunctionTagsOfType(self.handle, tag_type.handle, count) - result = [] - for i in range(0, count.value): - result.append(binaryninja.binaryview.Tag(core.BNNewTagReference(tags[i]))) - core.BNFreeTagList(tags, count.value) - return result + 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) - def add_user_function_tag(self, tag): + 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 <binaryninja.function.Function.create_user_function_tag>` + :meth:`create_user_function_tag <function.Function.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): + 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. @@ -2094,40 +729,29 @@ class Function(object): core.BNAddUserFunctionTag(self.handle, tag.handle) return tag - def remove_user_function_tag(self, tag): + 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. - :param Tag tag: Tag object to be removed + :param Tag tag: Tag object to be added :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 - Since this removes user tags, it will be added to the current undo buffer. - - :param TagType tag_type: TagType object to match for removing - :rtype: None + def add_auto_function_tag(self, tag:'binaryview.Tag') -> None: """ - 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. + ``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 <binaryninja.function.Function.create_auto_function_tag>` - + :meth:`create_auto_function_tag <function.Function.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): + def create_auto_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. + ``create_auto_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 @@ -2144,145 +768,132 @@ class Function(object): core.BNAddAutoFunctionTag(self.handle, tag.handle) return tag - def remove_auto_function_tag(self, tag): - """ - ``remove_user_function_tag`` removes a Tag object as a function tag. - - :param Tag tag: Tag object to be removed - :rtype: None - """ - core.BNRemoveAutoFunctionTag(self.handle, tag.handle) - - def remove_auto_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 - - :param TagType tag_type: TagType object to match for removing - :rtype: None - """ - 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) + 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) @property - def llil(self): + def llil(self) -> 'lowlevelil.LowLevelILFunction': """returns LowLevelILFunction used to represent Function low level IL (read-only)""" - return binaryninja.lowlevelil.LowLevelILFunction(self.arch, core.BNGetFunctionLowLevelIL(self.handle), self) + return lowlevelil.LowLevelILFunction(self.arch, core.BNGetFunctionLowLevelIL(self.handle), self) @property - def llil_if_available(self): + 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 binaryninja.lowlevelil.LowLevelILFunction(self.arch, result, self) + return lowlevelil.LowLevelILFunction(self.arch, result, self) @property - def lifted_il(self): + def lifted_il(self) -> 'lowlevelil.LowLevelILFunction': """returns LowLevelILFunction used to represent lifted IL (read-only)""" - return binaryninja.lowlevelil.LowLevelILFunction(self.arch, core.BNGetFunctionLiftedIL(self.handle), self) + return lowlevelil.LowLevelILFunction(self.arch, core.BNGetFunctionLiftedIL(self.handle), self) @property - def lifted_il_if_available(self): + 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 binaryninja.lowlevelil.LowLevelILFunction(self.arch, result, self) + return 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) + def medium_level_il(self) -> 'mediumlevelil.MediumLevelILFunction': + """Function medium level IL (read-only)""" + return mediumlevelil.MediumLevelILFunction(self.arch, core.BNGetFunctionMediumLevelIL(self.handle), self) @property - def mlil(self): + def mlil(self) -> 'mediumlevelil.MediumLevelILFunction': """Function medium level IL (read-only)""" - return binaryninja.mediumlevelil.MediumLevelILFunction(self.arch, core.BNGetFunctionMediumLevelIL(self.handle), self) + return mediumlevelil.MediumLevelILFunction(self.arch, core.BNGetFunctionMediumLevelIL(self.handle), self) @property - def mlil_if_available(self): + 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 binaryninja.mediumlevelil.MediumLevelILFunction(self.arch, result, self) + return 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) + 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 hlil(self): + def hlil(self) -> 'highlevelil.HighLevelILFunction': """Function high level IL (read-only)""" - return binaryninja.highlevelil.HighLevelILFunction(self.arch, core.BNGetFunctionHighLevelIL(self.handle), self) + return highlevelil.HighLevelILFunction(self.arch, core.BNGetFunctionHighLevelIL(self.handle), self) @property - def hlil_if_available(self): + 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 binaryninja.highlevelil.HighLevelILFunction(self.arch, result, self) + return 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""" + 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) @function_type.setter - def function_type(self, value): + 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 stack_layout(self): + 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) - 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) - return result + 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) @property - def vars(self): - """List of function variables (read-only)""" + def vars(self) -> Generator['variable.Variable', None, None]: + """Generator of function variables (read-only)""" count = ctypes.c_ulonglong() v = core.BNGetFunctionVariables(self.handle, count) - 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) - return result + 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 indirect_branches(self): + 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(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)) + 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 @property - def unresolved_indirect_branches(self): + 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]) @@ -2290,12 +901,12 @@ class Function(object): return result @property - def has_unresolved_indirect_branches(self): + def has_unresolved_indirect_branches(self) -> bool: """Has unresolved indirect branches (read-only)""" return core.BNHasUnresolvedIndirectBranches(self.handle) @property - def session_data(self): + 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: @@ -2306,9 +917,10 @@ class Function(object): return Function._associated_data[handle.value] @property - def analysis_performance_info(self): + 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 @@ -2316,12 +928,12 @@ class Function(object): return result @property - def type_tokens(self): + def type_tokens(self) -> List['InstructionTextToken']: """Text tokens for this function's prototype""" return self.get_type_tokens()[0].tokens @property - def return_type(self): + def return_type(self) -> Optional['types.Type']: """Return type of the function""" result = core.BNGetFunctionReturnType(self.handle) if not result.type: @@ -2329,7 +941,7 @@ class Function(object): return types.Type(result.type, platform = self.platform, confidence = result.confidence) @return_type.setter - def return_type(self, value): + def return_type(self, value:'types.Type') -> None: type_conf = core.BNTypeWithConfidence() if value is None: type_conf.type = None @@ -2340,9 +952,12 @@ class Function(object): core.BNSetUserFunctionReturnType(self.handle, type_conf) @property - def return_regs(self): + 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])) @@ -2351,28 +966,30 @@ class Function(object): return regs @return_regs.setter - def return_regs(self, value): + 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 hasattr(value, 'confidence'): + if isinstance(value, types.RegisterSet): regs.confidence = value.confidence else: - regs.confidence = types.max_confidence + regs.confidence = core.max_confidence core.BNSetUserFunctionReturnRegisters(self.handle, regs) @property - def calling_convention(self): + 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 binaryninja.callingconvention.CallingConvention(None, handle = result.convention, confidence = result.confidence) + return callingconvention.CallingConvention(None, handle = result.convention, confidence = result.confidence) @calling_convention.setter - def calling_convention(self, value): + def calling_convention(self, value:'callingconvention.CallingConvention') -> None: conv_conf = core.BNCallingConventionWithConfidence() if value is None: conv_conf.convention = None @@ -2383,18 +1000,18 @@ class Function(object): core.BNSetUserFunctionCallingConvention(self.handle, conv_conf) @property - def parameter_vars(self): + 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(self, result.vars[i].type, result.vars[i].index, result.vars[i].storage)) + 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 ParameterVariables(var_list, confidence, self) + return variable.ParameterVariables(var_list, confidence, self) @parameter_vars.setter - def parameter_vars(self, value): + def parameter_vars(self, value:Optional[Union['variable.ParameterVariables', List['variable.Variable']]]) -> None: # type: ignore if value is None: var_list = [] else: @@ -2408,49 +1025,52 @@ 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, types.RegisterSet): var_conf.confidence = value.confidence else: - var_conf.confidence = types.max_confidence + var_conf.confidence = core.max_confidence core.BNSetUserFunctionParameterVariables(self.handle, var_conf) @property - def has_variable_arguments(self): + 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): + def has_variable_arguments(self, value:Union[bool, 'types.BoolWithConfidence']) -> None: # type: ignore 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 + bc.confidence = core.max_confidence core.BNSetUserFunctionHasVariableArguments(self.handle, bc) @property - def stack_adjustment(self): + 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) @stack_adjustment.setter - def stack_adjustment(self, value): + 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 = types.max_confidence + oc.confidence = core.max_confidence core.BNSetUserFunctionStackAdjustment(self.handle, oc) @property - def reg_stack_adjustments(self): + 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) @@ -2461,24 +1081,31 @@ class Function(object): return result @reg_stack_adjustments.setter - def reg_stack_adjustments(self, value): + 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) - if isinstance(value[reg_stack], types.RegisterStackAdjustmentWithConfidence): - adjust[i].adjustment = value[reg_stack].value - adjust[i].confidence = value[reg_stack].confidence + entry = value[reg_stack] + if isinstance(entry, types.RegisterStackAdjustmentWithConfidence): + adjust[i].adjustment = entry.value + adjust[i].confidence = entry.confidence else: - adjust[i].adjustment = value[reg_stack] - adjust[i].confidence = types.max_confidence + adjust[i].adjustment = int(entry) + adjust[i].confidence = core.max_confidence i += 1 core.BNSetUserFunctionRegisterStackAdjustments(self.handle, adjust, len(value)) @property - def clobbered_regs(self): + 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])) @@ -2487,48 +1114,51 @@ class Function(object): return regs @clobbered_regs.setter - def clobbered_regs(self, value): + def clobbered_regs(self, value:Union['types.RegisterSet', List['architecture.RegisterType']]) -> None: # type: ignore regs = core.BNRegisterSetWithConfidence() + + 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 hasattr(value, 'confidence'): + if isinstance(value, types.RegisterSet): regs.confidence = value.confidence else: - regs.confidence = types.max_confidence + regs.confidence = core.max_confidence core.BNSetUserFunctionClobberedRegisters(self.handle, regs) @property - def global_pointer_value(self): + 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 RegisterValue(self.arch, result.value, confidence = result.confidence) + return variable.RegisterValue(self.arch, result.value, confidence = result.confidence) @property - def comment(self): + def comment(self) -> str: """Gets the comment for the current function""" return core.BNGetFunctionComment(self.handle) @comment.setter - def comment(self, comment): + def comment(self, comment:str) -> None: """Sets a comment for the current function""" return core.BNSetFunctionComment(self.handle, comment) @property - def llil_basic_blocks(self): + 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 @property - def mlil_basic_blocks(self): + 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 instructions(self): + 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 @@ -2537,65 +1167,65 @@ class Function(object): start += i[1] @property - def llil_instructions(self): + 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): + 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 @property - def too_large(self): + def too_large(self) -> bool: """Whether the function is too large to automatically perform analysis (read-only)""" return core.BNIsFunctionTooLarge(self.handle) @property - def analysis_skipped(self): + def analysis_skipped(self) -> bool: """Whether automatic analysis was skipped for this function, set to true to disable analysis.""" return core.BNIsFunctionAnalysisSkipped(self.handle) @analysis_skipped.setter - def analysis_skipped(self, skip): + def analysis_skipped(self, skip:bool) -> None: if skip: core.BNSetFunctionAnalysisSkipOverride(self.handle, FunctionAnalysisSkipOverride.AlwaysSkipFunctionAnalysis) else: core.BNSetFunctionAnalysisSkipOverride(self.handle, FunctionAnalysisSkipOverride.NeverSkipFunctionAnalysis) @property - def analysis_skip_reason(self): + def analysis_skip_reason(self) -> AnalysisSkipReason: """Function analysis skip reason""" return AnalysisSkipReason(core.BNGetAnalysisSkipReason(self.handle)) @property - def analysis_skip_override(self): + def analysis_skip_override(self) -> FunctionAnalysisSkipOverride: """Override for skipping of automatic analysis""" return FunctionAnalysisSkipOverride(core.BNGetFunctionAnalysisSkipOverride(self.handle)) @analysis_skip_override.setter - def analysis_skip_override(self, override): + def analysis_skip_override(self, override:FunctionAnalysisSkipOverride) -> None: core.BNSetFunctionAnalysisSkipOverride(self.handle, override) @property - def unresolved_stack_adjustment_graph(self): + 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 binaryninja.flowgraph.CoreFlowGraph(graph) + return flowgraph.CoreFlowGraph(graph) - def mark_recent_use(self): + def mark_recent_use(self) -> None: core.BNMarkFunctionAsRecentlyUsed(self.handle) - def get_comment_at(self, addr): + def get_comment_at(self, addr:int) -> str: return core.BNGetCommentForAddress(self.handle, addr) - def set_comment(self, addr, comment): + 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) - def set_comment_at(self, addr, comment): + def set_comment_at(self, addr:int, comment:str) -> None: """ ``set_comment_at`` sets a comment for the current function at the address specified @@ -2609,7 +1239,7 @@ class Function(object): """ core.BNSetCommentForAddress(self.handle, addr, comment) - def add_user_code_ref(self, from_addr, to_addr, from_arch=None): + 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 @@ -2618,7 +1248,7 @@ class Function(object): :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 + :param Architecture arch: (optional) architecture of the source instruction :rtype: None :Example: @@ -2626,12 +1256,14 @@ class Function(object): """ - if from_arch is None: - from_arch = 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 - core.BNAddUserCodeReference(self.handle, from_arch.handle, from_addr, to_addr) + core.BNAddUserCodeReference(self.handle, arch.handle, from_addr, to_addr) - def remove_user_code_ref(self, from_addr, to_addr, from_arch=None): + 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 @@ -2648,11 +1280,14 @@ class Function(object): """ 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 core.BNRemoveUserCodeReference(self.handle, from_arch.handle, from_addr, to_addr) - def add_user_type_ref(self, from_addr, name, from_arch=None): + 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 @@ -2670,12 +1305,14 @@ class Function(object): """ 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) + _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): + 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 @@ -2692,12 +1329,15 @@ class Function(object): """ 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.BNRemoveUserTypeReference(self.handle, from_arch.handle, from_addr, name) + _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): + 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 @@ -2717,13 +1357,16 @@ class Function(object): """ 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.BNAddUserTypeFieldReference(self.handle, from_arch.handle, from_addr, name,\ + _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, name, offset, from_arch = None, size = 0): + 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 @@ -2742,13 +1385,15 @@ class Function(object): """ 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.BNRemoveUserTypeFieldReference(self.handle, from_arch.handle, from_addr, name,\ + _name = types.QualifiedName(name)._get_core_struct() + core.BNRemoveUserTypeFieldReference(self.handle, from_arch.handle, from_addr, _name,\ offset, size) - def get_low_level_il_at(self, addr, arch=None): + 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 @@ -2757,11 +1402,13 @@ class Function(object): :rtype: LowLevelILInstruction :Example: - >>> func = bv.functions[0] + >>> func = next(bv.functions) >>> func.get_low_level_il_at(func.start) <il: push(rbp)> """ 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 idx = core.BNGetLowLevelILForInstruction(self.handle, arch.handle, addr) @@ -2771,7 +1418,7 @@ class Function(object): return self.llil[idx] - def get_llil_at(self, addr, arch=None): + 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 @@ -2780,11 +1427,13 @@ class Function(object): :rtype: LowLevelILInstruction :Example: - >>> func = bv.functions[0] + >>> func = next(bv.functions) >>> func.get_llil_at(func.start) <il: push(rbp)> """ 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 idx = core.BNGetLowLevelILForInstruction(self.handle, arch.handle, addr) @@ -2794,7 +1443,7 @@ class Function(object): return self.llil[idx] - def get_llils_at(self, addr, arch=None): + 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 @@ -2803,72 +1452,328 @@ class Function(object): :rtype: list(LowLevelILInstruction) :Example: - >>> func = bv.functions[0] + >>> func = next(bv.functions) >>> func.get_llils_at(func.start) [<il: push(rbp)>] """ 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 - def get_low_level_il_exits_at(self, addr, arch=None): + 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 - def get_reg_value_at(self, addr, reg, arch=None): + 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 :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 + :rtype: variable.RegisterValue :Example: >>> func.get_reg_value_at(0x400dbe, 'rdi') <const 0x2> """ 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 = RegisterValue(arch, value) + result = variable.RegisterValue(arch, value) return result - def get_reg_value_after(self, addr, reg, arch=None): + @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) + assert tags is not None, "core.BNGetAutoAddressTagReferences returned None" + 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 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) + 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 + + def get_reg_value_after(self, addr:int, reg:'architecture.RegisterType', + arch:Optional['architecture.Architecture']=None) -> 'variable.RegisterValue': """ ``get_reg_value_after`` gets the value instruction address corresponding to the given virtual address :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 + :rtype: variable.RegisterValue :Example: >>> func.get_reg_value_after(0x400dbe, 'rdi') <undetermined> """ 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 reg = arch.get_reg_index(reg) value = core.BNGetRegisterValueAfterInstruction(self.handle, arch.handle, addr, reg) - result = RegisterValue(arch, value) + result = variable.RegisterValue(arch, value) + return result + + def get_auto_address_tags_at(self, addr, arch=None): + """ + ``get_auto_address_tags_at`` gets a list of all auto-defined 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) + """ + 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): + 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_user_address_tags_at(self, addr, arch=None): + """ + ``get_user_address_tags_at`` gets a list of all user 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) + """ + 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): + 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_stack_contents_at(self, addr, offset, size, arch=None): + def get_address_tags_of_type(self, addr, tag_type, arch=None): + """ + ``get_address_tags_of_type`` gets a list of all Tags in the function at a given address with a given type. + + :param int addr: Address to get tags at + :param TagType tag_type: TagType object to match in searching + :param Architecture arch: Architecture for the block in which the Tags are located (optional) + :return: A list of data Tags + :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): + 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_auto_address_tags_of_type(self, addr, tag_type, arch=None): + """ + ``get_auto_address_tags_of_type`` gets a list of all auto-defined Tags in the function at a given address with a given type. + + :param int addr: Address to get tags at + :param TagType tag_type: TagType object to match in searching + :param Architecture arch: Architecture for the block in which the Tags are located (optional) + :return: A list of data Tags + :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): + 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_user_address_tags_of_type(self, addr, tag_type, arch=None): + """ + ``get_user_address_tags_of_type`` gets a list of all user Tags in the function at a given address with a given type. + + :param int addr: Address to get tags at + :param TagType tag_type: TagType object to match in searching + :param Architecture arch: Architecture for the block in which the Tags are located (optional) + :return: A list of data Tags + :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): + 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_address_tags_in_range(self, address_range, arch=None): + """ + ``get_address_tags_in_range`` gets a list of all Tags in the function at a given address. + Range is inclusive at the start, exclusive at the end. + + :param AddressRange address_range: Address range from which to get tags + :param Architecture arch: Architecture for the block in which the Tag is located (optional) + :return: A list of (arch, address, Tag) tuples + :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_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 get_auto_address_tags_in_range(self, address_range, arch=None): + """ + ``get_auto_address_tags_in_range`` gets a list of all auto-defined Tags in the function at a given address. + Range is inclusive at the start, exclusive at the end. + + :param AddressRange address_range: Address range from which to get tags + :param Architecture arch: Architecture for the block in which the Tag is located (optional) + :return: A list of (arch, address, Tag) tuples + :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_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 get_user_address_tags_in_range(self, address_range, arch=None): + """ + ``get_user_address_tags_in_range`` gets a list of all user Tags in the function at a given address. + Range is inclusive at the start, exclusive at the end. + + :param AddressRange address_range: Address range from which to get tags + :param Architecture arch: Architecture for the block in which the Tag is located (optional) + :return: A list of (arch, address, Tag) tuples + :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_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 get_stack_contents_at(self, addr:int, offset:int, size:int, + arch:Optional['architecture.Architecture']=None) -> 'variable.RegisterValue': """ ``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. @@ -2877,7 +1782,7 @@ class Function(object): :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 + :rtype: variable.RegisterValue .. 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 @@ -2888,83 +1793,179 @@ class Function(object): <range: 0x8 to 0xffffffff> """ 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.BNGetStackContentsAtInstruction(self.handle, arch.handle, addr, offset, size) - result = RegisterValue(arch, value) + result = variable.RegisterValue(arch, value) return result - def get_stack_contents_after(self, addr, offset, size, arch=None): + 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 = RegisterValue(arch, value) + result = variable.RegisterValue(arch, value) return result - def get_parameter_at(self, addr, func_type, i, arch=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 + + _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 = RegisterValue(arch, value) + _func_type = func_type.handle + value = core.BNGetParameterValueAtInstruction(self.handle, arch.handle, addr, _func_type, i) + result = variable.RegisterValue(arch, value) return result - def get_parameter_at_low_level_il_instruction(self, instr, func_type, i): + def remove_user_address_tags_of_type(self, addr, tag_type, arch=None): + """ + ``remove_user_address_tags_of_type`` removes all tags at the given address of the given type. + Since this removes user tags, it will be added to the current undo buffer. + + :param int addr: Address at which to remove the tag + :param Tag tag_type: TagType object to match for removing + :param Architecture arch: Architecture for the block in which the Tags is located (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.BNRemoveUserAddressTagsOfType(self.handle, arch.handle, addr, tag_type.handle) + + 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 = RegisterValue(self.arch, value) + _func_type = func_type.handle + value = core.BNGetParameterValueAtLowLevelILInstruction(self.handle, instr, _func_type, i) + result = variable.RegisterValue(self.arch, value) return result - def get_regs_read_by(self, addr, arch=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 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 - def get_regs_written_by(self, addr, arch=None): + 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 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 get_stack_vars_referenced_by(self, addr, 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 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 remove_auto_address_tag with no architecture specified") + arch = self.arch + core.BNRemoveAutoAddressTag(self.handle, arch.handle, addr, tag.handle) + + def remove_auto_address_tags_of_type(self, addr, tag_type, arch=None): + """ + ``remove_auto_address_tags_of_type`` removes all tags at the given address of the given type. + + :param int addr: Address at which to remove the tags + :param Tag tag_type: TagType object to match for removing + :param Architecture arch: Architecture for the block in which the Tags is located (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.BNRemoveAutoAddressTagsOfType(self.handle, arch.handle, addr, tag_type.handle) + + 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() 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): 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), + 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 - def get_constants_referenced_by(self, addr, arch=None): - if arch is None: - arch = self.arch + @property + def auto_function_tags(self): + """ + ``auto_function_tags`` gets a list of all auto-defined function Tags for the function. + + :rtype: list(Tag) + """ count = ctypes.c_ulonglong() - refs = core.BNGetConstantsReferencedByInstruction(self.handle, arch.handle, addr, count) + 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(ConstantReference(refs[i].value, refs[i].size, refs[i].pointer, refs[i].intermediate)) - core.BNFreeConstantReferenceList(refs) + 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_lifted_il_at(self, addr, arch=None): + @property + def user_function_tags(self): + """ + ``user_function_tags`` gets a list of all user function Tags for the function. + + :rtype: list(Tag) + """ + 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): + 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_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 idx = core.BNGetLiftedILForInstruction(self.handle, arch.handle, addr) @@ -2974,7 +1975,8 @@ class Function(object): return self.lifted_il[idx] - def get_lifted_ils_at(self, addr, arch=None): + 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 @@ -2982,74 +1984,199 @@ class Function(object): :param Architecture arch: (optional) Architecture for the given function :rtype: list(LowLevelILInstruction) :Example: - - >>> func = bv.functions[0] + >>> func = next(bv.functions) >>> func.get_lifted_ils_at(func.start) [<il: push(rbp)>] """ 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_lifted_il_flag_uses_for_definition(self, i, flag): + 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): + 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_auto_function_tags_of_type(self, tag_type): + """ + ``get_auto_function_tags_of_type`` gets a list of all auto-defined 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.BNGetAutoFunctionTagsOfType(self.handle, tag_type.handle, count) + assert tags is not None, "core.BNGetAutoFunctionTagsOfType returned None" + result = [] + for i in range(0, count.value): + 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_user_function_tags_of_type(self, tag_type): + """ + ``get_user_function_tags_of_type`` gets a list of all user 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.BNGetUserFunctionTagsOfType(self.handle, tag_type.handle, count) + assert tags is not None, "core.BNGetUserFunctionTagsOfType returned None" + result = [] + for i in range(0, count.value): + 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 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 + Since this removes user tags, it will be added to the current undo buffer. + + :param TagType tag_type: TagType object to match for removing + :rtype: None + """ + core.BNRemoveUserFunctionTagsOfType(self.handle, tag_type.handle) + + 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:'binaryview.Tag') -> None: + """ + ``remove_user_function_tag`` removes a Tag object as a function tag. + + :param Tag tag: Tag object to be added + :rtype: None + """ + core.BNRemoveAutoFunctionTag(self.handle, tag.handle) + + def remove_auto_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 + + :param TagType tag_type: TagType object to match for removing + :rtype: None + """ + core.BNRemoveAutoFunctionTagsOfType(self.handle, tag_type.handle) + + 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() 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(instrs[i]) + for j in range(0, count.value): + result.append(instrs[lowlevelil.InstructionIndex(j)]) core.BNFreeILInstructionList(instrs) return result - def get_lifted_il_flag_definitions_for_use(self, i, flag): + 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() 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(instrs[i]) + for j in range(0, count.value): + result.append(instrs[lowlevelil.InstructionIndex(j)]) core.BNFreeILInstructionList(instrs) return result - def get_flags_read_by_lifted_il_instruction(self, i): + 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() 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(self.arch._flags_by_index[flags[i]]) + for j in range(0, count.value): + result.append(self.arch._flags_by_index[flags[j]]) core.BNFreeRegisterList(flags) return result - def get_flags_written_by_lifted_il_instruction(self, i): + def get_flags_written_by_lifted_il_instruction(self, i:'lowlevelil.InstructionIndex') -> \ + List['architecture.FlagName']: count = ctypes.c_ulonglong() + 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(self.arch._flags_by_index[flags[i]]) + for j in range(0, count.value): + result.append(self.arch._flags_by_index[flags[j]]) core.BNFreeRegisterList(flags) return result - def create_graph(self, graph_type = FunctionGraphType.NormalFunctionGraph, settings = None): + def create_graph(self, graph_type:FunctionGraphType=FunctionGraphType.NormalFunctionGraph, + settings:'DisassemblySettings'=None) -> flowgraph.CoreFlowGraph: 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)) + return flowgraph.CoreFlowGraph(core.BNCreateFunctionGraph(self.handle, graph_type, settings_obj)) - def apply_imported_types(self, sym, type=None): + 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): + 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, branches, source_arch=None): + 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)): @@ -3057,8 +2184,11 @@ class Function(object): 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): + 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)): @@ -3066,35 +2196,46 @@ class Function(object): 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, arch=None): + 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() branches = core.BNGetIndirectBranchesAt(self.handle, arch.handle, addr, count) - 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 + 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) - def get_block_annotations(self, addr, arch=None): + 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) - 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 + 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) - def set_auto_type(self, value): + def set_auto_type(self, value:'types.Type') -> None: core.BNSetFunctionAutoType(self.handle, value.handle) - def set_user_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): + def set_auto_return_type(self, value:'types.Type') -> None: type_conf = core.BNTypeWithConfidence() if value is None: type_conf.type = None @@ -3104,19 +2245,22 @@ class Function(object): type_conf.confidence = value.confidence core.BNSetAutoFunctionReturnType(self.handle, type_conf) - def set_auto_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 + regs.confidence = core.max_confidence core.BNSetAutoFunctionReturnRegisters(self.handle, regs) - def set_auto_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 @@ -3126,9 +2270,14 @@ class Function(object): conv_conf.confidence = value.confidence core.BNSetAutoFunctionCallingConvention(self.handle, conv_conf) - def set_auto_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() @@ -3140,42 +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 + var_conf.confidence = core.max_confidence core.BNSetAutoFunctionParameterVariables(self.handle, var_conf) - def set_auto_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 + bc.confidence = core.max_confidence core.BNSetAutoFunctionHasVariableArguments(self.handle, bc) - def set_auto_can_return(self, value): + def set_auto_can_return(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 + bc.confidence = core.max_confidence core.BNSetAutoFunctionCanReturn(self.handle, bc) - def set_auto_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 + oc.confidence = core.max_confidence core.BNSetAutoFunctionStackAdjustment(self.handle, oc) - def set_auto_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): @@ -3183,28 +2335,33 @@ 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.BNSetAutoFunctionRegisterStackAdjustments(self.handle, adjust, len(value)) - def set_auto_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 + regs.confidence = core.max_confidence core.BNSetAutoFunctionClobberedRegisters(self.handle, regs) - def get_int_display_type(self, instr_addr, value, operand, arch=None): + 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)) - def set_int_display_type(self, instr_addr, value, operand, display_type, arch=None): + def set_int_display_type(self, instr_addr:int, value:int, operand:int, display_type:IntegerDisplayType, arch:Optional['architecture.Architecture']=None) -> None: """ :param int instr_addr: @@ -3214,12 +2371,14 @@ class Function(object): :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) - def reanalyze(self): + def reanalyze(self) -> None: """ ``reanalyze`` causes this functions to be reanalyzed. This function does not wait for the analysis to finish. @@ -3227,22 +2386,15 @@ class Function(object): """ core.BNReanalyzeFunction(self.handle) - @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): + def request_advanced_analysis_data(self) -> None: core.BNRequestAdvancedFunctionAnalysisData(self.handle) self._advanced_analysis_requests += 1 - def release_advanced_analysis_data(self): + def release_advanced_analysis_data(self) -> None: core.BNReleaseAdvancedFunctionAnalysisData(self.handle) self._advanced_analysis_requests -= 1 - def get_basic_block_at(self, addr, arch=None): + 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``. @@ -3254,13 +2406,15 @@ class Function(object): <block: x86_64@0x100000f30-0x100000f50> """ 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.basicblock.BasicBlock(block, self._view) + return basicblock.BasicBlock(block, self._view) - def get_instr_highlight(self, addr, arch=None): + def get_instr_highlight(self, addr:int, arch:Optional['architecture.Architecture']=None) -> '_highlight.HighlightColor': """ :Example: >>> current_function.set_user_instr_highlight(here, highlight.HighlightColor(red=0xff, blue=0xff, green=0)) @@ -3268,17 +2422,20 @@ class Function(object): <color: #ff00ff> """ 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) + 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) + 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) + 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): + def set_auto_instr_highlight(self, addr:int, color:Union['_highlight.HighlightColor', HighlightStandardColor], + arch:Optional['architecture.Architecture']=None): """ ``set_auto_instr_highlight`` highlights the instruction at the specified address with the supplied color @@ -3289,14 +2446,18 @@ class Function(object): :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 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) + 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): + + def set_user_instr_highlight(self, addr:int, color:Union['_highlight.HighlightColor', HighlightStandardColor], + arch:Optional['architecture.Architecture']=None): """ ``set_user_instr_highlight`` highlights the instruction at the specified address with the supplied color @@ -3309,32 +2470,35 @@ class Function(object): >>> 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): + 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) core.BNSetUserInstructionHighlight(self.handle, arch.handle, addr, color._get_core_struct()) - def create_auto_stack_var(self, offset, var_type, name): + 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 create_user_stack_var(self, offset, var_type, name): + 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): + def delete_auto_stack_var(self, offset:int) -> None: core.BNDeleteAutoStackVariable(self.handle, offset) - def delete_user_stack_var(self, offset): + def delete_user_stack_var(self, offset:int) -> None: core.BNDeleteUserStackVariable(self.handle, offset) - def create_auto_var(self, var, var_type, name, ignore_disjoint_uses = False): + 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 @@ -3344,7 +2508,8 @@ class Function(object): 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): + 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 @@ -3354,66 +2519,80 @@ class Function(object): tc.confidence = var_type.confidence core.BNCreateUserVariable(self.handle, var_data, tc, name, ignore_disjoint_uses) - def delete_auto_var(self, var): + 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) - def delete_user_var(self, var): + 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): + 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) - def get_stack_var_at_frame_offset(self, offset, addr, arch=None): + 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(self, found_var.var.type, found_var.var.index, found_var.var.storage, + 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 - def get_type_tokens(self, settings=None): + def get_type_tokens(self, settings:'DisassemblySettings'=None) -> List['DisassemblyTextLine']: + _settings = None if settings is not None: - settings = settings.handle + _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.get_instruction_lines(lines[i].tokens, lines[i].count) + 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_reg_value_at_exit(self, reg): + 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") + result = core.BNGetFunctionRegisterValueAtExit(self.handle, self.arch.get_reg_index(reg)) - return RegisterValue(self.arch, result.value, confidence = result.confidence) + return variable.RegisterValue(self.arch, result.value, confidence = result.confidence) - def set_auto_call_stack_adjustment(self, addr, adjust, arch=None): + 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) - def set_auto_call_reg_stack_adjustment(self, addr, adjust, arch=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 @@ -3427,8 +2606,11 @@ class Function(object): 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): + 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): @@ -3436,8 +2618,11 @@ class Function(object): core.BNSetAutoCallRegisterStackAdjustmentForRegisterStack(self.handle, arch.handle, addr, reg_stack, adjust.value, adjust.confidence) - def set_call_type_adjustment(self, addr, adjust_type, arch=None): + 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 @@ -3447,15 +2632,22 @@ class Function(object): tc.confidence = adjust_type.confidence core.BNSetUserCallTypeAdjustment(self.handle, arch.handle, addr, tc) - def set_call_stack_adjustment(self, addr, adjust, arch=None): + 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) - def set_call_reg_stack_adjustment(self, addr, adjust, arch=None): + 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 @@ -3469,8 +2661,11 @@ class Function(object): 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): + 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): @@ -3478,26 +2673,34 @@ class Function(object): core.BNSetUserCallRegisterStackAdjustmentForRegisterStack(self.handle, arch.handle, addr, reg_stack, adjust.value, adjust.confidence) - def get_call_type_adjustment(self, addr, arch=None): + 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 result.type: - platform = self.platform - return types.Type(result.type, platform = platform, confidence = result.confidence) - return None + if not result.type: + return None + platform = self.platform + return types.Type(result.type, platform = platform, confidence = result.confidence) - def get_call_stack_adjustment(self, addr, arch=None): + 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) - def get_call_reg_stack_adjustment(self, addr, arch=None): + 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() 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[arch.get_reg_stack_name(adjust[i].regStack)] = types.RegisterStackAdjustmentWithConfidence( @@ -3505,20 +2708,25 @@ class Function(object): core.BNFreeRegisterStackAdjustments(adjust) return result - def get_call_reg_stack_adjustment_for_reg_stack(self, addr, reg_stack, 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 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): + 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, def_addr, value): + def set_user_var_value(self, var:'variable.Variable', def_addr:int, value:'variable.PossibleValueSet') -> None: """ `set_user_var_value` allows the user to specify a PossibleValueSet value for an MLIL variable at its \ definition site. @@ -3540,6 +2748,8 @@ class Function(object): >>> value = PossibleValueSet.constant(5) >>> current_function.set_user_var_value(var, def_site, value) """ + 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") @@ -3560,7 +2770,7 @@ class Function(object): 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): + def clear_user_var_value(self, var:'variable.Variable', def_addr:int) -> None: """ Clears a previously defined user variable value. @@ -3571,6 +2781,9 @@ class Function(object): var_defs = self.mlil.get_var_definitions(var) if var_defs is None: raise ValueError("Could not get definition for Variable") + + 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: @@ -3588,7 +2801,8 @@ class Function(object): var_data.storage = var.storage core.BNClearUserVariableValue(self.handle, var_data, def_site) - def get_all_user_var_values(self): + def get_all_user_var_values(self) -> \ + Mapping['variable.Variable', Mapping['ArchAndAddr', 'variable.PossibleValueSet']]: """ Returns a map of current defined user variable values. @@ -3597,19 +2811,20 @@ class Function(object): """ 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(self, var_val.var.type, var_val.var.index, var_val.var.storage) + 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] = PossibleValueSet(def_site.arch, var_val.value) + result[var][def_site] = variable.PossibleValueSet(def_site.arch, var_val.value) core.BNFreeUserVariableValues(var_values) return result - def clear_all_user_var_values(self): + def clear_all_user_var_values(self) -> None: """ Clear all user defined variable values. @@ -3620,9 +2835,9 @@ class Function(object): for def_site in all_values[var]: self.clear_user_var_value(var, def_site.addr) - def request_debug_report(self, name): + def request_debug_report(self, name:str) -> None: """ - ``request_debug_report`` can generate interanl debug reports for a variety of analysis. + ``request_debug_report`` can generate internal debug reports for a variety of analysis. Current list of possible values include: - mlil_translator @@ -3636,7 +2851,7 @@ class Function(object): self.view.update_analysis() @property - def call_sites(self): + 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 @@ -3647,23 +2862,24 @@ class Function(object): """ 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): if refs[i].func: - func = binaryninja.function.Function(self, core.BNNewFunctionReference(refs[i].func)) + func = Function(self.view, core.BNNewFunctionReference(refs[i].func)) else: func = None if refs[i].arch: - arch = binaryninja.architecture.CoreArchitecture._from_cache(refs[i].arch) + arch = architecture.CoreArchitecture._from_cache(refs[i].arch) else: arch = None addr = refs[i].addr - result.append(binaryninja.architecture.ReferenceSource(func, arch, addr)) + result.append(binaryview.ReferenceSource(func, arch, addr)) core.BNFreeCodeReferences(refs, count.value) return result @property - def callees(self): + 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. @@ -3679,7 +2895,7 @@ class Function(object): return called @property - def callee_addresses(self): + 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. @@ -3693,7 +2909,7 @@ class Function(object): return result @property - def callers(self): + 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. @@ -3707,7 +2923,14 @@ class Function(object): functions.append(ref.function) return functions - def get_mlil_var_refs(self, var): + @property + def workflow(self): + handle = core.BNGetWorkflowForFunction(self.handle) + if handle is None: + return None + return workflow.Workflow(handle = handle) + + def get_mlil_var_refs(self, var:'variable.Variable') -> List[ILReferenceSource]: """ ``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, @@ -3730,23 +2953,25 @@ class Function(object): 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): if refs[i].func: - func = binaryninja.function.Function(self, core.BNNewFunctionReference(refs[i].func)) + func = Function(self.view, core.BNNewFunctionReference(refs[i].func)) else: func = None if refs[i].arch: - arch = binaryninja.architecture.CoreArchitecture._from_cache(refs[i].arch) + arch = architecture.CoreArchitecture._from_cache(refs[i].arch) else: arch = None - result.append(binaryninja.ILReferenceSource( + result.append(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): + 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 @@ -3764,27 +2989,35 @@ class Function(object): """ result = [] count = ctypes.c_ulonglong(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 length is None: - refs = core.BNGetMediumLevelILVariableReferencesFrom(self.handle, self.arch.handle, addr, count) + refs = core.BNGetMediumLevelILVariableReferencesFrom(self.handle, arch.handle, addr, count) + assert refs is not None, "core.BNGetMediumLevelILVariableReferencesFrom returned None" else: - refs = core.BNGetMediumLevelILVariableReferencesInRange(self.handle, self.arch.handle, addr, length, count) + 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(self, refs[i].var.type, refs[i].var.index, refs[i].var.storage) + var = variable.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)) + func = Function(self.view, core.BNNewFunctionReference(refs[i].source.func)) else: func = None if refs[i].source.arch: - arch = binaryninja.architecture.CoreArchitecture._from_cache(refs[i].source.arch) + _arch = architecture.CoreArchitecture._from_cache(refs[i].source.arch) else: - arch = None + _arch = arch - src = ILReferenceSource(func, arch, refs[i].source.addr, refs[i].source.type, refs[i].source.exprId) + 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): + 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, @@ -3804,22 +3037,24 @@ class Function(object): 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 = binaryninja.function.Function(self, core.BNNewFunctionReference(refs[i].func)) + func = Function(self.view, core.BNNewFunctionReference(refs[i].func)) else: func = None if refs[i].arch: - arch = binaryninja.architecture.CoreArchitecture._from_cache(refs[i].arch) + arch = architecture.CoreArchitecture._from_cache(refs[i].arch) else: arch = None - result.append(binaryninja.ILReferenceSource( + result.append(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): + 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 @@ -3834,36 +3069,47 @@ class Function(object): """ 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 length is None: - refs = core.BNGetHighLevelILVariableReferencesFrom(self.handle, self.arch.handle, addr, count) + refs = core.BNGetHighLevelILVariableReferencesFrom(self.handle, arch.handle, addr, count) + assert refs is not None, "core.BNGetHighLevelILVariableReferencesFrom returned None" else: - refs = core.BNGetHighLevelILVariableReferencesInRange(self.handle, self.arch.handle, addr, length, count) + 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(self, refs[i].var.type, refs[i].var.index, refs[i].var.storage) + var = variable.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)) + func = Function(self.view, core.BNNewFunctionReference(refs[i].source.func)) else: func = None if refs[i].source.arch: - arch = binaryninja.architecture.CoreArchitecture._from_cache(refs[i].source.arch) + _arch = architecture.CoreArchitecture._from_cache(refs[i].source.arch) else: - arch = None + _arch = arch - src = ILReferenceSource(func, arch, refs[i].source.addr, refs[i].source.type, refs[i].source.exprId) + 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 + 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 + + start = ctypes.c_ulonglong() + if core.BNGetInstructionContainingAddress(self.handle, arch.handle, addr, start): + return start.value + return None class AdvancedFunctionAnalysisDataRequestor(object): - def __init__(self, func = None): + def __init__(self, func:'Function'=None): self._function = func if self._function is not None: self._function.request_advanced_analysis_data() @@ -3873,42 +3119,40 @@ class AdvancedFunctionAnalysisDataRequestor(object): self._function.release_advanced_analysis_data() @property - def function(self): + def function(self) -> Optional['Function']: return self._function @function.setter - def function(self, func): + 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() - def close(self): + def close(self) -> None: 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): + 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() + 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 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) self._highlight = color def __str__(self): - result = "" - for token in self.tokens: - result += token.text - return result + return "".join(map(str, self.tokens)) def __repr__(self): if self.address is None: @@ -3916,349 +3160,301 @@ class DisassemblyTextLine(object): 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 tokens(self): - """ """ + def tokens(self) -> List['InstructionTextToken']: return self._tokens @tokens.setter - def tokens(self, value): + def tokens(self, value:List['InstructionTextToken']) -> None: self._tokens = value @property - def il_instruction(self): - """ """ + def il_instruction(self) -> Optional[ILInstructionType]: return self._il_instruction @il_instruction.setter - def il_instruction(self, value): + def il_instruction(self, value:ILInstructionType) -> None: self._il_instruction = value @property - def highlight(self): - """ """ + def highlight(self) -> '_highlight.HighlightColor': return self._highlight @highlight.setter - def highlight(self, value): + def highlight(self, value:'_highlight.HighlightColor') -> None: self._highlight = value -class DisassemblySettings(object): - def __init__(self, handle = None): +class DisassemblyTextRenderer(object): + def __init__(self, func:AnyFunctionType=None, settings:'DisassemblySettings'=None, + handle:core.BNDisassemblySettings=None): if handle is None: - self.handle = core.BNCreateDisassemblySettings() + if func is None: + raise ValueError("function required for disassembly") + settings_obj = None + if settings is not None: + settings_obj = settings.handle + if isinstance(func, Function): + self.handle = core.BNCreateDisassemblyTextRenderer(func.handle, settings_obj) + elif isinstance(func, lowlevelil.LowLevelILFunction): + self.handle = core.BNCreateLowLevelILDisassemblyTextRenderer(func.handle, settings_obj) + elif isinstance(func, mediumlevelil.MediumLevelILFunction): + self.handle = core.BNCreateMediumLevelILDisassemblyTextRenderer(func.handle, settings_obj) + elif isinstance(func, highlevelil.HighLevelILFunction): + self.handle = core.BNCreateHighLevelILDisassemblyTextRenderer(func.handle, settings_obj) + else: + raise TypeError("invalid function object") 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 "<reg: size %d, offset %d in %s%s>" % (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 "<reg stack: %d regs, stack top in %s>" % (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 "<input: %s>" % str(self._type) - return "<input: %s %s>" % (str(self._type), self._name) - - @property - def name(self): - """ """ - return self._name - - @name.setter - def name(self, value): - self._name = value + core.BNFreeDisassemblyTextRenderer(self.handle) @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 "<intrinsic: %s -> %s>" % (repr(self._inputs), repr(self._outputs)) + def function(self) -> 'Function': + return Function(handle = core.BNGetDisassemblyTextRendererFunction(self.handle)) @property - def inputs(self): - """ """ - return self._inputs - - @inputs.setter - def inputs(self, value): - self._inputs = value + def il_function(self) -> Optional[ILFunctionType]: + llil = core.BNGetDisassemblyTextRendererLowLevelILFunction(self.handle) + if llil: + return lowlevelil.LowLevelILFunction(handle = llil) + mlil = core.BNGetDisassemblyTextRendererMediumLevelILFunction(self.handle) + if mlil: + return mediumlevelil.MediumLevelILFunction(handle = mlil) + hlil = core.BNGetDisassemblyTextRendererHighLevelILFunction(self.handle) + if hlil: + return highlevelil.HighLevelILFunction(handle = hlil) + return None @property - def outputs(self): - """ """ - return self._outputs + def basic_block(self) -> Optional['basicblock.BasicBlock']: + result = core.BNGetDisassemblyTextRendererBasicBlock(self.handle) + if result: + return basicblock.BasicBlock(handle = result) + return None - @outputs.setter - def outputs(self, value): - self._outputs = value + @basic_block.setter + 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 index(self): - """ """ - return self._index - - @index.setter - def index(self, value): - self._index = value + def arch(self) -> 'architecture.Architecture': + return architecture.CoreArchitecture._from_cache(handle = core.BNGetDisassemblyTextRendererArchitecture(self.handle)) - -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) + @arch.setter + def arch(self, arch='architecture.Architecture') -> None: + core.BNSetDisassemblyTextRendererArchitecture(self.handle, arch.handle) @property - def type(self): - """ """ - return self._type + def settings(self) -> 'DisassemblySettings': + return DisassemblySettings(handle = core.BNGetDisassemblyTextRendererSettings(self.handle)) - @type.setter - def type(self, value): - self._type = value + @settings.setter + def settings(self, settings:'DisassemblySettings') -> None: + if settings is not None: + core.BNSetDisassemblyTextRendererSettings(self.handle, settings.handle) + core.BNSetDisassemblyTextRendererSettings(self.handle, None) @property - def target(self): - """ """ - return self._target - - @target.setter - def target(self, value): - self._target = value + def il(self) -> bool: + return core.BNIsILDisassemblyTextRenderer(self.handle) @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 has_data_flow(self) -> bool: + return core.BNDisassemblyTextRendererHasDataFlow(self.handle) - def __len__(self): - return self._length + def get_instruction_annotations(self, addr:int) -> List['InstructionTextToken']: + count = ctypes.c_ulonglong() + tokens = core.BNGetDisassemblyTextRendererInstructionAnnotations(self.handle, addr, count) + assert tokens is not None + result = InstructionTextToken._from_core_struct(tokens, count.value) + core.BNFreeInstructionText(tokens, count.value) + return result - def __repr__(self): - branch_delay = "" - if self._branch_delay: - branch_delay = ", delay slot" - return "<instr: %d bytes%s, %s>" % (self._length, branch_delay, repr(self._branches)) + 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): + yield None, 0 + return + il_function = self.il_function + 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) - @property - def length(self): - """ """ - return self._length + 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: + yield None, 0 + return + il_function = self.il_function + 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) - @length.setter - def length(self, value): - self._length = value + 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, line in enumerate(in_lines): + if isinstance(line, str): + line = DisassemblyTextLine([InstructionTextToken(InstructionTextTokenType.TextToken, line)]) + if not isinstance(line, DisassemblyTextLine): + line = DisassemblyTextLine(line) + if line.address is None: + if len(line.tokens) > 0: + line_buf[i].addr = line.tokens[0].address + else: + line_buf[i].addr = 0 + else: + line_buf[i].addr = line.address + if line.il_instruction is not None: + line_buf[i].instrIndex = line.il_instruction.instr_index + 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 isinstance(color, HighlightStandardColor): + 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_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 + 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) - @property - def arch_transition_by_target_addr(self): - """ """ - return self._arch_transition_by_target_addr + def reset_deduplicated_comments(self) -> None: + core.BNResetDisassemblyTextRendererDeduplicatedComments(self.handle) - @arch_transition_by_target_addr.setter - def arch_transition_by_target_addr(self, value): - self._arch_transition_by_target_addr = value + 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 + 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 - @property - def branch_delay(self): - """ """ - return self._branch_delay + 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 + else: + stack_ref.sourceOperand = ref.source_operand + if ref.type is None: + stack_ref.type = None + stack_ref.typeConfidence = 0 + else: + stack_ref.type = ref.type.handle + stack_ref.typeConfidence = ref.type.confidence + stack_ref.name = ref.name + stack_ref.varIdentifier = ref.var.identifier + stack_ref.referencedOffset = ref.referenced_offset + stack_ref.size = ref.size + count = ctypes.c_ulonglong() + new_tokens = core.BNGetDisassemblyTextRendererStackVariableReferenceTokens(self.handle, stack_ref, count) + assert new_tokens is not None + result = InstructionTextToken._from_core_struct(new_tokens, count.value) + tokens += result + core.BNFreeInstructionText(new_tokens, count.value) - @branch_delay.setter - def branch_delay(self, value): - self._branch_delay = value + @staticmethod + def is_integer_token(token:'InstructionTextToken') -> bool: + return core.BNIsIntegerToken(token) - @property - def branches(self): - """ """ - return self._branches + 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_core_struct([int_token]) + count = ctypes.c_ulonglong() + new_tokens = core.BNGetDisassemblyTextRendererIntegerTokens(self.handle, in_token_obj, arch, addr, count) + assert new_tokens is not None + result = InstructionTextToken._from_core_struct(new_tokens, count.value) + tokens += result + core.BNFreeInstructionText(new_tokens, count.value) - @branches.setter - def branches(self, value): - self._branches = value + 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: + cur_line_obj.instrIndex = 0xffffffffffffffff + 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_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 + if (new_lines[i].instrIndex != 0xffffffffffffffff) and (il_function is not None): + il_instr = il_function[new_lines[i].instrIndex] + else: + il_instr = None + 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): @@ -4309,8 +3505,9 @@ class InstructionTextToken(object): ========================== ============================================ """ - def __init__(self, token_type, text, value = 0, size = 0, operand = 0xffffffff, - context = InstructionTextTokenContext.NoTokenContext, address = 0, confidence = types.max_confidence, typeNames=[], width=0): + 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 @@ -4324,33 +3521,14 @@ class InstructionTextToken(object): 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 = [] + @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("charmap") + text = text.decode("utf-8") width = tokens[j].width value = tokens[j].value size = tokens[j].size @@ -4361,12 +3539,33 @@ class InstructionTextToken(object): typeNames = [] for i in range(tokens[j].namesCount): if not isinstance(tokens[j].typeNames[i], str): - typeNames.append(tokens[j].typeNames[i].decode("charmap")) + 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 @@ -4374,340 +3573,77 @@ class InstructionTextToken(object): return repr(self._text) @property - def type(self): - """ """ + def type(self) -> InstructionTextTokenType: return self._type @type.setter - def type(self, value): + def type(self, value:InstructionTextTokenType) -> None: self._type = value @property - def text(self): - """ """ + def text(self) -> str: return self._text @text.setter - def text(self, value): + def text(self, value:str) -> None: self._text = value @property - def value(self): - """ """ + def value(self) -> int: return self._value @value.setter - def value(self, value): + def value(self, value:int) -> None: self._value = value @property - def size(self): - """ """ + def size(self) -> int: return self._size @size.setter - def size(self, value): + def size(self, value:int) -> None: self._size = value @property - def operand(self): - """ """ + def operand(self) -> int: return self._operand @operand.setter - def operand(self, value): + def operand(self, value:int) -> None: self._operand = value @property - def context(self): - """ """ + def context(self) -> InstructionTextTokenContext: return self._context @context.setter - def context(self, value): + def context(self, value:InstructionTextTokenContext) -> None: self._context = value @property - def confidence(self): - """ """ + def confidence(self) -> int: return self._confidence @confidence.setter - def confidence(self, value): + def confidence(self, value:int) -> None: self._confidence = value @property - def address(self): - """ """ + def address(self) -> int: return self._address @address.setter - def address(self, value): + def address(self, value:int) -> None: self._address = value @property - def typeNames(self): - """ """ + def typeNames(self) -> List[str]: return self._typeNames @typeNames.setter - def typeNames(self, value): + def typeNames(self, value:List[str]) -> None: self._typeNames = value @property - def width(self): + def width(self) -> int: return self._width - - - -class DisassemblyTextRenderer(object): - def __init__(self, func = None, settings = None, handle = None): - if handle is None: - if func is None: - raise ValueError("function required for disassembly") - settings_obj = None - if settings is not None: - settings_obj = settings.handle - if isinstance(func, Function): - self.handle = core.BNCreateDisassemblyTextRenderer(func.handle, settings_obj) - elif isinstance(func, binaryninja.lowlevelil.LowLevelILFunction): - self.handle = core.BNCreateLowLevelILDisassemblyTextRenderer(func.handle, settings_obj) - elif isinstance(func, binaryninja.mediumlevelil.MediumLevelILFunction): - self.handle = core.BNCreateMediumLevelILDisassemblyTextRenderer(func.handle, settings_obj) - elif isinstance(func, binaryninja.highlevelil.HighLevelILFunction): - self.handle = core.BNCreateHighLevelILDisassemblyTextRenderer(func.handle, settings_obj) - else: - raise TypeError("invalid function object") - else: - self.handle = handle - - def __del__(self): - core.BNFreeDisassemblyTextRenderer(self.handle) - - @property - def function(self): - return Function(handle = core.BNGetDisassemblyTextRendererFunction(self.handle)) - - @property - def il_function(self): - llil = core.BNGetDisassemblyTextRendererLowLevelILFunction(self.handle) - if llil: - return binaryninja.lowlevelil.LowLevelILFunction(handle = llil) - mlil = core.BNGetDisassemblyTextRendererMediumLevelILFunction(self.handle) - if mlil: - return binaryninja.mediumlevelil.MediumLevelILFunction(handle = mlil) - hlil = core.BNGetDisassemblyTextRendererHighLevelILFunction(self.handle) - if hlil: - return binaryninja.highlevelil.HighLevelILFunction(handle = hlil) - return None - - @property - def basic_block(self): - result = core.BNGetDisassemblyTextRendererBasicBlock(self.handle) - if result: - return binaryninja.basicblock.BasicBlock(handle = result) - return None - - @basic_block.setter - def basic_block(self, block): - 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)) - - @arch.setter - def arch(self, arch): - core.BNSetDisassemblyTextRendererArchitecture(self.handle, arch.handle) - - @property - def settings(self): - return DisassemblySettings(handle = core.BNGetDisassemblyTextRendererSettings(self.handle)) - - @settings.setter - def settings(self, settings): - if settings is not None: - core.BNSetDisassemblyTextRendererSettings(self.handle, settings.handle) - core.BNSetDisassemblyTextRendererSettings(self.handle, None) - - @property - def il(self): - return core.BNIsILDisassemblyTextRenderer(self.handle) - - @property - def has_data_flow(self): - return core.BNDisassemblyTextRendererHasDataFlow(self.handle) - - def get_instruction_annotations(self, addr): - count = ctypes.c_ulonglong() - tokens = core.BNGetDisassemblyTextRendererInstructionAnnotations(self.handle, addr, count) - result = InstructionTextToken.get_instruction_lines(tokens, count.value) - core.BNFreeInstructionText(tokens, count.value) - return result - - def get_instruction_text(self, addr): - 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 - 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) - - def get_disassembly_text(self, addr): - 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 - 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) - - def post_process_lines(self, addr, length, in_lines, indent_spaces=""): - 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] - if isinstance(line, str): - line = DisassemblyTextLine([InstructionTextToken(InstructionTextTokenType.TextToken, line)]) - if not isinstance(line, DisassemblyTextLine): - line = DisassemblyTextLine(line) - if line.address is None: - if len(line.tokens) > 0: - line_buf[i].addr = line.tokens[0].address - else: - line_buf[i].addr = 0 - else: - line_buf[i].addr = line.address - if line.il_instruction is not None: - line_buf[i].instrIndex = line.il_instruction.instr_index - 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 isinstance(color, HighlightStandardColor): - 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) - count = ctypes.c_ulonglong() - lines = ctypes.POINTER(core.BNDisassemblyTextLine)() - lines = core.BNPostProcessDisassemblyTextRendererLines(self.handle, addr, length, line_buf, len(in_lines), count, indent_spaces) - 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 - - def reset_deduplicated_comments(self): - core.BNResetDisassemblyTextRendererDeduplicatedComments(self.handle) - - def add_symbol_token(self, tokens, addr, size, operand = None): - 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) - tokens += result - core.BNFreeInstructionText(new_tokens, count.value) - return True - - def add_stack_var_reference_tokens(self, tokens, ref): - stack_ref = core.BNStackVariableReference() - if ref.source_operand is None: - stack_ref.sourceOperand = 0xffffffff - else: - stack_ref.sourceOperand = ref.source_operand - if ref.type is None: - stack_ref.type = None - stack_ref.typeConfidence = 0 - else: - stack_ref.type = ref.type.handle - stack_ref.typeConfidence = ref.type.confidence - stack_ref.name = ref.name - stack_ref.varIdentifier = ref.var.identifier - stack_ref.referencedOffset = ref.referenced_offset - 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) - tokens += result - core.BNFreeInstructionText(new_tokens, count.value) - - @classmethod - def is_integer_token(self, token): - return core.BNIsIntegerToken(token) - - def add_integer_token(self, tokens, int_token, addr, arch = None): - if arch is not None: - arch = arch.handle - in_token_obj = InstructionTextToken.get_instruction_lines([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) - tokens += result - core.BNFreeInstructionText(new_tokens, count.value) - - def wrap_comment(self, lines, cur_line, comment, has_auto_annotations, leading_spaces = " ", indent_spaces = ""): - cur_line_obj = core.BNDisassemblyTextLine() - cur_line_obj.addr = cur_line.address - if cur_line.il_instruction is None: - cur_line_obj.instrIndex = 0xffffffffffffffff - 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.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) - il_function = self.il_function - for i in range(0, count.value): - addr = new_lines[i].addr - if (new_lines[i].instrIndex != 0xffffffffffffffff) and (il_function is not None): - 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) - lines.append(DisassemblyTextLine(tokens, addr, il_instr, color)) - core.BNFreeDisassemblyTextLines(new_lines, count.value) diff --git a/python/functionrecognizer.py b/python/functionrecognizer.py index 4636df91..ff639aca 100644 --- a/python/functionrecognizer.py +++ b/python/functionrecognizer.py @@ -21,13 +21,13 @@ import traceback # Binary Ninja components -from binaryninja import _binaryninjacore as core -from binaryninja import function -from binaryninja import filemetadata -from binaryninja import binaryview -from binaryninja import lowlevelil -from binaryninja import log -from binaryninja import mediumlevelil +from . import _binaryninjacore as core +from . import function +from . import filemetadata +from . import binaryview +from . import lowlevelil +from . import log +from . import mediumlevelil class FunctionRecognizer(object): diff --git a/python/generator.cpp b/python/generator.cpp index ed28e6f3..16fa5def 100644 --- a/python/generator.cpp +++ b/python/generator.cpp @@ -185,10 +185,10 @@ int main(int argc, char* argv[]) FILE* out = fopen(argv[2], "w"); FILE* enums = fopen(argv[3], "w"); - fprintf(out, "from __future__ import absolute_import\n"); fprintf(out, "import ctypes, os\n\n"); + fprintf(out, "from typing import Optional"); fprintf(enums, "import enum"); - + fprintf(out, "# Load core module\n"); fprintf(out, "import platform\n"); fprintf(out, "core = None\n"); @@ -206,7 +206,16 @@ int main(int argc, char* argv[]) fprintf(out, "else:\n"); fprintf(out, "\traise Exception(\"OS not supported\")\n\n\n"); - fprintf(out, "from binaryninja import cstr, pyNativeStr\n\n\n"); + fprintf(out, "def cstr(var) -> Optional[str]:\n"); + fprintf(out, " if var is None:\n"); + fprintf(out, " return None\n"); + fprintf(out, " return var.encode(\"utf-8\")\n"); + + fprintf(out, "def pyNativeStr(arg):\n"); + fprintf(out, " if isinstance(arg, str):\n"); + fprintf(out, " return arg\n"); + fprintf(out, " else:\n"); + fprintf(out, " return arg.decode('utf8')\n"); // Create type objects fprintf(out, "# Type definitions\n"); @@ -513,23 +522,14 @@ int main(int argc, char* argv[]) fprintf(out, "\n"); } + fprintf(out, "\nmax_confidence = %d\n", BN_FULL_CONFIDENCE); + fprintf(out, "\n# Helper functions\n"); fprintf(out, "def handle_of_type(value, handle_type):\n"); fprintf(out, "\tif isinstance(value, ctypes.POINTER(handle_type)) or isinstance(value, ctypes.c_void_p):\n"); fprintf(out, "\t\treturn ctypes.cast(value, ctypes.POINTER(handle_type))\n"); fprintf(out, "\traise ValueError('expected pointer to %%s' %% str(handle_type))\n"); - // The following method is addapted from python/enum/__init__.py, lines 25-36 - fprintf(out, "\ntry:\n"); - fprintf(out, "\tbasestring\n"); - fprintf(out, "\tunicode\n"); - fprintf(out, "except NameError:\n"); - fprintf(out, "\t# In Python 2 basestring is the ancestor of both str and unicode\n"); - fprintf(out, "\t# in Python 3 it's just str, but was missing in 3.1\n"); - fprintf(out, "\t# In Python 3 unicode no longer exists (it's just str)\n"); - fprintf(out, "\tbasestring = str\n"); - fprintf(out, "\tunicode = str\n"); - fprintf(out, "\n# Set path for core plugins\n"); fprintf(out, "BNSetBundledPluginDirectory(os.path.join(_base_path, \"plugins\"))\n"); diff --git a/python/highlevelil.py b/python/highlevelil.py index 45f6def4..71c58920 100644 --- a/python/highlevelil.py +++ b/python/highlevelil.py @@ -20,21 +20,28 @@ import ctypes import struct -from typing import List +from typing import Optional, Generator, List, Union, Any, NewType # Binary Ninja components -import binaryninja -from binaryninja import _binaryninjacore as core -from binaryninja.enums import HighLevelILOperation, FunctionGraphType -from binaryninja import function -from binaryninja import lowlevelil -from binaryninja import mediumlevelil -from binaryninja import basicblock -from binaryninja import types - -# 2-3 compatibility -from binaryninja import range +from . import _binaryninjacore as core +from .enums import HighLevelILOperation, InstructionTextTokenType, DataFlowQueryOption, FunctionGraphType +from .variable import Variable +from . import function +from . import binaryview +from . import architecture +from . import lowlevelil +from . import mediumlevelil +from . import basicblock +from . import types +from . import highlight +from . import flowgraph +from . import variable +LinesType = Generator['function.DisassemblyTextLine', None, None] +ExpressionIndex = NewType('ExpressionIndex', int) +InstructionIndex = NewType('InstructionIndex', int) +HLILInstructionsType = Generator['HighLevelILInstruction', None, None] +HLILBasicBlocksType = Generator['HighLevelILBasicBlock', None, None] class HighLevelILOperationAndSize(object): def __init__(self, operation, size): @@ -63,7 +70,6 @@ class HighLevelILOperationAndSize(object): @property def operation(self): - """ """ return self._operation @operation.setter @@ -72,7 +78,6 @@ class HighLevelILOperationAndSize(object): @property def size(self): - """ """ return self._size @size.setter @@ -241,9 +246,12 @@ class HighLevelILInstruction(object): HighLevelILOperation.HLIL_FCMP_UO: [("left", "expr"), ("right", "expr")] } - def __init__(self, func, expr_index, as_ast = True, instr_index = None): + def __init__(self, func:'HighLevelILFunction', expr_index:ExpressionIndex, as_ast:bool = True, + instr_index:InstructionIndex = None): instr = core.BNGetHighLevelILByIndex(func.handle, expr_index, as_ast) self._function = func + if func.arch is None: + raise Exception("HighLevelILFunction architecture must not be None") self._expr_index = expr_index if instr_index is None: self._instr_index = core.BNGetHighLevelILInstructionForExpr(func.handle, expr_index) @@ -260,6 +268,7 @@ class HighLevelILInstruction(object): i = 0 for operand in operands: name, operand_type = operand + value = None if operand_type == "int": value = instr.operands[i] value = (value & ((1 << 63) - 1)) - (value & (1 << 63)) @@ -275,15 +284,16 @@ class HighLevelILInstruction(object): elif operand_type == "intrinsic": value = lowlevelil.ILIntrinsic(func.arch, instr.operands[i]) elif operand_type == "var": - value = function.Variable.from_identifier(self._function.source_function, instr.operands[i]) + value = variable.Variable.from_identifier(self._function.source_function, instr.operands[i]) elif operand_type == "var_ssa": - var = function.Variable.from_identifier(self._function.source_function, instr.operands[i]) + var = variable.Variable.from_identifier(self._function.source_function, instr.operands[i]) version = instr.operands[i + 1] i += 1 value = mediumlevelil.SSAVariable(var, version) elif operand_type == "int_list": count = ctypes.c_ulonglong() operand_list = core.BNHighLevelILGetOperandList(func.handle, self._expr_index, i, count) + assert operand_list is not None, "core.BNHighLevelILGetOperandList returned None" value = [] for j in range(count.value): value.append(operand_list[j]) @@ -291,6 +301,7 @@ class HighLevelILInstruction(object): elif operand_type == "expr_list": count = ctypes.c_ulonglong() operand_list = core.BNHighLevelILGetOperandList(func.handle, self._expr_index, i, count) + assert operand_list is not None, "core.BNHighLevelILGetOperandList returned None" i += 1 value = [] for j in range(count.value): @@ -299,12 +310,13 @@ class HighLevelILInstruction(object): elif operand_type == "var_ssa_list": count = ctypes.c_ulonglong() operand_list = core.BNHighLevelILGetOperandList(func.handle, self._expr_index, i, count) + assert operand_list is not None, "core.BNHighLevelILGetOperandList returned None" i += 1 value = [] for j in range(count.value // 2): var_id = operand_list[j * 2] var_version = operand_list[(j * 2) + 1] - value.append(mediumlevelil.SSAVariable(function.Variable.from_identifier(self._function.source_function, + value.append(mediumlevelil.SSAVariable(variable.Variable.from_identifier(self._function.source_function, var_id), var_version)) core.BNHighLevelILFreeOperandList(operand_list) elif operand_type == "member_index": @@ -336,9 +348,9 @@ class HighLevelILInstruction(object): first_line = "<invalid>" else: first_line = "" - for token in lines[0].tokens: + for token in next(lines).tokens: first_line += token.text - if len(lines) > 1: + if len(list(lines)) > 1: continuation = "..." return "<%s: %s%s>" % (self._operation.name, first_line, continuation) @@ -376,25 +388,26 @@ class HighLevelILInstruction(object): return hash((self._function, self._expr_index)) @property - def lines(self): + def lines(self) -> LinesType: """HLIL text lines (read-only)""" count = ctypes.c_ulonglong() lines = core.BNGetHighLevelILExprText(self._function.handle, self._expr_index, self._as_ast, count, None) - result = [] - for i in range(0, count.value): - addr = lines[i].addr - if lines[i].instrIndex != 0xffffffffffffffff: - il_instr = self._function[lines[i].instrIndex] - else: - il_instr = None - color = binaryninja.highlight.HighlightColor._from_core_struct(lines[i].highlight) - tokens = binaryninja.function.InstructionTextToken.get_instruction_lines(lines[i].tokens, lines[i].count) - result.append(binaryninja.function.DisassemblyTextLine(tokens, addr, il_instr, color)) - core.BNFreeDisassemblyTextLines(lines, count.value) - return result + assert lines is not None, "core.BNGetHighLevelILExprText returned None" + try: + for i in range(0, count.value): + addr = lines[i].addr + if lines[i].instrIndex != 0xffffffffffffffff: + il_instr = self._function[lines[i].instrIndex] + else: + il_instr = None + color = highlight.HighlightColor._from_core_struct(lines[i].highlight) + tokens = function.InstructionTextToken._from_core_struct(lines[i].tokens, lines[i].count) + yield function.DisassemblyTextLine(tokens, addr, il_instr, color) + finally: + core.BNFreeDisassemblyTextLines(lines, count.value) @property - def prefix_operands(self): + def prefix_operands(self) -> List[Any]: """All operands in the expression tree in prefix order""" result = [HighLevelILOperationAndSize(self._operation, self._size)] for operand in self._operands: @@ -405,7 +418,7 @@ class HighLevelILInstruction(object): return result @property - def postfix_operands(self): + def postfix_operands(self) -> List[Any]: """All operands in the expression tree in postfix order""" result = [] for operand in self._operands: @@ -417,141 +430,144 @@ class HighLevelILInstruction(object): return result @property - def function(self): - """ """ + def function(self) -> 'HighLevelILFunction': return self._function @property - def expr_index(self): - """ """ + def expr_index(self) -> ExpressionIndex: return self._expr_index @expr_index.setter - def expr_index(self, value): + def expr_index(self, value:ExpressionIndex) -> None: self._expr_index = value @property - def instr_index(self): + def instr_index(self) -> int: """Index of the statement that this expression belongs to (read-only)""" return core.BNGetHighLevelILInstructionForExpr(self._function.handle, self._expr_index) @property - def instr(self): + def instr(self) -> 'HighLevelILInstruction': """The statement that this expression belongs to (read-only)""" return self._function[self.instr_index] @property - def ast(self): + def ast(self) -> 'HighLevelILInstruction': """This expression with full AST printing (read-only)""" if self._as_ast: return self return HighLevelILInstruction(self._function, self._expr_index, True) @property - def non_ast(self): + def non_ast(self) -> 'HighLevelILInstruction': """This expression without full AST printing (read-only)""" if not self._as_ast: return self return HighLevelILInstruction(self._function, self._expr_index, False) @property - def operation(self): - """ """ + def operation(self) -> HighLevelILOperation: return self._operation @operation.setter - def operation(self, value): + def operation(self, value:HighLevelILOperation) -> None: self._operation = value @property - def size(self): - """ """ + def size(self) -> int: return self._size @size.setter - def size(self, value): + def size(self, value:int) -> None: self._size = value @property - def address(self): - """ """ + def address(self) -> int: return self._address @address.setter - def address(self, value): + def address(self, value:int) -> None: self._address = value @property def source_operand(self): - """ """ - return self._source_operand + return self._source_operand @source_operand.setter def source_operand(self, value): self._source_operand = value @property - def operands(self): - """ """ + def operands(self) -> Any: return self._operands @operands.setter - def operands(self, value): + def operands(self, value: Any): self._operands = value @property - def parent(self): + def parent(self) -> Optional['HighLevelILInstruction']: if self._parent >= core.BNGetHighLevelILExprCount(self._function.handle): return None return HighLevelILInstruction(self._function, self._parent, self._as_ast) @property - def ssa_form(self): + def ssa_form(self) -> 'HighLevelILInstruction': """SSA form of expression (read-only)""" + assert self._function.ssa_form is not None return HighLevelILInstruction(self._function.ssa_form, core.BNGetHighLevelILSSAExprIndex(self._function.handle, self._expr_index), self._as_ast) @property - def non_ssa_form(self): + def non_ssa_form(self) -> Optional['HighLevelILInstruction']: """Non-SSA form of expression (read-only)""" + if self._function.non_ssa_form is None: + return None return HighLevelILInstruction(self._function.non_ssa_form, core.BNGetHighLevelILNonSSAExprIndex(self._function.handle, self._expr_index), self._as_ast) @property - def medium_level_il(self): + def medium_level_il(self) -> Optional['mediumlevelil.MediumLevelILInstruction']: """Medium level IL form of this expression""" expr = self._function.get_medium_level_il_expr_index(self._expr_index) if expr is None: return None - return mediumlevelil.MediumLevelILInstruction(self._function.medium_level_il.ssa_form, expr) + mlil = self._function.medium_level_il + if mlil is None: + return None + ssa_func = mlil.ssa_form + assert ssa_func is not None, "medium_level_il.ssa_form is None" + return mediumlevelil.MediumLevelILInstruction(ssa_func, expr) @property - def mlil(self): + def mlil(self) -> Optional['mediumlevelil.MediumLevelILInstruction']: """Alias for medium_level_il""" return self.medium_level_il @property - def mlils(self): - exprs = self._function.get_medium_level_il_expr_indexes(self._expr_index) - result = [] - for expr in exprs: - result.append(mediumlevelil.MediumLevelILInstruction(self._function.medium_level_il.ssa_form, expr)) - return result + def mlils(self) -> Generator['mediumlevelil.MediumLevelILInstruction', None, None]: + for expr in self._function.get_medium_level_il_expr_indexes(self._expr_index): + mlil = self._function.medium_level_il + if mlil is None: + return + ssa_func = mlil.ssa_form + assert ssa_func is not None, "medium_level_il.ssa_form is None" + yield mediumlevelil.MediumLevelILInstruction(ssa_func, expr) @property - def low_level_il(self): + def low_level_il(self) -> Optional['lowlevelil.LowLevelILInstruction']: """Low level IL form of this expression""" if self.mlil is None: return None return self.mlil.llil @property - def llil(self): + def llil(self) -> Optional['lowlevelil.LowLevelILInstruction']: """Alias for low_level_il""" return self.low_level_il @property - def llils(self): + def llils(self) -> List['lowlevelil.LowLevelILExpr']: result = set() for mlil_expr in self.mlils: for llil_expr in mlil_expr.llils: @@ -559,34 +575,34 @@ class HighLevelILInstruction(object): return list(result) @property - def il_basic_block(self): + def il_basic_block(self) -> Optional['HighLevelILBasicBlock']: """ IL basic block object containing this expression (read-only) (only available on finalized functions). Returns None for HLIL_BLOCK expressions as these can contain multiple basic blocks. """ block = core.BNGetHighLevelILBasicBlockForInstruction(self._function.handle, self._instr_index) - if not block: + if not block or self._function.source_function is None: return None return HighLevelILBasicBlock(self._function.source_function.view, block, self._function) @property - def value(self): + def value(self) -> 'variable.RegisterValue': """Value of expression if constant or a known value (read-only)""" mlil = self.mlil if mlil is None: - return function.RegisterValue() + return variable.RegisterValue() return mlil.value @property - def possible_values(self): + def possible_values(self) -> 'variable.PossibleValueSet': """Possible values of expression using path-sensitive static data flow analysis (read-only)""" mlil = self.mlil if mlil is None: - return function.PossibleValueSet() + return variable.PossibleValueSet() return mlil.possible_values @property - def expr_type(self): + def expr_type(self) -> Optional['types.Type']: """Type of expression""" result = core.BNGetHighLevelILExprType(self._function.handle, self._expr_index) if result.type: @@ -596,18 +612,18 @@ class HighLevelILInstruction(object): return types.Type(result.type, platform = platform, confidence = result.confidence) return None - def get_possible_values(self, options = []): + def get_possible_values(self, options:List[DataFlowQueryOption]=[]) -> 'variable.PossibleValueSet': mlil = self.mlil if mlil is None: - return function.RegisterValue() + return variable.PossibleValueSet() return mlil.get_possible_values(options) @property - def ssa_memory_version(self): + def ssa_memory_version(self) -> int: """Version of active memory contents in SSA form for this instruction""" return core.BNGetHighLevelILSSAMemoryVersionAtILInstruction(self._function.handle, self._instr_index) - def get_ssa_var_version(self, var): + def get_ssa_var_version(self, var:'variable.Variable') -> int: var_data = core.BNVariable() var_data.type = var.source_type var_data.index = var.index @@ -622,31 +638,28 @@ class HighLevelILExpr(object): .. note:: This class shouldn't be instantiated directly. Rather the helper members of HighLevelILFunction should be \ used instead. """ - def __init__(self, index): + def __init__(self, index:ExpressionIndex): self._index = index @property - def index(self): - """ """ + def index(self) -> ExpressionIndex: return self._index - @index.setter - def index(self, value): - self._index = value class HighLevelILFunction(object): """ ``class HighLevelILFunction`` contains the a HighLevelILInstruction object that makes up the abstract syntax tree of - a binaryninja.function. + a function. """ - def __init__(self, arch = None, handle = None, source_func = None): + def __init__(self, arch:Optional['architecture.Architecture']=None, handle:core.BNHighLevelILFunction=None, + source_func:'function.Function'=None): self._arch = arch self._source_function = source_func if handle is not None: self.handle = core.handle_of_type(handle, core.BNHighLevelILFunction) if self._source_function is None: - self._source_function = binaryninja.function.Function(handle = core.BNGetHighLevelILOwnerFunction(self.handle)) + self._source_function = function.Function(handle = core.BNGetHighLevelILOwnerFunction(self.handle)) if self._arch is None: self._arch = self._source_function.arch else: @@ -655,8 +668,12 @@ class HighLevelILFunction(object): raise ValueError("IL functions must be created with an associated function") if self._arch is None: self._arch = self._source_function.arch + if self._arch is None: + raise ValueError("IL functions must be created with an associated Architecture") func_handle = self._source_function.handle - self.handle = core.BNCreateHighLevelILFunction(arch.handle, func_handle) + self.handle = core.BNCreateHighLevelILFunction(self._arch.handle, func_handle) + assert self._source_function is not None + assert self._arch is not None def __del__(self): if self.handle is not None: @@ -672,6 +689,8 @@ class HighLevelILFunction(object): def __eq__(self, other): if not isinstance(other, self.__class__): return NotImplemented + assert self.handle is not None + assert other.handle is not None return ctypes.addressof(self.handle.contents) == ctypes.addressof(other.handle.contents) def __ne__(self, other): @@ -682,22 +701,61 @@ class HighLevelILFunction(object): def __hash__(self): return hash(('HLIL', self._source_function)) + def __len__(self): + return int(core.BNGetHighLevelILInstructionCount(self.handle)) + + def __getitem__(self, i:Union[HighLevelILExpr, HighLevelILInstruction, int]) -> HighLevelILInstruction: + if isinstance(i, slice) or isinstance(i, tuple): + raise IndexError("expected integer instruction index") + if isinstance(i, HighLevelILExpr): + return HighLevelILInstruction(self, i.index) + # for backwards compatibility + if isinstance(i, HighLevelILInstruction): + return i + if i < -len(self) or i >= len(self): + raise IndexError("index out of range") + if i < 0: + i = len(self) + i + return HighLevelILInstruction(self, core.BNGetHighLevelILIndexForInstruction(self.handle, i), False, + InstructionIndex(i)) + + def __setitem__(self, i, j): + raise IndexError("instruction modification not implemented") + + def __iter__(self) -> Generator['HighLevelILBasicBlock', None, None]: + count = ctypes.c_ulonglong() + blocks = core.BNGetHighLevelILBasicBlockList(self.handle, count) + assert blocks is not None, "core.BNGetHighLevelILBasicBlockList returned None" + view = None + if self._source_function is not None: + view = self._source_function.view + try: + for i in range(0, count.value): + core_block = core.BNNewBasicBlockReference(blocks[i]) + assert core_block is not None + yield HighLevelILBasicBlock(view, core_block, self) + finally: + core.BNFreeBasicBlockList(blocks, count.value) + + def __str__(self) -> str: + return str(self.root) + @property - def current_address(self): + def current_address(self) -> int: """Current IL Address (read/write)""" return core.BNHighLevelILGetCurrentAddress(self.handle) @current_address.setter - def current_address(self, value): - core.BNHighLevelILSetCurrentAddress(self.handle, self._arch.handle, value) + def current_address(self, value:int) -> None: + core.BNHighLevelILSetCurrentAddress(self.handle, self.arch.handle, value) - def set_current_address(self, value, arch = None): + def set_current_address(self, value:int, arch:Optional['architecture.Architecture'] = None) -> None: if arch is None: - arch = self._arch + arch = self.arch core.BNHighLevelILSetCurrentAddress(self.handle, arch.handle, value) @property - def root(self): + def root(self) -> Optional[HighLevelILInstruction]: """Root of the abstract syntax tree""" expr_index = core.BNGetHighLevelILRootExpr(self.handle) if expr_index >= core.BNGetHighLevelILExprCount(self.handle): @@ -705,53 +763,80 @@ class HighLevelILFunction(object): return HighLevelILInstruction(self, expr_index) @root.setter - def root(self, value): + def root(self, value:HighLevelILInstruction) -> None: core.BNSetHighLevelILRootExpr(value.expr_index) @property - def basic_blocks(self): + def basic_blocks(self) -> Generator['HighLevelILBasicBlock', None, None]: """list of HighLevelILBasicBlock objects (read-only)""" count = ctypes.c_ulonglong() blocks = core.BNGetHighLevelILBasicBlockList(self.handle, count) - result = [] + assert blocks is not None, "core.BNGetHighLevelILBasicBlockList returned None" + view = None if self._source_function is not None: view = self._source_function.view - for i in range(0, count.value): - result.append(HighLevelILBasicBlock(view, core.BNNewBasicBlockReference(blocks[i]), self)) - core.BNFreeBasicBlockList(blocks, count.value) - return result + + try: + for i in range(0, count.value): + core_block = core.BNNewBasicBlockReference(blocks[i]) + assert core_block is not None + yield HighLevelILBasicBlock(view, core_block, self) + finally: + core.BNFreeBasicBlockList(blocks, count.value) @property - def instructions(self): + def instructions(self) -> Generator[HighLevelILInstruction, None, None]: """A generator of hlil instructions of the current function""" for block in self.basic_blocks: for i in block: yield i @property - def ssa_form(self): + def ssa_form(self) -> 'HighLevelILFunction': """High level IL in SSA form (read-only)""" result = core.BNGetHighLevelILSSAForm(self.handle) - if not result: - return None + assert result is not None return HighLevelILFunction(self._arch, result, self._source_function) @property - def non_ssa_form(self): + def non_ssa_form(self) -> Optional['HighLevelILFunction']: """High level IL in non-SSA (default) form (read-only)""" result = core.BNGetHighLevelILNonSSAForm(self.handle) if not result: return None return HighLevelILFunction(self._arch, result, self._source_function) - def get_ssa_instruction_index(self, instr): + @property + def arch(self) -> 'architecture.Architecture': + assert self._arch is not None + return self._arch + + @property + def source_function(self) -> 'function.Function': + assert self._source_function is not None + return self._source_function + + @property + def medium_level_il(self) -> Optional['mediumlevelil.MediumLevelILFunction']: + """Medium level IL for this function""" + result = core.BNGetMediumLevelILForHighLevelILFunction(self.handle) + if not result: + return None + return mediumlevelil.MediumLevelILFunction(self._arch, result, self._source_function) + + @property + def mlil(self) -> Optional['mediumlevelil.MediumLevelILFunction']: + """Alias for medium_level_il""" + return self.medium_level_il + + def get_ssa_instruction_index(self, instr:int) -> int: return core.BNGetHighLevelILSSAInstructionIndex(self.handle, instr) - def get_non_ssa_instruction_index(self, instr): + def get_non_ssa_instruction_index(self, instr:int) -> int: return core.BNGetHighLevelILNonSSAInstructionIndex(self.handle, instr) - def get_ssa_var_definition(self, ssa_var): + def get_ssa_var_definition(self, ssa_var:'mediumlevelil.SSAVariable') -> Optional[HighLevelILInstruction]: var_data = core.BNVariable() var_data.type = ssa_var.var.source_type var_data.index = ssa_var.var.index @@ -761,35 +846,37 @@ class HighLevelILFunction(object): return None return HighLevelILInstruction(self, result) - def get_ssa_memory_definition(self, version): + def get_ssa_memory_definition(self, version:int) -> Optional[HighLevelILInstruction]: result = core.BNGetHighLevelILSSAMemoryDefinition(self.handle, version) if result >= core.BNGetHighLevelILExprCount(self.handle): return None return HighLevelILInstruction(self, result) - def get_ssa_var_uses(self, ssa_var): + def get_ssa_var_uses(self, ssa_var:'mediumlevelil.SSAVariable') -> List[HighLevelILInstruction]: count = ctypes.c_ulonglong() var_data = core.BNVariable() var_data.type = ssa_var.var.source_type var_data.index = ssa_var.var.index var_data.storage = ssa_var.var.storage instrs = core.BNGetHighLevelILSSAVarUses(self.handle, var_data, ssa_var.version, count) + assert instrs is not None, "core.BNGetHighLevelILSSAVarUses returned None" result = [] for i in range(0, count.value): result.append(HighLevelILInstruction(self, instrs[i])) core.BNFreeILInstructionList(instrs) return result - def get_ssa_memory_uses(self, version): + def get_ssa_memory_uses(self, version:int) -> List[HighLevelILInstruction]: count = ctypes.c_ulonglong() instrs = core.BNGetHighLevelILSSAMemoryUses(self.handle, version, count) + assert instrs is not None, "core.BNGetHighLevelILSSAMemoryUses returned None" result = [] for i in range(0, count.value): result.append(HighLevelILInstruction(self, instrs[i])) core.BNFreeILInstructionList(instrs) return result - def is_ssa_var_live(self, ssa_var): + def is_ssa_var_live(self, ssa_var:'mediumlevelil.SSAVariable') -> bool: """ ``is_ssa_var_live`` determines if ``ssa_var`` is live at any point in the function @@ -803,81 +890,43 @@ class HighLevelILFunction(object): var_data.storage = ssa_var.var.storage return core.BNIsHighLevelILSSAVarLive(self.handle, var_data, ssa_var.version) - def get_var_definitions(self, var): + def get_var_definitions(self, var:'variable.Variable') -> List[HighLevelILInstruction]: count = ctypes.c_ulonglong() var_data = core.BNVariable() var_data.type = var.source_type var_data.index = var.index var_data.storage = var.storage instrs = core.BNGetHighLevelILVariableDefinitions(self.handle, var_data, count) + assert instrs is not None, "core.BNGetHighLevelILVariableDefinitions returned None" result = [] for i in range(0, count.value): result.append(HighLevelILInstruction(self, instrs[i])) core.BNFreeILInstructionList(instrs) return result - def get_var_uses(self, var): + def get_var_uses(self, var:'variable.Variable') -> List[HighLevelILInstruction]: count = ctypes.c_ulonglong() var_data = core.BNVariable() var_data.type = var.source_type var_data.index = var.index var_data.storage = var.storage instrs = core.BNGetHighLevelILVariableUses(self.handle, var_data, count) + assert instrs is not None, "core.BNGetHighLevelILVariableUses returned None" result = [] for i in range(0, count.value): result.append(HighLevelILInstruction(self, instrs[i])) core.BNFreeILInstructionList(instrs) return result - def __setattr__(self, name, value): - try: - object.__setattr__(self, name, value) - except AttributeError: - raise AttributeError("attribute '%s' is read only" % name) - - def __len__(self): - return int(core.BNGetHighLevelILInstructionCount(self.handle)) - - def __getitem__(self, i): - if isinstance(i, slice) or isinstance(i, tuple): - raise IndexError("expected integer instruction index") - if isinstance(i, HighLevelILExpr): - return HighLevelILInstruction(self, i.index) - # for backwards compatibility - if isinstance(i, HighLevelILInstruction): - return i - if i < -len(self) or i >= len(self): - raise IndexError("index out of range") - if i < 0: - i = len(self) + i - return HighLevelILInstruction(self, core.BNGetHighLevelILIndexForInstruction(self.handle, i), False, i) - - def __setitem__(self, i, j): - raise IndexError("instruction modification not implemented") - - def __iter__(self): - count = ctypes.c_ulonglong() - blocks = core.BNGetHighLevelILBasicBlockList(self.handle, count) - view = None - if self._source_function is not None: - view = self._source_function.view - try: - for i in range(0, count.value): - yield HighLevelILBasicBlock(view, core.BNNewBasicBlockReference(blocks[i]), self) - finally: - core.BNFreeBasicBlockList(blocks, count.value) - - def __str__(self): - return str(self.root) - - def expr(self, operation, a = 0, b = 0, c = 0, d = 0, e = 0, size = 0): + def expr(self, operation:Union[str, HighLevelILOperation], a:int = 0, b:int = 0, c:int = 0, + d:int = 0, e:int = 0, size:int = 0) -> HighLevelILExpr: if isinstance(operation, str): - operation = HighLevelILOperation[operation] + operation_value = HighLevelILOperation[operation] elif isinstance(operation, HighLevelILOperation): - operation = operation.value - return HighLevelILExpr(core.BNHighLevelILAddExpr(self.handle, operation, size, a, b, c, d, e)) + operation_value = operation.value + return HighLevelILExpr(core.BNHighLevelILAddExpr(self.handle, operation_value, size, a, b, c, d, e)) - def add_operand_list(self, operands): + def add_operand_list(self, operands:List[int]) -> HighLevelILExpr: """ ``add_operand_list`` returns an operand list expression for the given list of integer operands. @@ -890,7 +939,7 @@ class HighLevelILFunction(object): operand_list[i] = operands[i] return HighLevelILExpr(core.BNHighLevelILAddOperandList(self.handle, operand_list, len(operands))) - def finalize(self): + def finalize(self) -> None: """ ``finalize`` ends the function and computes the list of basic blocks. @@ -898,21 +947,12 @@ class HighLevelILFunction(object): """ core.BNFinalizeHighLevelILFunction(self.handle) - def create_graph(self, settings = None): + def create_graph(self, settings:'function.DisassemblySettings'=None) -> 'flowgraph.CoreFlowGraph': if settings is not None: settings_obj = settings.handle else: settings_obj = None - return binaryninja.flowgraph.CoreFlowGraph(core.BNCreateHighLevelILFunctionGraph(self.handle, settings_obj)) - - @property - def arch(self): - """ """ - return self._arch - - @arch.setter - def arch(self, value): - self._arch = value + return flowgraph.CoreFlowGraph(core.BNCreateHighLevelILFunctionGraph(self.handle, settings_obj)) @property def source_function(self): @@ -981,7 +1021,7 @@ class HighLevelILFunction(object): """Alias for medium_level_il""" return self.medium_level_il - def get_medium_level_il_expr_index(self, expr): + def get_medium_level_il_expr_index(self, expr:ExpressionIndex) -> Optional['mediumlevelil.ExpressionIndex']: medium_il = self.medium_level_il if medium_il is None: return None @@ -993,24 +1033,26 @@ class HighLevelILFunction(object): return None return result - def get_medium_level_il_expr_indexes(self, expr): + def get_medium_level_il_expr_indexes(self, expr:ExpressionIndex) -> List['mediumlevelil.ExpressionIndex']: count = ctypes.c_ulonglong() exprs = core.BNGetMediumLevelILExprIndexesFromHighLevelIL(self.handle, expr, count) + assert exprs is not None, "core.BNGetMediumLevelILExprIndexesFromHighLevelIL returned None" result = [] for i in range(0, count.value): result.append(exprs[i]) core.BNFreeILInstructionList(exprs) return result - def get_label(self, label_idx): + def get_label(self, label_idx:int) -> Optional[HighLevelILInstruction]: result = core.BNGetHighLevelILExprIndexForLabel(self.handle, label_idx) if result >= core.BNGetHighLevelILExprCount(self.handle): return None return HighLevelILInstruction(self, result) - def get_label_uses(self, label_idx): + def get_label_uses(self, label_idx:int) -> List[HighLevelILInstruction]: count = ctypes.c_ulonglong() uses = core.BNGetHighLevelILUsesForLabel(self.handle, label_idx, count) + assert uses is not None, "core.BNGetHighLevelILUsesForLabel returned None" result = [] for i in range(0, count.value): result.append(HighLevelILInstruction(self, uses[i])) @@ -1019,15 +1061,15 @@ class HighLevelILFunction(object): class HighLevelILBasicBlock(basicblock.BasicBlock): - def __init__(self, view, handle, owner): + def __init__(self, view:Optional['binaryview.BinaryView'], handle:core.BNBasicBlock, owner:HighLevelILFunction): super(HighLevelILBasicBlock, self).__init__(handle, view) - self.il_function = owner + self._il_function = owner - def __iter__(self): + def __iter__(self) -> Generator[HighLevelILInstruction, None, None]: for idx in range(self.start, self.end): yield self.il_function[idx] - def __getitem__(self, idx): + def __getitem__(self, idx) -> HighLevelILInstruction: size = self.end - self.start if isinstance(idx, slice): return [self[index] for index in range(*idx.indices(size))] @@ -1038,7 +1080,7 @@ class HighLevelILBasicBlock(basicblock.BasicBlock): else: return self.il_function[self.end + idx] - def _create_instance(self, handle, view): + def _create_instance(self, handle:core.BNBasicBlock, view:'binaryview.BinaryView'): """Internal method by super to instantiate child instances""" return HighLevelILBasicBlock(view, handle, self.il_function) @@ -1054,10 +1096,9 @@ class HighLevelILBasicBlock(basicblock.BasicBlock): return False @property - def il_function(self): - """ """ - return self._il_function + def instruction_count(self) -> int: + return self.end - self.start - @il_function.setter - def il_function(self, value): - self._il_function = value + @property + def il_function(self) -> HighLevelILFunction: + return self._il_function diff --git a/python/highlight.py b/python/highlight.py index 889eef33..539e71a5 100644 --- a/python/highlight.py +++ b/python/highlight.py @@ -20,8 +20,8 @@ # Binary Ninja components -from binaryninja import _binaryninjacore as core -from binaryninja.enums import HighlightColorStyle, HighlightStandardColor +from . import _binaryninjacore as core +from .enums import HighlightColorStyle, HighlightStandardColor class HighlightColor(object): @@ -49,7 +49,6 @@ class HighlightColor(object): @property def alpha(self): - """ """ return self._alpha @alpha.setter @@ -58,7 +57,6 @@ class HighlightColor(object): @property def mix(self): - """ """ return self._mix @mix.setter @@ -67,7 +65,6 @@ class HighlightColor(object): @property def mix_color(self): - """ """ return self._mix_color @mix_color.setter @@ -76,7 +73,6 @@ class HighlightColor(object): @property def color(self): - """ """ return self._color @color.setter @@ -85,7 +81,6 @@ class HighlightColor(object): @property def style(self): - """ """ return self._style @style.setter @@ -94,7 +89,6 @@ class HighlightColor(object): @property def green(self): - """ """ return self._green @green.setter @@ -103,7 +97,6 @@ class HighlightColor(object): @property def red(self): - """ """ return self._red @red.setter @@ -112,7 +105,6 @@ class HighlightColor(object): @property def blue(self): - """ """ return self._blue @blue.setter @@ -192,3 +184,4 @@ class HighlightColor(object): elif color.style == HighlightColorStyle.CustomHighlightColor: return HighlightColor(red=color.r, green=color.g, blue=color.b, alpha=color.alpha) return HighlightColor(color=HighlightStandardColor.NoHighlightColor) + diff --git a/python/interaction.py b/python/interaction.py index 7cc4442b..aecb3b44 100644 --- a/python/interaction.py +++ b/python/interaction.py @@ -20,23 +20,21 @@ import ctypes import traceback +from typing import Optional # Binary Ninja components -from binaryninja import _binaryninjacore as core -from binaryninja.enums import FormInputFieldType, MessageBoxIcon, MessageBoxButtonSet, MessageBoxButtonResult, ReportType -from binaryninja import binaryview -from binaryninja import log -from binaryninja import flowgraph - -# 2-3 compatibility -from binaryninja import range +from . import _binaryninjacore as core +from .enums import FormInputFieldType, MessageBoxIcon, MessageBoxButtonSet, MessageBoxButtonResult, ReportType +from . import binaryview +from . import log +from . import flowgraph class LabelField(object): """ ``LabelField`` adds a text label to the display. """ - def __init__(self, text): + def __init__(self, text:str): self._text = text def _fill_core_struct(self, value): @@ -51,12 +49,11 @@ class LabelField(object): pass @property - def text(self): - """ """ + def text(self) -> str: return self._text @text.setter - def text(self, value): + def text(self, value:str) -> None: self._text = value @@ -99,7 +96,6 @@ class TextLineField(object): @property def prompt(self): - """ """ return self._prompt @prompt.setter @@ -108,7 +104,6 @@ class TextLineField(object): @property def result(self): - """ """ return self._result @result.setter @@ -141,7 +136,6 @@ class MultilineTextField(object): @property def prompt(self): - """ """ return self._prompt @prompt.setter @@ -150,7 +144,6 @@ class MultilineTextField(object): @property def result(self): - """ """ return self._result @result.setter @@ -182,7 +175,6 @@ class IntegerField(object): @property def prompt(self): - """ """ return self._prompt @prompt.setter @@ -191,7 +183,6 @@ class IntegerField(object): @property def result(self): - """ """ return self._result @result.setter @@ -234,7 +225,6 @@ class AddressField(object): @property def prompt(self): - """ """ return self._prompt @prompt.setter @@ -243,7 +233,6 @@ class AddressField(object): @property def view(self): - """ """ return self._view @view.setter @@ -252,7 +241,6 @@ class AddressField(object): @property def current_address(self): - """ """ return self._current_address @current_address.setter @@ -261,7 +249,6 @@ class AddressField(object): @property def result(self): - """ """ return self._result @result.setter @@ -303,7 +290,6 @@ class ChoiceField(object): @property def prompt(self): - """ """ return self._prompt @prompt.setter @@ -312,7 +298,6 @@ class ChoiceField(object): @property def choices(self): - """ """ return self._choices @choices.setter @@ -321,7 +306,6 @@ class ChoiceField(object): @property def result(self): - """ """ return self._result @result.setter @@ -355,7 +339,6 @@ class OpenFileNameField(object): @property def prompt(self): - """ """ return self._prompt @prompt.setter @@ -364,7 +347,6 @@ class OpenFileNameField(object): @property def ext(self): - """ """ return self._ext @ext.setter @@ -373,7 +355,6 @@ class OpenFileNameField(object): @property def result(self): - """ """ return self._result @result.setter @@ -409,7 +390,6 @@ class SaveFileNameField(object): @property def prompt(self): - """ """ return self._prompt @prompt.setter @@ -418,7 +398,6 @@ class SaveFileNameField(object): @property def ext(self): - """ """ return self._ext @ext.setter @@ -427,7 +406,6 @@ class SaveFileNameField(object): @property def default_name(self): - """ """ return self._default_name @default_name.setter @@ -436,7 +414,6 @@ class SaveFileNameField(object): @property def result(self): - """ """ return self._result @result.setter @@ -471,7 +448,6 @@ class DirectoryNameField(object): @property def prompt(self): - """ """ return self._prompt @prompt.setter @@ -480,7 +456,6 @@ class DirectoryNameField(object): @property def default_name(self): - """ """ return self._default_name @default_name.setter @@ -489,7 +464,6 @@ class DirectoryNameField(object): @property def result(self): - """ """ return self._result @result.setter @@ -756,7 +730,6 @@ class PlainTextReport(object): @property def view(self): - """ """ return self._view @view.setter @@ -765,7 +738,6 @@ class PlainTextReport(object): @property def title(self): - """ """ return self._title @title.setter @@ -774,7 +746,6 @@ class PlainTextReport(object): @property def contents(self): - """ """ return self._contents @contents.setter @@ -797,7 +768,6 @@ class MarkdownReport(object): @property def view(self): - """ """ return self._view @view.setter @@ -806,7 +776,6 @@ class MarkdownReport(object): @property def title(self): - """ """ return self._title @title.setter @@ -815,7 +784,6 @@ class MarkdownReport(object): @property def contents(self): - """ """ return self._contents @contents.setter @@ -824,7 +792,6 @@ class MarkdownReport(object): @property def plaintext(self): - """ """ return self._plaintext @plaintext.setter @@ -847,7 +814,6 @@ class HTMLReport(object): @property def view(self): - """ """ return self._view @view.setter @@ -856,7 +822,6 @@ class HTMLReport(object): @property def title(self): - """ """ return self._title @title.setter @@ -865,7 +830,6 @@ class HTMLReport(object): @property def contents(self): - """ """ return self._contents @contents.setter @@ -874,7 +838,6 @@ class HTMLReport(object): @property def plaintext(self): - """ """ return self._plaintext @plaintext.setter @@ -893,7 +856,6 @@ class FlowGraphReport(object): @property def view(self): - """ """ return self._view @view.setter @@ -902,7 +864,6 @@ class FlowGraphReport(object): @property def title(self): - """ """ return self._title @title.setter @@ -911,7 +872,6 @@ class FlowGraphReport(object): @property def graph(self): - """ """ return self._graph @graph.setter @@ -1188,7 +1148,7 @@ def get_choice_input(prompt, title, choices): return value.value -def get_open_filename_input(prompt, ext=""): +def get_open_filename_input(prompt:str, ext:str="") -> Optional[str]: """ ``get_open_filename_input`` prompts the user for a file name to open @@ -1197,25 +1157,26 @@ def get_open_filename_input(prompt, ext=""): Multiple file selection groups can be included if separated by two semicolons. Multiple file wildcards may be specified by using a space within the parenthesis. - Also, a simple selector of "\*.extension" by itself may also be used instead of specifying the description. + Also, a simple selector of "*.extension" by itself may also be used instead of specifying the description. :param str prompt: Prompt to display. :param str ext: Optional, file extension :Example: >>> get_open_filename_input("filename:", "Executables (*.exe *.com);;Python Files (*.py);;All Files (*)") - b'foo.exe' + 'foo.exe' >>> get_open_filename_input("filename:", "*.py") - b'test.py' + 'test.py' """ value = ctypes.c_char_p() if not core.BNGetOpenFileNameInput(value, prompt, ext): return None result = value.value + assert result is not None core.BNFreeString(ctypes.cast(value, ctypes.POINTER(ctypes.c_byte))) - return result + return result.decode("utf-8") -def get_save_filename_input(prompt, ext="", default_name=""): +def get_save_filename_input(prompt:str, ext:str="", default_name:str="") -> Optional[str]: """ ``get_save_filename_input`` prompts the user for a file name to save as, optionally providing a file extension and \ default_name @@ -1235,11 +1196,12 @@ def get_save_filename_input(prompt, ext="", default_name=""): if not core.BNGetSaveFileNameInput(value, prompt, ext, default_name): return None result = value.value + assert result is not None core.BNFreeString(ctypes.cast(value, ctypes.POINTER(ctypes.c_byte))) - return result + return result.decode("utf-8") -def get_directory_name_input(prompt, default_name=""): +def get_directory_name_input(prompt:str, default_name:str=""): """ ``get_directory_name_input`` prompts the user for a directory name to save as, optionally providing a default_name @@ -1257,8 +1219,9 @@ def get_directory_name_input(prompt, default_name=""): if not core.BNGetDirectoryNameInput(value, prompt, default_name): return None result = value.value + assert result is not None core.BNFreeString(ctypes.cast(value, ctypes.POINTER(ctypes.c_byte))) - return result + return result.decode("utf-8") def get_form_input(fields, title): @@ -1275,7 +1238,7 @@ def get_form_input(fields, title): SeparatorField Vertical spacing TextLineField Prompt for a string value MultilineTextField Prompt for multi-line string value - IntegerFieldch Prompt for an integer + IntegerField Prompt for an integer AddressField Prompt for an address ChoiceField Prompt for a choice from provided options OpenFileNameField Prompt for file to open diff --git a/python/lineardisassembly.py b/python/lineardisassembly.py index e4b094e3..3cbdd06e 100644 --- a/python/lineardisassembly.py +++ b/python/lineardisassembly.py @@ -21,11 +21,11 @@ import ctypes import binaryninja -from binaryninja import _binaryninjacore as core -from binaryninja import highlight -from binaryninja import function -from binaryninja import basicblock -from binaryninja.enums import LinearViewObjectIdentifierType +from . import _binaryninjacore as core +from . import highlight +from . import function +from . import basicblock +from .enums import LinearViewObjectIdentifierType class LinearDisassemblyLine(object): @@ -115,8 +115,8 @@ class LinearViewObjectIdentifier(object): def has_range(self): return self._start is not None and self._end is not None - @classmethod - def _from_api_object(cls, obj): + @staticmethod + def _from_api_object(obj): if obj.type == LinearViewObjectIdentifierType.AddressLinearViewObject: result = LinearViewObjectIdentifier(obj.name, obj.start) elif obj.type == LinearViewObjectIdentifierType.AddressRangeLinearViewObject: @@ -244,19 +244,22 @@ class LinearViewObject(object): count = ctypes.c_ulonglong(0) lines = core.BNGetLinearViewObjectLines(self.handle, prev_obj, next_obj, count) + assert lines is not None, "core.BNGetLinearViewObjectLines returned None" result = [] for i in range(0, count.value): func = None block = None if lines[i].function: - func = binaryninja.function.Function(self, core.BNNewFunctionReference(lines[i].function)) + func = function.Function(None, core.BNNewFunctionReference(lines[i].function)) if lines[i].block: - block = binaryninja.basicblock.BasicBlock(core.BNNewBasicBlockReference(lines[i].block), self) + core_block = core.BNNewBasicBlockReference(lines[i].block) + assert core_block is not None, "core.BNNewBasicBlockReference returned None" + block = basicblock.BasicBlock(core_block, None) color = highlight.HighlightColor._from_core_struct(lines[i].contents.highlight) addr = lines[i].contents.addr - tokens = binaryninja.function.InstructionTextToken.get_instruction_lines(lines[i].contents.tokens, lines[i].contents.count) - contents = binaryninja.function.DisassemblyTextLine(tokens, addr, color = color) + tokens = function.InstructionTextToken._from_core_struct(lines[i].contents.tokens, lines[i].contents.count) + contents = function.DisassemblyTextLine(tokens, addr, color = color) result.append(LinearDisassemblyLine(lines[i].type, func, block, contents)) core.BNFreeLinearDisassemblyLines(lines, count.value) @@ -265,62 +268,62 @@ class LinearViewObject(object): def ordering_index_for_child(self, child): return core.BNGetLinearViewObjectOrderingIndexForChild(self.handle, child.handle) - @classmethod - def disassembly(cls, view, settings = None): + @staticmethod + def disassembly(view, settings = None): if settings is not None: settings = settings.handle return LinearViewObject(core.BNCreateLinearViewDisassembly(view.handle, settings)) - @classmethod - def lifted_il(cls, view, settings = None): + @staticmethod + def lifted_il(view, settings = None): if settings is not None: settings = settings.handle return LinearViewObject(core.BNCreateLinearViewLiftedIL(view.handle, settings)) - @classmethod - def llil(cls, view, settings = None): + @staticmethod + def llil(view, settings = None): if settings is not None: settings = settings.handle return LinearViewObject(core.BNCreateLinearViewLowLevelIL(view.handle, settings)) - @classmethod - def llil_ssa_form(cls, view, settings = None): + @staticmethod + def llil_ssa_form(view, settings = None): if settings is not None: settings = settings.handle return LinearViewObject(core.BNCreateLinearViewLowLevelILSSAForm(view.handle, settings)) - @classmethod - def mlil(cls, view, settings = None): + @staticmethod + def mlil(view, settings = None): if settings is not None: settings = settings.handle return LinearViewObject(core.BNCreateLinearViewMediumLevelIL(view.handle, settings)) - @classmethod - def mlil_ssa_form(cls, view, settings = None): + @staticmethod + def mlil_ssa_form(view, settings = None): if settings is not None: settings = settings.handle return LinearViewObject(core.BNCreateLinearViewMediumLevelILSSAForm(view.handle, settings)) - @classmethod - def mmlil(cls, view, settings = None): + @staticmethod + def mmlil(view, settings = None): if settings is not None: settings = settings.handle return LinearViewObject(core.BNCreateLinearViewMappedMediumLevelIL(view.handle, settings)) - @classmethod - def mmlil_ssa_form(cls, view, settings = None): + @staticmethod + def mmlil_ssa_form(view, settings = None): if settings is not None: settings = settings.handle return LinearViewObject(core.BNCreateLinearViewMappedMediumLevelILSSAForm(view.handle, settings)) - @classmethod - def hlil(cls, view, settings = None): + @staticmethod + def hlil(view, settings = None): if settings is not None: settings = settings.handle return LinearViewObject(core.BNCreateLinearViewHighLevelIL(view.handle, settings)) - @classmethod - def hlil_ssa_form(cls, view, settings = None): + @staticmethod + def hlil_ssa_form(view, settings = None): if settings is not None: settings = settings.handle return LinearViewObject(core.BNCreateLinearViewHighLevelILSSAForm(view.handle, settings)) @@ -393,6 +396,7 @@ class LinearViewCursor(object): def current_object(self): count = ctypes.c_ulonglong(0) path = core.BNGetLinearViewCursorPathObjects(self.handle, count) + assert path is not None, "core.BNGetLinearViewCursorPathObjects returned None" result = None for i in range(0, count.value): result = LinearViewObject(core.BNNewLinearViewObjectReference(path[i]), result) @@ -403,6 +407,7 @@ class LinearViewCursor(object): def path(self): count = ctypes.c_ulonglong(0) path = core.BNGetLinearViewCursorPath(self.handle, count) + assert path is not None, "core.BNGetLinearViewCursorPath returned None" result = [] for i in range(0, count.value): result.append(LinearViewObjectIdentifier._from_api_object(path[i])) @@ -413,6 +418,7 @@ class LinearViewCursor(object): def path_objects(self): count = ctypes.c_ulonglong(0) path = core.BNGetLinearViewCursorPathObjects(self.handle, count) + assert path is not None, "core.BNGetLinearViewCursorPathObjects returned None" result = [] parent = None for i in range(0, count.value): @@ -464,19 +470,22 @@ class LinearViewCursor(object): def lines(self): count = ctypes.c_ulonglong(0) lines = core.BNGetLinearViewCursorLines(self.handle, count) + assert lines is not None, "core.BNGetLinearViewCursorLines returned None" result = [] for i in range(0, count.value): func = None block = None if lines[i].function: - func = binaryninja.function.Function(self, core.BNNewFunctionReference(lines[i].function)) + func = function.Function(None, core.BNNewFunctionReference(lines[i].function)) if lines[i].block: - block = binaryninja.basicblock.BasicBlock(core.BNNewBasicBlockReference(lines[i].block), self) + core_block = core.BNNewBasicBlockReference(lines[i].block) + assert core_block is not None, "core.BNNewBasicBlockReference returned None" + block = basicblock.BasicBlock(core_block, None) color = highlight.HighlightColor._from_core_struct(lines[i].contents.highlight) addr = lines[i].contents.addr - tokens = binaryninja.function.InstructionTextToken.get_instruction_lines(lines[i].contents.tokens, lines[i].contents.count) - contents = binaryninja.function.DisassemblyTextLine(tokens, addr, color = color) + tokens = function.InstructionTextToken._from_core_struct(lines[i].contents.tokens, lines[i].contents.count) + contents = function.DisassemblyTextLine(tokens, addr, color = color) result.append(LinearDisassemblyLine(lines[i].type, func, block, contents)) core.BNFreeLinearDisassemblyLines(lines, count.value) @@ -485,6 +494,6 @@ class LinearViewCursor(object): def duplicate(self): return LinearViewCursor(None, handle = core.BNDuplicateLinearViewCursor(self.handle)) - @classmethod - def compare(cls, a, b): + @staticmethod + def compare(a, b): return core.BNCompareLinearViewCursors(a.handle, b.handle) diff --git a/python/log.py b/python/log.py index 327ec561..e72a843a 100644 --- a/python/log.py +++ b/python/log.py @@ -20,8 +20,8 @@ # Binary Ninja components -from binaryninja import _binaryninjacore as core -from binaryninja.enums import LogLevel +from . import _binaryninjacore as core +from .enums import LogLevel _output_to_log = False diff --git a/python/lowlevelil.py b/python/lowlevelil.py index f374cd13..3cde2135 100644 --- a/python/lowlevelil.py +++ b/python/lowlevelil.py @@ -19,22 +19,34 @@ # IN THE SOFTWARE. import ctypes +from enum import Flag import struct +from typing import Generator, List, Optional, Any, Mapping, Union, Tuple, NewType # Binary Ninja components -import binaryninja -from binaryninja import _binaryninjacore as core -from binaryninja.enums import LowLevelILOperation, LowLevelILFlagCondition, FunctionGraphType, VariableSourceType -from binaryninja import basicblock #required for LowLevelILBasicBlock - -from typing import List, Union - -# 2-3 compatibility -from binaryninja import range +from .enums import LowLevelILOperation, LowLevelILFlagCondition, InstructionTextTokenType, DataFlowQueryOption, FunctionGraphType +from . import _binaryninjacore as core +from . import basicblock #required for LowLevelILBasicBlock +from . import function +from . import mediumlevelil +from . import highlevelil +from . import flowgraph +from . import variable +from . import binaryview +from . import architecture +from . import types +ExpressionIndex = NewType('ExpressionIndex', int) +InstructionIndex = NewType('InstructionIndex', int) +Index = Union[ExpressionIndex, InstructionIndex] +TokenList = List['function.InstructionTextToken'] +InstructionOrExpression = Union['LowLevelILInstruction', 'LowLevelILExpr', Index] +ILRegisterType = Union[str, 'ILRegister', int] +LLILInstructionsType = Generator['LowLevelILInstruction', None, None] +LLILBasicBlocksType = Generator['LowLevelILBasicBlock', None, None] class LowLevelILLabel(object): - def __init__(self, handle = None): + def __init__(self, handle:core.BNLowLevelILLabel=None): if handle is None: self.handle = (core.BNLowLevelILLabel * 1)() core.BNLowLevelILInitLabel(self.handle) @@ -44,17 +56,17 @@ class LowLevelILLabel(object): # TODO : It would be nice to add a `.versions` to IL vars (regs, stack regs, flags) to see all the SSA versions of the given variable. Would need to associate the source function class ILRegister(object): - def __init__(self, arch, reg): + def __init__(self, arch:'architecture.Architecture', reg:'architecture.RegisterIndex'): self._arch = arch self._index = reg self._temp = (self._index & 0x80000000) != 0 if self._temp: - self._name = "temp%d" % (self._index & 0x7fffffff) + self._name = architecture.RegisterName("temp%d" % (self._index & 0x7fffffff)) else: - self._name = self._arch.get_reg_name(self._index) + self._name = architecture.RegisterName(self._arch.get_reg_name(self._index)) @property - def info(self): + def info(self) -> 'architecture.RegisterInfo': return self._arch.regs[self._name] def __repr__(self): @@ -64,8 +76,10 @@ class ILRegister(object): return self._name def __eq__(self, other): - if isinstance(other, str) and other in self._arch.regs: - other = binaryninja.lowlevelil.ILRegister(self._arch, self._arch.regs[other].index) + if isinstance(other, architecture.RegisterName) and other in self._arch.regs: + index = self._arch.regs[other].index + assert index is not None + other = ILRegister(self._arch, index) elif not isinstance(other, self.__class__): return NotImplemented return (self._arch, self._index, self._name) == (other._arch, other._index, other._name) @@ -79,44 +93,24 @@ class ILRegister(object): return hash((self._arch, self._index, self._name)) @property - def arch(self): - """ """ + def arch(self) -> 'architecture.Architecture': return self._arch - @arch.setter - def arch(self, value): - self._arch = value - @property - def index(self): - """ """ + def index(self) -> 'architecture.RegisterIndex': return self._index - @index.setter - def index(self, value): - self._index = value - @property - def temp(self): - """ """ + def temp(self) -> bool: return self._temp - @temp.setter - def temp(self, value): - self._temp = value - @property - def name(self): - """ """ + def name(self) -> str: return self._name - @name.setter - def name(self, value): - self._name = value - class ILRegisterStack(object): - def __init__(self, arch, reg_stack): + def __init__(self, arch:'architecture.Architecture', reg_stack:'architecture.RegisterStackIndex'): self._arch = arch self._index = reg_stack self._name = self._arch.get_reg_stack_name(self._index) @@ -145,35 +139,20 @@ class ILRegisterStack(object): return hash((self._arch, self._index, self._name)) @property - def arch(self): - """ """ + def arch(self) -> 'architecture.Architecture': return self._arch - @arch.setter - def arch(self, value): - self._arch = value - @property - def index(self): - """ """ + def index(self) -> 'architecture.RegisterStackIndex': return self._index - @index.setter - def index(self, value): - self._index = value - @property - def name(self): - """ """ + def name(self) -> str: return self._name - @name.setter - def name(self, value): - self._name = value - class ILFlag(object): - def __init__(self, arch, flag): + def __init__(self, arch:'architecture.Architecture', flag:'architecture.FlagIndex'): self._arch = arch self._index = flag self._temp = (self._index & 0x80000000) != 0 @@ -201,45 +180,28 @@ class ILFlag(object): def __hash__(self): return hash((self._arch, self._index, self._name)) + def __int__(self): + return self._index + @property - def arch(self): - """ """ + def arch(self) -> 'architecture.Architecture': return self._arch - @arch.setter - def arch(self, value): - self._arch = value - @property - def index(self): - """ """ + def index(self) -> 'architecture.FlagIndex': return self._index - @index.setter - def index(self, value): - self._index = value - @property def temp(self): - """ """ return self._temp - @temp.setter - def temp(self, value): - self._temp = value - @property - def name(self): - """ """ + def name(self) -> str: return self._name - @name.setter - def name(self, value): - self._name = value - class ILSemanticFlagClass(object): - def __init__(self, arch, sem_class): + def __init__(self, arch:'architecture.Architecture', sem_class:'architecture.SemanticClassIndex'): self._arch = arch self._index = sem_class self._name = self._arch.get_semantic_flag_class_name(self._index) @@ -264,35 +226,20 @@ class ILSemanticFlagClass(object): return hash((self._arch, self._index, self._name)) @property - def arch(self): - """ """ + def arch(self) -> 'architecture.Architecture': return self._arch - @arch.setter - def arch(self, value): - self._arch = value - @property - def index(self): - """ """ + def index(self) -> 'architecture.SemanticClassIndex': return self._index - @index.setter - def index(self, value): - self._index = value - @property - def name(self): - """ """ + def name(self) -> str: return self._name - @name.setter - def name(self, value): - self._name = value - class ILSemanticFlagGroup(object): - def __init__(self, arch, sem_group): + def __init__(self, arch:'architecture.Architecture', sem_group:'architecture.SemanticGroupIndex'): self._arch = arch self._index = sem_group self._name = self._arch.get_semantic_flag_group_name(self._index) @@ -317,35 +264,20 @@ class ILSemanticFlagGroup(object): return hash((self._arch, self._index, self._name)) @property - def arch(self): - """ """ + def arch(self) -> 'architecture.Architecture': return self._arch - @arch.setter - def arch(self, value): - self._arch = value - @property - def index(self): - """ """ + def index(self) -> 'architecture.SemanticGroupIndex': return self._index - @index.setter - def index(self, value): - self._index = value - @property - def name(self): - """ """ + def name(self) -> 'architecture.SemanticGroupName': return self._name - @name.setter - def name(self, value): - self._name = value - class ILIntrinsic(object): - def __init__(self, arch, intrinsic): + def __init__(self, arch:'architecture.Architecture', intrinsic:'architecture.IntrinsicIndex'): self._arch = arch self._index = intrinsic self._name = self._arch.get_intrinsic_name(self._index) @@ -373,53 +305,30 @@ class ILIntrinsic(object): return hash((self._arch, self._index, self._name)) @property - def arch(self): - """ """ + def arch(self) -> 'architecture.Architecture': return self._arch - @arch.setter - def arch(self, value): - self._arch = value - @property - def index(self): - """ """ + def index(self) -> 'architecture.IntrinsicIndex': return self._index - @index.setter - def index(self, value): - self._index = value - @property - def name(self): - """ """ + def name(self) -> str: return self._name - @name.setter - def name(self, value): - self._name = value - @property - def inputs(self): + def inputs(self) -> List['architecture.IntrinsicInput']: """``inputs`` is only available if the IL intrinsic is an Architecture intrinsic """ return self._inputs - @inputs.setter - def inputs(self, value): - self._inputs = value - @property - def outputs(self): + def outputs(self) -> List['types.Type']: """``outputs`` is only available if the IL intrinsic is an Architecture intrinsic """ return self._outputs - @outputs.setter - def outputs(self, value): - self._outputs = value - class SSARegister(object): - def __init__(self, reg, version): + def __init__(self, reg:ILRegister, version:int): self._reg = reg self._version = version @@ -440,17 +349,11 @@ class SSARegister(object): return hash((self._reg, self._version)) @property - def reg(self): - """ """ + def reg(self) -> ILRegister: return self._reg - @reg.setter - def reg(self, value): - self._reg = value - @property - def version(self): - """ """ + def version(self) -> int: return self._version @version.setter @@ -459,7 +362,7 @@ class SSARegister(object): class SSARegisterStack(object): - def __init__(self, reg_stack, version): + def __init__(self, reg_stack:ILRegisterStack, version:int): self._reg_stack = reg_stack self._version = version @@ -480,26 +383,16 @@ class SSARegisterStack(object): return hash((self._reg_stack, self._version)) @property - def reg_stack(self): - """ """ + def reg_stack(self) -> ILRegisterStack: return self._reg_stack - @reg_stack.setter - def reg_stack(self, value): - self._reg_stack = value - @property - def version(self): - """ """ + def version(self) -> int: return self._version - @version.setter - def version(self, value): - self._version = value - class SSAFlag(object): - def __init__(self, flag, version): + def __init__(self, flag:ILFlag, version:int): self._flag = flag self._version = version @@ -520,26 +413,16 @@ class SSAFlag(object): return hash((self._flag, self._version)) @property - def flag(self): - """ """ + def flag(self) -> ILFlag: return self._flag - @flag.setter - def flag(self, value): - self._flag = value - @property - def version(self): - """ """ + def version(self) -> int: return self._version - @version.setter - def version(self, value): - self._version = value - class SSARegisterOrFlag(object): - def __init__(self, reg_or_flag, version): + def __init__(self, reg_or_flag:Union[ILRegister, ILFlag], version:int): self._reg_or_flag = reg_or_flag self._version = version @@ -560,26 +443,16 @@ class SSARegisterOrFlag(object): return hash((self._reg_or_flag, self._version)) @property - def reg_or_flag(self): - """ """ - return self._reg_or_flag - - @reg_or_flag.setter - def reg_or_flag(self, value): - self._reg_or_flag = value + def reg_or_flag(self) -> Union[ILRegister, ILFlag]: + return self._reg_or_flag @property - def version(self): - """ """ + def version(self) -> int: return self._version - @version.setter - def version(self, value): - self._version = value - class LowLevelILOperationAndSize(object): - def __init__(self, operation, size): + def __init__(self, operation:'LowLevelILOperation', size:int): self._operation = operation self._size = size @@ -602,13 +475,11 @@ class LowLevelILOperationAndSize(object): return hash((self._operation, self._size)) @property - def operation(self): - """ """ + def operation(self) -> 'LowLevelILOperation': return self._operation @property - def size(self): - """ """ + def size(self) -> int: return self._size @@ -757,8 +628,10 @@ class LowLevelILInstruction(object): LowLevelILOperation.LLIL_MEM_PHI: [("dest_memory", "int"), ("src_memory", "int_list")] } - def __init__(self, func, expr_index, instr_index=None): + def __init__(self, func:'LowLevelILFunction', expr_index:ExpressionIndex, instr_index:InstructionIndex=None): instr = core.BNGetLowLevelILByIndex(func.handle, expr_index) + if func.arch is None: + raise Exception("Attempting to create a LowLevelILInstruction with function that doesn't have an architecture.") self._function = func self._expr_index = expr_index self._instr_index = instr_index @@ -775,10 +648,13 @@ class LowLevelILInstruction(object): operands = LowLevelILInstruction.ILOperations[instr.operation] self._operands = [] i = 0 + for operand in operands: name, operand_type = operand + value:Optional[Any] = None if operand_type == "int": value = instr.operands[i] + assert isinstance(value, int) value = (value & ((1 << 63) - 1)) - (value & (1 << 63)) elif operand_type == "float": if instr.size == 4: @@ -809,7 +685,7 @@ class LowLevelILInstruction(object): value = SSARegisterStack(reg_stack, instr.operands[i]) i += 1 self._operands.append(value) - self.dest = value + self.__dict__['dest'] = value value = SSARegisterStack(reg_stack, instr.operands[i]) elif operand_type == "flag": value = ILFlag(func.arch, instr.operands[i]) @@ -829,6 +705,7 @@ class LowLevelILInstruction(object): elif operand_type == "int_list": count = ctypes.c_ulonglong() operand_list = core.BNLowLevelILGetOperandList(func.handle, self.expr_index, i, count) + assert operand_list is not None, "core.BNLowLevelILGetOperandList returned None" i += 1 value = [] for j in range(count.value): @@ -837,6 +714,7 @@ class LowLevelILInstruction(object): elif operand_type == "expr_list": count = ctypes.c_ulonglong() operand_list = core.BNLowLevelILGetOperandList(func.handle, self.expr_index, i, count) + assert operand_list is not None, "core.BNLowLevelILGetOperandList returned None" i += 1 value = [] for j in range(count.value): @@ -845,6 +723,7 @@ class LowLevelILInstruction(object): elif operand_type == "reg_or_flag_list": count = ctypes.c_ulonglong() operand_list = core.BNLowLevelILGetOperandList(func.handle, self.expr_index, i, count) + assert operand_list is not None, "core.BNLowLevelILGetOperandList returned None" i += 1 value = [] for j in range(count.value): @@ -856,6 +735,7 @@ class LowLevelILInstruction(object): elif operand_type == "reg_ssa_list": count = ctypes.c_ulonglong() operand_list = core.BNLowLevelILGetOperandList(func.handle, self.expr_index, i, count) + assert operand_list is not None, "core.BNLowLevelILGetOperandList returned None" i += 1 value = [] for j in range(count.value // 2): @@ -866,6 +746,7 @@ class LowLevelILInstruction(object): elif operand_type == "reg_stack_ssa_list": count = ctypes.c_ulonglong() operand_list = core.BNLowLevelILGetOperandList(func.handle, self.expr_index, i, count) + assert operand_list is not None, "core.BNLowLevelILGetOperandList returned None" i += 1 value = [] for j in range(count.value // 2): @@ -876,6 +757,7 @@ class LowLevelILInstruction(object): elif operand_type == "flag_ssa_list": count = ctypes.c_ulonglong() operand_list = core.BNLowLevelILGetOperandList(func.handle, self.expr_index, i, count) + assert operand_list is not None, "core.BNLowLevelILGetOperandList returned None" i += 1 value = [] for j in range(count.value // 2): @@ -886,6 +768,7 @@ class LowLevelILInstruction(object): elif operand_type == "reg_or_flag_ssa_list": count = ctypes.c_ulonglong() operand_list = core.BNLowLevelILGetOperandList(func.handle, self.expr_index, i, count) + assert operand_list is not None, "core.BNLowLevelILGetOperandList returned None" i += 1 value = [] for j in range(count.value // 2): @@ -899,6 +782,7 @@ class LowLevelILInstruction(object): elif operand_type == "reg_stack_adjust": count = ctypes.c_ulonglong() operand_list = core.BNLowLevelILGetOperandList(func.handle, self.expr_index, i, count) + assert operand_list is not None, "core.BNLowLevelILGetOperandList returned None" i += 1 value = {} for j in range(count.value // 2): @@ -911,6 +795,7 @@ class LowLevelILInstruction(object): elif operand_type == "target_map": count = ctypes.c_ulonglong() operand_list = core.BNLowLevelILGetOperandList(func.handle, self.expr_index, i, count) + assert operand_list is not None, "core.BNLowLevelILGetOperandList returned None" i += 1 value = {} for j in range(count.value // 2): @@ -968,89 +853,97 @@ class LowLevelILInstruction(object): return hash((self._function, self.expr_index)) @property - def tokens(self): + def tokens(self) -> Optional[TokenList]: """LLIL tokens (read-only)""" count = ctypes.c_ulonglong() + assert self._function.arch is not None tokens = ctypes.POINTER(core.BNInstructionTextToken)() if (self._instr_index is not None) and (self._function.source_function is not None): if not core.BNGetLowLevelILInstructionText(self._function.handle, self._function.source_function.handle, self._function.arch.handle, self._instr_index, tokens, count): return None else: + # TODO: Special case LLIL_CALL_OUTPUT_SSA if not core.BNGetLowLevelILExprText(self._function.handle, self._function.arch.handle, self.expr_index, tokens, count): return None - result = binaryninja.function.InstructionTextToken.get_instruction_lines(tokens, count.value) + result = function.InstructionTextToken._from_core_struct(tokens, count.value) core.BNFreeInstructionText(tokens, count.value) return result @property - def il_basic_block(self): + def il_basic_block(self) -> 'LowLevelILBasicBlock': """IL basic block object containing this expression (read-only) (only available on finalized functions)""" - view = None - if self._function.source_function is not None: - view = self._function.source_function.view - return LowLevelILBasicBlock(view, core.BNGetLowLevelILBasicBlockForInstruction(self._function.handle, self._instr_index), self._function) + assert self._function.source_function is not None + view = self._function.source_function.view + core_block = core.BNGetLowLevelILBasicBlockForInstruction(self._function.handle, self._instr_index) + assert core_block is not None, "BNGetLowLevelILBasicBlockForInstruction returned None" + return LowLevelILBasicBlock(view, core_block, self._function) @property - def ssa_form(self): + def ssa_form(self) -> 'LowLevelILInstruction': """SSA form of expression (read-only)""" - return LowLevelILInstruction(self._function.ssa_form, + ssa_func = self._function.ssa_form + assert ssa_func is not None + return LowLevelILInstruction(ssa_func, core.BNGetLowLevelILSSAExprIndex(self._function.handle, self.expr_index), core.BNGetLowLevelILSSAInstructionIndex(self._function.handle, self._instr_index) if self._instr_index is not None else None) @property - def non_ssa_form(self): + def non_ssa_form(self) -> 'LowLevelILInstruction': """Non-SSA form of expression (read-only)""" - return LowLevelILInstruction(self._function.non_ssa_form, + non_ssa_function = self._function.non_ssa_form + assert non_ssa_function is not None + return LowLevelILInstruction(non_ssa_function, core.BNGetLowLevelILNonSSAExprIndex(self._function.handle, self.expr_index), core.BNGetLowLevelILNonSSAInstructionIndex(self._function.handle, self._instr_index) if self._instr_index is not None else None) @property - def medium_level_il(self): + def medium_level_il(self) -> Optional['mediumlevelil.MediumLevelILInstruction']: """Gets the medium level IL expression corresponding to this expression (may be None for eliminated instructions)""" expr = self._function.get_medium_level_il_expr_index(self.expr_index) if expr is None: return None - return binaryninja.mediumlevelil.MediumLevelILInstruction(self._function.medium_level_il, expr) + mlil_func = self._function.medium_level_il + assert mlil_func is not None, "self._function.medium_level_il is None" + return mediumlevelil.MediumLevelILInstruction(mlil_func, expr) @property - def mlil(self): + def mlil(self) -> Optional['mediumlevelil.MediumLevelILInstruction']: return self.medium_level_il @property - def mlils(self): - exprs = self._function.get_medium_level_il_expr_indexes(self.expr_index) + def mlils(self) -> List['mediumlevelil.MediumLevelILInstruction']: result = [] - for expr in exprs: - result.append(binaryninja.mediumlevelil.MediumLevelILInstruction(self._function.medium_level_il, expr)) + for expr in self._function.get_medium_level_il_expr_indexes(self.expr_index): + result.append(mediumlevelil.MediumLevelILInstruction(self._function.medium_level_il, expr)) return result @property - def mapped_medium_level_il(self): + def mapped_medium_level_il(self) -> Optional['mediumlevelil.MediumLevelILInstruction']: """Gets the mapped medium level IL expression corresponding to this expression""" expr = self._function.get_mapped_medium_level_il_expr_index(self.expr_index) if expr is None: return None - return binaryninja.mediumlevelil.MediumLevelILInstruction(self._function.mapped_medium_level_il, expr) + return mediumlevelil.MediumLevelILInstruction(self._function.mapped_medium_level_il, expr) @property - def mmlil(self): + def mmlil(self) -> Optional['mediumlevelil.MediumLevelILInstruction']: return self.mapped_medium_level_il @property - def high_level_il(self): + def high_level_il(self) -> Optional['highlevelil.HighLevelILInstruction']: """Gets the high level IL expression corresponding to this expression (may be None for eliminated instructions)""" if self.mlil is None: return None return self.mlil.hlil @property - def hlil(self): + def hlil(self) -> Optional['highlevelil.HighLevelILInstruction']: return self.high_level_il @property - def hlils(self): + def hlils(self) -> List['highlevelil.HighLevelILInstruction']: result = set() for mlil_expr in self.mlils: for hlil_expr in mlil_expr.hlils: @@ -1058,23 +951,23 @@ class LowLevelILInstruction(object): return list(result) @property - def value(self): + def value(self) -> 'variable.RegisterValue': """Value of expression if constant or a known value (read-only)""" value = core.BNGetLowLevelILExprValue(self._function.handle, self.expr_index) - result = binaryninja.function.RegisterValue(self._function.arch, value) - return result + return variable.RegisterValue(self._function.arch, value) @property - def possible_values(self): + def possible_values(self) -> 'variable.PossibleValueSet': """Possible values of expression using path-sensitive static data flow analysis (read-only)""" value = core.BNGetLowLevelILPossibleExprValues(self._function.handle, self.expr_index, None, 0) - result = binaryninja.function.PossibleValueSet(self._function.arch, value) + result = variable.PossibleValueSet(self._function.arch, value) core.BNFreePossibleValueSet(value) return result @property - def prefix_operands(self): + def prefix_operands(self) -> List[Any]: """All operands in the expression tree in prefix order""" + # TODO: There is probably a much better type hint we can provide here result = [LowLevelILOperationAndSize(self._operation, self._size)] for operand in self._operands: if isinstance(operand, LowLevelILInstruction): @@ -1084,8 +977,9 @@ class LowLevelILInstruction(object): return result @property - def postfix_operands(self): + def postfix_operands(self) -> List[Any]: """All operands in the expression tree in postfix order""" + # TODO: There is probably a much better type hint we can provide here result = [] for operand in self._operands: if isinstance(operand, LowLevelILInstruction): @@ -1095,30 +989,34 @@ class LowLevelILInstruction(object): result.append(LowLevelILOperationAndSize(self._operation, self._size)) return result - def get_possible_values(self, options = []): + def get_possible_values(self, options:List[DataFlowQueryOption]=[]) -> variable.PossibleValueSet: option_array = (ctypes.c_int * len(options))() idx = 0 for option in options: option_array[idx] = option idx += 1 value = core.BNGetLowLevelILPossibleExprValues(self._function.handle, self.expr_index, option_array, len(options)) - result = binaryninja.function.PossibleValueSet(self._function.arch, value) + result = variable.PossibleValueSet(self._function.arch, value) core.BNFreePossibleValueSet(value) return result - def get_reg_value(self, reg): + def get_reg_value(self, reg:'architecture.RegisterType') -> variable.RegisterValue: + if self._function.arch is None: + raise Exception("Can not call get_reg_value on function with Architecture set to None") reg = self._function.arch.get_reg_index(reg) value = core.BNGetLowLevelILRegisterValueAtInstruction(self._function.handle, reg, self._instr_index) - result = binaryninja.function.RegisterValue(self._function.arch, value) - return result + return variable.RegisterValue(self._function.arch, value) - def get_reg_value_after(self, reg): + def get_reg_value_after(self, reg:'architecture.RegisterType') -> variable.RegisterValue: + if self._function.arch is None: + raise Exception("Can not call get_reg_value_after on function with Architecture set to None") reg = self._function.arch.get_reg_index(reg) value = core.BNGetLowLevelILRegisterValueAfterInstruction(self._function.handle, reg, self._instr_index) - result = binaryninja.function.RegisterValue(self._function.arch, value) - return result + return variable.RegisterValue(self._function.arch, value) - def get_possible_reg_values(self, reg, options = []): + def get_possible_reg_values(self, reg:'architecture.RegisterType', options:List[DataFlowQueryOption]=[]) -> 'variable.PossibleValueSet': + if self._function.arch is None: + raise Exception("Can not call get_possible_reg_values on function with Architecture set to None") reg = self._function.arch.get_reg_index(reg) option_array = (ctypes.c_int * len(options))() idx = 0 @@ -1127,11 +1025,13 @@ class LowLevelILInstruction(object): idx += 1 value = core.BNGetLowLevelILPossibleRegisterValuesAtInstruction(self._function.handle, reg, self._instr_index, option_array, len(options)) - result = binaryninja.function.PossibleValueSet(self._function.arch, value) + result = variable.PossibleValueSet(self._function.arch, value) core.BNFreePossibleValueSet(value) return result - def get_possible_reg_values_after(self, reg, options = []): + def get_possible_reg_values_after(self, reg:'architecture.RegisterType', options:List[DataFlowQueryOption]=[]) -> 'variable.PossibleValueSet': + if self._function.arch is None: + raise Exception("Can not call get_possible_reg_values_after on function with Architecture set to None") reg = self._function.arch.get_reg_index(reg) option_array = (ctypes.c_int * len(options))() idx = 0 @@ -1140,23 +1040,29 @@ class LowLevelILInstruction(object): idx += 1 value = core.BNGetLowLevelILPossibleRegisterValuesAfterInstruction(self._function.handle, reg, self._instr_index, option_array, len(options)) - result = binaryninja.function.PossibleValueSet(self._function.arch, value) + result = variable.PossibleValueSet(self._function.arch, value) core.BNFreePossibleValueSet(value) return result - def get_flag_value(self, flag): + def get_flag_value(self, flag:'architecture.FlagType') -> 'variable.RegisterValue': + if self._function.arch is None: + raise Exception("Can not call get_flag_value on function with Architecture set to None") flag = self._function.arch.get_flag_index(flag) value = core.BNGetLowLevelILFlagValueAtInstruction(self._function.handle, flag, self._instr_index) - result = binaryninja.function.RegisterValue(self._function.arch, value) + result = variable.RegisterValue(self._function.arch, value) return result - def get_flag_value_after(self, flag): + def get_flag_value_after(self, flag:'architecture.FlagType') -> 'variable.RegisterValue': + if self._function.arch is None: + raise Exception("Can not call get_flag_value_after on function with Architecture set to None") flag = self._function.arch.get_flag_index(flag) value = core.BNGetLowLevelILFlagValueAfterInstruction(self._function.handle, flag, self._instr_index) - result = binaryninja.function.RegisterValue(self._function.arch, value) + result = variable.RegisterValue(self._function.arch, value) return result - def get_possible_flag_values(self, flag, options = []): + def get_possible_flag_values(self, flag:'architecture.FlagType', options:List[DataFlowQueryOption]=[]) -> 'variable.PossibleValueSet': + if self._function.arch is None: + raise Exception("Can not call get_possible_flag_values on function with Architecture set to None") flag = self._function.arch.get_flag_index(flag) option_array = (ctypes.c_int * len(options))() idx = 0 @@ -1165,11 +1071,13 @@ class LowLevelILInstruction(object): idx += 1 value = core.BNGetLowLevelILPossibleFlagValuesAtInstruction(self._function.handle, flag, self._instr_index, option_array, len(options)) - result = binaryninja.function.PossibleValueSet(self._function.arch, value) + result = variable.PossibleValueSet(self._function.arch, value) core.BNFreePossibleValueSet(value) return result - def get_possible_flag_values_after(self, flag, options = []): + def get_possible_flag_values_after(self, flag:'architecture.FlagType', options:List[DataFlowQueryOption]=[]) -> 'variable.PossibleValueSet': + if self._function.arch is None: + raise Exception("Can not call get_possible_flag_values_after on function with Architecture set to None") flag = self._function.arch.get_flag_index(flag) option_array = (ctypes.c_int * len(options))() idx = 0 @@ -1178,21 +1086,21 @@ class LowLevelILInstruction(object): idx += 1 value = core.BNGetLowLevelILPossibleFlagValuesAfterInstruction(self._function.handle, flag, self._instr_index, option_array, len(options)) - result = binaryninja.function.PossibleValueSet(self._function.arch, value) + result = variable.PossibleValueSet(self._function.arch, value) core.BNFreePossibleValueSet(value) return result - def get_stack_contents(self, offset, size): + def get_stack_contents(self, offset:int, size:int) -> 'variable.RegisterValue': value = core.BNGetLowLevelILStackContentsAtInstruction(self._function.handle, offset, size, self._instr_index) - result = binaryninja.function.RegisterValue(self._function.arch, value) + result = variable.RegisterValue(self._function.arch, value) return result - def get_stack_contents_after(self, offset, size): + def get_stack_contents_after(self, offset:int, size:int) -> 'variable.RegisterValue': value = core.BNGetLowLevelILStackContentsAfterInstruction(self._function.handle, offset, size, self._instr_index) - result = binaryninja.function.RegisterValue(self._function.arch, value) + result = variable.RegisterValue(self._function.arch, value) return result - def get_possible_stack_contents(self, offset, size, options = []): + def get_possible_stack_contents(self, offset:int, size:int, options:List[DataFlowQueryOption]=[]) -> variable.PossibleValueSet: option_array = (ctypes.c_int * len(options))() idx = 0 for option in options: @@ -1200,11 +1108,11 @@ class LowLevelILInstruction(object): idx += 1 value = core.BNGetLowLevelILPossibleStackContentsAtInstruction(self._function.handle, offset, size, self._instr_index, option_array, len(options)) - result = binaryninja.function.PossibleValueSet(self._function.arch, value) + result = variable.PossibleValueSet(self._function.arch, value) core.BNFreePossibleValueSet(value) return result - def get_possible_stack_contents_after(self, offset, size, options = []): + def get_possible_stack_contents_after(self, offset:int, size:int, options:List[DataFlowQueryOption]=[]) -> variable.PossibleValueSet: option_array = (ctypes.c_int * len(options))() idx = 0 for option in options: @@ -1212,53 +1120,44 @@ class LowLevelILInstruction(object): idx += 1 value = core.BNGetLowLevelILPossibleStackContentsAfterInstruction(self._function.handle, offset, size, self._instr_index, option_array, len(options)) - result = binaryninja.function.PossibleValueSet(self._function.arch, value) + result = variable.PossibleValueSet(self._function.arch, value) core.BNFreePossibleValueSet(value) return result @property - def function(self): - """ """ + def function(self) -> 'LowLevelILFunction': return self._function @property - def expr_index(self): - """ """ + def expr_index(self) -> ExpressionIndex: return self._expr_index @property - def instr_index(self): - """ """ + def instr_index(self) -> Optional[InstructionIndex]: return self._instr_index @property - def operation(self): - """ """ + def operation(self) -> LowLevelILOperation: return self._operation @property - def size(self): - """ """ + def size(self) -> int: return self._size @property - def address(self): - """ """ + def address(self) -> int: return self._address @property - def source_operand(self): - """ """ + def source_operand(self) -> Optional[ExpressionIndex]: return self._source_operand @property - def flags(self): - """ """ + def flags(self) -> Optional[str]: return self._flags @property - def operands(self): - """ """ + def operands(self) -> List[Any]: return self._operands @@ -1269,22 +1168,20 @@ class LowLevelILExpr(object): .. note:: This class shouldn't be instantiated directly. Rather the helper members of LowLevelILFunction should be \ used instead. """ - def __init__(self, index): + def __init__(self, index:ExpressionIndex): self._index = index + def __int__(self) -> int: + return self._index + @property def index(self): - """ """ return self._index - @index.setter - def index(self, value): - self._index = value - class LowLevelILFunction(object): """ - ``class LowLevelILFunction`` contains the list of LowLevelILExpr objects that make up a binaryninja.function. LowLevelILExpr + ``class LowLevelILFunction`` contains the list of LowLevelILExpr objects that make up a function. LowLevelILExpr objects can be added to the LowLevelILFunction by calling :func:`append` and passing the result of the various class methods which return LowLevelILExpr objects. @@ -1310,27 +1207,37 @@ class LowLevelILFunction(object): LLFC_NO !overflow No overflow ======================= ========== =============================== """ - def __init__(self, arch = None, handle = None, source_func = None): + def __init__(self, arch:Optional['architecture.Architecture']=None, handle:Optional[core.BNLowLevelILFunction]=None, + source_func:'function.Function'=None): self._arch = arch self._source_function = source_func if handle is not None: self.handle = core.handle_of_type(handle, core.BNLowLevelILFunction) + assert self.handle is not None if self._source_function is None: source_handle = core.BNGetLowLevelILOwnerFunction(self.handle) if source_handle: - self._source_function = binaryninja.function.Function(handle = source_handle) + self._source_function = function.Function(handle = source_handle) else: self._source_function = None if self._arch is None: + if self._source_function is None: + raise Exception("Can not instantiate LowLevelILFunction without an architecture") self._arch = self._source_function.arch else: if self._arch is None: + if self._source_function is None: + raise Exception("Can not instantiate LowLevelILFunction without an architecture") self._arch = self._source_function.arch + assert self._arch is not None if self._source_function is None: func_handle = None else: func_handle = self._source_function.handle - self.handle = core.BNCreateLowLevelILFunction(arch.handle, func_handle) + self.handle = core.BNCreateLowLevelILFunction(self._arch.handle, func_handle) + assert self.handle is not None + assert self._arch is not None + assert self._source_function is not None def __del__(self): if self.handle is not None: @@ -1340,9 +1247,9 @@ class LowLevelILFunction(object): source_function = getattr(self, "source_function", None) arch = getattr(source_function, "arch", None) if arch and source_function: - return "<llil func: %s@%#x>" % (arch.name, self.source_function.start) + return "<llil func: %s@%#x>" % (arch.name, self.__dict__['source_function'].start) elif source_function: - return "<llil func: %#x>" % self.source_function.start + return "<llil func: %#x>" % self.__dict__['source_function'].start else: return "<llil func: anonymous>" @@ -1352,6 +1259,8 @@ class LowLevelILFunction(object): def __eq__(self, other): if not isinstance(other, self.__class__): return NotImplemented + assert self.handle is not None + assert other.handle is not None return ctypes.addressof(self.handle.contents) == ctypes.addressof(other.handle.contents) def __ne__(self, other): @@ -1360,7 +1269,8 @@ class LowLevelILFunction(object): return not (self == other) def __hash__(self): - return hash(('LLIL', self._source_function)) + assert self.handle is not None + return hash(ctypes.addressof(self.handle.contents)) def __getitem__(self, i): if isinstance(i, slice) or isinstance(i, tuple): @@ -1379,124 +1289,125 @@ class LowLevelILFunction(object): def __setitem__(self, i, j): raise IndexError("instruction modification not implemented") - def __iter__(self): + def __iter__(self) -> Generator['LowLevelILBasicBlock', None, None]: count = ctypes.c_ulonglong() blocks = core.BNGetLowLevelILBasicBlockList(self.handle, count) + assert blocks is not None, "core.BNGetLowLevelILBasicBlockList returned None" view = None if self._source_function is not None: view = self._source_function.view try: for i in range(0, count.value): - yield LowLevelILBasicBlock(view, core.BNNewBasicBlockReference(blocks[i]), self) + core_block = core.BNNewBasicBlockReference(blocks[i]) + assert core_block is not None + yield LowLevelILBasicBlock(view, core_block, self) finally: core.BNFreeBasicBlockList(blocks, count.value) @property - def current_address(self): + def current_address(self) -> int: """Current IL Address (read/write)""" return core.BNLowLevelILGetCurrentAddress(self.handle) @current_address.setter - def current_address(self, value): - core.BNLowLevelILSetCurrentAddress(self.handle, self._arch.handle, value) + def current_address(self, value:int) -> None: + core.BNLowLevelILSetCurrentAddress(self.handle, self.arch.handle, value) - def set_current_address(self, value, arch = None): + def set_current_address(self, value:int, arch:Optional['architecture.Architecture']=None): if arch is None: - arch = self._arch + arch = self.arch core.BNLowLevelILSetCurrentAddress(self.handle, arch.handle, value) def set_current_source_block(self, block): core.BNLowLevelILSetCurrentSourceBlock(self.handle, block.handle) @property - def temp_reg_count(self): + def temp_reg_count(self) -> int: """Number of temporary registers (read-only)""" return core.BNGetLowLevelILTemporaryRegisterCount(self.handle) @property - def temp_flag_count(self): + def temp_flag_count(self) -> int: """Number of temporary flags (read-only)""" return core.BNGetLowLevelILTemporaryFlagCount(self.handle) @property - def basic_blocks(self): + def basic_blocks(self) -> Generator['LowLevelILBasicBlock', None, None]: """list of LowLevelILBasicBlock objects (read-only)""" count = ctypes.c_ulonglong() blocks = core.BNGetLowLevelILBasicBlockList(self.handle, count) - result = [] + assert blocks is not None, "core.BNGetLowLevelILBasicBlockList returned None" view = None if self._source_function is not None: view = self._source_function.view - for i in range(0, count.value): - result.append(LowLevelILBasicBlock(view, core.BNNewBasicBlockReference(blocks[i]), self)) - core.BNFreeBasicBlockList(blocks, count.value) - return result + try: + for i in range(0, count.value): + core_block = core.BNNewBasicBlockReference(blocks[i]) + assert core_block is not None + yield LowLevelILBasicBlock(view, core_block, self) + finally: + core.BNFreeBasicBlockList(blocks, count.value) @property - def instructions(self): + def instructions(self) -> Generator['LowLevelILInstruction', None, None]: """A generator of llil instructions of the current llil function""" for block in self.basic_blocks: for i in block: yield i @property - def ssa_form(self): + def ssa_form(self) -> 'LowLevelILFunction': """Low level IL in SSA form (read-only)""" result = core.BNGetLowLevelILSSAForm(self.handle) - if not result: - return None + assert result is not None, "Failed to retrieve ssa-form" return LowLevelILFunction(self._arch, result, self._source_function) @property - def non_ssa_form(self): + def non_ssa_form(self) -> 'LowLevelILFunction': """Low level IL in non-SSA (default) form (read-only)""" result = core.BNGetLowLevelILNonSSAForm(self.handle) - if not result: - return None + assert result is not None, "Failed to retrieve non-ssa-form" return LowLevelILFunction(self._arch, result, self._source_function) @property - def medium_level_il(self): + def medium_level_il(self) -> 'mediumlevelil.MediumLevelILFunction': """Medium level IL for this low level IL.""" result = core.BNGetMediumLevelILForLowLevelIL(self.handle) - if not result: - return None - return binaryninja.mediumlevelil.MediumLevelILFunction(self._arch, result, self._source_function) + assert result is not None, "MLIL not present" + return mediumlevelil.MediumLevelILFunction(self._arch, result, self._source_function) @property - def mlil(self): + def mlil(self) -> 'mediumlevelil.MediumLevelILFunction': return self.medium_level_il @property - def mapped_medium_level_il(self): + def mapped_medium_level_il(self) -> 'mediumlevelil.MediumLevelILFunction': """Medium level IL with mappings between low level IL and medium level IL. Unused stores are not removed. Typically, this should only be used to answer queries on assembly or low level IL where the query is easier to perform on medium level IL.""" result = core.BNGetMappedMediumLevelIL(self.handle) - if not result: - return None - return binaryninja.mediumlevelil.MediumLevelILFunction(self._arch, result, self._source_function) + assert result is not None, "MLIL not present" + return mediumlevelil.MediumLevelILFunction(self._arch, result, self._source_function) @property - def mmlil(self): + def mmlil(self) -> 'mediumlevelil.MediumLevelILFunction': return self.mapped_medium_level_il @property - def arch(self): - """ """ + def arch(self) -> 'architecture.Architecture': + assert self._arch is not None return self._arch @arch.setter - def arch(self, value): + def arch(self, value='architecture.Architecture') -> None: self._arch = value @property - def source_function(self): - """ """ + def source_function(self) -> Optional['function.Function']: return self._source_function @source_function.setter - def source_function(self, value): + def source_function(self, value:'function.Function') -> None: self._source_function = value @property @@ -1630,36 +1541,42 @@ class LowLevelILFunction(object): return self.ssa_registers + self.ssa_register_stacks + self.ssa_flags return [] - def get_instruction_start(self, addr, arch = None): + def get_instruction_start(self, addr:int, arch:Optional['architecture.Architecture']=None) -> Optional[int]: if arch is None: - arch = self._arch + arch = self.arch result = core.BNLowLevelILGetInstructionStart(self.handle, arch.handle, addr) if result >= core.BNGetLowLevelILInstructionCount(self.handle): return None return result - def clear_indirect_branches(self): + def clear_indirect_branches(self) -> None: core.BNLowLevelILClearIndirectBranches(self.handle) - def set_indirect_branches(self, branches): + def set_indirect_branches(self, branches:List[Tuple['architecture.Architecture', int]]) -> None: 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.BNLowLevelILSetIndirectBranches(self.handle, branch_list, len(branches)) - def expr(self, operation, a = 0, b = 0, c = 0, d = 0, size = 0, flags = None): + def expr(self, operation, a:int=0, b:int=0, c:int=0, d:int=0, size:int=0, + flags:Union['architecture.FlagWriteTypeName', 'architecture.FlagType']=None): + _flags = architecture.FlagIndex(0) if isinstance(operation, str): operation = LowLevelILOperation[operation] elif isinstance(operation, LowLevelILOperation): operation = operation.value - if isinstance(flags, str): - flags = self._arch.get_flag_write_type_by_name(flags) + if isinstance(flags, architecture.FlagWriteTypeName): + _flags = self.arch.get_flag_write_type_by_name(flags) + elif isinstance(flags, ILFlag): + _flags = flags.index elif flags is None: - flags = 0 - return LowLevelILExpr(core.BNLowLevelILAddExpr(self.handle, operation, size, flags, a, b, c, d)) + _flags = architecture.FlagIndex(0) + else: + assert False, "flags type unsupported" + return LowLevelILExpr(core.BNLowLevelILAddExpr(self.handle, operation, size, _flags, a, b, c, d)) - def replace_expr(self, original, new): + def replace_expr(self, original:InstructionOrExpression, new:InstructionOrExpression) -> None: """ ``replace_expr`` allows modification of LowLevelILExpressions but ONLY during lifting. @@ -1681,7 +1598,7 @@ class LowLevelILFunction(object): core.BNReplaceLowLevelILExpr(self.handle, original, new) - def append(self, expr): + def append(self, expr:LowLevelILExpr) -> int: """ ``append`` adds the LowLevelILExpr ``expr`` to the current LowLevelILFunction. @@ -1691,7 +1608,7 @@ class LowLevelILFunction(object): """ return core.BNLowLevelILAddInstruction(self.handle, expr.index) - def nop(self): + def nop(self) -> LowLevelILExpr: """ ``nop`` no operation, this instruction does nothing @@ -1700,7 +1617,8 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_NOP) - def set_reg(self, size, reg, value, flags = 0): + def set_reg(self, size:int, reg:'architecture.RegisterType', value:LowLevelILExpr, + flags:Optional['architecture.FlagType']=None) -> LowLevelILExpr: """ ``set_reg`` sets the register ``reg`` of size ``size`` to the expression ``value`` @@ -1711,10 +1629,13 @@ class LowLevelILFunction(object): :return: The expression ``reg = value`` :rtype: LowLevelILExpr """ - reg = self._arch.get_reg_index(reg) - return self.expr(LowLevelILOperation.LLIL_SET_REG, reg, value.index, size = size, flags = flags) + _reg = ExpressionIndex(self.arch.get_reg_index(reg)) + if flags is None: + flags = architecture.FlagIndex(0) + return self.expr(LowLevelILOperation.LLIL_SET_REG, _reg, value.index, size = size, flags = flags) - def set_reg_split(self, size, hi, lo, value, flags = 0): + def set_reg_split(self, size:int, hi:'architecture.RegisterType', lo:'architecture.RegisterType', + value:LowLevelILExpr, flags:'architecture.FlagType'=None) -> LowLevelILExpr: """ ``set_reg_split`` uses ``hi`` and ``lo`` as a single extended register setting ``hi:lo`` to the expression ``value``. @@ -1727,11 +1648,14 @@ class LowLevelILFunction(object): :return: The expression ``hi:lo = value`` :rtype: LowLevelILExpr """ - hi = self._arch.get_reg_index(hi) - lo = self._arch.get_reg_index(lo) - return self.expr(LowLevelILOperation.LLIL_SET_REG_SPLIT, hi, lo, value.index, size = size, flags = flags) + _hi = ExpressionIndex(self.arch.get_reg_index(hi)) + _lo = ExpressionIndex(self.arch.get_reg_index(lo)) + if flags is None: + flags = architecture.FlagIndex(0) + return self.expr(LowLevelILOperation.LLIL_SET_REG_SPLIT, _hi, _lo, value.index, size = size, flags = flags) - def set_reg_stack_top_relative(self, size, reg_stack, entry, value, flags = 0): + def set_reg_stack_top_relative(self, size:int, reg_stack:'architecture.RegisterStackType', entry:LowLevelILExpr, + value:LowLevelILExpr, flags:'architecture.FlagType'=None) -> LowLevelILExpr: """ ``set_reg_stack_top_relative`` sets the top-relative entry ``entry`` of size ``size`` in register stack ``reg_stack`` to the expression ``value`` @@ -1744,11 +1668,14 @@ class LowLevelILFunction(object): :return: The expression ``reg_stack[entry] = value`` :rtype: LowLevelILExpr """ - reg_stack = self._arch.get_reg_stack_index(reg_stack) - return self.expr(LowLevelILOperation.LLIL_SET_REG_STACK_REL, reg_stack, entry.index, value.index, + _reg_stack = ExpressionIndex(self.arch.get_reg_stack_index(reg_stack)) + if flags is None: + flags = architecture.FlagIndex(0) + return self.expr(LowLevelILOperation.LLIL_SET_REG_STACK_REL, _reg_stack, entry.index, value.index, size = size, flags = flags) - def reg_stack_push(self, size, reg_stack, value, flags = 0): + def reg_stack_push(self, size:int, reg_stack:'architecture.RegisterStackType', value:LowLevelILExpr, + flags:'architecture.FlagType'=None) -> LowLevelILExpr: """ ``reg_stack_push`` pushes the expression ``value`` of size ``size`` onto the top of the register stack ``reg_stack`` @@ -1760,10 +1687,12 @@ class LowLevelILFunction(object): :return: The expression ``reg_stack.push(value)`` :rtype: LowLevelILExpr """ - reg_stack = self._arch.get_reg_stack_index(reg_stack) - return self.expr(LowLevelILOperation.LLIL_REG_STACK_PUSH, reg_stack, value.index, size = size, flags = flags) + _reg_stack = ExpressionIndex(self.arch.get_reg_stack_index(reg_stack)) + if flags is None: + flags = architecture.FlagIndex(0) + return self.expr(LowLevelILOperation.LLIL_REG_STACK_PUSH, _reg_stack, value.index, size = size, flags = flags) - def set_flag(self, flag, value): + def set_flag(self, flag:'architecture.FlagName', value:LowLevelILExpr) -> LowLevelILExpr: """ ``set_flag`` sets the flag ``flag`` to the LowLevelILExpr ``value`` @@ -1772,9 +1701,10 @@ class LowLevelILFunction(object): :return: The expression FLAG.flag = value :rtype: LowLevelILExpr """ - return self.expr(LowLevelILOperation.LLIL_SET_FLAG, self._arch.get_flag_by_name(flag), value.index) + return self.expr(LowLevelILOperation.LLIL_SET_FLAG, ExpressionIndex(self.arch.get_flag_by_name(flag)), + value.index) - def load(self, size, addr): + def load(self, size:int, addr:LowLevelILExpr) -> LowLevelILExpr: """ ``load`` Reads ``size`` bytes from the expression ``addr`` @@ -1785,7 +1715,7 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_LOAD, addr.index, size=size) - def store(self, size, addr, value, flags=None): + def store(self, size:int, addr:LowLevelILExpr, value:LowLevelILExpr, flags=None) -> LowLevelILExpr: """ ``store`` Writes ``size`` bytes to expression ``addr`` read from expression ``value`` @@ -1798,7 +1728,7 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_STORE, addr.index, value.index, size=size, flags=flags) - def push(self, size, value): + def push(self, size:int, value:LowLevelILExpr) -> LowLevelILExpr: """ ``push`` writes ``size`` bytes from expression ``value`` to the stack, adjusting the stack by ``size``. @@ -1809,7 +1739,7 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_PUSH, value.index, size=size) - def pop(self, size): + def pop(self, size:int) -> LowLevelILExpr: """ ``pop`` reads ``size`` bytes from the stack, adjusting the stack by ``size``. @@ -1819,7 +1749,7 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_POP, size=size) - def reg(self, size, reg): + def reg(self, size:int, reg:'architecture.RegisterType') -> LowLevelILExpr: """ ``reg`` returns a register of size ``size`` with name ``reg`` @@ -1828,10 +1758,10 @@ class LowLevelILFunction(object): :return: A register expression for the given string :rtype: LowLevelILExpr """ - reg = self._arch.get_reg_index(reg) - return self.expr(LowLevelILOperation.LLIL_REG, reg, size=size) + _reg = ExpressionIndex(self.arch.get_reg_index(reg)) + return self.expr(LowLevelILOperation.LLIL_REG, _reg, size=size) - def reg_split(self, size, hi, lo): + def reg_split(self, size:int, hi:'architecture.RegisterType', lo:'architecture.RegisterType') -> LowLevelILExpr: """ ``reg_split`` combines registers of size ``size`` with names ``hi`` and ``lo`` @@ -1841,11 +1771,11 @@ class LowLevelILFunction(object): :return: The expression ``hi:lo`` :rtype: LowLevelILExpr """ - hi = self._arch.get_reg_index(hi) - lo = self._arch.get_reg_index(lo) - return self.expr(LowLevelILOperation.LLIL_REG_SPLIT, hi, lo, size=size) + _hi = ExpressionIndex(self.arch.get_reg_index(hi)) + _lo = ExpressionIndex(self.arch.get_reg_index(lo)) + return self.expr(LowLevelILOperation.LLIL_REG_SPLIT, _hi, _lo, size=size) - def reg_stack_top_relative(self, size, reg_stack, entry): + def reg_stack_top_relative(self, size:int, reg_stack:'architecture.RegisterStackType', entry:LowLevelILExpr) -> LowLevelILExpr: """ ``reg_stack_top_relative`` returns a register stack entry of size ``size`` at top-relative location ``entry`` in register stack with name ``reg_stack`` @@ -1856,10 +1786,10 @@ class LowLevelILFunction(object): :return: The expression ``reg_stack[entry]`` :rtype: LowLevelILExpr """ - reg_stack = self._arch.get_reg_stack_index(reg_stack) - return self.expr(LowLevelILOperation.LLIL_REG_STACK_REL, reg_stack, entry.index, size=size) + _reg_stack = self.arch.get_reg_stack_index(reg_stack) + return self.expr(LowLevelILOperation.LLIL_REG_STACK_REL, _reg_stack, entry.index, size=size) - def reg_stack_pop(self, size, reg_stack): + def reg_stack_pop(self, size:int, reg_stack:'architecture.RegisterStackType') -> LowLevelILExpr: """ ``reg_stack_pop`` returns the top entry of size ``size`` in register stack with name ``reg_stack``, and removes the entry from the stack @@ -1869,10 +1799,10 @@ class LowLevelILFunction(object): :return: The expression ``reg_stack.pop`` :rtype: LowLevelILExpr """ - reg_stack = self._arch.get_reg_stack_index(reg_stack) - return self.expr(LowLevelILOperation.LLIL_REG_STACK_POP, reg_stack, size=size) + _reg_stack = ExpressionIndex(self.arch.get_reg_stack_index(reg_stack)) + return self.expr(LowLevelILOperation.LLIL_REG_STACK_POP, _reg_stack, size=size) - def const(self, size, value): + def const(self, size:int, value:int) -> LowLevelILExpr: """ ``const`` returns an expression for the constant integer ``value`` with size ``size`` @@ -1881,9 +1811,9 @@ class LowLevelILFunction(object): :return: A constant expression of given value and size :rtype: LowLevelILExpr """ - return self.expr(LowLevelILOperation.LLIL_CONST, value, size=size) + return self.expr(LowLevelILOperation.LLIL_CONST, ExpressionIndex(value), size=size) - def const_pointer(self, size, value): + def const_pointer(self, size:int, value:int) -> LowLevelILExpr: """ ``const_pointer`` returns an expression for the constant pointer ``value`` with size ``size`` @@ -1894,7 +1824,7 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_CONST_PTR, value, size=size) - def reloc_pointer(self, size, value): + def reloc_pointer(self, size:int, value:int) -> LowLevelILExpr: """ ``reloc_pointer`` returns an expression for the constant relocated pointer ``value`` with size ``size`` @@ -1905,7 +1835,7 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_EXTERN_PTR, value, size=size) - def float_const_raw(self, size, value): + def float_const_raw(self, size:int, value:int) -> LowLevelILExpr: """ ``float_const_raw`` returns an expression for the constant raw binary floating point value ``value`` with size ``size`` @@ -1917,7 +1847,7 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_FLOAT_CONST, value, size=size) - def float_const_single(self, value): + def float_const_single(self, value:float) -> LowLevelILExpr: """ ``float_const_single`` returns an expression for the single precision floating point value ``value`` @@ -1927,7 +1857,7 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_FLOAT_CONST, struct.unpack("I", struct.pack("f", value))[0], size=4) - def float_const_double(self, value): + def float_const_double(self, value:float) -> LowLevelILExpr: """ ``float_const_double`` returns an expression for the double precision floating point value ``value`` @@ -1937,17 +1867,17 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_FLOAT_CONST, struct.unpack("Q", struct.pack("d", value))[0], size=8) - def flag(self, reg): + def flag(self, reg:'architecture.FlagName') -> LowLevelILExpr: """ ``flag`` returns a flag expression for the given flag name. - :param str reg: name of the flag expression to retrieve + :param architecture.FlagName reg: name of the flag expression to retrieve :return: A flag expression of given flag name :rtype: LowLevelILExpr """ - return self.expr(LowLevelILOperation.LLIL_FLAG, self._arch.get_flag_by_name(reg)) + return self.expr(LowLevelILOperation.LLIL_FLAG, self.arch.get_flag_by_name(reg)) - def flag_bit(self, size, reg, bit): + def flag_bit(self, size:int, reg:'architecture.FlagName', bit:int) -> LowLevelILExpr: """ ``flag_bit`` sets the flag named ``reg`` and size ``size`` to the constant integer value ``bit`` @@ -1957,9 +1887,9 @@ class LowLevelILFunction(object): :return: A constant expression of given value and size ``FLAG.reg = bit`` :rtype: LowLevelILExpr """ - return self.expr(LowLevelILOperation.LLIL_FLAG_BIT, self._arch.get_flag_by_name(reg), bit, size=size) + return self.expr(LowLevelILOperation.LLIL_FLAG_BIT, self.arch.get_flag_by_name(reg), bit, size=size) - def add(self, size, a, b, flags=None): + def add(self, size:int, a:LowLevelILExpr, b:LowLevelILExpr, flags:'architecture.FlagType'=None) -> LowLevelILExpr: """ ``add`` adds expression ``a`` to expression ``b`` potentially setting flags ``flags`` and returning an expression of ``size`` bytes. @@ -1973,7 +1903,8 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_ADD, a.index, b.index, size=size, flags=flags) - def add_carry(self, size, a, b, carry, flags=None): + def add_carry(self, size:int, a:LowLevelILExpr, b:LowLevelILExpr, carry:LowLevelILExpr, + flags:'architecture.FlagType'=None) -> LowLevelILExpr: """ ``add_carry`` adds with carry expression ``a`` to expression ``b`` potentially setting flags ``flags`` and returning an expression of ``size`` bytes. @@ -1988,7 +1919,7 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_ADC, a.index, b.index, carry.index, size=size, flags=flags) - def sub(self, size, a, b, flags=None): + def sub(self, size:int, a:LowLevelILExpr, b:LowLevelILExpr, flags:'architecture.FlagType'=None) -> LowLevelILExpr: """ ``sub`` subtracts expression ``b`` from expression ``a`` potentially setting flags ``flags`` and returning an expression of ``size`` bytes. @@ -2002,7 +1933,8 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_SUB, a.index, b.index, size=size, flags=flags) - def sub_borrow(self, size, a, b, carry, flags=None): + def sub_borrow(self, size:int, a:LowLevelILExpr, b:LowLevelILExpr, carry:LowLevelILExpr, + flags:'architecture.FlagType'=None) -> LowLevelILExpr: """ ``sub_borrow`` subtracts with borrow expression ``b`` from expression ``a`` potentially setting flags ``flags`` and returning an expression of ``size`` bytes. @@ -2017,7 +1949,7 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_SBB, a.index, b.index, carry.index, size=size, flags=flags) - def and_expr(self, size, a, b, flags=None): + def and_expr(self, size:int, a:LowLevelILExpr, b:LowLevelILExpr, flags:'architecture.FlagType'=None) -> LowLevelILExpr: """ ``and_expr`` bitwise and's expression ``a`` and expression ``b`` potentially setting flags ``flags`` and returning an expression of ``size`` bytes. @@ -2031,7 +1963,7 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_AND, a.index, b.index, size=size, flags=flags) - def or_expr(self, size, a, b, flags=None): + def or_expr(self, size:int, a:LowLevelILExpr, b:LowLevelILExpr, flags:'architecture.FlagType'=None) -> LowLevelILExpr: """ ``or_expr`` bitwise or's expression ``a`` and expression ``b`` potentially setting flags ``flags`` and returning an expression of ``size`` bytes. @@ -2045,7 +1977,7 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_OR, a.index, b.index, size=size, flags=flags) - def xor_expr(self, size, a, b, flags=None): + def xor_expr(self, size:int, a:LowLevelILExpr, b:LowLevelILExpr, flags:'architecture.FlagType'=None) -> LowLevelILExpr: """ ``xor_expr`` xor's expression ``a`` with expression ``b`` potentially setting flags ``flags`` and returning an expression of ``size`` bytes. @@ -2059,7 +1991,7 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_XOR, a.index, b.index, size=size, flags=flags) - def shift_left(self, size, a, b, flags=None): + def shift_left(self, size:int, a:LowLevelILExpr, b:LowLevelILExpr, flags:'architecture.FlagType'=None) -> LowLevelILExpr: """ ``shift_left`` shifts left expression ``a`` by expression ``b`` from expression ``a`` potentially setting flags ``flags`` and returning an expression of ``size`` bytes. @@ -2073,7 +2005,7 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_LSL, a.index, b.index, size=size, flags=flags) - def logical_shift_right(self, size, a, b, flags=None): + def logical_shift_right(self, size:int, a:LowLevelILExpr, b:LowLevelILExpr, flags:'architecture.FlagType'=None) -> LowLevelILExpr: """ ``logical_shift_right`` shifts logically right expression ``a`` by expression ``b`` potentially setting flags ``flags`` and returning an expression of ``size`` bytes. @@ -2087,7 +2019,7 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_LSR, a.index, b.index, size=size, flags=flags) - def arith_shift_right(self, size, a, b, flags=None): + def arith_shift_right(self, size:int, a:LowLevelILExpr, b:LowLevelILExpr, flags:'architecture.FlagType'=None) -> LowLevelILExpr: """ ``arith_shift_right`` shifts arithmetic right expression ``a`` by expression ``b`` potentially setting flags ``flags`` and returning an expression of ``size`` bytes. @@ -2101,7 +2033,7 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_ASR, a.index, b.index, size=size, flags=flags) - def rotate_left(self, size, a, b, flags=None): + def rotate_left(self, size:int, a:LowLevelILExpr, b:LowLevelILExpr, flags:'architecture.FlagType'=None) -> LowLevelILExpr: """ ``rotate_left`` bitwise rotates left expression ``a`` by expression ``b`` potentially setting flags ``flags`` and returning an expression of ``size`` bytes. @@ -2115,7 +2047,8 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_ROL, a.index, b.index, size=size, flags=flags) - def rotate_left_carry(self, size, a, b, carry, flags=None): + def rotate_left_carry(self, size:int, a:LowLevelILExpr, b:LowLevelILExpr, carry:LowLevelILExpr, + flags:'architecture.FlagType'=None) -> LowLevelILExpr: """ ``rotate_left_carry`` bitwise rotates left with carry expression ``a`` by expression ``b`` potentially setting flags ``flags`` and returning an expression of ``size`` bytes. @@ -2130,7 +2063,7 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_RLC, a.index, b.index, carry.index, size=size, flags=flags) - def rotate_right(self, size, a, b, flags=None): + def rotate_right(self, size:int, a:LowLevelILExpr, b:LowLevelILExpr, flags:'architecture.FlagType'=None) -> LowLevelILExpr: """ ``rotate_right`` bitwise rotates right expression ``a`` by expression ``b`` potentially setting flags ``flags`` and returning an expression of ``size`` bytes. @@ -2144,7 +2077,8 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_ROR, a.index, b.index, size=size, flags=flags) - def rotate_right_carry(self, size, a, b, carry, flags=None): + def rotate_right_carry(self, size:int, a:LowLevelILExpr, b:LowLevelILExpr, carry:LowLevelILExpr, + flags:'architecture.FlagType'=None) -> LowLevelILExpr: """ ``rotate_right_carry`` bitwise rotates right with carry expression ``a`` by expression ``b`` potentially setting flags ``flags`` and returning an expression of ``size`` bytes. @@ -2159,7 +2093,7 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_RRC, a.index, b.index, carry.index, size=size, flags=flags) - def mult(self, size, a, b, flags=None): + def mult(self, size:int, a:LowLevelILExpr, b:LowLevelILExpr, flags:'architecture.FlagType'=None) -> LowLevelILExpr: """ ``mult`` multiplies expression ``a`` by expression ``b`` potentially setting flags ``flags`` and returning an expression of ``size`` bytes. @@ -2173,7 +2107,7 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_MUL, a.index, b.index, size=size, flags=flags) - def mult_double_prec_signed(self, size, a, b, flags=None): + def mult_double_prec_signed(self, size:int, a:LowLevelILExpr, b:LowLevelILExpr, flags:'architecture.FlagType'=None) -> LowLevelILExpr: """ ``mult_double_prec_signed`` multiplies signed with double precision expression ``a`` by expression ``b`` potentially setting flags ``flags`` and returning an expression of ``size`` bytes. @@ -2187,7 +2121,7 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_MULS_DP, a.index, b.index, size=size, flags=flags) - def mult_double_prec_unsigned(self, size, a, b, flags=None): + def mult_double_prec_unsigned(self, size:int, a:LowLevelILExpr, b:LowLevelILExpr, flags:'architecture.FlagType'=None) -> LowLevelILExpr: """ ``mult_double_prec_unsigned`` multiplies unsigned with double precision expression ``a`` by expression ``b`` potentially setting flags ``flags`` and returning an expression of ``size`` bytes. @@ -2201,7 +2135,7 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_MULU_DP, a.index, b.index, size=size, flags=flags) - def div_signed(self, size, a, b, flags=None): + def div_signed(self, size:int, a:LowLevelILExpr, b:LowLevelILExpr, flags:'architecture.FlagType'=None) -> LowLevelILExpr: """ ``div_signed`` signed divide expression ``a`` by expression ``b`` potentially setting flags ``flags`` and returning an expression of ``size`` bytes. @@ -2215,7 +2149,7 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_DIVS, a.index, b.index, size=size, flags=flags) - def div_double_prec_signed(self, size, a, b, flags=None): + def div_double_prec_signed(self, size:int, a:LowLevelILExpr, b:LowLevelILExpr, flags:'architecture.FlagType'=None) -> LowLevelILExpr: """ ``div_double_prec_signed`` signed double precision divide using expression ``a`` as a single double precision register by expression ``b`` potentially setting flags ``flags`` and returning an @@ -2230,7 +2164,7 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_DIVS_DP, a.index, b.index, size=size, flags=flags) - def div_unsigned(self, size, a, b, flags=None): + def div_unsigned(self, size:int, a:LowLevelILExpr, b:LowLevelILExpr, flags:'architecture.FlagType'=None) -> LowLevelILExpr: """ ``div_unsigned`` unsigned divide expression ``a`` by expression ``b`` potentially setting flags ``flags`` and returning an expression of ``size`` bytes. @@ -2244,7 +2178,7 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_DIVU, a.index, b.index, size=size, flags=flags) - def div_double_prec_unsigned(self, size, a, b, flags=None): + def div_double_prec_unsigned(self, size:int, a:LowLevelILExpr, b:LowLevelILExpr, flags:'architecture.FlagType'=None) -> LowLevelILExpr: """ ``div_double_prec_unsigned`` unsigned double precision divide using expression ``a`` as a single double precision register by expression ``b`` potentially setting flags ``flags`` and returning an @@ -2259,7 +2193,7 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_DIVU_DP, a.index, b.index, size=size, flags=flags) - def mod_signed(self, size, a, b, flags=None): + def mod_signed(self, size:int, a:LowLevelILExpr, b:LowLevelILExpr, flags:'architecture.FlagType'=None) -> LowLevelILExpr: """ ``mod_signed`` signed modulus expression ``a`` by expression ``b`` potentially setting flags ``flags`` and returning an expression of ``size`` bytes. @@ -2273,7 +2207,7 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_MODS, a.index, b.index, size=size, flags=flags) - def mod_double_prec_signed(self, size, a, b, flags=None): + def mod_double_prec_signed(self, size:int, a:LowLevelILExpr, b:LowLevelILExpr, flags:'architecture.FlagType'=None) -> LowLevelILExpr: """ ``mod_double_prec_signed`` signed double precision modulus using expression ``a`` as a single double precision register by expression ``b`` potentially setting flags ``flags`` and returning an expression @@ -2288,7 +2222,7 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_MODS_DP, a.index, b.index, size=size, flags=flags) - def mod_unsigned(self, size, a, b, flags=None): + def mod_unsigned(self, size:int, a:LowLevelILExpr, b:LowLevelILExpr, flags:'architecture.FlagType'=None) -> LowLevelILExpr: """ ``mod_unsigned`` unsigned modulus expression ``a`` by expression ``b`` potentially setting flags ``flags`` and returning an expression of ``size`` bytes. @@ -2302,7 +2236,7 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_MODU, a.index, b.index, size=size, flags=flags) - def mod_double_prec_unsigned(self, size, a, b, flags=None): + def mod_double_prec_unsigned(self, size:int, a:LowLevelILExpr, b:LowLevelILExpr, flags:'architecture.FlagType'=None) -> LowLevelILExpr: """ ``mod_double_prec_unsigned`` unsigned double precision modulus using expression ``a`` as a single double precision register by expression ``b`` potentially setting flags ``flags`` and returning an @@ -2317,7 +2251,7 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_MODU_DP, a.index, b.index, size=size, flags=flags) - def neg_expr(self, size, value, flags=None): + def neg_expr(self, size:int, value:LowLevelILExpr, flags:'architecture.FlagType'=None) -> LowLevelILExpr: """ ``neg_expr`` two's complement sign negation of expression ``value`` of size ``size`` potentially setting flags @@ -2329,7 +2263,7 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_NEG, value.index, size=size, flags=flags) - def not_expr(self, size, value, flags=None): + def not_expr(self, size:int, value:LowLevelILExpr, flags:'architecture.FlagType'=None) -> LowLevelILExpr: """ ``not_expr`` bitwise inverse of expression ``value`` of size ``size`` potentially setting flags @@ -2341,7 +2275,7 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_NOT, value.index, size=size, flags=flags) - def sign_extend(self, size, value, flags=None): + def sign_extend(self, size:int, value:LowLevelILExpr, flags:'architecture.FlagType'=None) -> LowLevelILExpr: """ ``sign_extend`` two's complement sign-extends the expression in ``value`` to ``size`` bytes @@ -2353,7 +2287,7 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_SX, value.index, size=size, flags=flags) - def zero_extend(self, size, value, flags=None): + def zero_extend(self, size:int, value:LowLevelILExpr, flags:'architecture.FlagType'=None) -> LowLevelILExpr: """ ``zero_extend`` zero-extends the expression in ``value`` to ``size`` bytes @@ -2364,7 +2298,7 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_ZX, value.index, size=size, flags=flags) - def low_part(self, size, value, flags=None): + def low_part(self, size:int, value:LowLevelILExpr, flags:'architecture.FlagType'=None) -> LowLevelILExpr: """ ``low_part`` truncates ``value`` to ``size`` bytes @@ -2375,7 +2309,7 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_LOW_PART, value.index, size=size, flags=flags) - def jump(self, dest): + def jump(self, dest:LowLevelILExpr) -> LowLevelILExpr: """ ``jump`` returns an expression which jumps (branches) to the expression ``dest`` @@ -2385,7 +2319,7 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_JUMP, dest.index) - def call(self, dest): + def call(self, dest:LowLevelILExpr) -> LowLevelILExpr: """ ``call`` returns an expression which first pushes the address of the next instruction onto the stack then jumps (branches) to the expression ``dest`` @@ -2396,7 +2330,7 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_CALL, dest.index) - def call_stack_adjust(self, dest, stack_adjust): + def call_stack_adjust(self, dest:LowLevelILExpr, stack_adjust:int) -> LowLevelILExpr: """ ``call_stack_adjust`` returns an expression which first pushes the address of the next instruction onto the stack then jumps (branches) to the expression ``dest``. After the function exits, ``stack_adjust`` is added to the @@ -2408,7 +2342,7 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_CALL_STACK_ADJUST, dest.index, stack_adjust) - def tailcall(self, dest): + def tailcall(self, dest:LowLevelILExpr) -> LowLevelILExpr: """ ``tailcall`` returns an expression which jumps (branches) to the expression ``dest`` @@ -2418,7 +2352,7 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_TAILCALL, dest.index) - def ret(self, dest): + def ret(self, dest:LowLevelILExpr) -> LowLevelILExpr: """ ``ret`` returns an expression which jumps (branches) to the expression ``dest``. ``ret`` is a special alias for jump that makes the disassembler stop disassembling. @@ -2429,7 +2363,7 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_RET, dest.index) - def no_ret(self): + def no_ret(self) -> LowLevelILExpr: """ ``no_ret`` returns an expression halts disassembly @@ -2438,7 +2372,8 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_NORET) - def flag_condition(self, cond, sem_class = None): + def flag_condition(self, cond:Union[str, LowLevelILFlagCondition, int], + sem_class:Optional['architecture.SemanticClassType']=None) -> LowLevelILExpr: """ ``flag_condition`` returns a flag_condition expression for the given LowLevelILFlagCondition @@ -2451,7 +2386,10 @@ class LowLevelILFunction(object): cond = LowLevelILFlagCondition[cond] elif isinstance(cond, LowLevelILFlagCondition): cond = cond.value - class_index = self._arch.get_semantic_flag_class_index(sem_class) + class_index = architecture.SemanticClassIndex(0) + if sem_class is not None: + class_index = self.arch.get_semantic_flag_class_index(sem_class) + assert isinstance(class_index, architecture.SemanticClassIndex) return self.expr(LowLevelILOperation.LLIL_FLAG_COND, cond, class_index) def flag_group(self, sem_group): @@ -2462,10 +2400,10 @@ class LowLevelILFunction(object): :return: A flag_group expression :rtype: LowLevelILExpr """ - group = self._arch.get_semantic_flag_group_index(sem_group) + group = self.arch.get_semantic_flag_group_index(sem_group) return self.expr(LowLevelILOperation.LLIL_FLAG_GROUP, group) - def compare_equal(self, size, a, b): + def compare_equal(self, size:int, a:LowLevelILExpr, b:LowLevelILExpr) -> LowLevelILExpr: """ ``compare_equal`` returns comparison expression of size ``size`` checking if expression ``a`` is equal to expression ``b`` @@ -2478,7 +2416,7 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_CMP_E, a.index, b.index, size = size) - def compare_not_equal(self, size, a, b): + def compare_not_equal(self, size:int, a:LowLevelILExpr, b:LowLevelILExpr) -> LowLevelILExpr: """ ``compare_not_equal`` returns comparison expression of size ``size`` checking if expression ``a`` is not equal to expression ``b`` @@ -2491,7 +2429,7 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_CMP_NE, a.index, b.index, size = size) - def compare_signed_less_than(self, size, a, b): + def compare_signed_less_than(self, size:int, a:LowLevelILExpr, b:LowLevelILExpr) -> LowLevelILExpr: """ ``compare_signed_less_than`` returns comparison expression of size ``size`` checking if expression ``a`` is signed less than expression ``b`` @@ -2504,7 +2442,7 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_CMP_SLT, a.index, b.index, size = size) - def compare_unsigned_less_than(self, size, a, b): + def compare_unsigned_less_than(self, size:int, a:LowLevelILExpr, b:LowLevelILExpr) -> LowLevelILExpr: """ ``compare_unsigned_less_than`` returns comparison expression of size ``size`` checking if expression ``a`` is unsigned less than expression ``b`` @@ -2517,7 +2455,7 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_CMP_ULT, a.index, b.index, size = size) - def compare_signed_less_equal(self, size, a, b): + def compare_signed_less_equal(self, size:int, a:LowLevelILExpr, b:LowLevelILExpr) -> LowLevelILExpr: """ ``compare_signed_less_equal`` returns comparison expression of size ``size`` checking if expression ``a`` is signed less than or equal to expression ``b`` @@ -2530,7 +2468,7 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_CMP_SLE, a.index, b.index, size = size) - def compare_unsigned_less_equal(self, size, a, b): + def compare_unsigned_less_equal(self, size:int, a:LowLevelILExpr, b:LowLevelILExpr) -> LowLevelILExpr: """ ``compare_unsigned_less_equal`` returns comparison expression of size ``size`` checking if expression ``a`` is unsigned less than or equal to expression ``b`` @@ -2543,7 +2481,7 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_CMP_ULE, a.index, b.index, size = size) - def compare_signed_greater_equal(self, size, a, b): + def compare_signed_greater_equal(self, size:int, a:LowLevelILExpr, b:LowLevelILExpr) -> LowLevelILExpr: """ ``compare_signed_greater_equal`` returns comparison expression of size ``size`` checking if expression ``a`` is signed greater than or equal to expression ``b`` @@ -2556,7 +2494,7 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_CMP_SGE, a.index, b.index, size = size) - def compare_unsigned_greater_equal(self, size, a, b): + def compare_unsigned_greater_equal(self, size:int, a:LowLevelILExpr, b:LowLevelILExpr) -> LowLevelILExpr: """ ``compare_unsigned_greater_equal`` returns comparison expression of size ``size`` checking if expression ``a`` is unsigned greater than or equal to expression ``b`` @@ -2569,7 +2507,7 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_CMP_UGE, a.index, b.index, size = size) - def compare_signed_greater_than(self, size, a, b): + def compare_signed_greater_than(self, size:int, a:LowLevelILExpr, b:LowLevelILExpr) -> LowLevelILExpr: """ ``compare_signed_greater_than`` returns comparison expression of size ``size`` checking if expression ``a`` is signed greater than or equal to expression ``b`` @@ -2582,7 +2520,7 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_CMP_SGT, a.index, b.index, size = size) - def compare_unsigned_greater_than(self, size, a, b): + def compare_unsigned_greater_than(self, size:int, a:LowLevelILExpr, b:LowLevelILExpr) -> LowLevelILExpr: """ ``compare_unsigned_greater_than`` returns comparison expression of size ``size`` checking if expression ``a`` is unsigned greater than or equal to expression ``b`` @@ -2595,10 +2533,10 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_CMP_UGT, a.index, b.index, size = size) - def test_bit(self, size, a, b): + def test_bit(self, size:int, a:LowLevelILExpr, b:LowLevelILExpr) -> LowLevelILExpr: return self.expr(LowLevelILOperation.LLIL_TEST_BIT, a.index, b.index, size = size) - def system_call(self): + def system_call(self) -> LowLevelILExpr: """ ``system_call`` return a system call expression. @@ -2607,7 +2545,8 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_SYSCALL) - def intrinsic(self, outputs, intrinsic, params, flags=None): + def intrinsic(self, outputs:List[Union[ILFlag, LowLevelILExpr]], intrinsic:'architecture.IntrinsicType', + params:List[LowLevelILExpr], flags:'architecture.FlagType'=None): """ ``intrinsic`` return an intrinsic expression. @@ -2625,9 +2564,9 @@ class LowLevelILFunction(object): param_list.append(param.index) call_param = self.expr(LowLevelILOperation.LLIL_CALL_PARAM, len(params), self.add_operand_list(param_list).index) return self.expr(LowLevelILOperation.LLIL_INTRINSIC, len(outputs), self.add_operand_list(output_list).index, - self._arch.get_intrinsic_index(intrinsic), call_param.index, flags = flags) + self.arch.get_intrinsic_index(intrinsic), call_param.index, flags = flags) - def breakpoint(self): + def breakpoint(self) -> LowLevelILExpr: """ ``breakpoint`` returns a processor breakpoint expression. @@ -2636,7 +2575,7 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_BP) - def trap(self, value): + def trap(self, value:int) -> LowLevelILExpr: """ ``trap`` returns a processor trap (interrupt) expression of the given integer ``value``. @@ -2646,7 +2585,7 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_TRAP, value) - def undefined(self): + def undefined(self) -> LowLevelILExpr: """ ``undefined`` returns the undefined expression. This should be used for instructions which perform functions but aren't important for dataflow or partial emulation purposes. @@ -2656,7 +2595,7 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_UNDEF) - def unimplemented(self): + def unimplemented(self) -> LowLevelILExpr: """ ``unimplemented`` returns the unimplemented expression. This should be used for all instructions which aren't implemented. @@ -2666,7 +2605,7 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_UNIMPL) - def unimplemented_memory_ref(self, size, addr): + def unimplemented_memory_ref(self, size:int, addr:LowLevelILExpr) -> LowLevelILExpr: """ ``unimplemented_memory_ref`` a memory reference to expression ``addr`` of size ``size`` with unimplemented operation. @@ -2677,7 +2616,7 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_UNIMPL_MEM, addr.index, size = size) - def float_add(self, size, a, b, flags=None): + def float_add(self, size:int, a:LowLevelILExpr, b:LowLevelILExpr, flags:'architecture.FlagType'=None) -> LowLevelILExpr: """ ``float_add`` adds floating point expression ``a`` to expression ``b`` potentially setting flags ``flags`` and returning an expression of ``size`` bytes. @@ -2691,7 +2630,7 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_FADD, a.index, b.index, size=size, flags=flags) - def float_sub(self, size, a, b, flags=None): + def float_sub(self, size:int, a:LowLevelILExpr, b:LowLevelILExpr, flags:'architecture.FlagType'=None) -> LowLevelILExpr: """ ``float_sub`` subtracts floating point expression ``b`` from expression ``a`` potentially setting flags ``flags`` and returning an expression of ``size`` bytes. @@ -2705,7 +2644,7 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_FSUB, a.index, b.index, size=size, flags=flags) - def float_mult(self, size, a, b, flags=None): + def float_mult(self, size:int, a:LowLevelILExpr, b:LowLevelILExpr, flags:'architecture.FlagType'=None) -> LowLevelILExpr: """ ``float_mult`` multiplies floating point expression ``a`` by expression ``b`` potentially setting flags ``flags`` and returning an expression of ``size`` bytes. @@ -2719,7 +2658,7 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_FMUL, a.index, b.index, size=size, flags=flags) - def float_div(self, size, a, b, flags=None): + def float_div(self, size:int, a:LowLevelILExpr, b:LowLevelILExpr, flags:'architecture.FlagType'=None) -> LowLevelILExpr: """ ``float_div`` divides floating point expression ``a`` by expression ``b`` potentially setting flags ``flags`` and returning an expression of ``size`` bytes. @@ -2733,7 +2672,7 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_FDIV, a.index, b.index, size=size, flags=flags) - def float_sqrt(self, size, value, flags=None): + def float_sqrt(self, size:int, value:LowLevelILExpr, flags:'architecture.FlagType'=None) -> LowLevelILExpr: """ ``float_sqrt`` returns square root of floating point expression ``value`` of size ``size`` potentially setting flags @@ -2745,7 +2684,7 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_FSQRT, value.index, size=size, flags=flags) - def float_neg(self, size, value, flags=None): + def float_neg(self, size:int, value:LowLevelILExpr, flags:'architecture.FlagType'=None) -> LowLevelILExpr: """ ``float_neg`` returns sign negation of floating point expression ``value`` of size ``size`` potentially setting flags @@ -2757,7 +2696,7 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_FNEG, value.index, size=size, flags=flags) - def float_abs(self, size, value, flags=None): + def float_abs(self, size:int, value:LowLevelILExpr, flags:'architecture.FlagType'=None) -> LowLevelILExpr: """ ``float_abs`` returns absolute value of floating point expression ``value`` of size ``size`` potentially setting flags @@ -2769,7 +2708,7 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_FABS, value.index, size=size, flags=flags) - def float_to_int(self, size, value, flags=None): + def float_to_int(self, size:int, value:LowLevelILExpr, flags:'architecture.FlagType'=None) -> LowLevelILExpr: """ ``float_to_int`` returns integer value of floating point expression ``value`` of size ``size`` potentially setting flags @@ -2781,7 +2720,7 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_FLOAT_TO_INT, value.index, size=size, flags=flags) - def int_to_float(self, size, value, flags=None): + def int_to_float(self, size:int, value:LowLevelILExpr, flags:'architecture.FlagType'=None) -> LowLevelILExpr: """ ``int_to_float`` returns floating point value of integer expression ``value`` of size ``size`` potentially setting flags @@ -2793,7 +2732,7 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_INT_TO_FLOAT, value.index, size=size, flags=flags) - def float_convert(self, size, value, flags=None): + def float_convert(self, size:int, value:LowLevelILExpr, flags:'architecture.FlagType'=None) -> LowLevelILExpr: """ ``int_to_float`` converts floating point value of expression ``value`` to size ``size`` potentially setting flags @@ -2805,7 +2744,7 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_FLOAT_CONV, value.index, size=size, flags=flags) - def round_to_int(self, size, value, flags=None): + def round_to_int(self, size:int, value:LowLevelILExpr, flags:'architecture.FlagType'=None) -> LowLevelILExpr: """ ``round_to_int`` rounds a floating point value to the nearest integer @@ -2817,7 +2756,7 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_ROUND_TO_INT, value.index, size=size, flags=flags) - def floor(self, size, value, flags=None): + def floor(self, size:int, value:LowLevelILExpr, flags:'architecture.FlagType'=None) -> LowLevelILExpr: """ ``floor`` rounds a floating point value to an integer towards negative infinity @@ -2829,7 +2768,7 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_FLOOR, value.index, size=size, flags=flags) - def ceil(self, size, value, flags=None): + def ceil(self, size:int, value:LowLevelILExpr, flags:'architecture.FlagType'=None) -> LowLevelILExpr: """ ``ceil`` rounds a floating point value to an integer towards positive infinity @@ -2841,7 +2780,7 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_CEIL, value.index, size=size, flags=flags) - def float_trunc(self, size, value, flags=None): + def float_trunc(self, size:int, value:LowLevelILExpr, flags:'architecture.FlagType'=None) -> LowLevelILExpr: """ ``float_trunc`` rounds a floating point value to an integer towards zero @@ -2853,7 +2792,7 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_FTRUNC, value.index, size=size, flags=flags) - def float_compare_equal(self, size, a, b): + def float_compare_equal(self, size:int, a:LowLevelILExpr, b:LowLevelILExpr) -> LowLevelILExpr: """ ``float_compare_equal`` returns floating point comparison expression of size ``size`` checking if expression ``a`` is equal to expression ``b`` @@ -2867,7 +2806,7 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_FCMP_E, a.index, b.index) - def float_compare_not_equal(self, size, a, b): + def float_compare_not_equal(self, size:int, a:LowLevelILExpr, b:LowLevelILExpr) -> LowLevelILExpr: """ ``float_compare_not_equal`` returns floating point comparison expression of size ``size`` checking if expression ``a`` is not equal to expression ``b`` @@ -2881,7 +2820,7 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_FCMP_NE, a.index, b.index) - def float_compare_less_than(self, size, a, b): + def float_compare_less_than(self, size:int, a:LowLevelILExpr, b:LowLevelILExpr) -> LowLevelILExpr: """ ``float_compare_less_than`` returns floating point comparison expression of size ``size`` checking if expression ``a`` is less than to expression ``b`` @@ -2895,7 +2834,7 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_FCMP_LT, a.index, b.index) - def float_compare_less_equal(self, size, a, b): + def float_compare_less_equal(self, size:int, a:LowLevelILExpr, b:LowLevelILExpr) -> LowLevelILExpr: """ ``float_compare_less_equal`` returns floating point comparison expression of size ``size`` checking if expression ``a`` is less than or equal to expression ``b`` @@ -2909,7 +2848,7 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_FCMP_LE, a.index, b.index) - def float_compare_greater_equal(self, size, a, b): + def float_compare_greater_equal(self, size:int, a:LowLevelILExpr, b:LowLevelILExpr) -> LowLevelILExpr: """ ``float_compare_greater_equal`` returns floating point comparison expression of size ``size`` checking if expression ``a`` is greater than or equal to expression ``b`` @@ -2923,7 +2862,7 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_FCMP_GE, a.index, b.index) - def float_compare_greater_than(self, size, a, b): + def float_compare_greater_than(self, size:int, a:LowLevelILExpr, b:LowLevelILExpr) -> LowLevelILExpr: """ ``float_compare_greater_than`` returns floating point comparison expression of size ``size`` checking if expression ``a`` is greater than or equal to expression ``b`` @@ -2937,7 +2876,7 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_FCMP_GT, a.index, b.index) - def float_compare_unordered(self, size, a, b): + def float_compare_unordered(self, size:int, a:LowLevelILExpr, b:LowLevelILExpr) -> LowLevelILExpr: """ ``float_compare_unordered`` returns floating point comparison expression of size ``size`` checking if expression ``a`` is unordered relative to expression ``b`` @@ -2951,7 +2890,7 @@ class LowLevelILFunction(object): """ return self.expr(LowLevelILOperation.LLIL_FCMP_UO, a.index, b.index) - def goto(self, label): + def goto(self, label:LowLevelILLabel) -> LowLevelILExpr: """ ``goto`` returns a goto expression which jumps to the provided LowLevelILLabel. @@ -2961,7 +2900,7 @@ class LowLevelILFunction(object): """ return LowLevelILExpr(core.BNLowLevelILGoto(self.handle, label.handle)) - def if_expr(self, operand, t, f): + def if_expr(self, operand:LowLevelILExpr, t:LowLevelILLabel, f:LowLevelILLabel) -> LowLevelILExpr: """ ``if_expr`` returns the ``if`` expression which depending on condition ``operand`` jumps to the LowLevelILLabel ``t`` when the condition expression ``operand`` is non-zero and ``f`` when it's zero. @@ -2974,7 +2913,7 @@ class LowLevelILFunction(object): """ return LowLevelILExpr(core.BNLowLevelILIf(self.handle, operand.index, t.handle, f.handle)) - def mark_label(self, label): + def mark_label(self, label:LowLevelILLabel) -> None: """ ``mark_label`` assigns a LowLevelILLabel to the current IL address. @@ -2983,35 +2922,44 @@ class LowLevelILFunction(object): """ core.BNLowLevelILMarkLabel(self.handle, label.handle) - def add_label_list(self, labels): + def add_label_map(self, labels:Mapping[int, LowLevelILLabel]) -> LowLevelILExpr: """ - ``add_label_list`` returns a label list expression for the given list of LowLevelILLabel objects. + ``add_label_map`` returns a label list expression for the given list of LowLevelILLabel objects. :param labels: the list of LowLevelILLabel to get a label list expression from - :type labels: list(LowLevelILLabel) + :type labels: dict(int, LowLevelILLabel) :return: the label list expression :rtype: LowLevelILExpr """ - label_list = (ctypes.POINTER(core.BNLowLevelILLabel) * len(labels))() - for i in range(len(labels)): - label_list[i] = labels[i].handle - return LowLevelILExpr(core.BNLowLevelILAddLabelList(self.handle, label_list, len(labels))) + label_list = (ctypes.POINTER(core.BNLowLevelILLabel) * len(labels))() # type: ignore + value_list = (ctypes.POINTER(ctypes.c_ulonglong) * len(labels))() # type: ignore + for i, (key, value) in enumerate(labels.items()): + value_list[i] = key + label_list[i] = value.handle - def add_operand_list(self, operands): + return LowLevelILExpr(core.BNLowLevelILAddLabelMap(self.handle, value_list, label_list, len(labels))) + + def add_operand_list(self, operands:List[Union[ExpressionIndex, LowLevelILExpr]]) -> LowLevelILExpr: """ ``add_operand_list`` returns an operand list expression for the given list of integer operands. :param operands: list of operand numbers - :type operands: list(int) + :type operands: List(Union[ExpressionIndex, LowLevelILExpr]) :return: an operand list expression :rtype: LowLevelILExpr """ operand_list = (ctypes.c_ulonglong * len(operands))() for i in range(len(operands)): - operand_list[i] = operands[i] + op = operands[i] + if isinstance(op, LowLevelILExpr): + operand_list[i] = op.index + elif isinstance(op, int): + operand_list[i] = op + else: + raise Exception("Invalid operand type") return LowLevelILExpr(core.BNLowLevelILAddOperandList(self.handle, operand_list, len(operands))) - def operand(self, n, expr): + def operand(self, n:int, expr:LowLevelILExpr) -> LowLevelILExpr: """ ``operand`` sets the operand number of the expression ``expr`` and passes back ``expr`` without modification. @@ -3023,7 +2971,7 @@ class LowLevelILFunction(object): core.BNLowLevelILSetExprSourceOperand(self.handle, expr.index, n) return expr - def finalize(self): + def finalize(self) -> None: """ ``finalize`` ends the function and computes the list of basic blocks. @@ -3039,7 +2987,7 @@ class LowLevelILFunction(object): """ core.BNGenerateLowLevelILSSAForm(self.handle) - def add_label_for_address(self, arch, addr): + def add_label_for_address(self, arch:'architecture.Architecture', addr:int) -> None: """ ``add_label_for_address`` adds a low-level IL label for the given architecture ``arch`` at the given virtual address ``addr`` @@ -3051,7 +2999,7 @@ class LowLevelILFunction(object): arch = arch.handle core.BNAddLowLevelILLabelForAddress(self.handle, arch, addr) - def get_label_for_address(self, arch, addr): + def get_label_for_address(self, arch:'architecture.Architecture', addr:int) -> Optional[LowLevelILLabel]: """ ``get_label_for_address`` returns the LowLevelILLabel for the given Architecture ``arch`` and IL address ``addr``. @@ -3067,74 +3015,77 @@ class LowLevelILFunction(object): return None return LowLevelILLabel(label) - def get_ssa_instruction_index(self, instr): + def get_ssa_instruction_index(self, instr:InstructionIndex) -> InstructionIndex: return core.BNGetLowLevelILSSAInstructionIndex(self.handle, instr) - def get_non_ssa_instruction_index(self, instr): + def get_non_ssa_instruction_index(self, instr:InstructionIndex) -> InstructionIndex: return core.BNGetLowLevelILNonSSAInstructionIndex(self.handle, instr) - def get_ssa_reg_definition(self, reg_ssa): - reg = self._arch.get_reg_index(reg_ssa.reg) + def get_ssa_reg_definition(self, reg_ssa:SSARegister) -> Optional[LowLevelILInstruction]: + reg = self.arch.get_reg_index(reg_ssa.reg) result = core.BNGetLowLevelILSSARegisterDefinition(self.handle, reg, reg_ssa.version) if result >= core.BNGetLowLevelILInstructionCount(self.handle): return None return self[result] - def get_ssa_flag_definition(self, flag_ssa): - flag = self._arch.get_flag_index(flag_ssa.flag) + def get_ssa_flag_definition(self, flag_ssa:SSAFlag) -> Optional[LowLevelILInstruction]: + flag = self.arch.get_flag_index(flag_ssa.flag) result = core.BNGetLowLevelILSSAFlagDefinition(self.handle, flag, flag_ssa.version) if result >= core.BNGetLowLevelILInstructionCount(self.handle): return None return self[result] - def get_ssa_memory_definition(self, index): + def get_ssa_memory_definition(self, index:int) -> Optional[LowLevelILInstruction]: result = core.BNGetLowLevelILSSAMemoryDefinition(self.handle, index) if result >= core.BNGetLowLevelILInstructionCount(self.handle): return None return self[result] - def get_ssa_reg_uses(self, reg_ssa): - reg = self._arch.get_reg_index(reg_ssa.reg) + def get_ssa_reg_uses(self, reg_ssa:SSARegister) -> List[LowLevelILInstruction]: + reg = self.arch.get_reg_index(reg_ssa.reg) count = ctypes.c_ulonglong() instrs = core.BNGetLowLevelILSSARegisterUses(self.handle, reg, reg_ssa.version, count) + assert instrs is not None, "core.BNGetLowLevelILSSARegisterUses returned None" result = [] for i in range(0, count.value): result.append(self[instrs[i]]) core.BNFreeILInstructionList(instrs) return result - def get_ssa_flag_uses(self, flag_ssa): - flag = self._arch.get_flag_index(flag_ssa.flag) + def get_ssa_flag_uses(self, flag_ssa:SSAFlag) -> List[LowLevelILInstruction]: + flag = self.arch.get_flag_index(flag_ssa.flag) count = ctypes.c_ulonglong() instrs = core.BNGetLowLevelILSSAFlagUses(self.handle, flag, flag_ssa.version, count) + assert instrs is not None, "core.BNGetLowLevelILSSAFlagUses returned None" result = [] for i in range(0, count.value): result.append(self[instrs[i]]) core.BNFreeILInstructionList(instrs) return result - def get_ssa_memory_uses(self, index): + def get_ssa_memory_uses(self, index:int) -> List[LowLevelILInstruction]: count = ctypes.c_ulonglong() instrs = core.BNGetLowLevelILSSAMemoryUses(self.handle, index, count) + assert instrs is not None, "core.BNGetLowLevelILSSAMemoryUses returned None" result = [] for i in range(0, count.value): result.append(self[instrs[i]]) core.BNFreeILInstructionList(instrs) return result - def get_ssa_reg_value(self, reg_ssa): - reg = self._arch.get_reg_index(reg_ssa.reg) + def get_ssa_reg_value(self, reg_ssa:SSARegister) -> 'variable.RegisterValue': + reg = self.arch.get_reg_index(reg_ssa.reg) value = core.BNGetLowLevelILSSARegisterValue(self.handle, reg, reg_ssa.version) - result = binaryninja.function.RegisterValue(self._arch, value) + result = variable.RegisterValue(self._arch, value) return result - def get_ssa_flag_value(self, flag_ssa): - flag = self._arch.get_flag_index(flag_ssa.flag) + def get_ssa_flag_value(self, flag_ssa:SSAFlag) -> 'variable.RegisterValue': + flag = self.arch.get_flag_index(flag_ssa.flag) value = core.BNGetLowLevelILSSAFlagValue(self.handle, flag, flag_ssa.version) - result = binaryninja.function.RegisterValue(self._arch, value) + result = variable.RegisterValue(self._arch, value) return result - def get_medium_level_il_instruction_index(self, instr): + def get_medium_level_il_instruction_index(self, instr:InstructionIndex) -> Optional['mediumlevelil.InstructionIndex']: med_il = self.medium_level_il if med_il is None: return None @@ -3143,7 +3094,7 @@ class LowLevelILFunction(object): return None return result - def get_medium_level_il_expr_index(self, expr): + def get_medium_level_il_expr_index(self, expr:ExpressionIndex) -> Optional['mediumlevelil.ExpressionIndex']: med_il = self.medium_level_il if med_il is None: return None @@ -3152,16 +3103,17 @@ class LowLevelILFunction(object): return None return result - def get_medium_level_il_expr_indexes(self, expr): + def get_medium_level_il_expr_indexes(self, expr:ExpressionIndex) -> List['mediumlevelil.ExpressionIndex']: count = ctypes.c_ulonglong() exprs = core.BNGetMediumLevelILExprIndexes(self.handle, expr, count) + assert exprs is not None, "core.BNGetMediumLevelILExprIndexes returned None" result = [] for i in range(0, count.value): result.append(exprs[i]) core.BNFreeILInstructionList(exprs) return result - def get_mapped_medium_level_il_instruction_index(self, instr): + def get_mapped_medium_level_il_instruction_index(self, instr:InstructionIndex) -> Optional[InstructionIndex]: med_il = self.mapped_medium_level_il if med_il is None: return None @@ -3170,7 +3122,7 @@ class LowLevelILFunction(object): return None return result - def get_mapped_medium_level_il_expr_index(self, expr): + def get_mapped_medium_level_il_expr_index(self, expr:ExpressionIndex) -> Optional['mediumlevelil.ExpressionIndex']: med_il = self.mapped_medium_level_il if med_il is None: return None @@ -3179,7 +3131,7 @@ class LowLevelILFunction(object): return None return result - def get_high_level_il_instruction_index(self, instr): + def get_high_level_il_instruction_index(self, instr:InstructionIndex) -> Optional['highlevelil.InstructionIndex']: med_il = self.medium_level_il if med_il is None: return None @@ -3188,7 +3140,7 @@ class LowLevelILFunction(object): return None return med_il.get_high_level_il_instruction_index(mlil_instr) - def get_high_level_il_expr_index(self, expr): + def get_high_level_il_expr_index(self, expr:ExpressionIndex) -> Optional['highlevelil.ExpressionIndex']: med_il = self.medium_level_il if med_il is None: return None @@ -3197,16 +3149,16 @@ class LowLevelILFunction(object): return None return med_il.get_high_level_il_expr_index(mlil_expr) - def create_graph(self, settings = None): + def create_graph(self, settings:Optional['function.DisassemblySettings']=None) -> flowgraph.CoreFlowGraph: if settings is not None: settings_obj = settings.handle else: settings_obj = None - return binaryninja.flowgraph.CoreFlowGraph(core.BNCreateLowLevelILFunctionGraph(self.handle, settings_obj)) + return flowgraph.CoreFlowGraph(core.BNCreateLowLevelILFunctionGraph(self.handle, settings_obj)) class LowLevelILBasicBlock(basicblock.BasicBlock): - def __init__(self, view, handle, owner): + def __init__(self, view:Optional['binaryview.BinaryView'], handle:core.BNBasicBlock, owner:'LowLevelILFunction'): super(LowLevelILBasicBlock, self).__init__(handle, view) self._il_function = owner @@ -3225,7 +3177,7 @@ class LowLevelILBasicBlock(basicblock.BasicBlock): return False return True - def __iter__(self): + def __iter__(self) -> Generator['LowLevelILInstruction', None, None]: for idx in range(self.start, self.end): yield self._il_function[idx] @@ -3245,22 +3197,21 @@ class LowLevelILBasicBlock(basicblock.BasicBlock): return LowLevelILBasicBlock(view, handle, self._il_function) @property - def il_function(self): - """ """ - return self._il_function + def instruction_count(self) -> int: + return self.end - self.start - @il_function.setter - def il_function(self, value): - self._il_function = value + @property + def il_function(self) -> LowLevelILFunction: + return self._il_function -def LLIL_TEMP(n): +def LLIL_TEMP(n:int) -> int: return n | 0x80000000 -def LLIL_REG_IS_TEMP(n): +def LLIL_REG_IS_TEMP(n:int) -> bool: return (n & 0x80000000) != 0 -def LLIL_GET_TEMP_REG_INDEX(n): +def LLIL_GET_TEMP_REG_INDEX(n:int) -> int: return n & 0x7fffffff diff --git a/python/mainthread.py b/python/mainthread.py index 3b5dc26e..d8b00993 100644 --- a/python/mainthread.py +++ b/python/mainthread.py @@ -19,9 +19,9 @@ # IN THE SOFTWARE. # Binary Ninja components -from binaryninja import _binaryninjacore as core -from binaryninja import scriptingprovider -from binaryninja import plugin +from . import _binaryninjacore as core +from . import scriptingprovider +from . import plugin def execute_on_main_thread(func): diff --git a/python/mediumlevelil.py b/python/mediumlevelil.py index c2e9a5eb..167930b6 100644 --- a/python/mediumlevelil.py +++ b/python/mediumlevelil.py @@ -20,23 +20,30 @@ import ctypes import struct -from typing import List +from typing import Optional, List, Any, Union, Mapping, Generator, NewType # Binary Ninja components -import binaryninja -from binaryninja import _binaryninjacore as core -from binaryninja.enums import MediumLevelILOperation, FunctionGraphType, ILBranchDependence -from binaryninja import basicblock #required for MediumLevelILBasicBlock argument -from binaryninja import function -from binaryninja import types -from binaryninja import lowlevelil +from . import _binaryninjacore as core +from .enums import MediumLevelILOperation, ILBranchDependence, DataFlowQueryOption +from . import basicblock #required for MediumLevelILBasicBlock argument +from . import function +from . import types +from . import lowlevelil +from . import highlevelil +from . import flowgraph +from . import variable +from . import architecture +from . import binaryview -# 2-3 compatibility -from binaryninja import range +OptionalTokens = Optional[List['function.InstructionTextToken']] +ExpressionIndex = NewType('ExpressionIndex', int) +InstructionIndex = NewType('InstructionIndex', int) +MLILInstructionsType = Generator['MediumLevelILInstruction', None, None] +MLILBasicBlocksType = Generator['MediumLevelILBasicBlock', None, None] class SSAVariable(object): - def __init__(self, var, version): + def __init__(self, var:'variable.Variable', version:int): self._var = var self._version = version @@ -57,26 +64,24 @@ class SSAVariable(object): return hash((self._var, self._version)) @property - def var(self): - """ """ + def var(self) -> 'variable.Variable': return self._var @var.setter - def var(self, value): + def var(self, value:'variable.Variable') -> None: self._var = value @property - def version(self): - """ """ + def version(self) -> int: return self._version @version.setter - def version(self, value): + def version(self, value=int) -> None: self._version = value class MediumLevelILLabel(object): - def __init__(self, handle = None): + def __init__(self, handle:Optional[core.BNMediumLevelILLabel]=None): if handle is None: self.handle = (core.BNMediumLevelILLabel * 1)() core.BNMediumLevelILInitLabel(self.handle) @@ -85,7 +90,7 @@ class MediumLevelILLabel(object): class MediumLevelILOperationAndSize(object): - def __init__(self, operation, size): + def __init__(self, operation:MediumLevelILOperation, size:int): self._operation = operation self._size = size @@ -110,13 +115,11 @@ class MediumLevelILOperationAndSize(object): return hash((self._operation, self._size)) @property - def operation(self): - """ """ + def operation(self) -> MediumLevelILOperation: return self._operation @property - def size(self): - """ """ + def size(self) -> int: return self._size @@ -261,7 +264,7 @@ class MediumLevelILInstruction(object): MediumLevelILOperation.MLIL_MEM_PHI: [("dest_memory", "int"), ("src_memory", "int_list")] } - def __init__(self, func, expr_index, instr_index=None): + def __init__(self, func:'MediumLevelILFunction', expr_index:ExpressionIndex, instr_index:InstructionIndex=None): instr = core.BNGetMediumLevelILByIndex(func.handle, expr_index) self._function = func self._expr_index = expr_index @@ -274,10 +277,11 @@ class MediumLevelILInstruction(object): self._address = instr.address self._source_operand = instr.sourceOperand operands = MediumLevelILInstruction.ILOperations[instr.operation] - self._operands = [] + self._operands:List[Any] = [] i = 0 for operand in operands: name, operand_type = operand + value = None if operand_type == "int": value = instr.operands[i] value = (value & ((1 << 63) - 1)) - (value & (1 << 63)) @@ -291,16 +295,17 @@ class MediumLevelILInstruction(object): elif operand_type == "expr": value = MediumLevelILInstruction(func, instr.operands[i]) elif operand_type == "intrinsic": + assert func.arch is not None, "Attempting to create ILInstrinsice from function with no Architecture" value = lowlevelil.ILIntrinsic(func.arch, instr.operands[i]) elif operand_type == "var": - value = function.Variable.from_identifier(self._function.source_function, instr.operands[i]) + value = variable.Variable.from_identifier(self._function.source_function, instr.operands[i]) elif operand_type == "var_ssa": - var = function.Variable.from_identifier(self._function.source_function, instr.operands[i]) + var = variable.Variable.from_identifier(self._function.source_function, instr.operands[i]) version = instr.operands[i + 1] i += 1 value = SSAVariable(var, version) elif operand_type == "var_ssa_dest_and_src": - var = function.Variable.from_identifier(self._function.source_function, instr.operands[i]) + var = variable.Variable.from_identifier(self._function.source_function, instr.operands[i]) dest_version = instr.operands[i + 1] src_version = instr.operands[i + 2] i += 2 @@ -311,6 +316,7 @@ class MediumLevelILInstruction(object): elif operand_type == "int_list": count = ctypes.c_ulonglong() operand_list = core.BNMediumLevelILGetOperandList(func.handle, self._expr_index, i, count) + assert operand_list is not None, "core.BNMediumLevelILGetOperandList returned None" value = [] for j in range(count.value): value.append(operand_list[j]) @@ -318,25 +324,28 @@ class MediumLevelILInstruction(object): elif operand_type == "var_list": count = ctypes.c_ulonglong() operand_list = core.BNMediumLevelILGetOperandList(func.handle, self._expr_index, i, count) + assert operand_list is not None, "core.BNMediumLevelILGetOperandList returned None" i += 1 value = [] for j in range(count.value): - value.append(function.Variable.from_identifier(self._function.source_function, operand_list[j])) + value.append(variable.Variable.from_identifier(self._function.source_function, operand_list[j])) core.BNMediumLevelILFreeOperandList(operand_list) elif operand_type == "var_ssa_list": count = ctypes.c_ulonglong() operand_list = core.BNMediumLevelILGetOperandList(func.handle, self._expr_index, i, count) + assert operand_list is not None, "core.BNMediumLevelILGetOperandList returned None" i += 1 value = [] for j in range(count.value // 2): var_id = operand_list[j * 2] var_version = operand_list[(j * 2) + 1] - value.append(SSAVariable(function.Variable.from_identifier(self._function.source_function, + value.append(SSAVariable(variable.Variable.from_identifier(self._function.source_function, var_id), var_version)) core.BNMediumLevelILFreeOperandList(operand_list) elif operand_type == "expr_list": count = ctypes.c_ulonglong() operand_list = core.BNMediumLevelILGetOperandList(func.handle, self._expr_index, i, count) + assert operand_list is not None, "core.BNMediumLevelILGetOperandList returned None" i += 1 value = [] for j in range(count.value): @@ -345,6 +354,7 @@ class MediumLevelILInstruction(object): elif operand_type == "target_map": count = ctypes.c_ulonglong() operand_list = core.BNMediumLevelILGetOperandList(func.handle, self._expr_index, i, count) + assert operand_list is not None, "core.BNMediumLevelILGetOperandList returned None" i += 1 value = {} for j in range(count.value // 2): @@ -368,27 +378,27 @@ class MediumLevelILInstruction(object): def __repr__(self): return "<il: %s>" % str(self) - def __eq__(self, other): + def __eq__(self, other:'MediumLevelILInstruction'): if not isinstance(other, self.__class__): return NotImplemented return self._function == other.function and self._expr_index == other.expr_index - def __lt__(self, other): + def __lt__(self, other:'MediumLevelILInstruction'): if not isinstance(other, self.__class__): return NotImplemented return self._function == other.function and self.expr_index < other.expr_index - def __le__(self, other): + def __le__(self, other:'MediumLevelILInstruction'): if not isinstance(other, self.__class__): return NotImplemented return self._function == other.function and self.expr_index <= other.expr_index - def __gt__(self, other): + def __gt__(self, other:'MediumLevelILInstruction'): if not isinstance(other, self.__class__): return NotImplemented return self._function == other.function and self.expr_index > other.expr_index - def __ge__(self, other): + def __ge__(self, other:'MediumLevelILInstruction'): if not isinstance(other, self.__class__): return NotImplemented return self._function == other.function and self.expr_index >= other.expr_index @@ -397,10 +407,12 @@ class MediumLevelILInstruction(object): return hash((self._instr_index, self._function)) @property - def tokens(self): + def tokens(self) -> OptionalTokens: """MLIL tokens (read-only)""" count = ctypes.c_ulonglong() tokens = ctypes.POINTER(core.BNInstructionTextToken)() + if self._function.arch is None: + raise Exception("Attempting to get tokens for MLIL Function with no Architecture set") if ((self._instr_index is not None) and (self._function.source_function is not None) and (self._expr_index == core.BNGetMediumLevelILIndexForInstruction(self._function.handle, self._instr_index))): if not core.BNGetMediumLevelILInstructionText(self._function.handle, self._function.source_function.handle, @@ -410,47 +422,55 @@ class MediumLevelILInstruction(object): if not core.BNGetMediumLevelILExprText(self._function.handle, self._function.arch.handle, self._expr_index, tokens, count, None): return None - result = binaryninja.function.InstructionTextToken.get_instruction_lines(tokens, count.value) + result = function.InstructionTextToken._from_core_struct(tokens, count.value) core.BNFreeInstructionText(tokens, count.value) return result @property - def il_basic_block(self): + def il_basic_block(self) -> 'MediumLevelILBasicBlock': """IL basic block object containing this expression (read-only) (only available on finalized functions)""" - return MediumLevelILBasicBlock(self._function.source_function.view, core.BNGetMediumLevelILBasicBlockForInstruction(self._function.handle, self._instr_index), self._function) + core_block = core.BNGetMediumLevelILBasicBlockForInstruction(self._function.handle, self._instr_index) + assert core_block is not None + assert self._function.source_function is not None + return MediumLevelILBasicBlock(core_block, self._function, self._function.source_function.view) @property - def ssa_form(self): + def ssa_form(self) -> 'MediumLevelILInstruction': """SSA form of expression (read-only)""" - return MediumLevelILInstruction(self._function.ssa_form, + ssa_func = self._function.ssa_form + assert ssa_func is not None + return MediumLevelILInstruction(ssa_func, core.BNGetMediumLevelILSSAExprIndex(self._function.handle, self._expr_index)) @property - def non_ssa_form(self): + def non_ssa_form(self) -> 'MediumLevelILInstruction': """Non-SSA form of expression (read-only)""" - return MediumLevelILInstruction(self._function.non_ssa_form, + non_ssa_func = self._function.non_ssa_form + assert non_ssa_func is not None + return MediumLevelILInstruction(non_ssa_func, core.BNGetMediumLevelILNonSSAExprIndex(self._function.handle, self._expr_index)) @property - def value(self): + def value(self) -> variable.RegisterValue: """Value of expression if constant or a known value (read-only)""" value = core.BNGetMediumLevelILExprValue(self._function.handle, self._expr_index) - result = function.RegisterValue(self._function.arch, value) + result = variable.RegisterValue(self._function.arch, value) return result @property - def possible_values(self): + def possible_values(self) -> variable.PossibleValueSet: """Possible values of expression using path-sensitive static data flow analysis (read-only)""" value = core.BNGetMediumLevelILPossibleExprValues(self._function.handle, self._expr_index, None, 0) - result = function.PossibleValueSet(self._function.arch, value) + result = variable.PossibleValueSet(self._function.arch, value) core.BNFreePossibleValueSet(value) return result @property - def branch_dependence(self): + def branch_dependence(self) -> Mapping[int, ILBranchDependence]: """Set of branching instructions that must take the true or false path to reach this instruction""" count = ctypes.c_ulonglong() deps = core.BNGetAllMediumLevelILBranchDependence(self._function.handle, self._instr_index, count) + assert deps is not None, "core.BNGetAllMediumLevelILBranchDependence returned None" result = {} for i in range(0, count.value): result[deps[i].branch] = ILBranchDependence(deps[i].dependence) @@ -458,54 +478,58 @@ class MediumLevelILInstruction(object): return result @property - def low_level_il(self): + def low_level_il(self) -> Optional['lowlevelil.LowLevelILInstruction']: """Low level IL form of this expression""" expr = self._function.get_low_level_il_expr_index(self._expr_index) - if expr is None: + if expr is None or self._function.low_level_il is None: return None return lowlevelil.LowLevelILInstruction(self._function.low_level_il.ssa_form, expr) @property - def llil(self): + def llil(self) -> Optional['lowlevelil.LowLevelILInstruction']: """Alias for low_level_il""" return self.low_level_il @property - def llils(self): + def llils(self) -> List['lowlevelil.LowLevelILInstruction']: exprs = self._function.get_low_level_il_expr_indexes(self.expr_index) + if self._function.low_level_il is None: + return [] result = [] for expr in exprs: result.append(lowlevelil.LowLevelILInstruction(self._function.low_level_il.ssa_form, expr)) return result @property - def high_level_il(self): + def high_level_il(self) -> Optional[highlevelil.HighLevelILInstruction]: """High level IL form of this expression""" expr = self._function.get_high_level_il_expr_index(self._expr_index) - if expr is None: + if expr is None or self._function.high_level_il is None: return None - return binaryninja.highlevelil.HighLevelILInstruction(self._function.high_level_il, expr) + return highlevelil.HighLevelILInstruction(self._function.high_level_il, expr) @property - def hlil(self): + def hlil(self) -> Optional[highlevelil.HighLevelILInstruction]: """Alias for high_level_il""" return self.high_level_il @property - def hlils(self): + def hlils(self) -> List[highlevelil.HighLevelILInstruction]: exprs = self._function.get_high_level_il_expr_indexes(self.expr_index) result = [] + if self._function.high_level_il is None: + return result for expr in exprs: - result.append(binaryninja.highlevelil.HighLevelILInstruction(self._function.high_level_il, expr)) + result.append(highlevelil.HighLevelILInstruction(self._function.high_level_il, expr)) return result @property - def ssa_memory_version(self): + def ssa_memory_version(self) -> int: """Version of active memory contents in SSA form for this instruction""" return core.BNGetMediumLevelILSSAMemoryVersionAtILInstruction(self._function.handle, self._instr_index) @property - def prefix_operands(self): + def prefix_operands(self) -> List[Any]: """All operands in the expression tree in prefix order""" result = [MediumLevelILOperationAndSize(self._operation, self._size)] for operand in self._operands: @@ -516,7 +540,7 @@ class MediumLevelILInstruction(object): return result @property - def postfix_operands(self): + def postfix_operands(self) -> List[Any]: """All operands in the expression tree in postfix order""" result = [] for operand in self._operands: @@ -528,60 +552,62 @@ class MediumLevelILInstruction(object): return result @property - def vars_written(self): + def vars_written(self) -> List[Union[variable.Variable, SSAVariable]]: """List of variables written by instruction""" + # We use self.__dict__ directly to work around the linter if self._operation in [MediumLevelILOperation.MLIL_SET_VAR, MediumLevelILOperation.MLIL_SET_VAR_FIELD, MediumLevelILOperation.MLIL_SET_VAR_SSA, MediumLevelILOperation.MLIL_SET_VAR_SSA_FIELD, MediumLevelILOperation.MLIL_SET_VAR_ALIASED, MediumLevelILOperation.MLIL_SET_VAR_ALIASED_FIELD, MediumLevelILOperation.MLIL_VAR_PHI]: return [self.dest] elif self._operation in [MediumLevelILOperation.MLIL_SET_VAR_SPLIT, MediumLevelILOperation.MLIL_SET_VAR_SPLIT_SSA]: - return [self.high, self.low] + return [self.__dict__['high'], self.__dict__['low']] elif self._operation in [MediumLevelILOperation.MLIL_CALL, MediumLevelILOperation.MLIL_SYSCALL, MediumLevelILOperation.MLIL_TAILCALL]: - return self.output + return self.__dict__['output'] elif self._operation in [MediumLevelILOperation.MLIL_CALL_UNTYPED, MediumLevelILOperation.MLIL_SYSCALL_UNTYPED, MediumLevelILOperation.MLIL_TAILCALL_UNTYPED, MediumLevelILOperation.MLIL_CALL_SSA, MediumLevelILOperation.MLIL_CALL_UNTYPED_SSA, MediumLevelILOperation.MLIL_SYSCALL_SSA, MediumLevelILOperation.MLIL_SYSCALL_UNTYPED_SSA, MediumLevelILOperation.MLIL_TAILCALL_SSA, MediumLevelILOperation.MLIL_TAILCALL_UNTYPED_SSA]: - return self.output.vars_written + return self.__dict__['output'].vars_written elif self._operation in [MediumLevelILOperation.MLIL_CALL_OUTPUT, MediumLevelILOperation.MLIL_CALL_OUTPUT_SSA]: - return self.dest + return self.__dict__['dest'] return [] @property - def vars_read(self): + def vars_read(self) -> List[variable.Variable]: """List of variables read by instruction""" + # We use self.__dict__ directly to work around the linter if self._operation in [MediumLevelILOperation.MLIL_SET_VAR, MediumLevelILOperation.MLIL_SET_VAR_FIELD, MediumLevelILOperation.MLIL_SET_VAR_SPLIT, MediumLevelILOperation.MLIL_SET_VAR_SSA, MediumLevelILOperation.MLIL_SET_VAR_SPLIT_SSA, MediumLevelILOperation.MLIL_SET_VAR_ALIASED]: - return self.src.vars_read + return self.__dict__['src'].vars_read elif self._operation in [MediumLevelILOperation.MLIL_SET_VAR_SSA_FIELD, MediumLevelILOperation.MLIL_SET_VAR_ALIASED_FIELD]: - return [self.prev] + self.src.vars_read + return [self.__dict__['prev']] + self.__dict__['src'].vars_read elif self._operation in [MediumLevelILOperation.MLIL_CALL, MediumLevelILOperation.MLIL_SYSCALL, MediumLevelILOperation.MLIL_TAILCALL, MediumLevelILOperation.MLIL_CALL_SSA, MediumLevelILOperation.MLIL_SYSCALL_SSA, MediumLevelILOperation.MLIL_TAILCALL_SSA]: result = [] - for param in self.params: + for param in self.__dict__['params']: result += param.vars_read return result elif self._operation in [MediumLevelILOperation.MLIL_CALL_UNTYPED, MediumLevelILOperation.MLIL_SYSCALL_UNTYPED, MediumLevelILOperation.MLIL_TAILCALL_UNTYPED, MediumLevelILOperation.MLIL_CALL_UNTYPED_SSA, MediumLevelILOperation.MLIL_SYSCALL_UNTYPED_SSA, MediumLevelILOperation.MLIL_TAILCALL_UNTYPED_SSA]: - return self.params.vars_read + return self.__dict__['params'].vars_read elif self._operation in [MediumLevelILOperation.MLIL_CALL_PARAM, MediumLevelILOperation.MLIL_CALL_PARAM_SSA, MediumLevelILOperation.MLIL_VAR_PHI]: - return self.src + return self.__dict__['src'] elif self._operation in [MediumLevelILOperation.MLIL_CALL_OUTPUT, MediumLevelILOperation.MLIL_CALL_OUTPUT_SSA]: return [] result = [] for operand in self._operands: - if (isinstance(operand, function.Variable)) or (isinstance(operand, SSAVariable)): + if (isinstance(operand, variable.Variable)) or (isinstance(operand, SSAVariable)): result.append(operand) elif isinstance(operand, MediumLevelILInstruction): result += operand.vars_read return result @property - def expr_type(self): + def expr_type(self) -> Optional['types.Type']: """Type of expression""" result = core.BNGetMediumLevelILExprType(self._function.handle, self._expr_index) if result.type: @@ -591,18 +617,18 @@ class MediumLevelILInstruction(object): return types.Type(result.type, platform = platform, confidence = result.confidence) return None - def get_possible_values(self, options = []): + def get_possible_values(self, options:List[DataFlowQueryOption]=[]) -> variable.PossibleValueSet: option_array = (ctypes.c_int * len(options))() idx = 0 for option in options: option_array[idx] = option idx += 1 value = core.BNGetMediumLevelILPossibleExprValues(self._function.handle, self._expr_index, option_array, len(options)) - result = function.PossibleValueSet(self._function.arch, value) + result = variable.PossibleValueSet(self._function.arch, value) core.BNFreePossibleValueSet(value) return result - def get_ssa_var_possible_values(self, ssa_var, options = []): + def get_ssa_var_possible_values(self, ssa_var:SSAVariable, options:List[DataFlowQueryOption]=[]): var_data = core.BNVariable() var_data.type = ssa_var.var.source_type var_data.index = ssa_var.var.index @@ -614,44 +640,45 @@ class MediumLevelILInstruction(object): idx += 1 value = core.BNGetMediumLevelILPossibleSSAVarValues(self._function.handle, var_data, ssa_var.version, self._instr_index, option_array, len(options)) - result = function.PossibleValueSet(self._function.arch, value) + result = variable.PossibleValueSet(self._function.arch, value) core.BNFreePossibleValueSet(value) return result - def get_ssa_var_version(self, var): + def get_ssa_var_version(self, var:variable.Variable) -> int: var_data = core.BNVariable() var_data.type = var.source_type var_data.index = var.index var_data.storage = var.storage return core.BNGetMediumLevelILSSAVarVersionAtILInstruction(self._function.handle, var_data, self._instr_index) - def get_var_for_reg(self, reg): + def get_var_for_reg(self, reg:'architecture.RegisterType') -> variable.Variable: reg = self._function.arch.get_reg_index(reg) result = core.BNGetMediumLevelILVariableForRegisterAtInstruction(self._function.handle, reg, self._instr_index) - return function.Variable(self._function.source_function, result.type, result.index, result.storage) + return variable.Variable(self._function.source_function, result.type, result.index, result.storage) - def get_var_for_flag(self, flag): + def get_var_for_flag(self, flag:'architecture.FlagType') -> variable.Variable: flag = self._function.arch.get_flag_index(flag) result = core.BNGetMediumLevelILVariableForFlagAtInstruction(self._function.handle, flag, self._instr_index) - return function.Variable(self._function.source_function, result.type, result.index, result.storage) + return variable.Variable(self._function.source_function, result.type, result.index, result.storage) - def get_var_for_stack_location(self, offset): + def get_var_for_stack_location(self, offset:int) -> variable.Variable: result = core.BNGetMediumLevelILVariableForStackLocationAtInstruction(self._function.handle, offset, self._instr_index) - return function.Variable(self._function.source_function, result.type, result.index, result.storage) + return variable.Variable(self._function.source_function, result.type, result.index, result.storage) - def get_reg_value(self, reg): + def get_reg_value(self, reg:'architecture.RegisterType') -> 'variable.RegisterValue': reg = self._function.arch.get_reg_index(reg) value = core.BNGetMediumLevelILRegisterValueAtInstruction(self._function.handle, reg, self._instr_index) - result = function.RegisterValue(self._function.arch, value) + result = variable.RegisterValue(self._function.arch, value) return result - def get_reg_value_after(self, reg): + def get_reg_value_after(self, reg:'architecture.RegisterType') -> 'variable.RegisterValue': reg = self._function.arch.get_reg_index(reg) value = core.BNGetMediumLevelILRegisterValueAfterInstruction(self._function.handle, reg, self._instr_index) - result = function.RegisterValue(self._function.arch, value) + result = variable.RegisterValue(self._function.arch, value) return result - def get_possible_reg_values(self, reg, options = []): + def get_possible_reg_values(self, reg:'architecture.RegisterType', + options:List[DataFlowQueryOption]=[]) -> 'variable.PossibleValueSet': reg = self._function.arch.get_reg_index(reg) option_array = (ctypes.c_int * len(options))() idx = 0 @@ -660,11 +687,12 @@ class MediumLevelILInstruction(object): idx += 1 value = core.BNGetMediumLevelILPossibleRegisterValuesAtInstruction(self._function.handle, reg, self._instr_index, option_array, len(options)) - result = function.PossibleValueSet(self._function.arch, value) + result = variable.PossibleValueSet(self._function.arch, value) core.BNFreePossibleValueSet(value) return result - def get_possible_reg_values_after(self, reg, options = []): + def get_possible_reg_values_after(self, reg:'architecture.RegisterType', + options:List[DataFlowQueryOption]=[]) -> 'variable.PossibleValueSet': reg = self._function.arch.get_reg_index(reg) option_array = (ctypes.c_int * len(options))() idx = 0 @@ -673,23 +701,24 @@ class MediumLevelILInstruction(object): idx += 1 value = core.BNGetMediumLevelILPossibleRegisterValuesAfterInstruction(self._function.handle, reg, self._instr_index, option_array, len(options)) - result = function.PossibleValueSet(self._function.arch, value) + result = variable.PossibleValueSet(self._function.arch, value) core.BNFreePossibleValueSet(value) return result - def get_flag_value(self, flag): + def get_flag_value(self, flag:'architecture.FlagType') -> 'variable.RegisterValue': flag = self._function.arch.get_flag_index(flag) value = core.BNGetMediumLevelILFlagValueAtInstruction(self._function.handle, flag, self._instr_index) - result = function.RegisterValue(self._function.arch, value) + result = variable.RegisterValue(self._function.arch, value) return result - def get_flag_value_after(self, flag): + def get_flag_value_after(self, flag:'architecture.FlagType') -> 'variable.RegisterValue': flag = self._function.arch.get_flag_index(flag) value = core.BNGetMediumLevelILFlagValueAfterInstruction(self._function.handle, flag, self._instr_index) - result = function.RegisterValue(self._function.arch, value) + result = variable.RegisterValue(self._function.arch, value) return result - def get_possible_flag_values(self, flag, options = []): + def get_possible_flag_values(self, flag:'architecture.FlagType', + options:List[DataFlowQueryOption]=[]) -> 'variable.PossibleValueSet': flag = self._function.arch.get_flag_index(flag) option_array = (ctypes.c_int * len(options))() idx = 0 @@ -698,11 +727,12 @@ class MediumLevelILInstruction(object): idx += 1 value = core.BNGetMediumLevelILPossibleFlagValuesAtInstruction(self._function.handle, flag, self._instr_index, option_array, len(options)) - result = function.PossibleValueSet(self._function.arch, value) + result = variable.PossibleValueSet(self._function.arch, value) core.BNFreePossibleValueSet(value) return result - def get_possible_flag_values_after(self, flag, options = []): + def get_possible_flag_values_after(self, flag:'architecture.FlagType', + options:List[DataFlowQueryOption]=[]) -> 'variable.PossibleValueSet': flag = self._function.arch.get_flag_index(flag) option_array = (ctypes.c_int * len(options))() idx = 0 @@ -711,21 +741,22 @@ class MediumLevelILInstruction(object): idx += 1 value = core.BNGetMediumLevelILPossibleFlagValuesAfterInstruction(self._function.handle, flag, self._instr_index, option_array, len(options)) - result = function.PossibleValueSet(self._function.arch, value) + result = variable.PossibleValueSet(self._function.arch, value) core.BNFreePossibleValueSet(value) return result - def get_stack_contents(self, offset, size): + def get_stack_contents(self, offset:int, size:int) -> 'variable.RegisterValue': value = core.BNGetMediumLevelILStackContentsAtInstruction(self._function.handle, offset, size, self._instr_index) - result = function.RegisterValue(self._function.arch, value) + result = variable.RegisterValue(self._function.arch, value) return result - def get_stack_contents_after(self, offset, size): + def get_stack_contents_after(self, offset:int, size:int) -> 'variable.RegisterValue': value = core.BNGetMediumLevelILStackContentsAfterInstruction(self._function.handle, offset, size, self._instr_index) - result = function.RegisterValue(self._function.arch, value) + result = variable.RegisterValue(self._function.arch, value) return result - def get_possible_stack_contents(self, offset, size, options = []): + def get_possible_stack_contents(self, offset:int, size:int, + options:List[DataFlowQueryOption]=[]) -> 'variable.PossibleValueSet': option_array = (ctypes.c_int * len(options))() idx = 0 for option in options: @@ -733,11 +764,12 @@ class MediumLevelILInstruction(object): idx += 1 value = core.BNGetMediumLevelILPossibleStackContentsAtInstruction(self._function.handle, offset, size, self._instr_index, option_array, len(options)) - result = function.PossibleValueSet(self._function.arch, value) + result = variable.PossibleValueSet(self._function.arch, value) core.BNFreePossibleValueSet(value) return result - def get_possible_stack_contents_after(self, offset, size, options = []): + def get_possible_stack_contents_after(self, offset:int, size:int, + options:List[DataFlowQueryOption]=[]) -> 'variable.PossibleValueSet': option_array = (ctypes.c_int * len(options))() idx = 0 for option in options: @@ -745,51 +777,43 @@ class MediumLevelILInstruction(object): idx += 1 value = core.BNGetMediumLevelILPossibleStackContentsAfterInstruction(self._function.handle, offset, size, self._instr_index, option_array, len(options)) - result = function.PossibleValueSet(self._function.arch, value) + result = variable.PossibleValueSet(self._function.arch, value) core.BNFreePossibleValueSet(value) return result - def get_branch_dependence(self, branch_instr): + def get_branch_dependence(self, branch_instr:int) -> ILBranchDependence: return ILBranchDependence(core.BNGetMediumLevelILBranchDependence(self._function.handle, self._instr_index, branch_instr)) @property - def function(self): - """ """ + def function(self) -> 'MediumLevelILFunction': return self._function @property - def expr_index(self): - """ """ + def expr_index(self) -> ExpressionIndex: return self._expr_index @property - def instr_index(self): - """ """ + def instr_index(self) -> InstructionIndex: return self._instr_index @property - def operation(self): - """ """ + def operation(self) -> MediumLevelILOperation: return self._operation @property - def size(self): - """ """ + def size(self) -> int: return self._size @property - def address(self): - """ """ + def address(self) -> int: return self._address @property - def source_operand(self): - """ """ + def source_operand(self) -> ExpressionIndex: return self._source_operand @property - def operands(self): - """ """ + def operands(self) -> List[Any]: return self._operands @@ -805,7 +829,6 @@ class MediumLevelILExpr(object): @property def index(self): - """ """ return self._index @index.setter @@ -815,27 +838,33 @@ class MediumLevelILExpr(object): class MediumLevelILFunction(object): """ - ``class MediumLevelILFunction`` contains the list of MediumLevelILExpr objects that make up a binaryninja.function. MediumLevelILExpr + ``class MediumLevelILFunction`` contains the list of MediumLevelILExpr objects that make up a function. MediumLevelILExpr objects can be added to the MediumLevelILFunction by calling :func:`append` and passing the result of the various class methods which return MediumLevelILExpr objects. """ - def __init__(self, arch = None, handle = None, source_func = None): - self._arch = arch - self._source_function = source_func + def __init__(self, arch:Optional['architecture.Architecture']=None, + handle:Optional[core.BNMediumLevelILFunction]=None, source_func:Optional['function.Function']=None): + _arch = arch + _source_function = source_func if handle is not None: - self.handle = core.handle_of_type(handle, core.BNMediumLevelILFunction) - if self._source_function is None: - self._source_function = binaryninja.function.Function(handle = core.BNGetMediumLevelILOwnerFunction(self.handle)) - if self._arch is None: - self._arch = self._source_function.arch + _handle = core.handle_of_type(handle, core.BNMediumLevelILFunction) + if _source_function is None: + _source_function = function.Function(handle = core.BNGetMediumLevelILOwnerFunction(_handle)) + if _arch is None: + _arch = _source_function.arch else: - if self._source_function is None: - self.handle = None + if _source_function is None: raise ValueError("IL functions must be created with an associated function") - if self._arch is None: - self._arch = self._source_function.arch - func_handle = self._source_function.handle - self.handle = core.BNCreateMediumLevelILFunction(arch.handle, func_handle) + if _arch is None: + _arch = _source_function.arch + func_handle = _source_function.handle + _handle = core.BNCreateMediumLevelILFunction(self.arch.handle, func_handle) + assert _source_function is not None + assert _arch is not None + assert _handle is not None + self.handle = _handle + self._arch = _arch + self._source_function = _source_function def __del__(self): if self.handle is not None: @@ -884,52 +913,59 @@ class MediumLevelILFunction(object): def __iter__(self): count = ctypes.c_ulonglong() blocks = core.BNGetMediumLevelILBasicBlockList(self.handle, count) + assert blocks is not None, "core.BNGetMediumLevelILBasicBlockList returned None" view = None if self._source_function is not None: view = self._source_function.view try: for i in range(0, count.value): - yield MediumLevelILBasicBlock(view, core.BNNewBasicBlockReference(blocks[i]), self) + core_block = core.BNNewBasicBlockReference(blocks[i]) + assert core_block is not None, "Got None from core.BNNewBasicBlockReference" + yield MediumLevelILBasicBlock(core_block, self, view) finally: core.BNFreeBasicBlockList(blocks, count.value) @property - def current_address(self): + def current_address(self) -> int: """Current IL Address (read/write)""" return core.BNMediumLevelILGetCurrentAddress(self.handle) @current_address.setter - def current_address(self, value): + def current_address(self, value:int) -> None: core.BNMediumLevelILSetCurrentAddress(self.handle, self._arch.handle, value) - def set_current_address(self, value, arch = None): - if arch is None: - arch = self._arch - core.BNMediumLevelILSetCurrentAddress(self.handle, arch.handle, value) + def set_current_address(self, value:int, arch:Optional['architecture.Architecture']=None) -> None: + _arch = arch + if _arch is None: + _arch = self._arch + core.BNMediumLevelILSetCurrentAddress(self.handle, _arch.handle, value) @property - def basic_blocks(self): + def basic_blocks(self) -> Generator['MediumLevelILBasicBlock', None, None]: """list of MediumLevelILBasicBlock objects (read-only)""" count = ctypes.c_ulonglong() blocks = core.BNGetMediumLevelILBasicBlockList(self.handle, count) - result = [] + assert blocks is not None, "core.BNGetMediumLevelILBasicBlockList returned None" view = None if self._source_function is not None: view = self._source_function.view - for i in range(0, count.value): - result.append(MediumLevelILBasicBlock(view, core.BNNewBasicBlockReference(blocks[i]), self)) - core.BNFreeBasicBlockList(blocks, count.value) - return result + try: + for i in range(0, count.value): + core_block = core.BNNewBasicBlockReference(blocks[i]) + assert core_block is not None + yield MediumLevelILBasicBlock(core_block, self, view) + finally: + core.BNFreeBasicBlockList(blocks, count.value) @property - def instructions(self): + def instructions(self) -> Generator[MediumLevelILInstruction, None, None]: """A generator of mlil instructions of the current function""" for block in self.basic_blocks: for i in block: yield i @property - def ssa_form(self): + def ssa_form(self) -> Optional['MediumLevelILFunction']: """Medium level IL in SSA form (read-only)""" result = core.BNGetMediumLevelILSSAForm(self.handle) if not result: @@ -937,7 +973,7 @@ class MediumLevelILFunction(object): return MediumLevelILFunction(self._arch, result, self._source_function) @property - def non_ssa_form(self): + def non_ssa_form(self) -> Optional['MediumLevelILFunction']: """Medium level IL in non-SSA (default) form (read-only)""" result = core.BNGetMediumLevelILNonSSAForm(self.handle) if not result: @@ -945,7 +981,7 @@ class MediumLevelILFunction(object): return MediumLevelILFunction(self._arch, result, self._source_function) @property - def low_level_il(self): + def low_level_il(self) -> Optional['lowlevelil.LowLevelILFunction']: """Low level IL for this function""" result = core.BNGetLowLevelILForMediumLevelIL(self.handle) if not result: @@ -953,38 +989,43 @@ class MediumLevelILFunction(object): return lowlevelil.LowLevelILFunction(self._arch, result, self._source_function) @property - def llil(self): + def llil(self) -> Optional['lowlevelil.LowLevelILFunction']: """Alias for low_level_il""" return self.low_level_il @property - def high_level_il(self): + def high_level_il(self) -> Optional[highlevelil.HighLevelILFunction]: """High level IL for this medium level IL.""" result = core.BNGetHighLevelILForMediumLevelIL(self.handle) if not result: return None - return binaryninja.highlevelil.HighLevelILFunction(self._arch, result, self._source_function) + return highlevelil.HighLevelILFunction(self._arch, result, self._source_function) @property - def hlil(self): + def hlil(self) -> Optional[highlevelil.HighLevelILFunction]: return self.high_level_il - def get_instruction_start(self, addr, arch = None): - if arch is None: - arch = self._arch - result = core.BNMediumLevelILGetInstructionStart(self.handle, arch.handle, addr) + def get_instruction_start(self, addr:int, arch:Optional['architecture.Architecture']=None) -> Optional[int]: + _arch = arch + if _arch is None: + if self._arch is None: + raise Exception("Attempting to get_instruction_start from a MLIL Function without an Architecture") + _arch = self._arch + result = core.BNMediumLevelILGetInstructionStart(self.handle, _arch.handle, addr) if result >= core.BNGetMediumLevelILInstructionCount(self.handle): return None return result - def expr(self, operation, a = 0, b = 0, c = 0, d = 0, e = 0, size = 0): + def expr(self, operation:MediumLevelILOperation, a:int=0, b:int=0, c:int=0, d:int=0, e:int=0, + size:int=0) -> MediumLevelILExpr: + _operation = operation if isinstance(operation, str): - operation = MediumLevelILOperation[operation] + _operation = MediumLevelILOperation[operation] elif isinstance(operation, MediumLevelILOperation): - operation = operation.value - return MediumLevelILExpr(core.BNMediumLevelILAddExpr(self.handle, operation, size, a, b, c, d, e)) + _operation = operation.value + return MediumLevelILExpr(core.BNMediumLevelILAddExpr(self.handle, _operation, size, a, b, c, d, e)) - def append(self, expr): + def append(self, expr:MediumLevelILExpr) -> int: """ ``append`` adds the MediumLevelILExpr ``expr`` to the current MediumLevelILFunction. @@ -994,7 +1035,7 @@ class MediumLevelILFunction(object): """ return core.BNMediumLevelILAddInstruction(self.handle, expr.index) - def goto(self, label): + def goto(self, label:MediumLevelILLabel) -> MediumLevelILExpr: """ ``goto`` returns a goto expression which jumps to the provided MediumLevelILLabel. @@ -1004,7 +1045,7 @@ class MediumLevelILFunction(object): """ return MediumLevelILExpr(core.BNMediumLevelILGoto(self.handle, label.handle)) - def if_expr(self, operand, t, f): + def if_expr(self, operand:MediumLevelILExpr, t:MediumLevelILLabel, f:MediumLevelILLabel) -> MediumLevelILExpr: """ ``if_expr`` returns the ``if`` expression which depending on condition ``operand`` jumps to the MediumLevelILLabel ``t`` when the condition expression ``operand`` is non-zero and ``f`` when it's zero. @@ -1017,7 +1058,7 @@ class MediumLevelILFunction(object): """ return MediumLevelILExpr(core.BNMediumLevelILIf(self.handle, operand.index, t.handle, f.handle)) - def mark_label(self, label): + def mark_label(self, label:MediumLevelILLabel) -> None: """ ``mark_label`` assigns a MediumLevelILLabel to the current IL address. @@ -1026,21 +1067,24 @@ class MediumLevelILFunction(object): """ core.BNMediumLevelILMarkLabel(self.handle, label.handle) - def add_label_list(self, labels): + def add_label_map(self, labels:Mapping[int, MediumLevelILLabel]) -> MediumLevelILExpr: """ - ``add_label_list`` returns a label list expression for the given list of MediumLevelILLabel objects. + ``add_label_map`` returns a label list expression for the given list of MediumLevelILLabel objects. :param labels: the list of MediumLevelILLabel to get a label list expression from - :type labels: list(MediumLevelILLabel) + :type labels: dict(int, MediumLevelILLabel) :return: the label list expression :rtype: MediumLevelILExpr """ - label_list = (ctypes.POINTER(core.BNMediumLevelILLabel) * len(labels))() - for i in range(len(labels)): - label_list[i] = labels[i].handle - return MediumLevelILExpr(core.BNMediumLevelILAddLabelList(self.handle, label_list, len(labels))) + label_list = (ctypes.POINTER(core.BNMediumLevelILLabel) * len(labels))() # type: ignore + value_list = (ctypes.POINTER(ctypes.c_ulonglong) * len(labels))() # type: ignore + for i, (key, value) in enumerate(labels.items()): + value_list[i] = key + label_list[i] = value.handle - def add_operand_list(self, operands): + return MediumLevelILExpr(core.BNMediumLevelILAddLabelMap(self.handle, value_list, label_list, len(labels))) + + def add_operand_list(self, operands:List[ExpressionIndex]) -> MediumLevelILExpr: """ ``add_operand_list`` returns an operand list expression for the given list of integer operands. @@ -1054,7 +1098,7 @@ class MediumLevelILFunction(object): operand_list[i] = operands[i] return MediumLevelILExpr(core.BNMediumLevelILAddOperandList(self.handle, operand_list, len(operands))) - def finalize(self): + def finalize(self) -> None: """ ``finalize`` ends the function and computes the list of basic blocks. @@ -1062,13 +1106,13 @@ class MediumLevelILFunction(object): """ core.BNFinalizeMediumLevelILFunction(self.handle) - def get_ssa_instruction_index(self, instr): + def get_ssa_instruction_index(self, instr:InstructionIndex) -> InstructionIndex: return core.BNGetMediumLevelILSSAInstructionIndex(self.handle, instr) - def get_non_ssa_instruction_index(self, instr): + def get_non_ssa_instruction_index(self, instr:InstructionIndex) -> InstructionIndex: return core.BNGetMediumLevelILNonSSAInstructionIndex(self.handle, instr) - def get_ssa_var_definition(self, ssa_var): + def get_ssa_var_definition(self, ssa_var:SSAVariable) -> Optional[MediumLevelILInstruction]: var_data = core.BNVariable() var_data.type = ssa_var.var.source_type var_data.index = ssa_var.var.index @@ -1078,35 +1122,37 @@ class MediumLevelILFunction(object): return None return self[result] - def get_ssa_memory_definition(self, version): + def get_ssa_memory_definition(self, version:int) -> Optional[MediumLevelILInstruction]: result = core.BNGetMediumLevelILSSAMemoryDefinition(self.handle, version) if result >= core.BNGetMediumLevelILInstructionCount(self.handle): return None return self[result] - def get_ssa_var_uses(self, ssa_var): + def get_ssa_var_uses(self, ssa_var:SSAVariable) -> List[MediumLevelILInstruction]: count = ctypes.c_ulonglong() var_data = core.BNVariable() var_data.type = ssa_var.var.source_type var_data.index = ssa_var.var.index var_data.storage = ssa_var.var.storage instrs = core.BNGetMediumLevelILSSAVarUses(self.handle, var_data, ssa_var.version, count) + assert instrs is not None, "core.BNGetMediumLevelILSSAVarUses returned None" result = [] for i in range(0, count.value): result.append(self[instrs[i]]) core.BNFreeILInstructionList(instrs) return result - def get_ssa_memory_uses(self, version): + def get_ssa_memory_uses(self, version:int) -> List[MediumLevelILInstruction]: count = ctypes.c_ulonglong() instrs = core.BNGetMediumLevelILSSAMemoryUses(self.handle, version, count) + assert instrs is not None, "core.BNGetMediumLevelILSSAMemoryUses returned None" result = [] for i in range(0, count.value): result.append(self[instrs[i]]) core.BNFreeILInstructionList(instrs) return result - def is_ssa_var_live(self, ssa_var): + def is_ssa_var_live(self, ssa_var:SSAVariable) -> bool: """ ``is_ssa_var_live`` determines if ``ssa_var`` is live at any point in the function @@ -1120,20 +1166,34 @@ class MediumLevelILFunction(object): var_data.storage = ssa_var.var.storage return core.BNIsMediumLevelILSSAVarLive(self.handle, var_data, ssa_var.version) - def get_var_definitions(self, var): + def get_var_definitions(self, var:'variable.Variable') -> List[MediumLevelILInstruction]: + count = ctypes.c_ulonglong() + var_data = core.BNVariable() + var_data.type = var.source_type + var_data.index = var.index + var_data.storage = var.storage + instrs = core.BNGetMediumLevelILVariableDefinitions(self.handle, var_data, count) + assert instrs is not None, "core.BNGetMediumLevelILVariableDefinitions returned None" + result = [] + for i in range(0, count.value): + result.append(self[instrs[i]]) + core.BNFreeILInstructionList(instrs) + return result + + def get_var_uses(self, var:'variable.Variable') -> List[MediumLevelILInstruction]: count = ctypes.c_ulonglong() var_data = core.BNVariable() var_data.type = var.source_type var_data.index = var.index var_data.storage = var.storage instrs = core.BNGetMediumLevelILVariableDefinitions(self.handle, var_data, count) + assert instrs is not None, "core.BNGetMediumLevelILVariableDefinitions returned None" result = [] for i in range(0, count.value): result.append(self[instrs[i]]) core.BNFreeILInstructionList(instrs) return result - def get_var_uses(self, var): count = ctypes.c_ulonglong() var_data = core.BNVariable() var_data.type = var.source_type @@ -1146,16 +1206,16 @@ class MediumLevelILFunction(object): core.BNFreeILInstructionList(instrs) return result - def get_ssa_var_value(self, ssa_var): + def get_ssa_var_value(self, ssa_var:SSAVariable) -> 'variable.RegisterValue': var_data = core.BNVariable() var_data.type = ssa_var.var.source_type var_data.index = ssa_var.var.index var_data.storage = ssa_var.var.storage value = core.BNGetMediumLevelILSSAVarValue(self.handle, var_data, ssa_var.version) - result = function.RegisterValue(self._arch, value) + result = variable.RegisterValue(self._arch, value) return result - def get_low_level_il_instruction_index(self, instr): + def get_low_level_il_instruction_index(self, instr:InstructionIndex) -> Optional['lowlevelil.InstructionIndex']: low_il = self.low_level_il if low_il is None: return None @@ -1167,7 +1227,7 @@ class MediumLevelILFunction(object): return None return result - def get_low_level_il_expr_index(self, expr): + def get_low_level_il_expr_index(self, expr:ExpressionIndex) -> Optional['lowlevelil.ExpressionIndex']: low_il = self.low_level_il if low_il is None: return None @@ -1179,16 +1239,17 @@ class MediumLevelILFunction(object): return None return result - def get_low_level_il_expr_indexes(self, expr): + def get_low_level_il_expr_indexes(self, expr:ExpressionIndex) -> List['lowlevelil.ExpressionIndex']: count = ctypes.c_ulonglong() exprs = core.BNGetLowLevelILExprIndexes(self.handle, expr, count) + assert exprs is not None, "core.BNGetLowLevelILExprIndexes returned None" result = [] for i in range(0, count.value): result.append(exprs[i]) core.BNFreeILInstructionList(exprs) return result - def get_high_level_il_instruction_index(self, instr): + def get_high_level_il_instruction_index(self, instr:InstructionIndex) -> Optional['highlevelil.InstructionIndex']: high_il = self.high_level_il if high_il is None: return None @@ -1197,7 +1258,7 @@ class MediumLevelILFunction(object): return None return result - def get_high_level_il_expr_index(self, expr): + def get_high_level_il_expr_index(self, expr:ExpressionIndex) -> Optional['highlevelil.ExpressionIndex']: high_il = self.high_level_il if high_il is None: return None @@ -1206,90 +1267,36 @@ class MediumLevelILFunction(object): return None return result - def get_high_level_il_expr_indexes(self, expr): + def get_high_level_il_expr_indexes(self, expr:ExpressionIndex) -> List['highlevelil.ExpressionIndex']: count = ctypes.c_ulonglong() exprs = core.BNGetHighLevelILExprIndexes(self.handle, expr, count) + assert exprs is not None, "core.BNGetHighLevelILExprIndexes returned None" result = [] for i in range(0, count.value): result.append(exprs[i]) core.BNFreeILInstructionList(exprs) return result - def create_graph(self, settings = None): + def create_graph(self, settings:'function.DisassemblySettings'=None) -> flowgraph.CoreFlowGraph: if settings is not None: settings_obj = settings.handle else: settings_obj = None - return binaryninja.flowgraph.CoreFlowGraph(core.BNCreateMediumLevelILFunctionGraph(self.handle, settings_obj)) + return flowgraph.CoreFlowGraph(core.BNCreateMediumLevelILFunctionGraph(self.handle, settings_obj)) @property - def arch(self): - """ """ + def arch(self) -> 'architecture.Architecture': return self._arch - @arch.setter - def arch(self, value): - self._arch = value - @property - def source_function(self): - """ """ + def source_function(self) -> 'function.Function': return self._source_function - @source_function.setter - def source_function(self, value): - self._source_function = value - - @property - def il_form(self) -> "binaryninja.enums.FunctionGraphType": - if len(self.basic_blocks) < 1: - return FunctionGraphType.InvalidILViewType - return FunctionGraphType(core.BNGetBasicBlockFunctionGraphType(self.basic_blocks[0].handle)) - - @property - def vars(self) -> List["binaryninja.function.Variable"]: - """This gets just the MLIL variables - you may be interested in the union of `MediumLevelIlFunction.source_function.param_vars` for all the variables used in the function""" - if self.source_function is None: - return [] - - if self.il_form in [FunctionGraphType.MediumLevelILFunctionGraph, FunctionGraphType.MediumLevelILSSAFormFunctionGraph, FunctionGraphType.MappedMediumLevelILFunctionGraph, FunctionGraphType.MappedMediumLevelILSSAFormFunctionGraph]: - count = ctypes.c_ulonglong() - core_variables = core.BNGetMediumLevelILVariables(self.handle, count) - result = [] - for var_i in range(count.value): - result.append(function.Variable(self.source_function, core_variables[var_i].type, core_variables[var_i].index, core_variables[var_i].storage)) - core.BNFreeVariableList(core_variables) - return result - return [] - - @property - def ssa_vars(self) -> List["binaryninja.mediumlevelil.SSAVariable"]: - """This gets just the MLIL SSA variables - you may be interested in the union of `MediumLevelIlFunction.source_function.param_vars` for all the variables used in the function""" - if self.source_function is None: - return [] - - if self.il_form in [FunctionGraphType.MediumLevelILSSAFormFunctionGraph, FunctionGraphType.MappedMediumLevelILSSAFormFunctionGraph]: - variable_count = ctypes.c_ulonglong() - core_variables = core.BNGetMediumLevelILVariables(self.handle, variable_count) - result = [] - for var_i in range(variable_count.value): - version_count = ctypes.c_ulonglong() - versions = core.BNGetMediumLevelILVariableSSAVersions(self.handle, core_variables[var_i], version_count) - - for version_i in range(version_count.value): - result.append(SSAVariable(function.Variable(self.source_function, core_variables[var_i].type, core_variables[var_i].index, core_variables[var_i].storage), versions[version_i])) - core.BNFreeILInstructionList(versions) - - core.BNFreeVariableList(core_variables) - return result - - return [] - class MediumLevelILBasicBlock(basicblock.BasicBlock): - def __init__(self, view, handle, owner): + def __init__(self, handle:core.BNBasicBlock, owner:MediumLevelILFunction, view:Optional['binaryview.BinaryView']=None): super(MediumLevelILBasicBlock, self).__init__(handle, view) - self.il_function = owner + self._il_function = owner def __repr__(self): arch = self.arch @@ -1300,7 +1307,7 @@ class MediumLevelILBasicBlock(basicblock.BasicBlock): def __iter__(self): for idx in range(self.start, self.end): - yield self.il_function[idx] + yield self._il_function[idx] def __getitem__(self, idx): size = self.end - self.start @@ -1309,12 +1316,12 @@ class MediumLevelILBasicBlock(basicblock.BasicBlock): if idx > size or idx < -size: raise IndexError("list index is out of range") if idx >= 0: - return self.il_function[idx + self.start] + return self._il_function[idx + self.start] else: - return self.il_function[self.end + idx] + return self._il_function[self.end + idx] def __hash__(self): - return hash((self.start, self.end, self.il_function)) + return hash((self.start, self.end, self._il_function)) def __contains__(self, instruction): if type(instruction) != MediumLevelILInstruction or instruction.il_basic_block != self: @@ -1324,15 +1331,14 @@ class MediumLevelILBasicBlock(basicblock.BasicBlock): else: return False - def _create_instance(self, handle, view): + def _create_instance(self, handle:core.BNBasicBlock, view:'binaryview.BinaryView') -> 'MediumLevelILBasicBlock': """Internal method by super to instantiate child instances""" - return MediumLevelILBasicBlock(view, handle, self.il_function) + return MediumLevelILBasicBlock(handle, self.il_function, view) @property - def il_function(self): - """ """ - return self._il_function + def instruction_count(self) -> int: + return self.end - self.start - @il_function.setter - def il_function(self, value): - self._il_function = value + @property + def il_function(self) -> 'MediumLevelILFunction': + return self._il_function diff --git a/python/metadata.py b/python/metadata.py index 99c52c43..7392e7a2 100644 --- a/python/metadata.py +++ b/python/metadata.py @@ -18,25 +18,21 @@ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # IN THE SOFTWARE. - -from __future__ import absolute_import import ctypes +from typing import Union, Optional # Binary Ninja components -from binaryninja import _binaryninjacore as core -from binaryninja.enums import MetadataType - -# 2-3 compatibility -from binaryninja import range -from binaryninja import pyNativeStr -import numbers +from . import _binaryninjacore as core +from .enums import MetadataType +MetadataValueType = Union[int, bool, str, bytes, float, list, tuple, dict] class Metadata(object): - def __init__(self, value=None, signed=None, raw=None, handle=None): + def __init__(self, value:MetadataValueType=None, signed:Optional[bool]=None, + raw:Optional[bool]=None, handle:Optional[core.BNMetadata]=None): if handle is not None: self.handle = handle - elif isinstance(value, numbers.Integral): + elif isinstance(value, int): if signed: self.handle = core.BNCreateMetadataSignedIntegerData(value) else: @@ -64,7 +60,7 @@ class Metadata(object): md = Metadata(value[elm], signed, raw) core.BNMetadataSetValueForKey(self.handle, str(elm), md.handle) else: - raise ValueError("{} doesn't contain type of: int, bool, str, float, list, dict".format(type(value).__name__)) + raise ValueError(f"{type(value)} doesn't contain type of: int, bool, str, float, list, dict") def __len__(self): if self.is_array or self.is_dict or self.is_string or self.is_raw: @@ -144,10 +140,11 @@ class Metadata(object): yield Metadata(handle=core.BNMetadataGetForIndex(self.handle, i)).value elif self.is_dict: result = core.BNMetadataGetValueStore(self.handle) + assert result is not None, "core.BNMetadataGetValueStore returned None" try: for i in range(result.contents.size): if isinstance(result.contents.keys[i], bytes): - yield str(pyNativeStr(result.contents.keys[i])) + yield result.contents.keys[i].decode("utf-8") else: yield result.contents.keys[i] finally: @@ -179,6 +176,7 @@ class Metadata(object): length = ctypes.c_ulonglong() length.value = 0 native_list = core.BNMetadataGetRaw(self.handle, ctypes.byref(length)) + assert native_list is not None, "core.BNMetadataGetRaw returned None" out_list = [] for i in range(length.value): out_list.append(native_list[i]) diff --git a/python/platform.py b/python/platform.py index 5039f1ca..9c9d0657 100644 --- a/python/platform.py +++ b/python/platform.py @@ -18,25 +18,24 @@ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # IN THE SOFTWARE. +import os import ctypes # Binary Ninja components import binaryninja -from binaryninja import _binaryninjacore as core -from binaryninja import types - -# 2-3 compatibility -from binaryninja import range -from binaryninja import with_metaclass -import os +from . import _binaryninjacore as core +from . import types +from . import callingconvention +from . import typelibrary +from . import architecture class _PlatformMetaClass(type): - def __iter__(self): binaryninja._init_plugins() count = ctypes.c_ulonglong() platforms = core.BNGetPlatformList(count) + assert platforms is not None, "core.BNGetPlatformList returned None" try: for i in range(0, count.value): yield Platform(handle = core.BNNewPlatformReference(platforms[i])) @@ -50,45 +49,8 @@ class _PlatformMetaClass(type): raise KeyError("'%s' is not a valid platform" % str(value)) return Platform(handle = platform) - @property - def list(self): - binaryninja._init_plugins() - count = ctypes.c_ulonglong() - platforms = core.BNGetPlatformList(count) - result = [] - for i in range(0, count.value): - result.append(Platform(handle = core.BNNewPlatformReference(platforms[i]))) - core.BNFreePlatformList(platforms, count.value) - return result - @property - def os_list(self): - binaryninja._init_plugins() - count = ctypes.c_ulonglong() - platforms = core.BNGetPlatformOSList(count) - result = [] - for i in range(0, count.value): - result.append(str(platforms[i])) - core.BNFreePlatformOSList(platforms, count.value) - return result - - def get_list(cls, os = None, arch = None): - binaryninja._init_plugins() - count = ctypes.c_ulonglong() - if os is None: - platforms = core.BNGetPlatformList(count) - elif arch is None: - platforms = core.BNGetPlatformListByOS(os) - else: - platforms = core.BNGetPlatformListByArchitecture(os, arch.handle) - result = [] - for i in range(0, count.value): - result.append(Platform(handle = core.BNNewPlatformReference(platforms[i]))) - core.BNFreePlatformList(platforms, count.value) - return result - - -class Platform(with_metaclass(_PlatformMetaClass, object)): +class Platform(metaclass=_PlatformMetaClass): """ ``class Platform`` contains all information related to the execution environment of the binary, mainly the calling conventions used. @@ -97,23 +59,29 @@ class Platform(with_metaclass(_PlatformMetaClass, object)): type_file_path = None # path to platform types file type_include_dirs = [] # list of directories available to #include from type_file_path - def __init__(self, arch = None, handle = None): + def __init__(self, arch:'architecture.Architecture'=None, handle=None): if handle is None: if arch is None: - self.handle = None raise ValueError("platform must have an associated architecture") - self._arch = arch + _arch = arch if self.__class__.type_file_path is None: - self.handle = core.BNCreatePlatform(arch.handle, self.__class__.name) + _handle = core.BNCreatePlatform(arch.handle, self.__class__.name) else: dir_buf = (ctypes.c_char_p * len(self.__class__.type_include_dirs))() for (i, dir) in enumerate(self.__class__.type_include_dirs): dir_buf[i] = dir.encode('charmap') - self.handle = core.BNCreatePlatformWithTypes(arch.handle, self.__class__.name, self.__class__.type_file_path, dir_buf, len(self.__class__.type_include_dirs)) + _handle = core.BNCreatePlatformWithTypes(arch.handle, self.__class__.name, self.__class__.type_file_path, dir_buf, len(self.__class__.type_include_dirs)) else: - self.handle = handle - self.__dict__["name"] = core.BNGetPlatformName(self.handle) - self._arch = binaryninja.architecture.CoreArchitecture._from_cache(core.BNGetPlatformArchitecture(self.handle)) + _handle = handle + _arch = architecture.CoreArchitecture._from_cache(core.BNGetPlatformArchitecture(_handle)) + assert _handle is not None + assert _arch is not None + self.handle = _handle + self._arch = _arch + + @property + def name(self): + return core.BNGetPlatformName(self.handle) def __del__(self): if self.handle is not None: @@ -135,10 +103,40 @@ class Platform(with_metaclass(_PlatformMetaClass, object)): return NotImplemented return not (self == other) + def __hash__(self): + return hash(ctypes.addressof(self.handle.contents)) + @property - def list(self): - """Allow tab completion to discover metaclass list property""" - pass + @classmethod + def os_list(cls): + binaryninja._init_plugins() + count = ctypes.c_ulonglong() + platforms = core.BNGetPlatformOSList(count) + assert platforms is not None, "core.BNGetPlatformOSList returned None" + result = [] + for i in range(0, count.value): + result.append(str(platforms[i])) + core.BNFreePlatformOSList(platforms, count.value) + return result + + @classmethod + def get_list(cls, os = None, arch = None): + binaryninja._init_plugins() + count = ctypes.c_ulonglong() + if os is None: + platforms = core.BNGetPlatformList(count) + assert platforms is not None, "core.BNGetPlatformList returned None" + elif arch is None: + platforms = core.BNGetPlatformListByOS(os) + assert platforms is not None, "core.BNGetPlatformListByOS returned None" + else: + platforms = core.BNGetPlatformListByArchitecture(os, arch.handle) + assert platforms is not None, "core.BNGetPlatformListByArchitecture returned None" + result = [] + for i in range(0, count.value): + result.append(Platform(handle = core.BNNewPlatformReference(platforms[i]))) + core.BNFreePlatformList(platforms, count.value) + return result @property def default_calling_convention(self): @@ -152,7 +150,7 @@ class Platform(with_metaclass(_PlatformMetaClass, object)): result = core.BNGetPlatformDefaultCallingConvention(self.handle) if result is None: return None - return binaryninja.callingconvention.CallingConvention(handle=result) + return callingconvention.CallingConvention(handle=result) @default_calling_convention.setter def default_calling_convention(self, value): @@ -166,7 +164,7 @@ class Platform(with_metaclass(_PlatformMetaClass, object)): result = core.BNGetPlatformCdeclCallingConvention(self.handle) if result is None: return None - return binaryninja.callingconvention.CallingConvention(handle=result) + return callingconvention.CallingConvention(handle=result) @cdecl_calling_convention.setter def cdecl_calling_convention(self, value): @@ -183,7 +181,7 @@ class Platform(with_metaclass(_PlatformMetaClass, object)): result = core.BNGetPlatformStdcallCallingConvention(self.handle) if result is None: return None - return binaryninja.callingconvention.CallingConvention(handle=result) + return callingconvention.CallingConvention(handle=result) @stdcall_calling_convention.setter def stdcall_calling_convention(self, value): @@ -200,7 +198,7 @@ class Platform(with_metaclass(_PlatformMetaClass, object)): result = core.BNGetPlatformFastcallCallingConvention(self.handle) if result is None: return None - return binaryninja.callingconvention.CallingConvention(handle=result) + return callingconvention.CallingConvention(handle=result) @fastcall_calling_convention.setter def fastcall_calling_convention(self, value): @@ -217,7 +215,7 @@ class Platform(with_metaclass(_PlatformMetaClass, object)): result = core.BNGetPlatformSystemCallConvention(self.handle) if result is None: return None - return binaryninja.callingconvention.CallingConvention(handle=result) + return callingconvention.CallingConvention(handle=result) @system_call_convention.setter def system_call_convention(self, value): @@ -236,9 +234,10 @@ class Platform(with_metaclass(_PlatformMetaClass, object)): """ count = ctypes.c_ulonglong() cc = core.BNGetPlatformCallingConventions(self.handle, count) + assert cc is not None, "core.BNGetPlatformCallingConventions returned None" result = [] for i in range(0, count.value): - result.append(binaryninja.callingconvention.CallingConvention(handle=core.BNNewCallingConventionReference(cc[i]))) + result.append(callingconvention.CallingConvention(handle=core.BNNewCallingConventionReference(cc[i]))) core.BNFreeCallingConventionList(cc, count.value) return result @@ -247,6 +246,7 @@ class Platform(with_metaclass(_PlatformMetaClass, object)): """List of platform-specific types (read-only)""" count = ctypes.c_ulonglong(0) type_list = core.BNGetPlatformTypes(self.handle, count) + assert type_list is not None, "core.BNGetPlatformTypes returned None" result = {} for i in range(0, count.value): name = types.QualifiedName._from_core_struct(type_list[i].name) @@ -259,6 +259,7 @@ class Platform(with_metaclass(_PlatformMetaClass, object)): """List of platform-specific variable definitions (read-only)""" count = ctypes.c_ulonglong(0) type_list = core.BNGetPlatformVariables(self.handle, count) + assert type_list is not None, "core.BNGetPlatformVariables returned None" result = {} for i in range(0, count.value): name = types.QualifiedName._from_core_struct(type_list[i].name) @@ -271,6 +272,7 @@ class Platform(with_metaclass(_PlatformMetaClass, object)): """List of platform-specific function definitions (read-only)""" count = ctypes.c_ulonglong(0) type_list = core.BNGetPlatformFunctions(self.handle, count) + assert type_list is not None, "core.BNGetPlatformFunctions returned None" result = {} for i in range(0, count.value): name = types.QualifiedName._from_core_struct(type_list[i].name) @@ -283,6 +285,7 @@ class Platform(with_metaclass(_PlatformMetaClass, object)): """List of system calls for this platform (read-only)""" count = ctypes.c_ulonglong(0) call_list = core.BNGetPlatformSystemCalls(self.handle, count) + assert call_list is not None, "core.BNGetPlatformSystemCalls returned None" result = {} for i in range(0, count.value): name = types.QualifiedName._from_core_struct(call_list[i].name) @@ -295,18 +298,20 @@ class Platform(with_metaclass(_PlatformMetaClass, object)): def type_libraries(self): count = ctypes.c_ulonglong(0) libs = core.BNGetPlatformTypeLibraries(self.handle, count) + assert libs is not None, "core.BNGetPlatformTypeLibraries returned None" result = [] for i in range(0, count.value): - result.append(binaryninja.TypeLibrary(core.BNNewTypeLibraryReference(libs[i]))) + result.append(typelibrary.TypeLibrary(core.BNNewTypeLibraryReference(libs[i]))) core.BNFreeTypeLibraryList(libs, count.value) return result def get_type_libraries_by_name(self, name): count = ctypes.c_ulonglong(0) libs = core.BNGetPlatformTypeLibrariesByName(self.handle, name, count) + assert libs is not None, "core.BNGetPlatformTypeLibrariesByName returned None" result = [] for i in range(0, count.value): - result.append(binaryninja.TypeLibrary(core.BNNewTypeLibraryReference(libs[i]))) + result.append(typelibrary.TypeLibrary(core.BNNewTypeLibraryReference(libs[i]))) core.BNFreeTypeLibraryList(libs, count.value) return result @@ -406,7 +411,7 @@ class Platform(with_metaclass(_PlatformMetaClass, object)): if filename is None: filename = "input" - if not (isinstance(source, str) or isinstance(source, unicode)): + if not isinstance(source, str): raise AttributeError("Source must be a string") dir_buf = (ctypes.c_char_p * len(include_dirs))() for i in range(0, len(include_dirs)): @@ -415,6 +420,7 @@ class Platform(with_metaclass(_PlatformMetaClass, object)): errors = ctypes.c_char_p() result = core.BNParseTypesFromSource(self.handle, source, filename, parse, errors, dir_buf, len(include_dirs), auto_type_source) + assert errors.value is not None, "core.BNParseTypesFromSource returned errors set to None" error_str = errors.value.decode("utf-8") core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) if not result: @@ -464,6 +470,7 @@ class Platform(with_metaclass(_PlatformMetaClass, object)): errors = ctypes.c_char_p() result = core.BNParseTypesFromSourceFile(self.handle, filename, parse, errors, dir_buf, len(include_dirs), auto_type_source) + assert errors.value is not None, "core.BNParseTypesFromSourceFile returned errors set to None" error_str = errors.value.decode("utf-8") core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) if not result: @@ -485,7 +492,6 @@ class Platform(with_metaclass(_PlatformMetaClass, object)): @property def arch(self): - """ """ return self._arch @arch.setter diff --git a/python/plugin.py b/python/plugin.py index cedae4a2..ae840ea2 100644 --- a/python/plugin.py +++ b/python/plugin.py @@ -24,15 +24,14 @@ import threading # Binary Ninja components import binaryninja -from binaryninja import _binaryninjacore as core -from binaryninja.enums import PluginCommandType -from binaryninja import filemetadata -from binaryninja import binaryview -from binaryninja import function - -# 2-3 compatibility -from binaryninja import range -from binaryninja import with_metaclass +from . import _binaryninjacore as core +from .enums import PluginCommandType +from . import filemetadata +from . import binaryview +from . import function +from . import log +from . import lowlevelil +from . import mediumlevelil class PluginCommandContext(object): @@ -48,7 +47,6 @@ class PluginCommandContext(object): @property def view(self): - """ """ return self._view @view.setter @@ -57,7 +55,6 @@ class PluginCommandContext(object): @property def address(self): - """ """ return self._address @address.setter @@ -66,7 +63,6 @@ class PluginCommandContext(object): @property def length(self): - """ """ return self._length @length.setter @@ -75,7 +71,6 @@ class PluginCommandContext(object): @property def function(self): - """ """ return self._function @function.setter @@ -84,7 +79,6 @@ class PluginCommandContext(object): @property def instruction(self): - """ """ return self._instruction @instruction.setter @@ -93,35 +87,19 @@ class PluginCommandContext(object): class _PluginCommandMetaClass(type): - @property - def list(self): - binaryninja._init_plugins() - count = ctypes.c_ulonglong() - commands = core.BNGetAllPluginCommands(count) - result = [] - for i in range(0, count.value): - result.append(PluginCommand(commands[i])) - core.BNFreePluginCommandList(commands) - return result - def __iter__(self): binaryninja._init_plugins() count = ctypes.c_ulonglong() commands = core.BNGetAllPluginCommands(count) + assert commands is not None, "core.BNGetAllPluginCommands returned None" try: for i in range(0, count.value): yield PluginCommand(commands[i]) finally: core.BNFreePluginCommandList(commands) - def __setattr__(self, name, value): - try: - type.__setattr__(self, name, value) - except AttributeError: - raise AttributeError("attribute '%s' is read only" % name) - -class PluginCommand(with_metaclass(_PluginCommandMetaClass, object)): +class PluginCommand(metaclass=_PluginCommandMetaClass): _registered_commands = [] def __init__(self, cmd): @@ -131,195 +109,190 @@ class PluginCommand(with_metaclass(_PluginCommandMetaClass, object)): self._description = str(cmd.description) self._type = PluginCommandType(cmd.type) - @property - def list(self): - """Allow tab completion to discover metaclass list property""" - pass - - @classmethod - def _default_action(cls, view, action): + @staticmethod + def _default_action(view, action): try: - file_metadata = binaryninja.filemetadata.FileMetadata(handle = core.BNGetFileForView(view)) - view_obj = binaryninja.binaryview.BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) + file_metadata = filemetadata.FileMetadata(handle = core.BNGetFileForView(view)) + view_obj = binaryview.BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) action(view_obj) except: - binaryninja.log.log_error(traceback.format_exc()) + log.log_error(traceback.format_exc()) - @classmethod - def _address_action(cls, view, addr, action): + @staticmethod + def _address_action(view, addr, action): try: - file_metadata = binaryninja.filemetadata.FileMetadata(handle = core.BNGetFileForView(view)) - view_obj = binaryninja.binaryview.BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) + file_metadata = filemetadata.FileMetadata(handle = core.BNGetFileForView(view)) + view_obj = binaryview.BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) action(view_obj, addr) except: - binaryninja.log.log_error(traceback.format_exc()) + log.log_error(traceback.format_exc()) - @classmethod - def _range_action(cls, view, addr, length, action): + @staticmethod + def _range_action(view, addr, length, action): try: - file_metadata = binaryninja.filemetadata.FileMetadata(handle = core.BNGetFileForView(view)) - view_obj = binaryninja.binaryview.BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) + file_metadata = filemetadata.FileMetadata(handle = core.BNGetFileForView(view)) + view_obj = binaryview.BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) action(view_obj, addr, length) except: - binaryninja.log.log_error(traceback.format_exc()) + log.log_error(traceback.format_exc()) - @classmethod - def _function_action(cls, view, func, action): + @staticmethod + def _function_action(view, func, action): try: - file_metadata = binaryninja.filemetadata.FileMetadata(handle = core.BNGetFileForView(view)) - view_obj = binaryninja.binaryview.BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) + file_metadata = filemetadata.FileMetadata(handle = core.BNGetFileForView(view)) + view_obj = binaryview.BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) func_obj = function.Function(view_obj, core.BNNewFunctionReference(func)) action(view_obj, func_obj) except: - binaryninja.log.log_error(traceback.format_exc()) + log.log_error(traceback.format_exc()) - @classmethod - def _low_level_il_function_action(cls, view, func, action): + @staticmethod + def _low_level_il_function_action(view, func, action): try: file_metadata = filemetadata.FileMetadata(handle = core.BNGetFileForView(view)) view_obj = binaryview.BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) owner = function.Function(view_obj, core.BNGetLowLevelILOwnerFunction(func)) - func_obj = binaryninja.lowlevelil.LowLevelILFunction(owner.arch, core.BNNewLowLevelILFunctionReference(func), owner) + func_obj = lowlevelil.LowLevelILFunction(owner.arch, core.BNNewLowLevelILFunctionReference(func), owner) action(view_obj, func_obj) except: - binaryninja.log.log_error(traceback.format_exc()) + log.log_error(traceback.format_exc()) - @classmethod - def _low_level_il_instruction_action(cls, view, func, instr, action): + @staticmethod + def _low_level_il_instruction_action(view, func, instr, action): try: file_metadata = filemetadata.FileMetadata(handle = core.BNGetFileForView(view)) view_obj = binaryview.BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) owner = function.Function(view_obj, core.BNGetLowLevelILOwnerFunction(func)) - func_obj = binaryninja.lowlevelil.LowLevelILFunction(owner.arch, core.BNNewLowLevelILFunctionReference(func), owner) + func_obj = lowlevelil.LowLevelILFunction(owner.arch, core.BNNewLowLevelILFunctionReference(func), owner) action(view_obj, func_obj[instr]) except: - binaryninja.log.log_error(traceback.format_exc()) + log.log_error(traceback.format_exc()) - @classmethod - def _medium_level_il_function_action(cls, view, func, action): + @staticmethod + def _medium_level_il_function_action(view, func, action): try: file_metadata = filemetadata.FileMetadata(handle = core.BNGetFileForView(view)) view_obj = binaryview.BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) owner = function.Function(view_obj, core.BNGetMediumLevelILOwnerFunction(func)) - func_obj = binaryninja.mediumlevelil.MediumLevelILFunction(owner.arch, core.BNNewMediumLevelILFunctionReference(func), owner) + func_obj = mediumlevelil.MediumLevelILFunction(owner.arch, core.BNNewMediumLevelILFunctionReference(func), owner) action(view_obj, func_obj) except: - binaryninja.log.log_error(traceback.format_exc()) + log.log_error(traceback.format_exc()) - @classmethod - def _medium_level_il_instruction_action(cls, view, func, instr, action): + @staticmethod + def _medium_level_il_instruction_action(view, func, instr, action): try: file_metadata = filemetadata.FileMetadata(handle = core.BNGetFileForView(view)) view_obj = binaryview.BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) owner = function.Function(view_obj, core.BNGetMediumLevelILOwnerFunction(func)) - func_obj = binaryninja.mediumlevelil.MediumLevelILFunction(owner.arch, core.BNNewMediumLevelILFunctionReference(func), owner) + func_obj = mediumlevelil.MediumLevelILFunction(owner.arch, core.BNNewMediumLevelILFunctionReference(func), owner) action(view_obj, func_obj[instr]) except: - binaryninja.log.log_error(traceback.format_exc()) + log.log_error(traceback.format_exc()) - @classmethod - def _default_is_valid(cls, view, is_valid): + @staticmethod + def _default_is_valid(view, is_valid): try: if is_valid is None: return True - file_metadata = binaryninja.filemetadata.FileMetadata(handle = core.BNGetFileForView(view)) - view_obj = binaryninja.binaryview.BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) + file_metadata = filemetadata.FileMetadata(handle = core.BNGetFileForView(view)) + view_obj = binaryview.BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) return is_valid(view_obj) except: - binaryninja.log.log_error(traceback.format_exc()) + log.log_error(traceback.format_exc()) return False - @classmethod - def _address_is_valid(cls, view, addr, is_valid): + @staticmethod + def _address_is_valid(view, addr, is_valid): try: if is_valid is None: return True - file_metadata = binaryninja.filemetadata.FileMetadata(handle = core.BNGetFileForView(view)) - view_obj = binaryninja.binaryview.BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) + file_metadata = filemetadata.FileMetadata(handle = core.BNGetFileForView(view)) + view_obj = binaryview.BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) return is_valid(view_obj, addr) except: - binaryninja.log.log_error(traceback.format_exc()) + log.log_error(traceback.format_exc()) return False - @classmethod - def _range_is_valid(cls, view, addr, length, is_valid): + @staticmethod + def _range_is_valid(view, addr, length, is_valid): try: if is_valid is None: return True - file_metadata = binaryninja.filemetadata.FileMetadata(handle = core.BNGetFileForView(view)) - view_obj = binaryninja.binaryview.BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) + file_metadata = filemetadata.FileMetadata(handle = core.BNGetFileForView(view)) + view_obj = binaryview.BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) return is_valid(view_obj, addr, length) except: - binaryninja.log.log_error(traceback.format_exc()) + log.log_error(traceback.format_exc()) return False - @classmethod - def _function_is_valid(cls, view, func, is_valid): + @staticmethod + def _function_is_valid(view, func, is_valid): try: if is_valid is None: return True - file_metadata = binaryninja.filemetadata.FileMetadata(handle = core.BNGetFileForView(view)) - view_obj = binaryninja.binaryview.BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) + file_metadata = filemetadata.FileMetadata(handle = core.BNGetFileForView(view)) + view_obj = binaryview.BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) func_obj = function.Function(view_obj, core.BNNewFunctionReference(func)) return is_valid(view_obj, func_obj) except: - binaryninja.log.log_error(traceback.format_exc()) + log.log_error(traceback.format_exc()) return False - @classmethod - def _low_level_il_function_is_valid(cls, view, func, is_valid): + @staticmethod + def _low_level_il_function_is_valid(view, func, is_valid): try: if is_valid is None: return True file_metadata = filemetadata.FileMetadata(handle = core.BNGetFileForView(view)) view_obj = binaryview.BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) owner = function.Function(view_obj, core.BNGetLowLevelILOwnerFunction(func)) - func_obj = binaryninja.lowlevelil.LowLevelILFunction(owner.arch, core.BNNewLowLevelILFunctionReference(func), owner) + func_obj = lowlevelil.LowLevelILFunction(owner.arch, core.BNNewLowLevelILFunctionReference(func), owner) return is_valid(view_obj, func_obj) except: - binaryninja.log.log_error(traceback.format_exc()) + log.log_error(traceback.format_exc()) return False - @classmethod - def _low_level_il_instruction_is_valid(cls, view, func, instr, is_valid): + @staticmethod + def _low_level_il_instruction_is_valid(view, func, instr, is_valid): try: if is_valid is None: return True file_metadata = filemetadata.FileMetadata(handle = core.BNGetFileForView(view)) view_obj = binaryview.BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) owner = function.Function(view_obj, core.BNGetLowLevelILOwnerFunction(func)) - func_obj = binaryninja.lowlevelil.LowLevelILFunction(owner.arch, core.BNNewLowLevelILFunctionReference(func), owner) + func_obj = lowlevelil.LowLevelILFunction(owner.arch, core.BNNewLowLevelILFunctionReference(func), owner) return is_valid(view_obj, func_obj[instr]) except: - binaryninja.log.log_error(traceback.format_exc()) + log.log_error(traceback.format_exc()) return False - @classmethod - def _medium_level_il_function_is_valid(cls, view, func, is_valid): + @staticmethod + def _medium_level_il_function_is_valid(view, func, is_valid): try: if is_valid is None: return True file_metadata = filemetadata.FileMetadata(handle = core.BNGetFileForView(view)) view_obj = binaryview.BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) owner = function.Function(view_obj, core.BNGetMediumLevelILOwnerFunction(func)) - func_obj = binaryninja.mediumlevelil.MediumLevelILFunction(owner.arch, core.BNNewMediumLevelILFunctionReference(func), owner) + func_obj = mediumlevelil.MediumLevelILFunction(owner.arch, core.BNNewMediumLevelILFunctionReference(func), owner) return is_valid(view_obj, func_obj) except: - binaryninja.log.log_error(traceback.format_exc()) + log.log_error(traceback.format_exc()) return False - @classmethod - def _medium_level_il_instruction_is_valid(cls, view, func, instr, is_valid): + @staticmethod + def _medium_level_il_instruction_is_valid(view, func, instr, is_valid): try: if is_valid is None: return True file_metadata = filemetadata.FileMetadata(handle = core.BNGetFileForView(view)) view_obj = binaryview.BinaryView(file_metadata = file_metadata, handle = core.BNNewViewReference(view)) owner = function.Function(view_obj, core.BNGetMediumLevelILOwnerFunction(func)) - func_obj = binaryninja.mediumlevelil.MediumLevelILFunction(owner.arch, core.BNNewMediumLevelILFunctionReference(func), owner) + func_obj = mediumlevelil.MediumLevelILFunction(owner.arch, core.BNNewMediumLevelILFunctionReference(func), owner) return is_valid(view_obj, func_obj[instr]) except: - binaryninja.log.log_error(traceback.format_exc()) + log.log_error(traceback.format_exc()) return False @classmethod @@ -329,8 +302,8 @@ class PluginCommand(with_metaclass(_PluginCommandMetaClass, object)): :param str name: name of the plugin (use 'Folder\\Name' to have the menu item nested in a folder) :param str description: description of the plugin - :param callback action: function to call with the :class:`~binaryninja.binaryview.BinaryView` as an argument - :param callback is_valid: optional argument of a function passed a :class:`~binaryninja.binaryview.BinaryView` to determine whether the plugin should be enabled for that view + :param callback action: function to call with the :class:`~binaryview.BinaryView` as an argument + :param callback is_valid: optional argument of a function passed a :class:`~binaryview.BinaryView` to determine whether the plugin should be enabled for that view :rtype: None .. warning:: Calling ``register`` with the same function name will replace the existing function but will leak the memory of the original plugin. @@ -348,8 +321,8 @@ class PluginCommand(with_metaclass(_PluginCommandMetaClass, object)): :param str name: name of the plugin (use 'Folder\\Name' to have the menu item nested in a folder) :param str description: description of the plugin - :param callback action: function to call with the :class:`~binaryninja.binaryview.BinaryView` and address as arguments - :param callback is_valid: optional argument of a function passed a :class:`~binaryninja.binaryview.BinaryView` to determine whether the plugin should be enabled for that view + :param callback action: function to call with the :class:`~binaryview.BinaryView` and address as arguments + :param callback is_valid: optional argument of a function passed a :class:`~binaryview.BinaryView` to determine whether the plugin should be enabled for that view :rtype: None .. warning:: Calling ``register_for_address`` with the same function name will replace the existing function but will leak the memory of the original plugin. @@ -367,8 +340,8 @@ class PluginCommand(with_metaclass(_PluginCommandMetaClass, object)): :param str name: name of the plugin (use 'Folder\\Name' to have the menu item nested in a folder) :param str description: description of the plugin - :param callback action: function to call with the :class:`~binaryninja.binaryview.BinaryView` and :class:`~binaryninja.binaryview.AddressRange` as arguments - :param callback is_valid: optional argument of a function passed a :class:`~binaryninja.binaryview.BinaryView` to determine whether the plugin should be enabled for that view + :param callback action: function to call with the :class:`~binaryview.BinaryView` and :class:`~binaryview.AddressRange` as arguments + :param callback is_valid: optional argument of a function passed a :class:`~binaryview.BinaryView` to determine whether the plugin should be enabled for that view :rtype: None .. warning:: Calling ``register_for_range`` with the same function name will replace the existing function but will leak the memory of the original plugin. @@ -386,8 +359,8 @@ class PluginCommand(with_metaclass(_PluginCommandMetaClass, object)): :param str name: name of the plugin (use 'Folder\\Name' to have the menu item nested in a folder) :param str description: description of the plugin - :param callback action: function to call with the :class:`~binaryninja.binaryview.BinaryView` and a :class:`~binaryninja.function.Function` as arguments - :param callback is_valid: optional argument of a function passed a :class:`~binaryninja.binaryview.BinaryView` to determine whether the plugin should be enabled for that view + :param callback action: function to call with the :class:`~binaryview.BinaryView` and a :class:`~function.Function` as arguments + :param callback is_valid: optional argument of a function passed a :class:`~binaryview.BinaryView` to determine whether the plugin should be enabled for that view :rtype: None .. warning:: Calling ``register_for_function`` with the same function name will replace the existing function but will leak the memory of the original plugin. @@ -405,8 +378,8 @@ class PluginCommand(with_metaclass(_PluginCommandMetaClass, object)): :param str name: name of the plugin (use 'Folder\\Name' to have the menu item nested in a folder) :param str description: description of the plugin - :param callback action: function to call with the :class:`~binaryninja.binaryview.BinaryView` and a :class:`~binaryninja.mediumlevelil.LowLevelILFunction` as arguments - :param callback is_valid: optional argument of a function passed a :class:`~binaryninja.binaryview.BinaryView` to determine whether the plugin should be enabled for that view + :param callback action: function to call with the :class:`~binaryview.BinaryView` and a :class:`~mediumlevelil.LowLevelILFunction` as arguments + :param callback is_valid: optional argument of a function passed a :class:`~binaryview.BinaryView` to determine whether the plugin should be enabled for that view :rtype: None .. warning:: Calling ``register_for_low_level_il_function`` with the same function name will replace the existing function but will leak the memory of the original plugin. @@ -424,8 +397,8 @@ class PluginCommand(with_metaclass(_PluginCommandMetaClass, object)): :param str name: name of the plugin (use 'Folder\\Name' to have the menu item nested in a folder) :param str description: description of the plugin - :param callback action: function to call with the :class:`~binaryninja.binaryview.BinaryView` and a :class:`~binaryninja.mediumlevelil.LowLevelILInstruction` as arguments - :param callback is_valid: optional argument of a function passed a :class:`~binaryninja.binaryview.BinaryView` to determine whether the plugin should be enabled for that view + :param callback action: function to call with the :class:`~binaryview.BinaryView` and a :class:`~mediumlevelil.LowLevelILInstruction` as arguments + :param callback is_valid: optional argument of a function passed a :class:`~binaryview.BinaryView` to determine whether the plugin should be enabled for that view :rtype: None .. warning:: Calling ``register_for_low_level_il_instruction`` with the same function name will replace the existing function but will leak the memory of the original plugin. @@ -443,8 +416,8 @@ class PluginCommand(with_metaclass(_PluginCommandMetaClass, object)): :param str name: name of the plugin (use 'Folder\\Name' to have the menu item nested in a folder) :param str description: description of the plugin - :param callback action: function to call with the :class:`~binaryninja.binaryview.BinaryView` and a :class:`~binaryninja.mediumlevelil.MediumLevelILFunction` as arguments - :param callback is_valid: optional argument of a function passed a :class:`~binaryninja.binaryview.BinaryView` to determine whether the plugin should be enabled for that view + :param callback action: function to call with the :class:`~binaryview.BinaryView` and a :class:`~mediumlevelil.MediumLevelILFunction` as arguments + :param callback is_valid: optional argument of a function passed a :class:`~binaryview.BinaryView` to determine whether the plugin should be enabled for that view :rtype: None .. warning:: Calling ``register_for_medium_level_il_function`` with the same function name will replace the existing function but will leak the memory of the original plugin. @@ -462,8 +435,8 @@ class PluginCommand(with_metaclass(_PluginCommandMetaClass, object)): :param str name: name of the plugin (use 'Folder\\Name' to have the menu item nested in a folder) :param str description: description of the plugin - :param callback action: function to call with the :class:`~binaryninja.binaryview.BinaryView` and a :class:`~binaryninja.mediumlevelil.MediumLevelILInstruction` as arguments - :param callback is_valid: optional argument of a function passed a :class:`~binaryninja.binaryview.BinaryView` to determine whether the plugin should be enabled for that view + :param callback action: function to call with the :class:`~binaryview.BinaryView` and a :class:`~mediumlevelil.MediumLevelILInstruction` as arguments + :param callback is_valid: optional argument of a function passed a :class:`~binaryview.BinaryView` to determine whether the plugin should be enabled for that view :rtype: None .. warning:: Calling ``register_for_medium_level_il_instruction`` with the same function name will replace the existing function but will leak the memory of the original plugin. @@ -477,7 +450,7 @@ class PluginCommand(with_metaclass(_PluginCommandMetaClass, object)): @classmethod def get_valid_list(cls, context): """Dict of registered plugins""" - commands = cls.list + commands = list(cls) result = {} for cmd in commands: if cmd.is_valid(context): @@ -516,7 +489,7 @@ class PluginCommand(with_metaclass(_PluginCommandMetaClass, object)): elif self._command.type == PluginCommandType.LowLevelILInstructionPluginCommand: if context.instruction is None: return False - if not isinstance(context.instruction, binaryninja.lowlevelil.LowLevelILInstruction): + if not isinstance(context.instruction, lowlevelil.LowLevelILInstruction): return False if not self._command.lowLevelILInstructionIsValid: return True @@ -531,7 +504,7 @@ class PluginCommand(with_metaclass(_PluginCommandMetaClass, object)): elif self._command.type == PluginCommandType.MediumLevelILInstructionPluginCommand: if context.instruction is None: return False - if not isinstance(context.instruction, binaryninja.mediumlevelil.MediumLevelILInstruction): + if not isinstance(context.instruction, mediumlevelil.MediumLevelILInstruction): return False if not self._command.mediumLevelILInstructionIsValid: return True @@ -543,7 +516,7 @@ class PluginCommand(with_metaclass(_PluginCommandMetaClass, object)): r""" ``execute`` Execute a Plugin - :param str context: PluginCommandContext to pass the PluginCommamnd + :param str context: PluginCommandContext to pass the PluginCommand :rtype: None >>> ctx = PluginCommandContext(bv); @@ -576,7 +549,6 @@ class PluginCommand(with_metaclass(_PluginCommandMetaClass, object)): @property def command(self): - """ """ return self._command @command.setter @@ -585,7 +557,6 @@ class PluginCommand(with_metaclass(_PluginCommandMetaClass, object)): @property def name(self): - """ """ return self._name @name.setter @@ -594,7 +565,6 @@ class PluginCommand(with_metaclass(_PluginCommandMetaClass, object)): @property def description(self): - """ """ return self._description @description.setter @@ -603,7 +573,6 @@ class PluginCommand(with_metaclass(_PluginCommandMetaClass, object)): @property def type(self): - """ """ return self._type @type.setter @@ -645,28 +614,18 @@ class MainThreadActionHandler(object): try: self.add_action(MainThreadAction(action)) except: - binaryninja.log.log_error(traceback.format_exc()) + log.log_error(traceback.format_exc()) def add_action(self, action): pass class _BackgroundTaskMetaclass(type): - @property - def list(self): - """List all running background tasks (read-only)""" - count = ctypes.c_ulonglong() - tasks = core.BNGetRunningBackgroundTasks(count) - result = [] - for i in range(0, count.value): - result.append(BackgroundTask(handle=core.BNNewBackgroundTaskReference(tasks[i]))) - core.BNFreeBackgroundTaskList(tasks, count.value) - return result - def __iter__(self): binaryninja._init_plugins() count = ctypes.c_ulonglong() tasks = core.BNGetRunningBackgroundTasks(count) + assert tasks is not None, "core.BNGetRunningBackgroundTasks returned None" try: for i in range(0, count.value): yield BackgroundTask(handle=core.BNNewBackgroundTaskReference(tasks[i])) @@ -674,7 +633,7 @@ class _BackgroundTaskMetaclass(type): core.BNFreeBackgroundTaskList(tasks, count.value) -class BackgroundTask(with_metaclass(_BackgroundTaskMetaclass, object)): +class BackgroundTask(metaclass=_BackgroundTaskMetaclass): def __init__(self, initial_progress_text = "", can_cancel = False, handle = None): if handle is None: self.handle = core.BNBeginBackgroundTask(initial_progress_text, can_cancel) @@ -685,11 +644,6 @@ class BackgroundTask(with_metaclass(_BackgroundTaskMetaclass, object)): core.BNFreeBackgroundTask(self.handle) @property - def list(self): - """Allow tab completion to discover metaclass list property""" - pass - - @property def progress(self): """Text description of the progress of the background task (displayed in status bar of the UI)""" return core.BNGetBackgroundTaskProgressText(self.handle) @@ -731,13 +685,15 @@ class BackgroundTask(with_metaclass(_BackgroundTaskMetaclass, object)): class BackgroundTaskThread(BackgroundTask): - def __init__(self, initial_progress_text = "", can_cancel = False): + def __init__(self, initial_progress_text:str="", can_cancel:bool=False): class _Thread(threading.Thread): - def __init__(self, task): + def __init__(self, task:'BackgroundTaskThread'): threading.Thread.__init__(self) self.task = task def run(self): + if self.task is None: + raise Exception("Can not call run more than once per thread") self.task.run() self.task.finish() self.task = None diff --git a/python/pluginmanager.py b/python/pluginmanager.py index 31e63620..114ba213 100644 --- a/python/pluginmanager.py +++ b/python/pluginmanager.py @@ -24,10 +24,8 @@ from datetime import datetime # Binary Ninja components import binaryninja -from binaryninja import _binaryninjacore as core -from binaryninja.enums import PluginType, PluginStatus -# 2-3 compatibility -from binaryninja import range +from . import _binaryninjacore as core +from .enums import PluginType class RepoPlugin(object): @@ -111,6 +109,7 @@ class RepoPlugin(object): result = [] count = ctypes.c_ulonglong(0) platforms = core.BNPluginGetApis(self.handle, count) + assert platforms is not None, "core.BNPluginGetApis returned None" for i in range(count.value): result.append(platforms[i].decode("utf-8")) core.BNFreePluginPlatforms(platforms, count) @@ -152,6 +151,7 @@ class RepoPlugin(object): result = [] count = ctypes.c_ulonglong(0) plugintypes = core.BNPluginGetPluginTypes(self.handle, count) + assert plugintypes is not None, "core.BNPluginGetPluginTypes returned None" for i in range(count.value): result.append(PluginType(plugintypes[i])) core.BNFreePluginTypes(plugintypes) @@ -199,6 +199,7 @@ class RepoPlugin(object): result = [] count = ctypes.c_ulonglong(0) platforms = core.BNPluginGetPlatforms(self.handle, count) + assert platforms is not None, "core.BNPluginGetPlatforms returned None" for i in range(count.value): result.append(platforms[i].decode("utf-8")) core.BNFreePluginPlatforms(platforms, count) @@ -294,6 +295,7 @@ class Repository(object): pluginlist = [] count = ctypes.c_ulonglong(0) result = core.BNRepositoryGetPlugins(self.handle, count) + assert result is not None, "core.BNRepositoryGetPlugins returned None" for i in range(count.value): pluginlist.append(RepoPlugin(core.BNNewPluginReference(result[i]))) core.BNFreeRepositoryPluginList(result, count.value) @@ -325,6 +327,7 @@ class RepositoryManager(object): result = [] count = ctypes.c_ulonglong(0) repos = core.BNRepositoryManagerGetRepositories(self.handle, count) + assert repos is not None, "core.BNRepositoryManagerGetRepositories returnedNone" for i in range(count.value): result.append(Repository(core.BNNewRepositoryReference(repos[i]))) core.BNFreeRepositoryManagerRepositoriesList(repos) diff --git a/python/scriptingprovider.py b/python/scriptingprovider.py index 8785c954..793ff4e0 100644 --- a/python/scriptingprovider.py +++ b/python/scriptingprovider.py @@ -27,22 +27,21 @@ import threading import abc import sys import subprocess -from pathlib import Path, PurePath +from pathlib import Path import re import os -import site -from typing import Generator +from typing import Generator, Optional, List, Tuple # Binary Ninja components import binaryninja -from binaryninja import bncompleter, log -from binaryninja import _binaryninjacore as core -from binaryninja import settings -from binaryninja.pluginmanager import RepositoryManager -from binaryninja.enums import ScriptingProviderExecuteResult, ScriptingProviderInputReadyState - -# 2-3 compatibility -from binaryninja import with_metaclass +from . import bncompleter, log +from . import _binaryninjacore as core +from . import settings +from . import binaryview +from . import basicblock +from . import function +from .pluginmanager import RepositoryManager +from .enums import ScriptingProviderExecuteResult, ScriptingProviderInputReadyState class _ThreadActionContext(object): @@ -155,7 +154,7 @@ class ScriptingInstance(object): def _set_current_binary_view(self, ctxt, view): try: if view: - view = binaryninja.binaryview.BinaryView(handle = core.BNNewViewReference(view)) + view = binaryview.BinaryView(handle = core.BNNewViewReference(view)) else: view = None self.perform_set_current_binary_view(view) @@ -165,7 +164,7 @@ class ScriptingInstance(object): def _set_current_function(self, ctxt, func): try: if func: - func = binaryninja.function.Function(handle = core.BNNewFunctionReference(func)) + func = function.Function(handle = core.BNNewFunctionReference(func)) else: func = None self.perform_set_current_function(func) @@ -179,8 +178,10 @@ class ScriptingInstance(object): if func is None: block = None else: - block = binaryninja.basicblock.BasicBlock(core.BNNewBasicBlockReference(block), - binaryninja.binaryview.BinaryView(handle = core.BNGetFunctionData(func))) + core_block = core.BNNewBasicBlockReference(block) + assert core_block is not None, "core.BNNewBasicBlockReference returned None" + block = basicblock.BasicBlock(core_block, + binaryview.BinaryView(handle = core.BNGetFunctionData(func))) core.BNFreeFunction(func) else: block = None @@ -204,10 +205,10 @@ class ScriptingInstance(object): try: if not isinstance(text, str): text = text.decode("charmap") - return ctypes.cast(binaryninja.cstr(self.perform_complete_input(text, state)), ctypes.c_void_p).value + return ctypes.cast(self.perform_complete_input(text, state).encode("utf-8"), ctypes.c_void_p).value # type: ignore except: log.log_error(traceback.format_exc()) - return ctypes.cast(binaryninja.cstr(""), ctypes.c_void_p).value + return "".encode("utf-8") @abc.abstractmethod def perform_destroy_instance(self): @@ -223,27 +224,27 @@ class ScriptingInstance(object): @abc.abstractmethod def perform_set_current_binary_view(self, view): - raise NotImplementedError + return NotImplemented @abc.abstractmethod def perform_set_current_function(self, func): - raise NotImplementedError + return NotImplemented @abc.abstractmethod def perform_set_current_basic_block(self, block): - raise NotImplementedError + return NotImplemented @abc.abstractmethod def perform_set_current_address(self, addr): - raise NotImplementedError + return NotImplemented @abc.abstractmethod def perform_set_current_selection(self, begin, end): - raise NotImplementedError + return NotImplemented @abc.abstractmethod - def perform_complete_input(self, text, state): - raise NotImplementedError + def perform_complete_input(self, text:str, state) -> str: + return NotImplemented @property def input_ready_state(self): @@ -308,93 +309,72 @@ class ScriptingInstance(object): class _ScriptingProviderMetaclass(type): - - @property - def list(self): - """List all ScriptingProvider types (read-only)""" - binaryninja._init_plugins() - count = ctypes.c_ulonglong() - types = core.BNGetScriptingProviderList(count) - result = [] - for i in range(0, count.value): - result.append(ScriptingProvider(types[i])) - core.BNFreeScriptingProviderList(types) - return result - - def __iter__(self): + def __iter__(self) -> Generator['ScriptingProvider', None, None]: binaryninja._init_plugins() count = ctypes.c_ulonglong() types = core.BNGetScriptingProviderList(count) + assert types is not None, "core.BNGetScriptingProviderList returned None" try: for i in range(0, count.value): yield ScriptingProvider(types[i]) finally: core.BNFreeScriptingProviderList(types) - def __getitem__(self, value): + def __getitem__(self, value) -> 'ScriptingProvider': binaryninja._init_plugins() provider = core.BNGetScriptingProviderByName(str(value)) if provider is None: raise KeyError("'%s' is not a valid scripting provider" % str(value)) return ScriptingProvider(provider) - def __setattr__(self, name, value): - try: - type.__setattr__(self, name, value) - except AttributeError: - raise AttributeError("attribute '%s' is read only" % name) - -class ScriptingProvider(with_metaclass(_ScriptingProviderMetaclass, object)): +class ScriptingProvider(metaclass=_ScriptingProviderMetaclass): _registered_providers = [] + name = '' + apiName = '' + instance_class: Optional['ScriptingInstance'] = None def __init__(self, handle = None): if handle is not None: self.handle = core.handle_of_type(handle, core.BNScriptingProvider) self.__dict__["name"] = core.BNGetScriptingProviderName(handle) - @property - def name(self): - return NotImplemented - - @property - def instance_class(self): - return NotImplemented - - @property - def list(self): - """Allow tab completion to discover metaclass list property""" - pass - - def register(self): + def register(self) -> None: self._cb = core.BNScriptingProviderCallbacks() self._cb.context = 0 self._cb.createInstance = self._cb.createInstance.__class__(self._create_instance) self._cb.loadModule = self._cb.loadModule.__class__(self._load_module) self._cb.installModules = self._cb.installModules.__class__(self._install_modules) + self._cb.moduleInstalled = self._cb.installModules.__class__(self._module_installed) self.handle = core.BNRegisterScriptingProvider(self.__class__.name, self.__class__.apiName, self._cb) self.__class__._registered_providers.append(self) def _create_instance(self, ctxt): try: - result = self.__class__.instance_class(self) + assert self.__class__.instance_class is not None + result = self.__class__.instance_class(self) # type: ignore if result is None: return None - return ctypes.cast(core.BNNewScriptingInstanceReference(result.handle), ctypes.c_void_p).value + script_instance = core.BNNewScriptingInstanceReference(result.handle) + assert script_instance is not None, "core.BNNewScriptingInstanceReference returned None" + return ctypes.cast(script_instance, ctypes.c_void_p).value except: log.log_error(traceback.format_exc()) return None - def create_instance(self): + def create_instance(self) -> Optional[ScriptingInstance]: result = core.BNCreateScriptingProviderInstance(self.handle) if result is None: return None return ScriptingInstance(self, handle = result) - def _load_plugin(self, ctx, repo_path, plugin_path, force): + def _load_module(self, ctx, repo_path:str, plugin_path:str, force:bool) -> bool: return False - def _install_modules(self, ctx, modules): + def _install_modules(self, ctx, modules:str) -> bool: + return False + + def _module_installed(self, ctx, module:str) -> bool: return False @@ -670,22 +650,23 @@ from binaryninja import * for line in code.split(b'\n'): self.interpreter.push(line.decode('charmap')) - tryNavigate = True - if isinstance(self.locals["here"], str) or isinstance(self.locals["current_address"], str): - try: - self.locals["here"] = self.active_view.parse_expression(self.locals["here"], self.active_addr) - except ValueError as e: - sys.stderr.write(e) - tryNavigate = False - if tryNavigate: - if self.locals["here"] != self.active_addr: - if not self.active_view.file.navigate(self.active_view.file.view, self.locals["here"]): - sys.stderr.write("Address 0x%x is not valid for the current view\n" % self.locals["here"]) - elif self.locals["current_address"] != self.active_addr: - if not self.active_view.file.navigate(self.active_view.file.view, self.locals["current_address"]): - sys.stderr.write("Address 0x%x is not valid for the current view\n" % self.locals["current_address"]) if self.active_view is not None: - self.active_view.update_analysis() + tryNavigate = True + if isinstance(self.locals["here"], str) or isinstance(self.locals["current_address"], str): + try: + self.locals["here"] = self.active_view.parse_expression(self.locals["here"], self.active_addr) + except ValueError as e: + sys.stderr.write(str(e)) + tryNavigate = False + if tryNavigate: + if self.locals["here"] != self.active_addr: + if not self.active_view.file.navigate(self.active_view.file.view, self.locals["here"]): + sys.stderr.write("Address 0x%x is not valid for the current view\n" % self.locals["here"]) + elif self.locals["current_address"] != self.active_addr: + if not self.active_view.file.navigate(self.active_view.file.view, self.locals["current_address"]): + sys.stderr.write("Address 0x%x is not valid for the current view\n" % self.locals["current_address"]) + if self.active_view is not None: + self.active_view.update_analysis() except: traceback.print_exc() finally: @@ -701,6 +682,7 @@ from binaryninja import * self.active_selection_end = self.current_selection_end self.locals.blacklist_enabled = False + self.locals["current_thread"] = self.interpreter self.locals["current_view"] = self.active_view self.locals["bv"] = self.active_view self.locals["current_function"] = self.active_func @@ -776,7 +758,7 @@ from binaryninja import * @abc.abstractmethod def perform_cancel_script_input(self): - for tid, tobj in threading._active.items(): + for tid, tobj in threading._active.items(): # type: ignore if tobj is self.interpreter: if ctypes.pythonapi.PyThreadState_SetAsyncExc(ctypes.c_long(tid), ctypes.py_object(KeyboardInterrupt)) != 1: ctypes.pythonapi.PyThreadState_SetAsyncExc(ctypes.c_long(tid), None) @@ -817,6 +799,13 @@ class PythonScriptingProvider(ScriptingProvider): apiName = f"python{sys.version_info.major}" # Used for plugin compatibility testing instance_class = PythonScriptingInstance + @property + def _python_bin(self) -> Optional[str]: + python_lib = settings.Settings().get_string("python.interpreter") + python_bin_override = settings.Settings().get_string("python.binaryOverride") + python_bin, status = self._get_executable_for_libpython(python_lib, python_bin_override) + return python_bin + def _load_module(self, ctx, repo_path, module, force): try: repo_path = repo_path.decode("utf-8") @@ -865,19 +854,18 @@ class PythonScriptingProvider(ScriptingProvider): return self._run_args([python_bin, "-m", "pip", "--version"])[0] def _satisfied_dependencies(self, python_bin:str) -> Generator[str, None, None]: + if python_bin is None: + return None success, result = self._run_args([python_bin, "-m", "pip", "freeze"]) if not success: return None for line in result.splitlines(): yield line.split("==", 2)[0] - def _dependency_satisfied(self, dependency:str, python_bin:str) -> bool: - return re.split('>|=|,', dependency.strip(), 1)[0] in self._satisfied_dependencies(python_bin) - def _bin_version(self, python_bin: str): return self._run_args([str(python_bin), "-c", "import sys; sys.stdout.write(f'{sys.version_info.major}.{sys.version_info.minor}')"])[1] - def _get_executable_for_libpython(self, python_lib: str, python_bin: str): + def _get_executable_for_libpython(self, python_lib: str, python_bin: str) -> Tuple[Optional[str], str]: python_lib_version = f"{sys.version_info.major}.{sys.version_info.minor}" if python_bin is not None and python_bin != "": python_bin_version = self._bin_version(python_bin) @@ -891,7 +879,7 @@ class PythonScriptingProvider(ScriptingProvider): if using_bundled_python: return (None, "Failed: Bundled python doesn't support dependency installation. Specify a full python installation in your 'Python Interpreter' and try again") - python_bin = Path(python_lib).parent / f"bin/python{python_lib_version}" + python_bin = str(Path(python_lib).parent / f"bin/python{python_lib_version}") elif sys.platform == "linux": class Dl_info(ctypes.Structure): _fields_ = [ @@ -936,7 +924,7 @@ class PythonScriptingProvider(ScriptingProvider): return (python_bin, "Success") - def _install_modules(self, ctx, modules): + def _install_modules(self, ctx, modules: str) -> bool: # This callback should not be called directly it is indirectly # executed binary ninja is executed with --pip option if len(modules.strip()) == 0: @@ -975,16 +963,24 @@ class PythonScriptingProvider(ScriptingProvider): if venv is not None and venv.endswith("site-packages") and Path(venv).is_dir() and not in_virtual_env: args.extend(["--target", venv]) else: - site_package_dir = Path(binaryninja.user_directory()) / f"python{sys.version_info.major}{sys.version_info.minor}" / "site-packages" + user_dir = binaryninja.user_directory() + if user_dir is None: + raise Exception("Unable to find user directory.") + site_package_dir = Path(user_dir) / f"python{sys.version_info.major}{sys.version_info.minor}" / "site-packages" site_package_dir.mkdir(parents=True, exist_ok=True) args.extend(["--target", str(site_package_dir)]) - args.extend(filter(len, modules.decode("utf-8").split("\n"))) + args.extend(list(filter(len, modules.split("\n")))) log.log_info(f"Running pip {args}") status, result = self._run_args(args) if not status: log.log_error(f"Error while attempting to install requirements {result}") return status + def _module_installed(self, ctx, module:str) -> bool: + if self._python_bin is None: + return False + return re.split('>|=|,', module.strip(), 1)[0] in self._satisfied_dependencies(self._python_bin) + PythonScriptingProvider().register() # Wrap stdin/stdout/stderr for Python scripting provider implementation diff --git a/python/settings.py b/python/settings.py index 91f6259c..ac968d3e 100644 --- a/python/settings.py +++ b/python/settings.py @@ -21,12 +21,8 @@ import ctypes # Binary Ninja components -from binaryninja import _binaryninjacore as core - -# 2-3 compatibility -from binaryninja import range -from binaryninja import pyNativeStr -from binaryninja.enums import SettingsScope +from . import _binaryninjacore as core +from .enums import SettingsScope class Settings(object): @@ -122,23 +118,26 @@ class Settings(object): True """ - handle = core.BNCreateSettings("default") + default_handle = core.BNCreateSettings("default") - def __init__(self, instance_id = "default", handle = None): + def __init__(self, instance_id:str="default", handle=None): if handle is None: if instance_id is None or instance_id == "": instance_id = "default" self._instance_id = instance_id if instance_id == "default": - self.handle = Settings.handle + assert Settings.default_handle is not None + _handle = Settings.default_handle else: - self.handle = core.BNCreateSettings(instance_id) + _handle = core.BNCreateSettings(instance_id) else: instance_id = core.BNGetUniqueIdentifierString() - self.handle = handle + _handle = handle + assert _handle is not None + self.handle = _handle def __del__(self): - if self.handle is not Settings.handle and self.handle is not None: + if self.handle is not Settings.default_handle and self.handle is not None: core.BNFreeSettings(self.handle) def __eq__(self, other): @@ -237,18 +236,20 @@ class Settings(object): """ length = ctypes.c_ulonglong() result = core.BNSettingsKeysList(self.handle, ctypes.byref(length)) + assert result is not None, "core.BNSettingsKeysList returned None" out_list = [] for i in range(length.value): - out_list.append(pyNativeStr(result[i])) + out_list.append(result[i].decode('utf8')) core.BNFreeStringList(result, length) return out_list def query_property_string_list(self, key, property_name): length = ctypes.c_ulonglong() result = core.BNSettingsQueryPropertyStringList(self.handle, key, property_name, ctypes.byref(length)) + assert result is not None, "core.BNSettingsQueryPropertyStringList returned None" out_list = [] for i in range(length.value): - out_list.append(pyNativeStr(result[i])) + out_list.append(result[i].decode('utf8')) core.BNFreeStringList(result, length) return out_list @@ -306,9 +307,10 @@ class Settings(object): view = view.handle length = ctypes.c_ulonglong() result = core.BNSettingsGetStringList(self.handle, key, view, None, ctypes.byref(length)) + assert result is not None, "core.BNSettingsGetStringList returned None" out_list = [] for i in range(length.value): - out_list.append(pyNativeStr(result[i])) + out_list.append(result[i].decode('utf8')) core.BNFreeStringList(result, length) return out_list @@ -351,9 +353,10 @@ class Settings(object): c_scope = core.SettingsScopeEnum(scope) length = ctypes.c_ulonglong() result = core.BNSettingsGetStringList(self.handle, key, view, ctypes.byref(c_scope), ctypes.byref(length)) + assert result is not None, "core.BNSettingsGetStringList returned None" out_list = [] for i in range(length.value): - out_list.append(pyNativeStr(result[i])) + out_list.append(result[i].decode('utf8')) core.BNFreeStringList(result, length) return (out_list, SettingsScope(c_scope.value)) diff --git a/python/startup.py b/python/startup.py deleted file mode 100644 index d0b66593..00000000 --- a/python/startup.py +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright (c) 2015-2021 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. - -#from binaryninja import _binaryninjacore as core - - -#_plugin_init = False - - -# def _init_plugins(): -# global _plugin_init -# if not _plugin_init: -# _plugin_init = True -# core.BNInitCorePlugins() -# core.BNInitUserPlugins() -# core.BNInitRepoPlugins() -# if not core.BNIsLicenseValidated(): -# raise RuntimeError("License is not valid. Please supply a valid license.") diff --git a/python/transform.py b/python/transform.py index 5f064b87..7052b3b1 100644 --- a/python/transform.py +++ b/python/transform.py @@ -24,45 +24,24 @@ import abc # Binary Ninja components import binaryninja -from binaryninja import log -from binaryninja import databuffer -from binaryninja import _binaryninjacore as core -from binaryninja.enums import TransformType - -# 2-3 compatibility -import numbers -from binaryninja import range -from binaryninja import with_metaclass +from . import log +from . import databuffer +from . import _binaryninjacore as core +from .enums import TransformType class _TransformMetaClass(type): - @property - def list(self): - binaryninja._init_plugins() - count = ctypes.c_ulonglong() - xforms = core.BNGetTransformTypeList(count) - result = [] - for i in range(0, count.value): - result.append(Transform(xforms[i])) - core.BNFreeTransformTypeList(xforms) - return result - def __iter__(self): binaryninja._init_plugins() count = ctypes.c_ulonglong() xforms = core.BNGetTransformTypeList(count) + assert xforms is not None, "core.BNGetTransformTypeList returned None" try: for i in range(0, count.value): yield Transform(xforms[i]) finally: core.BNFreeTransformTypeList(xforms) - def __setattr__(self, name, value): - try: - type.__setattr__(self, name, value) - except AttributeError: - raise AttributeError("attribute '%s' is read only" % name) - def __getitem__(cls, name): binaryninja._init_plugins() xform = core.BNGetTransformByName(name) @@ -70,20 +49,6 @@ class _TransformMetaClass(type): raise KeyError("'%s' is not a valid transform" % str(name)) return Transform(xform) - def register(cls): - binaryninja._init_plugins() - if cls.name is None: - raise ValueError("transform 'name' is not defined") - if cls.long_name is None: - cls.long_name = cls.name - if cls.transform_type is None: - raise ValueError("transform 'transform_type' is not defined") - if cls.group is None: - cls.group = "" - xform = cls(None) - cls._registered_cb = xform._cb - xform.handle = core.BNRegisterTransformType(cls.transform_type, cls.name, cls.long_name, cls.group, xform._cb) - class TransformParameter(object): def __init__(self, name, long_name = None, fixed_length = 0): @@ -115,10 +80,10 @@ class TransformParameter(object): return self._fixed_length -class Transform(with_metaclass(_TransformMetaClass, object)): +class Transform(metaclass=_TransformMetaClass): """ - ``class Transform`` is an implementation of the TransformMetaClass that implements custom transformations. New - transformations may be added at runtime, so an instance of a transform is created like:: + ``class Transform`` allows users to implement custom transformations. New transformations may be added at runtime, + so an instance of a transform is created like:: >>> list(Transform) [<transform: Zlib>, <transform: StringEscape>, <transform: RawHex>, <transform: HexDump>, <transform: Base64>, <transform: Reverse>, <transform: CArray08>, <transform: CArrayA16>, <transform: CArrayA32>, <transform: CArrayA64>, <transform: CArrayB16>, <transform: CArrayB32>, <transform: CArrayB64>, <transform: IntList08>, <transform: IntListA16>, <transform: IntListA32>, <transform: IntListA64>, <transform: IntListB16>, <transform: IntListB32>, <transform: IntListB64>, <transform: MD4>, <transform: MD5>, <transform: SHA1>, <transform: SHA224>, <transform: SHA256>, <transform: SHA384>, <transform: SHA512>, <transform: AES-128 ECB>, <transform: AES-128 CBC>, <transform: AES-256 ECB>, <transform: AES-256 CBC>, <transform: DES ECB>, <transform: DES CBC>, <transform: Triple DES ECB>, <transform: Triple DES CBC>, <transform: RC2 ECB>, <transform: RC2 CBC>, <transform: Blowfish ECB>, <transform: Blowfish CBC>, <transform: CAST ECB>, <transform: CAST CBC>, <transform: RC4>, <transform: XOR>] @@ -153,6 +118,7 @@ class Transform(with_metaclass(_TransformMetaClass, object)): self._pending_param_lists = {} self.type = self.__class__.transform_type if not isinstance(self.type, str): + assert self.type is not None, "Transform Type is None" self.type = TransformType(self.type) self.name = self.__class__.name self.long_name = self.__class__.long_name @@ -166,6 +132,7 @@ class Transform(with_metaclass(_TransformMetaClass, object)): self.group = core.BNGetTransformGroup(self.handle) count = ctypes.c_ulonglong() params = core.BNGetTransformParameterList(self.handle, count) + assert params is not None, "core.BNGetTransformParameterList returned None" self.parameters = [] for i in range(0, count.value): self.parameters.append(TransformParameter(params[i].name, params[i].longName, params[i].fixedLength)) @@ -187,6 +154,21 @@ class Transform(with_metaclass(_TransformMetaClass, object)): def __hash__(self): return hash(ctypes.addressof(self.handle.contents)) + @classmethod + def register(cls): + binaryninja._init_plugins() + if cls.name is None: + raise ValueError("transform 'name' is not defined") + if cls.long_name is None: + cls.long_name = cls.name + if cls.transform_type is None: + raise ValueError("transform 'transform_type' is not defined") + if cls.group is None: + cls.group = "" + xform = cls(None) + cls._registered_cb = xform._cb + xform.handle = core.BNRegisterTransformType(cls.transform_type, cls.name, cls.long_name, cls.group, xform._cb) + def _get_parameters(self, ctxt, count): try: count[0] = len(self.parameters) @@ -246,11 +228,6 @@ class Transform(with_metaclass(_TransformMetaClass, object)): log.log_error(traceback.format_exc()) return False - @property - def list(self): - """Allow tab completion to discover metaclass list property""" - pass - @abc.abstractmethod def perform_decode(self, data, params): if self.type == TransformType.InvertingTransform: @@ -262,7 +239,7 @@ class Transform(with_metaclass(_TransformMetaClass, object)): return None def decode(self, input_buf, params = {}): - if isinstance(input_buf, int) or isinstance(input_buf, numbers.Integral): + if isinstance(input_buf, int) or isinstance(input_buf, int): return None input_buf = databuffer.DataBuffer(input_buf) output_buf = databuffer.DataBuffer() @@ -278,7 +255,7 @@ class Transform(with_metaclass(_TransformMetaClass, object)): return bytes(output_buf) def encode(self, input_buf, params = {}): - if isinstance(input_buf, int) or isinstance(input_buf, numbers.Integral): + if isinstance(input_buf, int) or isinstance(input_buf, int): return None input_buf = databuffer.DataBuffer(input_buf) output_buf = databuffer.DataBuffer() diff --git a/python/typelibrary.py b/python/typelibrary.py index 94e030d8..2a1a5bab 100644 --- a/python/typelibrary.py +++ b/python/typelibrary.py @@ -18,24 +18,17 @@ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # IN THE SOFTWARE. -import struct -import traceback import ctypes -import abc -import numbers +from typing import Optional # Binary Ninja components -from binaryninja import _binaryninjacore as core import binaryninja -from binaryninja import log -from binaryninja import types -from binaryninja import metadata -from binaryninja import platform -from binaryninja import architecture +from . import _binaryninjacore as core +from . import types +from . import metadata +from . import platform +from . import architecture -# 2-3 compatibility -from binaryninja import range -from binaryninja import with_metaclass class TypeLibrary(object): def __init__(self, handle): @@ -47,8 +40,8 @@ class TypeLibrary(object): def __repr__(self): return "<typelib '{}':{}>".format(self.name, self.arch.name) - @classmethod - def new(cls, arch, name): + @staticmethod + def new(arch, name): """ Creates an empty type library object with a random GUID and the provided name. @@ -60,8 +53,8 @@ class TypeLibrary(object): handle = core.BNNewTypeLibrary(arch.handle, name) return TypeLibrary(handle) - @classmethod - def load_from_file(cls, path): + @staticmethod + def load_from_file(path): """ Loads a finalized type library instance from file @@ -82,8 +75,8 @@ class TypeLibrary(object): """ core.BNWriteTypeLibraryToFile(self.handle, path) - @classmethod - def from_name(cls, arch, name): + @staticmethod + def from_name(arch, name): """ `from_name` looks up the first type library found with a matching name. Keep in mind that names are not necessarily unique. @@ -97,8 +90,8 @@ class TypeLibrary(object): return None return TypeLibrary(handle) - @classmethod - def from_guid(cls, arch, guid): + @staticmethod + def from_guid(arch, guid): """ `from_guid` attempts to grab a type library associated with the provided Architecture and GUID pair @@ -113,12 +106,11 @@ class TypeLibrary(object): return TypeLibrary(handle) @property - def arch(self): + def arch(self) -> 'architecture.Architecture': """The Architecture this type library is associated with""" arch = core.BNGetTypeLibraryArchitecture(self.handle) - if arch is None: - return None - return binaryninja.architecture.CoreArchitecture._from_cache(handle=arch) + assert arch is not None, "core.BNGetTypeLibraryArchitecture returned None" + return architecture.CoreArchitecture._from_cache(handle=arch) @property def name(self): @@ -165,6 +157,7 @@ class TypeLibrary(object): count = ctypes.c_ulonglong(0) result = [] names = core.BNGetTypeLibraryAlternateNames(self.handle, count) + assert names is not None, "core.BNGetTypeLibraryAlternateNames returned None" for i in range(0, count.value): result.append(names[i]) core.BNFreeStringList(names, count.value) @@ -186,6 +179,7 @@ class TypeLibrary(object): count = ctypes.c_ulonglong(0) result = [] platforms = core.BNGetTypeLibraryPlatforms(self.handle, count) + assert platforms is not None, "core.BNGetTypeLibraryPlatforms returned None" for i in range(0, count.value): result.append(platforms[i]) core.BNFreeStringList(platforms, count.value) @@ -349,6 +343,7 @@ class TypeLibrary(object): count = ctypes.c_ulonglong(0) result = {} named_types = core.BNGetTypeLibraryNamedObjects(self.handle, count) + assert named_types is not None, "core.BNGetTypeLibraryNamedObjects returned None" for i in range(0, count.value): name = types.QualifiedName._from_core_struct(named_types[i].name) result[name] = types.Type(core.BNNewTypeReference(named_types[i].type)) @@ -363,6 +358,7 @@ class TypeLibrary(object): count = ctypes.c_ulonglong(0) result = {} named_types = core.BNGetTypeLibraryNamedTypes(self.handle, count) + assert named_types is not None, "core.BNGetTypeLibraryNamedTypes returned None" for i in range(0, count.value): name = types.QualifiedName._from_core_struct(named_types[i].name) result[name] = types.Type(core.BNNewTypeReference(named_types[i].type)) diff --git a/python/types.py b/python/types.py index af62e5b4..bead9a64 100644 --- a/python/types.py +++ b/python/types.py @@ -18,34 +18,33 @@ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # IN THE SOFTWARE. -from __future__ import absolute_import - -from binaryninja.log import log_warn -max_confidence = 255 - import ctypes +from typing import Generator, List, Union # Binary Ninja components -import binaryninja -from binaryninja import _binaryninjacore as core -from binaryninja.enums import SymbolType, SymbolBinding, TypeClass, NamedTypeReferenceClass, InstructionTextTokenType, StructureType, ReferenceType, VariableSourceType, TypeReferenceType - -# 2-3 compatibility -from binaryninja import range -from binaryninja import pyNativeStr +from . import _binaryninjacore as core +from .enums import SymbolType, SymbolBinding, TypeClass, NamedTypeReferenceClass, StructureType, ReferenceType, VariableSourceType, TypeReferenceType +from . import callingconvention +from . import function +from . import variable +from . import architecture +from . import log +QualifiedNameType = Union[List[str], str, 'QualifiedName', List[bytes]] class QualifiedName(object): - def __init__(self, name = []): + def __init__(self, name:QualifiedNameType=[]): + self._name:List[str] = [] if isinstance(name, str): self._name = [name] - self._byte_name = [name.encode('charmap')] elif isinstance(name, self.__class__): - self._name = name.name - self._byte_name = [n.encode('charmap') for n in name.name] - else: - self._name = [pyNativeStr(i) for i in name] - self._byte_name = name + self._name = name._name + elif isinstance(name, list): + for i in name: + if isinstance(i, bytes): + self._name.append(i.decode("utf-8")) + else: + self._name.append(str(i)) def __str__(self): return "::".join(self.name) @@ -117,36 +116,26 @@ class QualifiedName(object): result = core.BNQualifiedName() name_list = (ctypes.c_char_p * len(self.name))() for i in range(0, len(self.name)): - name_list[i] = self.name[i].encode('charmap') + name_list[i] = self.name[i].encode("utf-8") result.name = name_list result.nameCount = len(self.name) return result - @classmethod - def _from_core_struct(cls, name): + @staticmethod + def _from_core_struct(name): result = [] for i in range(0, name.nameCount): - result.append(name.name[i]) + result.append(name.name[i].decode("utf-8")) return QualifiedName(result) @property - def name(self): - """ """ + def name(self) -> List[str]: return self._name @name.setter - def name(self, value): + def name(self, value:List[str]) -> None: self._name = value - @property - def byte_name(self): - """ """ - return self._byte_name - - @byte_name.setter - def byte_name(self, value): - self._byte_name = value - class TypeReferenceSource(object): def __init__(self, name, offset, ref_type): @@ -168,17 +157,14 @@ class TypeReferenceSource(object): @property def name(self): - """ """ return self._name @property def offset(self): - """ """ return self._offset @property def ref_type(self): - """ """ return self._ref_type def __eq__(self, other): @@ -243,11 +229,11 @@ class NameSpace(QualifiedName): result.nameCount = len(self.name) return result - @classmethod - def _from_core_struct(cls, name): + @staticmethod + def _from_core_struct(name): result = [] for i in range(0, name.nameCount): - result.append(name.name[i]) + result.append(name.name[i].decode("utf-8")) return NameSpace(result) @@ -269,7 +255,7 @@ class Symbol(object): """ def __init__(self, sym_type, addr, short_name, full_name=None, raw_name=None, handle=None, binding=None, namespace=None, ordinal=0): if handle is not None: - self.handle = core.handle_of_type(handle, core.BNSymbol) + _handle = core.handle_of_type(handle, core.BNSymbol) else: if isinstance(sym_type, str): sym_type = SymbolType[sym_type] @@ -283,7 +269,9 @@ class Symbol(object): namespace = NameSpace(namespace) if isinstance(namespace, NameSpace): namespace = namespace._get_core_struct() - self.handle = core.BNCreateSymbol(sym_type, short_name, full_name, raw_name, addr, binding, namespace, ordinal) + _handle = core.BNCreateSymbol(sym_type, short_name, full_name, raw_name, addr, binding, namespace, ordinal) + assert _handle is not None + self.handle = _handle def __del__(self): core.BNFreeSymbol(self.handle) @@ -370,7 +358,6 @@ class FunctionParameter(object): @property def type(self): - """ """ return self._type @type.setter @@ -379,7 +366,6 @@ class FunctionParameter(object): @property def name(self): - """ """ return self._name @name.setter @@ -388,7 +374,6 @@ class FunctionParameter(object): @property def location(self): - """ """ return self._location @location.setter @@ -408,7 +393,7 @@ class Type(object): :py:meth:`parse_types_from_source_file <binaryninja.platform.Platform.parse_types_from_source_file>` """ - def __init__(self, handle, platform = None, confidence = max_confidence): + def __init__(self, handle, platform = None, confidence = core.max_confidence): self._handle = handle self._mutable = isinstance(handle.contents, core.BNTypeBuilder) self._confidence = confidence @@ -421,8 +406,8 @@ class Type(object): core.BNFreeType(self._handle) def __repr__(self): - if self._confidence < max_confidence: - return "<type: %s, %d%% confidence>" % (str(self), (self._confidence * 100) // max_confidence) + if self._confidence < core.max_confidence: + return "<type: %s, %d%% confidence>" % (str(self), (self._confidence * 100) // core.max_confidence) return "<type: %s>" % str(self) def __str__(self): @@ -507,7 +492,7 @@ class Type(object): if hasattr(value, 'confidence'): bc.confidence = value.confidence else: - bc.confidence = max_confidence + bc.confidence = core.max_confidence core.BNTypeBuilderSetConst(self._handle, bc) @property @@ -528,7 +513,7 @@ class Type(object): if hasattr(value, 'confidence'): bc.confidence = value.confidence else: - bc.confidence = max_confidence + bc.confidence = core.max_confidence core.BNTypeBuilderSetVolatile(self._handle, bc) @property @@ -580,7 +565,7 @@ class Type(object): result = core.BNGetTypeCallingConvention(self._handle) if not result.convention: return None - return binaryninja.callingconvention.CallingConvention(None, handle = result.convention, confidence = result.confidence) + return callingconvention.CallingConvention(None, handle = result.convention, confidence = result.confidence) @property def parameters(self): @@ -588,8 +573,10 @@ class Type(object): count = ctypes.c_ulonglong() if self._mutable: params = core.BNGetTypeBuilderParameters(self._handle, count) + assert params is not None, "core.BNGetTypeBuilderParameters returned None" else: params = core.BNGetTypeParameters(self._handle, count) + assert params is not None, "core.BNGetTypeParameters returned None" result = [] for i in range(0, count.value): param_type = Type(core.BNNewTypeReference(params[i].type), platform = self._platform, confidence = params[i].typeConfidence) @@ -601,7 +588,7 @@ class Type(object): name = self._platform.arch.get_reg_name(params[i].location.storage) elif params[i].location.type == VariableSourceType.StackVariableSourceType: name = "arg_%x" % params[i].location.storage - param_location = binaryninja.function.Variable(None, params[i].location.type, params[i].location.index, + param_location = variable.Variable(None, params[i].location.type, params[i].location.index, params[i].location.storage, name, param_type) result.append(FunctionParameter(param_type, params[i].name, param_location)) core.BNFreeTypeParameterList(params, count.value) @@ -635,7 +622,7 @@ class Type(object): if hasattr(value, 'confidence'): bc.confidence = value.confidence else: - bc.confidence = max_confidence + bc.confidence = core.max_confidence core.BNSetFunctionTypeBuilderCanReturn(self._handle, bc) @property @@ -725,59 +712,66 @@ class Type(object): """Type string as a list of tokens (read-only)""" return self.get_tokens() - def get_tokens(self, base_confidence = max_confidence): + def get_tokens(self, base_confidence = core.max_confidence): count = ctypes.c_ulonglong() platform = None if self._platform is not None: platform = self._platform.handle if self._mutable: tokens = core.BNGetTypeBuilderTokens(self._handle, platform, base_confidence, count) + assert tokens is not None, "core.BNGetTypeBuilderTokens returned None" else: tokens = core.BNGetTypeTokens(self._handle, platform, base_confidence, count) - result = binaryninja.function.InstructionTextToken.get_instruction_lines(tokens, count.value) + assert tokens is not None, "core.BNGetTypeTokens returned None" + + result = function.InstructionTextToken._from_core_struct(tokens, count.value) core.BNFreeInstructionText(tokens, count.value) return result - def get_tokens_before_name(self, base_confidence = max_confidence): + def get_tokens_before_name(self, base_confidence = core.max_confidence): count = ctypes.c_ulonglong() platform = None if self._platform is not None: platform = self._platform.handle if self._mutable: tokens = core.BNGetTypeBuilderTokensBeforeName(self._handle, platform, base_confidence, count) + assert tokens is not None, "core.BNGetTypeBuilderTokensBeforeName returned None" else: tokens = core.BNGetTypeTokensBeforeName(self._handle, platform, base_confidence, count) - result = binaryninja.function.InstructionTextToken.get_instruction_lines(tokens, count.value) + assert tokens is not None, "core.BNGetTypeTokensBeforeName returned None" + result = function.InstructionTextToken._from_core_struct(tokens, count.value) core.BNFreeInstructionText(tokens, count.value) return result - def get_tokens_after_name(self, base_confidence = max_confidence): + def get_tokens_after_name(self, base_confidence = core.max_confidence): count = ctypes.c_ulonglong() platform = None if self._platform is not None: platform = self._platform.handle if self._mutable: tokens = core.BNGetTypeBuilderTokensAfterName(self._handle, platform, base_confidence, count) + assert tokens is not None, "core.BNGetTypeBuilderTokensAfterName returned None" else: tokens = core.BNGetTypeTokensAfterName(self._handle, platform, base_confidence, count) - result = binaryninja.function.InstructionTextToken.get_instruction_lines(tokens, count.value) + assert tokens is not None, "core.BNGetTypeTokensAfterName returned None" + result = function.InstructionTextToken._from_core_struct(tokens, count.value) core.BNFreeInstructionText(tokens, count.value) return result - @classmethod - def void(cls): + @staticmethod + def void(): return Type(core.BNCreateVoidTypeBuilder()) - @classmethod - def bool(self): + @staticmethod + def bool(): return Type(core.BNCreateBoolTypeBuilder()) - @classmethod - def char(self): + @staticmethod + def char(): return Type.int(1, True) - @classmethod - def int(self, width, sign = None, altname=""): + @staticmethod + def int(width, sign = None, altname=""): """ ``int`` class method for creating an int Type. @@ -796,8 +790,8 @@ class Type(object): return Type(core.BNCreateIntegerTypeBuilder(width, sign_conf, altname)) - @classmethod - def float(self, width, altname=""): + @staticmethod + def float(width, altname=""): """ ``float`` class method for creating floating point Types. @@ -806,8 +800,8 @@ class Type(object): """ return Type(core.BNCreateFloatTypeBuilder(width, altname)) - @classmethod - def wide_char(self, width, altname=""): + @staticmethod + def wide_char(width, altname=""): """ ``wide_char`` class method for creating wide char Types. @@ -816,41 +810,41 @@ class Type(object): """ return Type(core.BNCreateWideCharTypeBuilder(width, altname)) - @classmethod - def structure_type(self, structure_type): + @staticmethod + def structure_type(structure_type): return Type(core.BNCreateStructureTypeBuilder(structure_type.handle)) - @classmethod - def named_type(self, named_type, width = 0, align = 1): + @staticmethod + def named_type(named_type, width = 0, align = 1): return Type(core.BNCreateNamedTypeReferenceBuilder(named_type.handle, width, align)) - @classmethod - def named_type_from_type_and_id(self, type_id, name, t): + @staticmethod + def named_type_from_type_and_id(type_id, name, t): name = QualifiedName(name)._get_core_struct() if t is not None: t = t.handle return Type(core.BNCreateNamedTypeReferenceBuilderFromTypeAndId(type_id, name, t)) - @classmethod - def named_type_from_type(self, name, t): + @staticmethod + def named_type_from_type(name, t): name = QualifiedName(name)._get_core_struct() if t is not None: t = t.handle return Type(core.BNCreateNamedTypeReferenceBuilderFromTypeAndId("", name, t)) - @classmethod - def named_type_from_registered_type(self, view, name): + @staticmethod + def named_type_from_registered_type(view, name): name = QualifiedName(name)._get_core_struct() return Type(core.BNCreateNamedTypeReferenceBuilderFromType(view.handle, name)) - @classmethod - def enumeration_type(self, arch, e, width=None, sign=False): + @staticmethod + def enumeration_type(arch, e, width=None, sign=False): if width is None: width = arch.default_int_size return Type(core.BNCreateEnumerationTypeBuilder(arch.handle, e.handle, width, sign)) - @classmethod - def pointer(self, arch, t, const=None, volatile=None, ref_type=None): + @staticmethod + def pointer(arch, t, const=None, volatile=None, ref_type=None): if const is None: const = BoolWithConfidence(False, confidence = 0) elif not isinstance(const, BoolWithConfidence): @@ -878,15 +872,15 @@ class Type(object): return Type(core.BNCreatePointerTypeBuilder(arch.handle, type_conf, const_conf, volatile_conf, ref_type)) - @classmethod - def array(self, t, count): + @staticmethod + def array(t, count): type_conf = core.BNTypeWithConfidence() type_conf.type = t.handle type_conf.confidence = t.confidence return Type(core.BNCreateArrayTypeBuilder(type_conf, count)) - @classmethod - def function(self, ret, params, calling_convention=None, variable_arguments=None, stack_adjust=None): + @staticmethod + def function(ret, params, calling_convention=None, variable_arguments=None, stack_adjust=None): """ ``function`` class method for creating an function Type. @@ -953,18 +947,18 @@ class Type(object): return Type(core.BNCreateFunctionTypeBuilder(ret_conf, conv_conf, param_buf, len(params), vararg_conf, stack_adjust_conf)) - @classmethod - def generate_auto_type_id(self, source, name): + @staticmethod + def generate_auto_type_id(source, name): name = QualifiedName(name)._get_core_struct() return core.BNGenerateAutoTypeId(source, name) - @classmethod - def generate_auto_demangled_type_id(self, name): + @staticmethod + def generate_auto_demangled_type_id(name): name = QualifiedName(name)._get_core_struct() return core.BNGenerateAutoDemangledTypeId(name) - @classmethod - def get_auto_demangled_type_id_source(self): + @staticmethod + def get_auto_demangled_type_id_source(): return core.BNGetAutoDemangledTypeIdSource() def with_confidence(self, confidence): @@ -972,7 +966,6 @@ class Type(object): @property def confidence(self): - """ """ return self._confidence @confidence.setter @@ -981,7 +974,6 @@ class Type(object): @property def platform(self): - """ """ return self._platform @platform.setter @@ -1004,7 +996,7 @@ class Type(object): class BoolWithConfidence(object): - def __init__(self, value, confidence = max_confidence): + def __init__(self, value, confidence = core.max_confidence): self._value = value self._confidence = confidence @@ -1022,7 +1014,6 @@ class BoolWithConfidence(object): @property def value(self): - """ """ return self._value @value.setter @@ -1031,7 +1022,6 @@ class BoolWithConfidence(object): @property def confidence(self): - """ """ return self._confidence @confidence.setter @@ -1040,7 +1030,7 @@ class BoolWithConfidence(object): class SizeWithConfidence(object): - def __init__(self, value, confidence = max_confidence): + def __init__(self, value:int, confidence:int=core.max_confidence): self._value = value self._confidence = confidence @@ -1054,26 +1044,24 @@ class SizeWithConfidence(object): return self._value @property - def value(self): - """ """ + def value(self) -> int: return self._value @value.setter - def value(self, value): + def value(self, value:int) -> None: self._value = value @property - def confidence(self): - """ """ + def confidence(self) -> int: return self._confidence @confidence.setter - def confidence(self, value): + def confidence(self, value:int) -> None: self._confidence = value class RegisterStackAdjustmentWithConfidence(object): - def __init__(self, value, confidence = max_confidence): + def __init__(self, value:int, confidence:int=core.max_confidence): self._value = value self._confidence = confidence @@ -1087,33 +1075,31 @@ class RegisterStackAdjustmentWithConfidence(object): return self._value @property - def value(self): - """ """ + def value(self) -> int: return self._value @value.setter - def value(self, value): + def value(self, value:int) -> None: self._value = value @property - def confidence(self): - """ """ + def confidence(self) -> int: return self._confidence @confidence.setter - def confidence(self, value): + def confidence(self, value:int) -> None: self._confidence = value class RegisterSet(object): - def __init__(self, reg_list, confidence = max_confidence): + def __init__(self, reg_list:List['architecture.RegisterName'], confidence:int=core.max_confidence): self._regs = reg_list self._confidence = confidence def __repr__(self): return repr(self._regs) - def __iter__(self): + def __iter__(self) -> Generator['architecture.RegisterName', None, None]: for reg in self._regs: yield reg @@ -1127,26 +1113,24 @@ class RegisterSet(object): return RegisterSet(list(self._regs), confidence = confidence) @property - def regs(self): - """ """ + def regs(self) -> List['architecture.RegisterName']: return self._regs @regs.setter - def regs(self, value): + def regs(self, value:List['architecture.RegisterName']) -> None: self._regs = value @property - def confidence(self): - """ """ + def confidence(self) -> int: return self._confidence @confidence.setter - def confidence(self, value): + def confidence(self, value:int) -> None: self._confidence = value class ReferenceTypeWithConfidence(object): - def __init__(self, value, confidence = max_confidence): + def __init__(self, value, confidence = core.max_confidence): self._value = value self._confidence = confidence @@ -1158,7 +1142,6 @@ class ReferenceTypeWithConfidence(object): @property def value(self): - """ """ return self._value @value.setter @@ -1167,7 +1150,6 @@ class ReferenceTypeWithConfidence(object): @property def confidence(self): - """ """ return self._confidence @confidence.setter @@ -1180,9 +1162,11 @@ class NamedTypeReference(object): if handle is None: if name is not None: name = QualifiedName(name)._get_core_struct() - self.handle = core.BNCreateNamedType(type_class, type_id, name) + _handle = core.BNCreateNamedType(type_class, type_id, name) else: - self.handle = handle + _handle = handle + assert _handle is not None + self.handle = _handle def __del__(self): core.BNFreeNamedTypeReference(self.handle) @@ -1226,13 +1210,13 @@ class NamedTypeReference(object): core.BNFreeQualifiedName(name) return result - @classmethod - def generate_auto_type_ref(self, type_class, source, name): + @staticmethod + def generate_auto_type_ref(type_class, source, name): type_id = Type.generate_auto_type_id(source, name) return NamedTypeReference(type_class, type_id, name) - @classmethod - def generate_auto_demangled_type_ref(self, type_class, name): + @staticmethod + def generate_auto_demangled_type_ref(type_class, name): type_id = Type.generate_auto_demangled_type_id(name) return NamedTypeReference(type_class, type_id, name) @@ -1284,7 +1268,6 @@ class StructureMember(object): @property def type(self): - """ """ return self._type @type.setter @@ -1293,7 +1276,6 @@ class StructureMember(object): @property def name(self): - """ """ return self._name @name.setter @@ -1302,7 +1284,6 @@ class StructureMember(object): @property def offset(self): - """ """ return self._offset @offset.setter @@ -1313,11 +1294,13 @@ class StructureMember(object): class Structure(object): def __init__(self, handle=None): if handle is None: - self._handle = core.BNCreateStructureBuilder() + _handle = core.BNCreateStructureBuilder() self._mutable = True else: - self._handle = handle + _handle = handle self._mutable = isinstance(handle.contents, core.BNStructureBuilder) + assert _handle is not None + self._handle = _handle def __del__(self): if self._mutable: @@ -1342,22 +1325,29 @@ class Structure(object): return hash(ctypes.addressof(self._handle.contents)) def __getitem__(self, name): + member = None try: if self._mutable: member = core.BNGetStructureBuilderMemberByName(self._handle, name) + assert member is not None, "core.BNGetStructureBuilderMemberByName returned None" else: member = core.BNGetStructureMemberByName(self._handle, name) + assert member is not None, "core.BNGetStructureMemberByName returned None" return StructureMember(Type(core.BNNewTypeReference(member.contents.type), confidence=member.contents.typeConfidence), member.contents.name, member.contents.offset) finally: - core.BNFreeStructureMember(member) + if member is not None: + core.BNFreeStructureMember(member) def member_at_offset(self, offset): + member = None try: if self._mutable: member = core.BNGetStructureBuilderMemberAtOffset(self._handle, offset, None) + assert member is not None, "core.BNGetStructureBuilderMemberAtOffset returned None" else: member = core.BNGetStructureMemberAtOffset(self._handle, offset, None) + assert member is not None, "core.BNGetStructureMemberAtOffset returned None" return StructureMember(Type(core.BNNewTypeReference(member.contents.type), confidence=member.contents.typeConfidence), member.contents.name, member.contents.offset) finally: @@ -1368,6 +1358,7 @@ class Structure(object): if self._mutable: # First use of a mutable Structure makes it immutable finalized = core.BNFinalizeStructureBuilder(self._handle) + assert finalized is not None, "core.BNFinalizeStructureBuilder returned None" core.BNFreeStructureBuilder(self._handle) self._handle = finalized self._mutable = False @@ -1379,8 +1370,10 @@ class Structure(object): count = ctypes.c_ulonglong() if self._mutable: members = core.BNGetStructureBuilderMembers(self._handle, count) + assert members is not None, "core.BNGetStructureBuilderMembers returned None" else: members = core.BNGetStructureMembers(self._handle, count) + assert members is not None, "core.BNGetStructureMembers returned None" try: result = [] for i in range(0, count.value): @@ -1501,7 +1494,6 @@ class EnumerationMember(object): @property def value(self): - """ """ return self._value @value.setter @@ -1510,7 +1502,6 @@ class EnumerationMember(object): @property def name(self): - """ """ return self._name @name.setter @@ -1519,7 +1510,6 @@ class EnumerationMember(object): @property def default(self): - """ """ return self._default @default.setter @@ -1530,11 +1520,13 @@ class EnumerationMember(object): class Enumeration(object): def __init__(self, handle=None): if handle is None: - self._handle = core.BNCreateEnumerationBuilder() + _handle = core.BNCreateEnumerationBuilder() self._mutable = True else: - self._handle = handle + _handle = handle self._mutable = isinstance(handle.contents, core.BNEnumerationBuilder) + assert _handle is not None + self._handle = _handle def __del__(self): if self._mutable: @@ -1563,6 +1555,7 @@ class Enumeration(object): if self._mutable: # First use of a mutable Enumeration makes it immutable finalized = core.BNFinalizeEnumerationBuilder(self._handle) + assert finalized is not None core.BNFreeEnumerationBuilder(self._handle) self._handle = finalized self._mutable = False @@ -1574,8 +1567,10 @@ class Enumeration(object): count = ctypes.c_ulonglong() if self._mutable: members = core.BNGetEnumerationBuilderMembers(self._handle, count) + assert members is not None, "core.BNGetEnumerationBuilderMembers returned None" else: members = core.BNGetEnumerationMembers(self._handle, count) + assert members is not None, "core.BNGetEnumerationMembers returned None" result = [] for i in range(0, count.value): result.append(EnumerationMember(members[i].name, members[i].value, members[i].isDefault)) @@ -1617,7 +1612,6 @@ class TypeParserResult(object): @property def types(self): - """ """ return self._types @types.setter @@ -1626,7 +1620,6 @@ class TypeParserResult(object): @property def variables(self): - """ """ return self._variables @variables.setter @@ -1635,7 +1628,6 @@ class TypeParserResult(object): @property def functions(self): - """ """ return self._functions @functions.setter @@ -1675,3 +1667,104 @@ def preprocess_source(source, filename=None, include_dirs=[]): if result: return (output_str, error_str) return (None, error_str) + + +class TypeFieldReference(object): + def __init__(self, func, arch, addr, size): + self._function = func + self._arch = arch + self._address = addr + self._size = size + + def __repr__(self): + if self._arch: + return "<ref: %s@%#x, size: %#x>" % (self._arch.name, self._address, self._size) + else: + return "<ref: %#x, size: %#x>" % (self._address, self._size) + + def __eq__(self, other): + if not isinstance(other, self.__class__): + return NotImplemented + return (self.function, self.arch, self.address, self._size) ==\ + (other.address, other.function, other.arch, other.size) + + 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.address < other.address: + return True + elif self.address > other.address: + return False + else: + return self.size < other.size + + def __gt__(self, other): + if not isinstance(other, self.__class__): + return NotImplemented + if self.address > other.address: + return True + elif self.address < other.address: + return False + else: + return self.size > other.size + + def __ge__(self, other): + if not isinstance(other, self.__class__): + return NotImplemented + if self.address > other.address: + return True + elif self.address < other.address: + return False + else: + return self.size >= other.size + + def __le__(self, other): + if not isinstance(other, self.__class__): + return NotImplemented + if self.address < other.address: + return True + elif self.address > other.address: + return False + else: + return self.size <= other.size + + def __hash__(self): + return hash((self._function, self._arch, self._address, self._size)) + + @property + def function(self): + return self._function + + @function.setter + def function(self, value): + self._function = value + + @property + def arch(self): + return self._arch + + @arch.setter + def arch(self, value): + self._arch = value + + @property + def address(self): + return self._address + + @address.setter + def address(self, value): + self._address = value + + @property + def size(self): + return self._size + + @size.setter + def size(self, value): + self._size = value + diff --git a/python/update.py b/python/update.py index 5cbcbb47..9537ad83 100644 --- a/python/update.py +++ b/python/update.py @@ -22,47 +22,20 @@ import traceback import ctypes # Binary Ninja components -from binaryninja import _binaryninjacore as core -from binaryninja import log - import binaryninja -from binaryninja.enums import UpdateResult +from . import _binaryninjacore as core +from .enums import UpdateResult +from . import log -# 2-3 compatibility -from binaryninja import range -from binaryninja import with_metaclass class _UpdateChannelMetaClass(type): - @property - def list(self): - binaryninja._init_plugins() - count = ctypes.c_ulonglong() - errors = ctypes.c_char_p() - channels = core.BNGetUpdateChannels(count, errors) - if errors: - error_str = errors.value - core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) - raise IOError(error_str) - result = [] - for i in range(0, count.value): - result.append(UpdateChannel(channels[i].name, channels[i].description, channels[i].latestVersion)) - core.BNFreeUpdateChannelList(channels, count.value) - return result - - @property - def active(self): - return core.BNGetActiveUpdateChannel() - - @active.setter - def active(self, value): - return core.BNSetActiveUpdateChannel(value) - def __iter__(self): binaryninja._init_plugins() count = ctypes.c_ulonglong() errors = ctypes.c_char_p() channels = core.BNGetUpdateChannels(count, errors) + assert channels is not None, "core.BNGetUpdateChannels returned None" if errors: error_str = errors.value core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) @@ -73,17 +46,12 @@ class _UpdateChannelMetaClass(type): finally: core.BNFreeUpdateChannelList(channels, count.value) - def __setattr__(self, name, value): - try: - type.__setattr__(self, name, value) - except AttributeError: - raise AttributeError("attribute '%s' is read only" % name) - def __getitem__(cls, name): binaryninja._init_plugins() count = ctypes.c_ulonglong() errors = ctypes.c_char_p() channels = core.BNGetUpdateChannels(count, errors) + assert channels is not None, "core.BNGetUpdateChannels returned None" if errors: error_str = errors.value core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) @@ -112,8 +80,15 @@ class UpdateProgressCallback(object): except: log.log_error(traceback.format_exc()) + @property + def active(cls): + return core.BNGetActiveUpdateChannel() + + @active.setter + def active(cls, value:str) -> None: + return core.BNSetActiveUpdateChannel(value) -class UpdateChannel(with_metaclass(_UpdateChannelMetaClass, object)): +class UpdateChannel(metaclass=_UpdateChannelMetaClass): def __init__(self, name, desc, ver): self._name = name self._description = desc @@ -125,6 +100,7 @@ class UpdateChannel(with_metaclass(_UpdateChannelMetaClass, object)): count = ctypes.c_ulonglong() errors = ctypes.c_char_p() versions = core.BNGetUpdateChannelVersions(self._name, count, errors) + assert versions is not None, "core.BNGetUpdateChannelVersions returned None" if errors: error_str = errors.value core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) @@ -141,6 +117,7 @@ class UpdateChannel(with_metaclass(_UpdateChannelMetaClass, object)): count = ctypes.c_ulonglong() errors = ctypes.c_char_p() versions = core.BNGetUpdateChannelVersions(self._name, count, errors) + assert versions is not None, "core.BNGetUpdateChannelVersions returned None" if errors: error_str = errors.value core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) @@ -188,7 +165,6 @@ class UpdateChannel(with_metaclass(_UpdateChannelMetaClass, object)): @property def name(self): - """ """ return self._name @name.setter @@ -197,7 +173,6 @@ class UpdateChannel(with_metaclass(_UpdateChannelMetaClass, object)): @property def description(self): - """ """ return self._description @description.setter @@ -206,7 +181,6 @@ class UpdateChannel(with_metaclass(_UpdateChannelMetaClass, object)): @property def latest_version_num(self): - """ """ return self._latest_version_num @latest_version_num.setter @@ -239,7 +213,6 @@ class UpdateVersion(object): @property def channel(self): - """ """ return self._channel @channel.setter @@ -248,7 +221,6 @@ class UpdateVersion(object): @property def version(self): - """ """ return self._version @version.setter @@ -257,7 +229,6 @@ class UpdateVersion(object): @property def notes(self): - """ """ return self._notes @notes.setter @@ -266,7 +237,6 @@ class UpdateVersion(object): @property def time(self): - """ """ return self._time @time.setter @@ -298,7 +268,7 @@ def get_time_since_last_update_check(): """ ``get_time_since_last_update_check`` returns the time stamp for the last time updates were checked. - :return: time stacmp for last update check + :return: time stamp for last update check :rtype: int """ return core.BNGetTimeSinceLastUpdateCheck() diff --git a/python/variable.py b/python/variable.py new file mode 100644 index 00000000..187d5e3d --- /dev/null +++ b/python/variable.py @@ -0,0 +1,953 @@ +# coding=utf-8 +# Copyright (c) 2015-2021 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, Mapping + +import binaryninja +from . import _binaryninjacore as core +from . import decorators +from .enums import RegisterValueType, VariableSourceType, DeadStoreElimination + + +@decorators.passive +class LookupTableEntry(object): + def __init__(self, from_values:List[int], to_value): + self._from_values = from_values + self._to_value = to_value + + def __repr__(self): + return f"[{', '.join([f'{i:#x}' for i in self.from_values])}] -> {self.to_value:#x}" + + 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) + + def __ne__(self, other): + if not isinstance(other, self.__class__): + return NotImplemented + return not (self == other) + + def __hash__(self): + return hash((self._from_values, self._to_value)) + + @property + def from_values(self): + return self._from_values + + @property + def to_value(self): + return self._to_value + + +@decorators.passive +class RegisterValue(object): + def __init__(self, arch:Optional['binaryninja.architecture.Architecture']=None, + value:core.BNRegisterValue=None, confidence:int=core.max_confidence): + self._is_constant = False + self._value = None + self._arch = None + # self._reg:Optional[Union[binaryninja.architecture.RegisterName, binaryninja.architecture.RegisterIndex]] = None + self._is_constant = False + self._offset = None + if value is None: + self._type = RegisterValueType.UndeterminedValue + else: + assert isinstance(value.value, int), "BNRegisterValue.value isn't an integer" + self._type = RegisterValueType(value.state) + if value.state == RegisterValueType.EntryValue: + self._arch = arch + if arch is not None: + self._value = arch.get_reg_name(binaryninja.architecture.RegisterIndex(value.value)) + else: + self._value = value.value + elif (value.state == RegisterValueType.ConstantValue) or (value.state == RegisterValueType.ConstantPointerValue): + self._value = value.value + self._is_constant = True + elif value.state == RegisterValueType.StackFrameOffset: + self._offset = value.value + elif value.state == RegisterValueType.ImportedAddressValue: + self._value = value.value + self._confidence = confidence + + def __repr__(self): + if self._type == RegisterValueType.EntryValue: + return f"<entry {self._value:s}>" + 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.ReturnAddressValue: + return "<return address>" + if self._type == RegisterValueType.ImportedAddressValue: + return f"<imported address from entry {self._value:#x}>" + return "<undetermined>" + + def __hash__(self): + if self._type in [RegisterValueType.ConstantValue, RegisterValueType.ConstantPointerValue, RegisterValueType.ImportedAddressValue, RegisterValueType.ReturnAddressValue, RegisterValueType.EntryValue]: + return hash(self._value) + elif self._type == RegisterValueType.StackFrameOffset: + return hash(self._offset) + + def __eq__(self, other): + if self._type in [RegisterValueType.ConstantValue, RegisterValueType.ConstantPointerValue, RegisterValueType.ImportedAddressValue, RegisterValueType.ReturnAddressValue] and isinstance(other, int): + 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._value == 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, int): + return self._offset == other + return NotImplemented + + 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 isinstance(self._value, binaryninja.architecture.RegisterName): + if self._arch is None: + raise Exception("Can not convert Variable to API object without an Architecture set") + result._value = self._arch.get_reg_index(self._value) + else: + result._value = self._value + 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(cls): + return RegisterValue() + + @classmethod + def entry_value(cls, arch:'binaryninja.architecture.Architecture', reg:'binaryninja.architecture.RegisterName') -> 'RegisterValue': + result = RegisterValue() + result._type = RegisterValueType.EntryValue + result._arch = arch + result._value = reg + return result + + @staticmethod + def constant(value:int) -> 'RegisterValue': + result = RegisterValue() + result._type = RegisterValueType.ConstantValue + result._value = value + result._is_constant = True + return result + + @staticmethod + def constant_ptr(value:int) -> 'RegisterValue': + result = RegisterValue() + result._type = RegisterValueType.ConstantPointerValue + result._value = value + result._is_constant = True + return result + + @staticmethod + def stack_frame_offset(offset:int) -> 'RegisterValue': + result = RegisterValue() + result._type = RegisterValueType.StackFrameOffset + result._offset = offset + return result + + @staticmethod + def imported_address(value) -> 'RegisterValue': + result = RegisterValue() + result._type = RegisterValueType.ImportedAddressValue + result._value = value + return result + + @staticmethod + def return_address() -> 'RegisterValue': + result = RegisterValue() + result._type = RegisterValueType.ReturnAddressValue + return result + + @property + def is_constant(self) -> bool: + """Boolean for whether the RegisterValue is known to be constant (read-only)""" + return self._is_constant + + @property + def type(self) -> RegisterValueType: + """:class:`~enums.RegisterValueType` (read-only)""" + return self._type + + @property + def arch(self) -> Optional['binaryninja.architecture.Architecture']: + """Architecture where it exists, None otherwise (read-only)""" + return self._arch + + @property + def reg(self) -> 'binaryninja.architecture.RegisterName': + """Register Name where the Architecture exists raises exception otherwise (read-only)""" + if not isinstance(self._value, binaryninja.architecture.RegisterName): + raise Exception("Attempting to access register when property doesn't exist") + return self._value + + @property + def value(self) -> Optional[Union[int, 'binaryninja.architecture.RegisterName']]: + """Value where it exists, None otherwise (read-only)""" + return self._value + + @property + def offset(self) -> Optional[int]: + """Offset where it exists, None otherwise (read-only)""" + return self._offset + + @property + def confidence(self) -> Optional[int]: + """Confidence where it exists, None otherwise (read-only)""" + return self._confidence + + +@decorators.passive +class ValueRange(object): + def __init__(self, start, end, step): + self._start = start + self._end = end + self._step = step + + 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 __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 + + def __contains__(self, other): + if not isinstance(other, int): + return NotImplemented + return other in range(self._start, self._end, self._step) + + @property + def start(self): + return self._start + + @property + def end(self): + return self._end + + @property + def step(self): + return self._step + + +@decorators.passive +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 __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.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 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_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, 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 + result.valueSet = ctypes.cast(values, ctypes.POINTER(ctypes.c_longlong)) + 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 ranges(self) -> List[ValueRange]: + return self._ranges + + @property + def table(self) -> List[LookupTableEntry]: + return self._table + + @property + def mapping(self) -> Mapping[int, int]: + return self._mapping + + @property + def values(self) -> Set[int]: + return self._values + + @property + def count(self) -> int: + return self._count + + @staticmethod + def undetermined() -> 'PossibleValueSet': + """ + Create a PossibleValueSet object of type UndeterminedValue. + + :return: PossibleValueSet object of type UndeterminedValue + :rtype: PossibleValueSet + """ + return PossibleValueSet() + + @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 + + @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 + + @staticmethod + def stack_frame_offset(offset:int) -> 'PossibleValueSet': + """ + Create a PossibleValueSet object for a stack frame offset. + + :param int value: Integer value of the offset + :rtype: PossibleValueSet + """ + result = PossibleValueSet() + result._type = RegisterValueType.StackFrameOffset + result._offset = offset + return result + + @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 + + @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 + + @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 + + @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 + + @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 + + +@decorators.passive +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 __repr__(self): + if self._source_operand is None: + if self._referenced_offset != self._var.storage: + return "<ref to %s%+#x>" % (self._name, self._referenced_offset - self._var.storage) + return "<ref to %s>" % self._name + if self._referenced_offset != self._var.storage: + return "<operand %d ref to %s%+#x>" % (self._source_operand, self._name, self._var.storage) + return "<operand %d ref to %s>" % (self._source_operand, self._name) + + 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 __ne__(self, other): + if not isinstance(other, self.__class__): + return NotImplemented + return not (self == other) + + def __hash__(self): + return hash((self._source_operand, self._type, self._name, self._var, self._referenced_offset, self._size)) + + @property + def source_operand(self): + return self._source_operand + + @property + def type(self): + return self._type + + @property + def name(self): + return self._name + + @property + def var(self): + return self._var + + @property + def referenced_offset(self): + return self._referenced_offset + + @property + def size(self): + return self._size + + +@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 __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 (self.identifier, self.function) == (other.identifier, 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 (self.identifier, self.function) < (other.identifier, other.function) + + def __gt__(self, other): + if not isinstance(other, self.__class__): + return NotImplemented + return (self.identifier, self.function) > (other.identifier, other.function) + + def __le__(self, other): + if not isinstance(other, self.__class__): + return NotImplemented + return (self.identifier, self.function) <= (other.identifier, other.function) + + def __ge__(self, other): + if not isinstance(other, self.__class__): + return NotImplemented + return (self.identifier, self.function) >= (other.identifier, other.function) + + def __hash__(self): + return hash((self.identifier, self.function)) + + @property + def function(self) -> 'binaryninja.function.Function': + """Function where the variable is defined""" + return self._function + + @function.setter + def function(self, value:'binaryninja.function.Function'): + self._function = value + + @property + def source_type(self) -> VariableSourceType: + """:class:`~enums.VariableSourceType`""" + if not isinstance(self._source_type, VariableSourceType): + self._source_type = VariableSourceType(self._source_type) + + return self._source_type + + @source_type.setter + def source_type(self, value:VariableSourceType) -> None: + self._source_type = value + + @property + def index(self) -> int: + return self._index + + @index.setter + def index(self, value:int) -> None: + self._index = value + + @property + def storage(self) -> int: + """Stack offset for StackVariableSourceType, register index for RegisterVariableSourceType""" + return self._storage + + @storage.setter + def storage(self, value:int) -> None: + self._storage = value + + @property + def identifier(self) -> int: + if self._identifier is None: + self._identifier = core.BNToVariableIdentifier(self.to_BNVariable()) + return self._identifier + + @property + def name(self): + """Name of the variable""" + if self._name is None: + if self._function is not None: + self._name = core.BNGetVariableName(self._function.handle, self.to_BNVariable()) + return self._name + + @property + def type(self) -> Optional['binaryninja.types.Type']: + 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 = binaryninja.types.Type(var_type_conf.type, platform = self._function.platform, confidence = var_type_conf.confidence) + return self._type + + def to_BNVariable(self): + v = core.BNVariable() + v.type = self.source_type + v.index = self._index + v.storage = self._storage + return v + + @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 + + @dead_store_elimination.setter + def dead_store_elimination(self, value): + core.BNSetFunctionVariableDeadStoreElimination(self._function.handle, self.to_BNVariable(), value) + + @staticmethod + def from_identifier(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) + +@decorators.passive +class ConstantReference(object): + def __init__(self, val, size, ptr, intermediate): + self._value = val + self._size = size + self._pointer = ptr + self._intermediate = intermediate + + 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) + + @property + def value(self): + return self._value + + @property + def size(self): + return self._size + + @property + def pointer(self): + return self._pointer + + @property + def intermediate(self): + return self._intermediate + + +@decorators.passive +class UserVariableValueInfo(object): + def __init__(self, var, def_site, value): + self.var = var + self.def_site = def_site + self.value = value + + def __repr__(self): + return "<user value for %s @ %s:%#x -> %s>" % (self.var, self.def_site.arch.name, self.def_site.addr, self.value) + + +@decorators.passive +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 + + def __repr__(self): + return "<branch %s:%#x -> %s:%#x>" % (self.source_arch.name, self.source_addr, self.dest_arch.name, self.dest_addr) + + +@decorators.passive +class ParameterVariables(object): + 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 repr(self._vars) + + def __len__(self): + return len(self._vars) + + def __iter__(self) -> Generator['Variable', None, None]: + for var in self._vars: + yield var + + 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 + + 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 + + +@decorators.passive +class AddressRange(object): + def __init__(self, start:int, end:int): + self._start = start + self._end = end + + def __repr__(self): + return "<%#x-%#x>" % (self._start, self._end) + + def __len__(self): + return self._end - self.start + + def __eq__(self, other): + if not isinstance(other, self.__class__): + return NotImplemented + return (self._start, self._end) == (other._start, other._end) + + 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)) + + @property + def length(self) -> int: + return self._end - self._start + + @property + def start(self) -> int: + return self._start + + @property + def end(self) -> int: + return self._end
\ No newline at end of file |
