diff options
Diffstat (limited to 'python/types.py')
| -rw-r--r-- | python/types.py | 2212 |
1 files changed, 1431 insertions, 781 deletions
diff --git a/python/types.py b/python/types.py index d3f6294d..ddaa5384 100644 --- a/python/types.py +++ b/python/types.py @@ -20,19 +20,43 @@ import ctypes from typing import Generator, List, Union, Mapping, Tuple, Optional -from dataclasses import dataclass +from dataclasses import dataclass, field +import uuid +from abc import ABC, abstractmethod # Binary Ninja components from . import _binaryninjacore as core -from .enums import SymbolType, SymbolBinding, TypeClass, NamedTypeReferenceClass, StructureType, ReferenceType, VariableSourceType, TypeReferenceType +from .enums import (StructureVariant, SymbolType, SymbolBinding, TypeClass, + NamedTypeReferenceClass, ReferenceType, VariableSourceType, TypeReferenceType, MemberAccess, MemberScope) from . import callingconvention -from . import function +from . import function as _function from . import variable from . import architecture from . import types +from . import binaryview +from . import platform as _platform from . import log +from . import typelibrary QualifiedNameType = Union[List[str], str, 'QualifiedName', List[bytes]] +BoolWithConfidenceType = Union[bool, 'BoolWithConfidence'] +SizeWithConfidenceType = Union[int, 'SizeWithConfidence'] +OffsetWithConfidenceType = Union[int, 'OffsetWithConfidence'] +ParamsType = Union[List['Type'], List['FunctionParameter'], List[Tuple['Type', str]]] +MembersType = Union[List['StructureMember'], List[Tuple['Type', str]]] +EnumMembersType = Union[List[Tuple[str,int]], List[str], List['EnumerationMember']] +SomeType = Union['MutableType', 'Type'] +TypeContainer = Union['binaryview.BinaryView', 'typelibrary.TypeLibrary'] +# The following are needed to prevent the type checker from getting +# confused as we have member functions in `Type` named the same thing +_int = int +_bool = bool +MemberName = str +MemberIndex = int +MemberOffset = int + +class TypeCreateException(ValueError): + pass class QualifiedName: def __init__(self, name:QualifiedNameType=[]): @@ -139,13 +163,13 @@ class QualifiedName: self._name = value +@dataclass(frozen=True) class TypeReferenceSource: - def __init__(self, name, offset, ref_type): - self._name = name - self._offset = offset - self._ref_type = ref_type + name:QualifiedName + offset:int + ref_type:TypeReferenceType - def __str__(self): + def __repr__(self): if self.ref_type == TypeReferenceType.DirectTypeReferenceType: s = 'direct' elif self.ref_type == TypeReferenceType.IndirectTypeReferenceType: @@ -154,70 +178,6 @@ class TypeReferenceSource: s = 'unknown' return '<type %s, offset 0x%x, %s>' % (self.name, self.offset, s) - def __repr__(self): - return repr(str(self)) - - @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): - if isinstance(other, self.__class__): - return self.name == other.name and self.offset == other.offset and self.ref_type == other.ref_type - return NotImplemented - - def __ne__(self, other): - if isinstance(other, self.__class__): - return not self.__eq__(other) - return NotImplemented - - def __lt__(self, other): - if isinstance(other, self.__class__): - if self.name < other.name: - return True - elif self.name > other.name: - return False - elif self.offset < other.offset: - return True - elif self.offset > other.offset: - return False - return self.ref_type < other.ref_type - return NotImplemented - - def __gt__(self, other): - if isinstance(other, self.__class__): - if self.name > other.name: - return True - elif self.name < other.name: - return False - elif self.offset > other.offset: - return True - elif self.offset < other.offset: - return False - return self.ref_type > other.ref_type - return NotImplemented - - def __cmp__(self, other): - if not isinstance(other, self.__class__): - return NotImplemented - - if self == other: - return 0 - elif self < other: - return -1 - return 1 - - def __hash__(self): - return hash(str(self)) - class NameSpace(QualifiedName): def __str__(self): @@ -278,7 +238,8 @@ class Symbol: self.handle = _handle def __del__(self): - core.BNFreeSymbol(self.handle) + if core is not None: + core.BNFreeSymbol(self.handle) def __repr__(self): return "<%s: \"%s\" @ %#x>" % (self.type, self.full_name, self.address) @@ -348,16 +309,172 @@ class Symbol: def auto(self): return core.BNIsSymbolAutoDefined(self.handle) -@dataclass(frozen=True) + +@dataclass class FunctionParameter: - type:'types.Type' + type:SomeType name:str = "" location:Optional['variable.VariableNameAndType'] = None def __repr__(self): if (self.location is not None) and (self.location.name != self.name): - return "%s %s%s @ %s" % (self.type.get_string_before_name(), self.name, self.type.get_string_after_name(), self.location.name) - return "%s %s%s" % (self.type.get_string_before_name(), self.name, self.type.get_string_after_name()) + return "%s %s%s @ %s" % (self.type.immutable_copy().get_string_before_name(), self.name, self.type.immutable_copy().get_string_after_name(), self.location.name) + return "%s %s%s" % (self.type.immutable_copy().get_string_before_name(), self.name, self.type.immutable_copy().get_string_after_name()) + + def immutable_copy(self) -> 'FunctionParameter': + return FunctionParameter(self.type.immutable_copy(), self.name, self.location) + + def mutable_copy(self) -> 'FunctionParameter': + return FunctionParameter(self.type.mutable_copy(), self.name, self.location) + + +@dataclass(frozen=True) +class OffsetWithConfidence: + value:int + confidence:int=core.max_confidence + + def __int__(self): + return self.value + + def to_core_struct(self) -> core.BNOffsetWithConfidence: + result = core.BNOffsetWithConfidence() + result.value = self.value + result.confidence = self.confidence + return result + + @classmethod + def from_core_struct(cls, core_struct:core.BNOffsetWithConfidence) -> 'OffsetWithConfidence': + return cls(core_struct.value, core_struct.confidence) + + +@dataclass(frozen=True) +class BoolWithConfidence: + value:bool + confidence:int=core.max_confidence + + def __bool__(self): + return self.value + + def to_core_struct(self) -> core.BNBoolWithConfidence: + result = core.BNBoolWithConfidence() + result.value = self.value + result.confidence = self.confidence + return result + + @classmethod + def from_core_struct(cls, core_struct:core.BNBoolWithConfidence) -> 'BoolWithConfidence': + return cls(core_struct.value, core_struct.confidence) + + +@dataclass(frozen=True) +class MemberAccessWithConfidence: + value:MemberAccess + confidence:int=core.max_confidence + + def to_core_struct(self) -> core.BNMemberAccessWithConfidence: + result = core.BNMemberAccessWithConfidence() + result.value = self.value + result.confidence = self.confidence + return result + + @classmethod + def from_core_struct(cls, core_struct:core.BNMemberAccessWithConfidence) -> 'MemberAccessWithConfidence': + return cls(core_struct.value, core_struct.confidence) + + +@dataclass(frozen=True) +class MemberScopeWithConfidence: + value:MemberScope + confidence:int=core.max_confidence + + def to_core_struct(self) -> core.BNMemberScopeWithConfidence: + result = core.BNMemberScopeWithConfidence() + result.value = self.value + result.confidence = self.confidence + return result + + @classmethod + def from_core_struct(cls, core_struct:core.BNMemberScopeWithConfidence) -> 'MemberScopeWithConfidence': + return cls(core_struct.value, core_struct.confidence) + + +@dataclass(frozen=True) +class SizeWithConfidence: + value:int + confidence:int=core.max_confidence + + def __int__(self): + return self.value + + def to_core_struct(self) -> core.BNSizeWithConfidence: + result = core.BNSizeWithConfidence() + result.value = self.value + result.confidence = self.confidence + return result + + @classmethod + def from_core_struct(cls, core_struct:core.BNSizeWithConfidence) -> 'SizeWithConfidence': + return cls(core_struct.value, core_struct.confidence) + + +class _TypeBuilder: + def __init__(self, handle, **kwargs): + assert isinstance(handle.contents, core.BNTypeBuilder), "Attempting to create mutable Type" + self.handle = handle + core.BNTypeBuilderSetConst(self.handle, kwargs.get("const", BoolWithConfidence(False)).to_core_struct()) + core.BNTypeBuilderSetVolatile(self.handle, kwargs.get("volatile", BoolWithConfidence(False)).to_core_struct()) + core.BNSetFunctionTypeBuilderCanReturn(self.handle, kwargs.get("can_return", BoolWithConfidence(False).to_core_struct())) + core.BNTypeBuilderSetMemberScope(self.handle, kwargs.get("member_scope", MemberScopeWithConfidence(MemberScope.NoScope).to_core_struct())) + core.BNTypeBuilderSetMemberAccess(self.handle, kwargs.get("member_access", MemberAccessWithConfidence(MemberAccess.NoAccess).to_core_struct())) + + def finalize(self): + type_handle = core.BNFinalizeTypeBuilder(self.handle) + assert type_handle is not None, "core.BNFinalizeTypeBuilder returned None" + return type_handle + + @property + def const(self) -> BoolWithConfidence: + """Whether type is const (read/write)""" + result = core.BNIsTypeBuilderConst(self.handle) + return BoolWithConfidence(result.value, confidence = result.confidence) + + @const.setter + def const(self, value:BoolWithConfidence) -> None: + core.BNTypeBuilderSetConst(self.handle, value.to_core_struct()) + + @property + def volatile(self) -> BoolWithConfidence: + """Whether type is volatile (read/write)""" + result = core.BNIsTypeBuilderVolatile(self.handle) + return BoolWithConfidence(result.value, confidence = result.confidence) + + @volatile.setter + def volatile(self, value:BoolWithConfidence) -> None: + core.BNTypeBuilderSetVolatile(self.handle, value.to_core_struct()) + + @property + def can_return(self): + core.BNFunctionTypeBuilderCanReturn(self.handle) + + @can_return.setter + def can_return(self, value): + core.BNSetFunctionTypeBuilderCanReturn(self.handle, value) + + @property + def member_scope(self): + core.BNTypeBuilderGetMemberScope(self.handle) + + @member_scope.setter + def member_scope(self, value): + core.BNTypeBuilderSetMemberScope(self.handle, value) + + @property + def member_access(self): + core.BNTypeBuilderGetMemberAccess(self.handle) + + @member_access.setter + def member_access(self, value): + core.BNTypeBuilderSetMemberAccess(self.handle, value) class Type: @@ -372,33 +489,39 @@ class Type: :py:meth:`parse_types_from_source_file <binaryninja.platform.Platform.parse_types_from_source_file>` """ - def __init__(self, handle, platform = None, confidence = core.max_confidence): + def __init__(self, handle, platform:'_platform.Platform'=None, confidence:int=core.max_confidence): + assert isinstance(handle.contents, core.BNType), "Attempting to create mutable Type" self._handle = handle - self._mutable = isinstance(handle.contents, core.BNTypeBuilder) self._confidence = confidence self._platform = platform + @classmethod + def create(cls, handle, platform:'_platform.Platform'=None, confidence:int=core.max_confidence): + type_class = TypeClass(core.BNGetTypeClass(handle)) + try: + return Types[type_class](handle, platform, confidence) + except TypeError: + assert False, f"{str(type_class)}" + def __del__(self): - if self._mutable: - core.BNFreeTypeBuilder(self._handle) - else: + if core is not None: core.BNFreeType(self._handle) def __repr__(self): if self._confidence < core.max_confidence: - return "<type: %s, %d%% confidence>" % (str(self), (self._confidence * 100) // core.max_confidence) - return "<type: %s>" % str(self) + return f"<type: {self}, {self._confidence * 100 // core.max_confidence}% confidence>" + return f"<type: {self}>" def __str__(self): platform = None if self._platform is not None: platform = self._platform.handle - if self._mutable: - return core.BNGetTypeBuilderString(self._handle, platform) - name = self.registered_name - if (name is not None) and (self.type_class != TypeClass.StructureTypeClass) and (self.type_class != TypeClass.EnumerationTypeClass): - return self.get_string_before_name() + " " + str(name.name) + self.get_string_after_name() return core.BNGetTypeString(self._handle, platform) + name = None + if isinstance(self, RegisteredNameType): + name = self.registered_name + if (name is not None) and (not isinstance(self, (StructureType, EnumerationType))): + return self.get_string_before_name() + " " + str(name.name) + self.get_string_after_name() def __len__(self): return self.width @@ -415,711 +538,606 @@ class Type: @property def handle(self): - if self._mutable: - # First use of a mutable Type makes it immutable - finalized = core.BNFinalizeTypeBuilder(self._handle) - core.BNFreeTypeBuilder(self._handle) - self._handle = finalized - self._mutable = False return self._handle @property - def type_class(self): + def type_class(self) -> TypeClass: """Type class (read-only)""" - if self._mutable: - return TypeClass(core.BNGetTypeBuilderClass(self._handle)) return TypeClass(core.BNGetTypeClass(self._handle)) @property - def width(self): + def width(self) -> int: """Type width (read-only)""" - if self._mutable: - return core.BNGetTypeBuilderWidth(self._handle) return core.BNGetTypeWidth(self._handle) @property - def alignment(self): + def alignment(self) -> int: """Type alignment (read-only)""" - if self._mutable: - return core.BNGetTypeBuilderAlignment(self._handle) return core.BNGetTypeAlignment(self._handle) @property - def signed(self): - """Whether type is signed (read-only)""" - if self._mutable: - result = core.BNIsTypeBuilderSigned(self._handle) - else: - result = core.BNIsTypeSigned(self._handle) - return BoolWithConfidence(result.value, confidence = result.confidence) - - @property - def const(self): - """Whether type is const (read/write)""" - if self._mutable: - result = core.BNIsTypeBuilderConst(self._handle) - else: - result = core.BNIsTypeConst(self._handle) - return BoolWithConfidence(result.value, confidence = result.confidence) - - @const.setter - def const(self, value): - if not self._mutable: - raise AttributeError("Finalized Type object is immutable, use mutable_copy()") - bc = core.BNBoolWithConfidence() - bc.value = bool(value) - if hasattr(value, 'confidence'): - bc.confidence = value.confidence - else: - bc.confidence = core.max_confidence - core.BNTypeBuilderSetConst(self._handle, bc) - - @property - def volatile(self): - """Whether type is volatile (read/write)""" - if self._mutable: - result = core.BNIsTypeBuilderVolatile(self._handle) - else: - result = core.BNIsTypeVolatile(self._handle) - return BoolWithConfidence(result.value, confidence = result.confidence) - - @volatile.setter - def volatile(self, value): - if not self._mutable: - raise AttributeError("Finalized Type object is immutable, use mutable_copy()") - bc = core.BNBoolWithConfidence() - bc.value = bool(value) - if hasattr(value, 'confidence'): - bc.confidence = value.confidence - else: - bc.confidence = core.max_confidence - core.BNTypeBuilderSetVolatile(self._handle, bc) - - @property - def floating_point(self): - """Whether type is floating point (read-only)""" - if self._mutable: - return core.BNIsTypeBuilderFloatingPoint(self._handle) - return core.BNIsTypeFloatingPoint(self._handle) - - @property - def target(self): - """Target (read-only)""" - if self._mutable: - result = core.BNGetTypeBuilderChildType(self._handle) - else: - result = core.BNGetChildType(self._handle) - if not result.type: - return None - return Type(result.type, platform = self._platform, confidence = result.confidence) - - @property - def element_type(self): - """Target (read-only)""" - if self._mutable: - result = core.BNGetTypeBuilderChildType(self._handle) - else: - result = core.BNGetChildType(self._handle) - if not result.type: - return None - return Type(result.type, platform = self._platform, confidence = result.confidence) - - @property - def return_value(self): - """Return value (read-only)""" - if self._mutable: - result = core.BNGetTypeBuilderChildType(self._handle) - else: - result = core.BNGetChildType(self._handle) - if not result.type: - return None - return Type(result.type, platform = self._platform, confidence = result.confidence) - - @property - def calling_convention(self): - """Calling convention (read-only)""" - if self._mutable: - result = core.BNGetTypeBuilderCallingConvention(self._handle) - else: - result = core.BNGetTypeCallingConvention(self._handle) - if not result.convention: - return None - return callingconvention.CallingConvention(None, handle = result.convention, confidence = result.confidence) - - @property - def parameters(self): - """Type parameters list (read-only)""" - 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) - if params[i].defaultLocation: - param_location = None - else: - name = params[i].name - if (params[i].location.type == VariableSourceType.RegisterVariableSourceType) and (self._platform is not None): - name = self._platform.arch.get_reg_name(params[i].location.storage) - elif params[i].location.type == VariableSourceType.StackVariableSourceType: - name = "arg_%x" % params[i].location.storage - param_location = variable.VariableNameAndType(params[i].location.type, params[i].location.index, - params[i].location.storage, name, param_type) - result.append(FunctionParameter(param_type, params[i].name, param_location)) - core.BNFreeTypeParameterList(params, count.value) - return result - - @property - def has_variable_arguments(self): - """Whether type has variable arguments (read-only)""" - if self._mutable: - result = core.BNTypeBuilderHasVariableArguments(self._handle) - else: - result = core.BNTypeHasVariableArguments(self._handle) - return BoolWithConfidence(result.value, confidence = result.confidence) - - @property - def can_return(self): - """Whether type can return""" - if self._mutable: - result = core.BNFunctionTypeBuilderCanReturn(self._handle) - else: - result = core.BNFunctionTypeCanReturn(self._handle) - return BoolWithConfidence(result.value, confidence = result.confidence) - - @can_return.setter - def can_return(self, value): - """Whether type can return (read-only)""" - if not self._mutable: - raise AttributeError("Finalized Type object is immutable, use mutable_copy()") - bc = core.BNBoolWithConfidence() - bc.value = bool(value) - if hasattr(value, 'confidence'): - bc.confidence = value.confidence - else: - bc.confidence = core.max_confidence - core.BNSetFunctionTypeBuilderCanReturn(self._handle, bc) - - @property - def structure(self): - """Structure of the type (read-only)""" - if self._mutable: - result = core.BNGetTypeBuilderStructure(self._handle) - else: - result = core.BNGetTypeStructure(self._handle) - if result is None: - return None - return Structure(result) - - @property - def enumeration(self): - """Type enumeration (read-only)""" - if self._mutable: - result = core.BNGetTypeBuilderEnumeration(self._handle) - else: - result = core.BNGetTypeEnumeration(self._handle) - if result is None: - return None - return Enumeration(result) - - @property - def named_type_reference(self): - """Reference to a named type (read-only)""" - if self._mutable: - result = core.BNGetTypeBuilderNamedTypeReference(self._handle) - else: - result = core.BNGetTypeNamedTypeReference(self._handle) - if result is None: - return None - return NamedTypeReference(handle = result) - - @property - def count(self): - """Type count (read-only)""" - if self._mutable: - return core.BNGetTypeBuilderElementCount(self._handle) - return core.BNGetTypeElementCount(self._handle) - - @property - def offset(self): + def offset(self) -> int: """Offset into structure (read-only)""" - if self._mutable: - return core.BNGetTypeBuilderOffset(self._handle) return core.BNGetTypeOffset(self._handle) @property - def stack_adjustment(self): - """Stack adjustment for function (read-only)""" - if self._mutable: - result = core.BNGetTypeBuilderStackAdjustment(self._handle) - else: - result = core.BNGetTypeStackAdjustment(self._handle) - return SizeWithConfidence(result.value, confidence = result.confidence) + def altname(self) -> str: + """Alternative name for the type object""" + return core.BNGetTypeAlternateName(self._handle) - @property - def registered_name(self): - """Name of type registered to binary view, if any (read-only)""" - if self._mutable: - return None - name = core.BNGetRegisteredTypeName(self._handle) - if not name: - return None - return NamedTypeReference(handle = name) + def to_core_struct(self) -> core.BNTypeWithConfidence: + type_conf = core.BNTypeWithConfidence() + type_conf.type = self.handle + type_conf.confidence = self.confidence + return type_conf - def get_string_before_name(self): + def get_string_before_name(self) -> str: platform = None if self._platform is not None: platform = self._platform.handle - if self._mutable: - return core.BNGetTypeBuilderStringBeforeName(self._handle, platform) return core.BNGetTypeStringBeforeName(self._handle, platform) - def get_string_after_name(self): + def get_string_after_name(self) -> str: platform = None if self._platform is not None: platform = self._platform.handle - if self._mutable: - return core.BNGetTypeBuilderStringAfterName(self._handle, platform) return core.BNGetTypeStringAfterName(self._handle, platform) @property - def tokens(self): + def tokens(self) -> List['_function.InstructionTextToken']: """Type string as a list of tokens (read-only)""" return self.get_tokens() - def get_tokens(self, base_confidence = core.max_confidence): + def get_tokens(self, base_confidence = core.max_confidence) -> List['_function.InstructionTextToken']: 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) - assert tokens is not None, "core.BNGetTypeTokens returned None" + tokens = core.BNGetTypeTokens(self._handle, platform, base_confidence, count) + assert tokens is not None, "core.BNGetTypeTokens returned None" - result = function.InstructionTextToken._from_core_struct(tokens, count.value) + result = _function.InstructionTextToken._from_core_struct(tokens, count.value) core.BNFreeInstructionText(tokens, count.value) return result - def get_tokens_before_name(self, base_confidence = core.max_confidence): + def get_tokens_before_name(self, base_confidence = core.max_confidence) -> List['_function.InstructionTextToken']: 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) - assert tokens is not None, "core.BNGetTypeTokensBeforeName returned None" - result = function.InstructionTextToken._from_core_struct(tokens, count.value) + tokens = core.BNGetTypeTokensBeforeName(self._handle, platform, base_confidence, count) + 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 = core.max_confidence): + def get_tokens_after_name(self, base_confidence = core.max_confidence) -> List['_function.InstructionTextToken']: 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) - assert tokens is not None, "core.BNGetTypeTokensAfterName returned None" - result = function.InstructionTextToken._from_core_struct(tokens, count.value) + tokens = core.BNGetTypeTokensAfterName(self._handle, platform, base_confidence, count) + 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 + def with_confidence(self, confidence) -> 'Type': + return Type.create(handle = core.BNNewTypeReference(self.handle), platform = self._platform, confidence = confidence) + + @property + def confidence(self) -> _int: + return self._confidence + + @confidence.setter + def confidence(self, value:_int) -> None: + self._confidence = value + + @property + def platform(self) -> Optional['_platform.Platform']: + return self._platform + + @platform.setter + def platform(self, value:'_platform.Platform') -> None: + self._platform = value + + @abstractmethod + def create_mutable(self): + raise NotImplementedError("No implementation for create_mutable on base class Type") + + def mutable_copy(self) -> 'MutableType': + return self.create_mutable() + + def get_builder(self, bv:'binaryview.BinaryView') -> 'MutableTypeBuilder': + t = Types[self.type_class](self._handle).create_mutable() + return MutableTypeBuilder(t, bv, self.name, self.platform, self._confidence) + + @staticmethod + def builder(bv:'binaryview.BinaryView', name:Optional[QualifiedName]=None, id:Optional[str]=None, + platform:'_platform.Platform'=None, confidence:int=core.max_confidence) -> 'MutableTypeBuilder': + if name is None and id is None: + raise TypeCreateException("Must specify either a name or id to create a builder object") + if name is None: + t = bv.get_type_by_id(id) + if t is None: + raise TypeCreateException("failed to look up type by id") + registered_name = t.registered_name + if registered_name is None: + raise TypeCreateException("Registered name for type is None") + name = registered_name.name + if name is None: + raise TypeCreateException("Name for registered name is None") + else: + t = bv.get_type_by_name(name) + if t is None: + raise TypeCreateException("failed to look up type by name") + return MutableTypeBuilder(t, bv, name, platform, confidence) + + def with_replaced_structure(self, from_struct, to_struct): + return Type.create(handle = core.BNTypeWithReplacedStructure(self._handle, from_struct.handle, to_struct.handle)) + + def with_replaced_enumeration(self, from_enum, to_enum): + return Type.create(handle = core.BNTypeWithReplacedEnumeration(self._handle, from_enum.handle, to_enum.handle)) + + def with_replaced_named_type_reference(self, from_ref, to_ref): + return Type.create(handle = core.BNTypeWithReplacedNamedTypeReference(self._handle, from_ref.handle, to_ref.handle)) + @staticmethod def void(): - return Type(core.BNCreateVoidTypeBuilder()) + return VoidType.create() @staticmethod def bool(): - return Type(core.BNCreateBoolTypeBuilder()) + return BoolType.create() @staticmethod def char(): return Type.int(1, True) @staticmethod - def int(width, sign = None, altname=""): + def int(width:_int, sign:BoolWithConfidenceType=BoolWithConfidence(True), altname:str="") -> 'IntegerType': """ ``int`` class method for creating an int Type. - :param int width: width of the integer in bytes :param bool sign: optional variable representing signedness :param str altname: alternate name for type """ - if sign is None: - sign = BoolWithConfidence(True, confidence = 0) - elif not isinstance(sign, BoolWithConfidence): - sign = BoolWithConfidence(sign) - - sign_conf = core.BNBoolWithConfidence() - sign_conf.value = sign.value - sign_conf.confidence = sign.confidence - - return Type(core.BNCreateIntegerTypeBuilder(width, sign_conf, altname)) + return IntegerType.create(width, sign, altname) @staticmethod - def float(width, altname=""): + def float(width:_int, altname:str="") -> 'FloatType': """ ``float`` class method for creating floating point Types. - :param int width: width of the floating point number in bytes :param str altname: alternate name for type """ - return Type(core.BNCreateFloatTypeBuilder(width, altname)) + return FloatType.create(width, altname) @staticmethod - def wide_char(width, altname=""): + def wide_char(width:_int, altname:str="") -> 'WideCharType': """ ``wide_char`` class method for creating wide char Types. - :param int width: width of the wide character in bytes :param str altname: alternate name for type """ - return Type(core.BNCreateWideCharTypeBuilder(width, altname)) + return WideCharType.create(width=width, altname=altname) @staticmethod - def structure_type(structure_type): - return Type(core.BNCreateStructureTypeBuilder(structure_type.handle)) + def structure_type(s:'Structure'): + return s.immutable_copy() @staticmethod - def named_type(named_type, width = 0, align = 1): - return Type(core.BNCreateNamedTypeReferenceBuilder(named_type.handle, width, align)) + def named_type(named_type:'NamedTypeReference') -> 'NamedTypeReferenceType': + return named_type.immutable_copy() @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)) + def named_type_from_type(name:QualifiedName, type:'Type') -> 'NamedTypeReferenceType': + return NamedTypeReferenceType.create_from_type(name, type) @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)) + def named_type_from_type_and_id(type_id:str, name:QualifiedName, type:'Type') -> 'NamedTypeReferenceType': + return NamedTypeReferenceType.create_from_type(name, type, type_id) - @staticmethod - def named_type_from_registered_type(view, name): - name = QualifiedName(name)._get_core_struct() - return Type(core.BNCreateNamedTypeReferenceBuilderFromType(view.handle, name)) + def generate_named_type_reference(self, guid:str, name:QualifiedName): + return NamedTypeReferenceType.create(NamedTypeReferenceClass.TypedefNamedTypeClass, guid, name) @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)) + def named_type_from_registered_type(view:'binaryview.BinaryView', name:QualifiedName) -> 'NamedTypeReferenceType': + return NamedTypeReferenceType.create_from_registered_type(view, name) @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): - const = BoolWithConfidence(const) + def enumeration_type(arch, e:'Enumeration', width:_int=None, sign:_bool=False) -> 'EnumerationType': + return EnumerationType.create(arch, e.members, e.width, e.sign) - if volatile is None: - volatile = BoolWithConfidence(False, confidence = 0) - elif not isinstance(volatile, BoolWithConfidence): - volatile = BoolWithConfidence(volatile) - - if ref_type is None: - ref_type = ReferenceType.PointerReferenceType - - type_conf = core.BNTypeWithConfidence() - type_conf.type = t.handle - type_conf.confidence = t.confidence - - const_conf = core.BNBoolWithConfidence() - const_conf.value = const.value - const_conf.confidence = const.confidence + @staticmethod + def pointer(type:'Type', arch:'architecture.Architecture'=None, + const:BoolWithConfidenceType=BoolWithConfidence(False), + volatile:BoolWithConfidenceType=BoolWithConfidence(False), + ref_type:ReferenceType=ReferenceType.PointerReferenceType, width:_int=None) -> 'PointerType': - volatile_conf = core.BNBoolWithConfidence() - volatile_conf.value = volatile.value - volatile_conf.confidence = volatile.confidence + if arch is not None: + width = arch.address_size + if width is None: + raise TypeCreateException("Must specify either an architecture or a width to create a pointer") - return Type(core.BNCreatePointerTypeBuilder(arch.handle, type_conf, const_conf, volatile_conf, ref_type)) + return PointerType.create_with_width(width, type, const, volatile, ref_type) @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)) + def array(type:'Type', count:_int) -> 'ArrayType': + return ArrayType.create(type, count) @staticmethod - def function(ret, params, calling_convention=None, variable_arguments=None, stack_adjust=None): + def function(ret:Optional['Type'], params:ParamsType=[], calling_convention:'callingconvention.CallingConvention'=None, + variable_arguments:BoolWithConfidenceType=BoolWithConfidence(False), + stack_adjust:OffsetWithConfidence=OffsetWithConfidence(0)) -> 'FunctionType': """ ``function`` class method for creating an function Type. - :param Type ret: return Type of the function :param params: list of parameter Types :type params: list(Type) :param CallingConvention calling_convention: optional argument for the function calling convention :param bool variable_arguments: optional boolean, true if the function has a variable number of arguments """ - param_buf = (core.BNFunctionParameter * len(params))() - for i in range(0, len(params)): - if isinstance(params[i], Type): - param_buf[i].name = "" - param_buf[i].type = params[i].handle - param_buf[i].typeConfidence = params[i].confidence - param_buf[i].defaultLocation = True - elif isinstance(params[i], FunctionParameter): - param_buf[i].name = params[i].name - param_buf[i].type = params[i].type.handle - param_buf[i].typeConfidence = params[i].type.confidence - if params[i].location is None: - param_buf[i].defaultLocation = True - else: - param_buf[i].defaultLocation = False - param_buf[i].location.type = params[i].location.source_type - param_buf[i].location.index = params[i].location.index - param_buf[i].location.storage = params[i].location.storage - else: - param_buf[i].name = params[i][1] - param_buf[i].type = params[i][0].handle - param_buf[i].typeConfidence = params[i][0].confidence - param_buf[i].defaultLocation = True - - ret_conf = core.BNTypeWithConfidence() - ret_conf.type = ret.handle - ret_conf.confidence = ret.confidence - - conv_conf = core.BNCallingConventionWithConfidence() - if calling_convention is None: - conv_conf.convention = None - conv_conf.confidence = 0 - else: - conv_conf.convention = calling_convention.handle - conv_conf.confidence = calling_convention.confidence + return FunctionType.create(ret, params, calling_convention, variable_arguments, stack_adjust) - if variable_arguments is None: - variable_arguments = BoolWithConfidence(False, confidence = 0) - elif not isinstance(variable_arguments, BoolWithConfidence): - variable_arguments = BoolWithConfidence(variable_arguments) + @staticmethod + def from_core_struct(core_type:core.BNType): + return Type.create(core.BNNewTypeReference(core_type)) - vararg_conf = core.BNBoolWithConfidence() - vararg_conf.value = variable_arguments.value - vararg_conf.confidence = variable_arguments.confidence + @staticmethod + def structure(members:MembersType=[], packed:_bool=False, type:StructureVariant=StructureVariant.StructStructureType) -> 'StructureType': + return StructureType.create(members, packed, type) - if stack_adjust is None: - stack_adjust = SizeWithConfidence(0, confidence = 0) - elif not isinstance(stack_adjust, SizeWithConfidence): - stack_adjust = SizeWithConfidence(stack_adjust) + @staticmethod + def enumeration(arch:Optional['architecture.Architecture'], members:EnumMembersType=[], + width:Optional[_int]=None, sign:BoolWithConfidenceType=BoolWithConfidence(False)) -> 'EnumerationType': + return EnumerationType.create(arch, members, width, sign) - stack_adjust_conf = core.BNOffsetWithConfidence() - stack_adjust_conf.value = stack_adjust.value - stack_adjust_conf.confidence = stack_adjust.confidence + @staticmethod + def named_type_reference(name:QualifiedName, type:Optional['Type']=None, guid:Optional[str]=None): + """ + Deprecated property kept for backward compability. + These operations can now be done directly on the Type object. + """ + return NamedTypeReferenceType.create_from_type(name, type, guid) - return Type(core.BNCreateFunctionTypeBuilder(ret_conf, conv_conf, param_buf, len(params), - vararg_conf, stack_adjust_conf)) + @property + @abstractmethod + def name(self) -> QualifiedName: + raise NotImplementedError("Name not implemented for this type") @staticmethod - def generate_auto_type_id(source, name): - name = QualifiedName(name)._get_core_struct() - return core.BNGenerateAutoTypeId(source, name) + def generate_auto_type_id(source, name:str) -> str: + _name = QualifiedName(name)._get_core_struct() + return core.BNGenerateAutoTypeId(source, _name) @staticmethod - def generate_auto_demangled_type_id(name): - name = QualifiedName(name)._get_core_struct() - return core.BNGenerateAutoDemangledTypeId(name) + def generate_auto_demangled_type_id(name:str) -> str: + _name = QualifiedName(name)._get_core_struct() + return core.BNGenerateAutoDemangledTypeId(_name) @staticmethod - def get_auto_demangled_type_id_source(): + def get_auto_demangled_type_id_source() -> str: return core.BNGetAutoDemangledTypeIdSource() - def with_confidence(self, confidence): - return Type(handle = core.BNNewTypeReference(self.handle), platform = self._platform, confidence = confidence) + def immutable_copy(self) -> 'Type': + return self - @property - def confidence(self): - return self._confidence - @confidence.setter - def confidence(self, value): - self._confidence = value +@dataclass(frozen=True) +class RegisterStackAdjustmentWithConfidence: + value:int + confidence:int=core.max_confidence + def __int__(self): + return self.value + + +class RegisteredNameType(Type): @property - def platform(self): - return self._platform + def registered_name(self) -> Optional['NamedTypeReferenceType']: + """Name of type registered to binary view, if any (read-only)""" + # assert self.handle is not None, "RegisteredNameType.handle is None" + # assert False, f"{str(self.type_class)}" + ntr_handle = core.BNGetRegisteredTypeName(self.handle) + if ntr_handle is None: + return None + # assert ntr_handle is not None, "core.BNGetRegisteredTypeName returned None" + return NamedTypeReferenceType(self.handle, self.platform, self.confidence, ntr_handle) - @platform.setter - def platform(self, value): - self._platform = value + # @property + # def name(self) -> Optional[QualifiedName]: + # registered_name = self.registered_name + # if registered_name is None: + # return None + # return registered_name.name - def mutable_copy(self): - if self._mutable: - return Type(core.BNDuplicateTypeBuilder(self._handle), confidence = self._confidence) - return Type(core.BNCreateTypeBuilderFromType(self._handle), confidence = self._confidence) - def with_replaced_structure(self, from_struct, to_struct): - return Type(handle = core.BNTypeWithReplacedStructure(self._handle, from_struct.handle, to_struct.handle)) +@dataclass +class MutableTypeBuilder: + type:'MutableType' + container:TypeContainer + name:QualifiedName + platform:Optional['_platform.Platform'] + confidence:int + user:bool = True - def with_replaced_enumeration(self, from_enum, to_enum): - return Type(handle = core.BNTypeWithReplacedEnumeration(self._handle, from_enum.handle, to_enum.handle)) + def __enter__(self): + return self.type - def with_replaced_named_type_reference(self, from_ref, to_ref): - return Type(handle = core.BNTypeWithReplacedNamedTypeReference(self._handle, from_ref.handle, to_ref.handle)) + def __exit__(self, type, value, traceback): + if isinstance(self.container, binaryview.BinaryView): + if self.user: + self.container.define_user_type(self.name, self.type.immutable_copy(self.platform, self.confidence)) + else: + type_id = types.Type.generate_auto_type_id(str(uuid.uuid4()), str(self.name)) + self.container.define_type(type_id, self.name, self.type.immutable_copy(self.platform, self.confidence)) + else: + self.container.add_named_type(self.name, self.type.immutable_copy()) -@dataclass(frozen=True) -class BoolWithConfidence: - value:bool - confidence:int=core.max_confidence +@dataclass +class MutableType(ABC): + @abstractmethod + def immutable_copy(self, platform:'_platform.Platform'=None, confidence:int=core.max_confidence) -> Type: + return NotImplemented - def __bool__(self): - return self.value + def builder(self, container:TypeContainer, name:'QualifiedName', user:bool=True, platform:'_platform.Platform'=None, confidence:int=core.max_confidence) -> MutableTypeBuilder: + return MutableTypeBuilder(self, container, name, platform, confidence, user) + @abstractmethod + def __len__(self): + return NotImplemented -@dataclass(frozen=True) -class SizeWithConfidence: - value:int - confidence:int=core.max_confidence + def mutable_copy(self) -> 'MutableType': + return self - def __int__(self): - return self.value +@dataclass +class CVQualified(MutableType): + const:BoolWithConfidence=BoolWithConfidence(False) + volatile:BoolWithConfidence=BoolWithConfidence(False) -@dataclass(frozen=True) -class RegisterStackAdjustmentWithConfidence: - value:int - confidence:int=core.max_confidence +class CVQualifiedType(Type): + @property + def const(self): + """Whether type is const (read/write)""" + result = core.BNIsTypeConst(self._handle) + return BoolWithConfidence(result.value, confidence = result.confidence) - def __int__(self): - return self.value + @property + def volatile(self): + """Whether type is volatile (read/write)""" + result = core.BNIsTypeVolatile(self._handle) + return BoolWithConfidence(result.value, confidence = result.confidence) + @staticmethod + def from_bools(const:BoolWithConfidenceType, volatile:BoolWithConfidenceType) -> Tuple[BoolWithConfidence, BoolWithConfidence]: + _const = const + if const is None: + _const = BoolWithConfidence(False, confidence = 0) + elif isinstance(const, bool): + _const = BoolWithConfidence(const) + if not isinstance(_const, BoolWithConfidence): + raise ValueError(f"unhandled type {type(const)} for 'const' argument") -@dataclass(frozen=True) -class RegisterSet: - regs:List['architecture.RegisterName'] - confidence:int=core.max_confidence + _volatile = volatile + if volatile is None: + _volatile = BoolWithConfidence(False, confidence = 0) + elif isinstance(volatile, bool): + _volatile = BoolWithConfidence(volatile) + if not isinstance(_volatile, BoolWithConfidence): + raise ValueError(f"unhandled type {type(volatile)} for 'volatile' argument") - def __iter__(self) -> Generator['architecture.RegisterName', None, None]: - for reg in self.regs: - yield reg + return (_const, _volatile) - def __getitem__(self, idx): - return self.regs[idx] + +class PointerLike(CVQualifiedType): + @property + def target(self) -> Type: + """Target (read-only)""" + result = core.BNGetChildType(self._handle) + assert result is not None, "core.BNGetChildType returned None" + return Type.create(core.BNNewTypeReference(result.type), self._platform, result.confidence) + +@dataclass +class Void(MutableType): + def immutable_copy(self, platform:'_platform.Platform'=None, confidence:int=core.max_confidence) -> 'VoidType': + return VoidType.create(platform, confidence) def __len__(self): - return len(self.regs) + return 0 - def with_confidence(self, confidence): - return RegisterSet(list(self.regs), confidence=confidence) +class VoidType(Type): + @classmethod + def create(cls, platform:'_platform.Platform'=None, confidence:int=core.max_confidence) -> 'VoidType': + core_void = core.BNCreateVoidType() + assert core_void is not None, "core.BNCreateVoidType returned None" + return cls(core.BNNewTypeReference(core_void), platform, confidence) + def create_mutable(self) -> 'Void': + return Void() -class NamedTypeReference: - def __init__(self, type_class = NamedTypeReferenceClass.UnknownNamedTypeClass, type_id = None, name = None, handle = None): - if handle is None: - if name is not None: - name = QualifiedName(name)._get_core_struct() - _handle = core.BNCreateNamedType(type_class, type_id, name) - else: - _handle = handle - assert _handle is not None - self.handle = _handle - def __del__(self): - core.BNFreeNamedTypeReference(self.handle) +@dataclass +class Integer(MutableType): + width:int + altname:str="" + signed:BoolWithConfidence=BoolWithConfidence(True) - def __repr__(self): - if self.type_class == NamedTypeReferenceClass.TypedefNamedTypeClass: - return "<named type: typedef %s>" % str(self.name) - if self.type_class == NamedTypeReferenceClass.StructNamedTypeClass: - return "<named type: struct %s>" % str(self.name) - if self.type_class == NamedTypeReferenceClass.UnionNamedTypeClass: - return "<named type: union %s>" % str(self.name) - if self.type_class == NamedTypeReferenceClass.EnumNamedTypeClass: - return "<named type: enum %s>" % str(self.name) - return "<named type: unknown %s>" % str(self.name) + def immutable_copy(self, **kwargs) -> 'IntegerType': + return IntegerType.create(self.width, self.signed, self.altname, **self.__dict__) - def __eq__(self, other): - if not isinstance(other, self.__class__): - return NotImplemented - return ctypes.addressof(self.handle.contents) == ctypes.addressof(other.handle.contents) + def __len__(self): + return self.width - def __ne__(self, other): - if not isinstance(other, self.__class__): - return NotImplemented - return not (self == other) - def __hash__(self): - return hash(ctypes.addressof(self.handle.contents)) +class IntegerType(CVQualifiedType): + @classmethod + def create(cls, width:int, sign:BoolWithConfidenceType=BoolWithConfidence(True), altname:str="", **kwargs) -> 'IntegerType': + if isinstance(sign, BoolWithConfidence): + _sign = sign.to_core_struct() + elif isinstance(sign, bool): + _sign = BoolWithConfidence(sign).to_core_struct() - @property - def type_class(self): - return NamedTypeReferenceClass(core.BNGetTypeReferenceClass(self.handle)) + builder_handle = core.BNCreateIntegerTypeBuilder(width, _sign, altname, **kwargs) + assert builder_handle is not None, "core.BNCreateIntegerTypeBuilder returned None" + + handle = _TypeBuilder(builder_handle, **kwargs).finalize() + return cls(core.BNNewTypeReference(handle), **kwargs) + + def create_mutable(self) -> 'Integer': + i = Integer(self.width, self.altname, self.signed) + i.__dict__['const']__ = self.const + i.__dict__['volatile']__ = self.volatile + return i @property - def type_id(self): - return core.BNGetTypeReferenceId(self.handle) + def signed(self) -> BoolWithConfidence: + """Whether type is signed (read-only)""" + return BoolWithConfidence.from_core_struct(core.BNIsTypeSigned(self._handle)) + + +@dataclass +class Bool(MutableType): + def immutable_copy(self, platform:'_platform.Platform'=None, + confidence:int=core.max_confidence) -> 'BoolType': + return BoolType.create(platform, confidence) + + def __len__(self): + return 1 + + +class BoolType(Type): + @classmethod + def create(cls, platform:'_platform.Platform'=None, confidence:int=core.max_confidence) -> 'BoolType': + handle = core.BNCreateBoolType() + assert handle is not None, "core.BNCreateBoolType returned None" + return cls(core.BNNewTypeReference(handle), platform, confidence) + + def create_mutable(self) -> 'Bool': + return Bool() + +@dataclass +class Char(Integer): + width:int=1 + altname:str="" + sign:bool=True + + def as_char(self, platform:'_platform.Platform'=None, confidence:int=core.max_confidence) -> 'CharType': + return CharType.create(self.width, self.altname, self.sign, platform, confidence) + + +class CharType(IntegerType): + @classmethod + def create(cls, width:int=1, altname:str="char", sign:bool=True, + platform:'_platform.Platform'=None, confidence:int=core.max_confidence, **kwargs) -> 'CharType': + return cls(IntegerType.create(width, sign, altname, **kwargs).handle, platform, confidence) + + def create_mutable(self) -> 'Char': + return Char(self.width, self.altname, self.signed, self.const, self.volatile) @property - def name(self): - name = core.BNGetTypeReferenceName(self.handle) - result = QualifiedName._from_core_struct(name) - core.BNFreeQualifiedName(name) - return result + def signed(self) -> BoolWithConfidence: + return BoolWithConfidence(False, core.max_confidence) - @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) - @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) +@dataclass +class Float(MutableType): + width:int + altname:str="" + def immutable_copy(self, platform:'_platform.Platform'=None, confidence:int=core.max_confidence) -> 'FloatType': + return FloatType.create(self.width, self.altname, platform, confidence) -@dataclass(frozen=True) + def __len__(self): + return self.width + +class FloatType(Type): + @classmethod + def create(cls, width:int, altname:str="", platform:'_platform.Platform'=None, confidence:int=core.max_confidence) -> 'FloatType': + """ + ``float`` class method for creating floating point Types. + + :param int width: width of the floating point number in bytes + :param str altname: alternate name for type + """ + core_float = core.BNCreateFloatType(width, altname) + assert core_float is not None, "core.BNCreateFloatType returned None" + return cls(core.BNNewTypeReference(core_float), platform, confidence) + + def create_mutable(self) -> 'Float': + return Float(self.width, self.altname) + + +@dataclass class StructureMember: - type:'types.Type' + type:Union[Type, MutableType] name:str offset:int + def __post_init__(self): + self.type = self.type.mutable_copy() + def __repr__(self): + # TODO: Consider showing this differently if the type is mutable if len(self.name) == 0: - return f"<member: {self.type}, offset {self.offset:#x}>" - return f"<{self.type.get_string_before_name()} {self.name}{self.type.get_string_after_name()}" + \ + return f"<member: {self.type.immutable_copy()}, offset {self.offset:#x}>" + return f"<{self.type.immutable_copy().get_string_before_name()} {self.name}{self.type.immutable_copy().get_string_after_name()}" + \ f", offset {self.offset:#x}>" + # return f"<StructureMember: {self.type} {self.name}>" -class Structure: - def __init__(self, handle=None): - if handle is None: - _handle = core.BNCreateStructureBuilder() - self._mutable = True - else: - _handle = handle - self._mutable = isinstance(handle.contents, core.BNStructureBuilder) - assert _handle is not None - self._handle = _handle + def __len__(self): + return len(self.type) + + +class StructureType(RegisteredNameType): + def __init__(self, handle, platform:'_platform.Platform'=None, confidence:int=core.max_confidence): + assert handle is not None, "Attempted to create EnumerationType with handle which is None" + super(StructureType, self).__init__(handle, platform, confidence) + struct_handle = core.BNGetTypeStructure(handle) + assert struct_handle is not None, "core.BNGetTypeStructure returned None" + self.struct_handle = struct_handle + + @classmethod + def create(cls, members:MembersType=[], packed:bool=False, variant:StructureVariant=StructureVariant.StructStructureType, + platform:'_platform.Platform'=None, confidence:int=core.max_confidence) -> 'StructureType': + builder = core.BNCreateStructureBuilderWithOptions(variant, packed) + assert builder is not None, "core.BNCreateStructureBuilder returned None" + + for member in members: + if isinstance(member, Tuple): + _type, _name = member + core.BNAddStructureBuilderMember(builder, _type.immutable_copy().to_core_struct(), _name) + elif isinstance(member, StructureMember): + core.BNAddStructureBuilderMemberAtOffset(builder, member.type.immutable_copy().to_core_struct(), + member.name, member.offset, False) + core_struct = core.BNFinalizeStructureBuilder(builder) + assert core_struct is not None, "core.BNFinalizeStructureBuilder returned None" + core_type = core.BNCreateStructureType(core_struct) + assert core_type is not None, "core.BNCreateStructureType returned None" + return cls(core.BNNewTypeReference(core_type), platform, confidence) + + def create_mutable(self) -> 'Structure': + return Structure(self.members, self.alignment, self.width, self.type, self.packed) + + @classmethod + def from_core_struct(cls, structure:core.BNStructure) -> 'StructureType': + return cls(core.BNNewTypeReference(core.BNCreateStructureType(structure))) def __del__(self): - if self._mutable: - core.BNFreeStructureBuilder(self._handle) - else: - core.BNFreeStructure(self._handle) + if core is not None: + core.BNFreeStructure(self.struct_handle) - def __repr__(self): - return "<struct: size %#x>" % self.width + # TODO: Commented to pass unit tests + # def __repr__(self): + # return f"<struct: {self.registered_name}>" def __eq__(self, other): if not isinstance(other, self.__class__): return NotImplemented - return ctypes.addressof(self._handle.contents) == ctypes.addressof(other.handle.contents) + assert other.handle is not None + return ctypes.addressof(self.struct_handle.contents) == ctypes.addressof(other.handle.contents) def __ne__(self, other): if not isinstance(other, self.__class__): @@ -1127,20 +1145,15 @@ class Structure: return not (self == other) def __hash__(self): - return hash(ctypes.addressof(self._handle.contents)) + return hash(ctypes.addressof(self.struct_handle.contents)) def __getitem__(self, name:str) -> StructureMember: member = None try: - if self._mutable: - member = core.BNGetStructureBuilderMemberByName(self._handle, name) - if member is None: - raise ValueError(f"Member {name} is not part of structure") - else: - member = core.BNGetStructureMemberByName(self._handle, name) - if member is None: - raise ValueError(f"Member {name} is not part of structure") - return StructureMember(Type(core.BNNewTypeReference(member.contents.type), confidence=member.contents.typeConfidence), + member = core.BNGetStructureMemberByName(self.struct_handle, name) + if member is None: + raise ValueError(f"Member {name} is not part of structure") + return StructureMember(Type.create(core.BNNewTypeReference(member.contents.type), confidence=member.contents.typeConfidence), member.contents.name, member.contents.offset) finally: if member is not None: @@ -1149,44 +1162,24 @@ class Structure: def member_at_offset(self, offset:int) -> StructureMember: member = None try: - if self._mutable: - member = core.BNGetStructureBuilderMemberAtOffset(self._handle, offset, None) - if member is None: - raise ValueError(f"No member exists a offset {offset}") - else: - member = core.BNGetStructureMemberAtOffset(self._handle, offset, None) - if member is None: - raise ValueError(f"No member exists a offset {offset}") - return StructureMember(Type(core.BNNewTypeReference(member.contents.type), confidence=member.contents.typeConfidence), + member = core.BNGetStructureMemberAtOffset(self.struct_handle, offset, None) + if member is None: + raise ValueError(f"No member exists a offset {offset}") + return StructureMember(Type.create(core.BNNewTypeReference(member.contents.type), confidence=member.contents.typeConfidence), member.contents.name, member.contents.offset) finally: core.BNFreeStructureMember(member) @property - def handle(self): - 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 - return self._handle - - @property def members(self): """Structure member list (read-only)""" 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" + members = core.BNGetStructureMembers(self.struct_handle, count) + assert members is not None, "core.BNGetStructureMembers returned None" try: result = [] for i in range(0, count.value): - result.append(StructureMember(Type(core.BNNewTypeReference(members[i].type), confidence=members[i].typeConfidence), + result.append(StructureMember(Type.create(core.BNNewTypeReference(members[i].type), confidence=members[i].typeConfidence), members[i].name, members[i].offset)) finally: core.BNFreeStructureMemberList(members, count.value) @@ -1195,137 +1188,264 @@ class Structure: @property def width(self): """Structure width""" - if self._mutable: - return core.BNGetStructureBuilderWidth(self._handle) - return core.BNGetStructureWidth(self._handle) - - @width.setter - def width(self, new_width): - if not self._mutable: - raise AttributeError("Finalized Structure object is immutable, use mutable_copy()") - core.BNSetStructureBuilderWidth(self._handle, new_width) + return core.BNGetStructureWidth(self.struct_handle) @property def alignment(self): """Structure alignment""" - if self._mutable: - return core.BNGetStructureBuilderAlignment(self._handle) - return core.BNGetStructureAlignment(self._handle) - - @alignment.setter - def alignment(self, align): - if not self._mutable: - raise AttributeError("Finalized Structure object is immutable, use mutable_copy()") - core.BNSetStructureBuilderAlignment(self._handle, align) + return core.BNGetStructureAlignment(self.struct_handle) @property def packed(self): - if self._mutable: - return core.BNIsStructureBuilderPacked(self._handle) - return core.BNIsStructurePacked(self._handle) + return core.BNIsStructurePacked(self.struct_handle) + + @property + def type(self) -> StructureVariant: + return StructureVariant(core.BNGetStructureType(self.struct_handle)) + + def with_replaced_structure(self, from_struct, to_struct) -> 'StructureType': + return Type.create(core.BNStructureWithReplacedStructure(self.struct_handle, from_struct.handle, to_struct.handle)) + + def with_replaced_enumeration(self, from_enum, to_enum) -> 'StructureType': + return Type.create(core.BNStructureWithReplacedEnumeration(self.struct_handle, from_enum.handle, to_enum.handle)) + + def with_replaced_named_type_reference(self, from_ref, to_ref) -> 'StructureType': + return Type.create(core.BNStructureWithReplacedNamedTypeReference(self.struct_handle, from_ref.handle, to_ref.handle)) + + def generate_named_type_reference(self, guid:str, name:QualifiedName): + if self.type == StructureVariant.StructStructureType: + ntr_type = NamedTypeReferenceClass.StructNamedTypeClass + elif self.type == StructureVariant.UnionStructureType: + ntr_type = NamedTypeReferenceClass.UnionNamedTypeClass + else: + ntr_type = NamedTypeReferenceClass.ClassNamedTypeClass + return NamedTypeReferenceType.create(ntr_type, guid, name, self.alignment, + self.width, self.platform, self.confidence) + + +@dataclass +class Structure(MutableType): + members:List[StructureMember] = field(default_factory=list) + _alignment:int=1 + _width:int=0 + type:StructureVariant=StructureVariant.StructStructureType + _packed:bool=False + + def immutable_copy(self, platform:'_platform.Platform'=None, confidence:int=core.max_confidence) -> 'StructureType': + return StructureType.create(self.members, self.packed, self.type, platform, confidence) + + @property + def packed(self) -> bool: + return self._packed @packed.setter - def packed(self, value): - if not self._mutable: - raise AttributeError("Finalized Structure object is immutable, use mutable_copy()") - core.BNSetStructureBuilderPacked(self._handle, value) + def packed(self, value:bool) -> None: + self._packed = value @property - def union(self): - if self._mutable: - return core.BNIsStructureBuilderUnion(self._handle) - return core.BNIsStructureUnion(self._handle) + def alignment(self) -> int: + return self._alignment + + @alignment.setter + def alignment(self, value:int) -> None: + if value == 1: + return + if value < 1: + raise ValueError("Can't set alignment to < 1") + self._alignment = value @property - def type(self): - if self._mutable: - return StructureType(core.BNGetStructureBuilderType(self._handle)) - return StructureType(core.BNGetStructureType(self._handle)) + def width(self) -> int: + return self._width - @type.setter - def type(self, value): - if not self._mutable: - raise AttributeError("Finalized Structure object is immutable, use mutable_copy()") - core.BNSetStructureBuilderType(self._handle, value) + @width.setter + def width(self, value:int) -> None: + # if greater than the current width: + # expands the structure's size + # if less than the current width: + # shrinks the structure's size removing any members which would exist outside the structure's bounds + if value < 0: + raise ValueError("Width of structure can not be negative") - def append(self, t, name = ""): - if not self._mutable: - raise AttributeError("Finalized Structure object is immutable, use mutable_copy()") - tc = core.BNTypeWithConfidence() - tc.type = t.handle - tc.confidence = t.confidence - core.BNAddStructureBuilderMember(self._handle, tc, name) + if value < self._width: + amount = self._width - (self._width - value) + self.clear_members(value, amount) + self._width = value - def insert(self, offset, t, name = "", overwriteExisting = True): - if not self._mutable: - raise AttributeError("Finalized Structure object is immutable, use mutable_copy()") - tc = core.BNTypeWithConfidence() - tc.type = t.handle - tc.confidence = t.confidence - core.BNAddStructureBuilderMemberAtOffset(self._handle, tc, name, offset, overwriteExisting) + def __repr__(self): + return f"<struct: size {self.width:#x}>" - def remove(self, i): - if not self._mutable: - raise AttributeError("Finalized Structure object is immutable, use mutable_copy()") - core.BNRemoveStructureBuilderMember(self._handle, i) + def __getitem__(self, name:str) -> Optional[StructureMember]: + for member in self.members: + if member.name == name: + return member + return None - def replace(self, i, t, name = "", overwriteExisting = True): - if not self._mutable: - raise AttributeError("Finalized Structure object is immutable, use mutable_copy()") - tc = core.BNTypeWithConfidence() - tc.type = t.handle - tc.confidence = t.confidence - core.BNReplaceStructureBuilderMember(self._handle, i, tc, name, overwriteExisting) + def __iter__(self) -> Generator[StructureMember, None, None]: + for member in self.members: + yield member - def mutable_copy(self): - if self._mutable: - return Structure(core.BNDuplicateStructureBuilder(self._handle)) - return Structure(core.BNCreateStructureBuilderFromStructure(self._handle)) + def __len__(self) -> int: + return self._width - def with_replaced_structure(self, from_struct, to_struct): - return Structure(core.BNStructureWithReplacedStructure(self._handle, from_struct.handle, to_struct.handle)) + def member_at_offset(self, offset:int) -> Optional[StructureMember]: + for member in self.members: + if member.offset == offset: + return member + return None - def with_replaced_enumeration(self, from_enum, to_enum): - return Structure(core.BNStructureWithReplacedEnumeration(self._handle, from_enum.handle, to_enum.handle)) + def index_by_name(self, name:MemberName) -> Optional[MemberIndex]: + for i, member in enumerate(self.members): + if member.name == name: + return i + return None - def with_replaced_named_type_reference(self, from_ref, to_ref): - return Structure(core.BNStructureWithReplacedNamedTypeReference(self._handle, from_ref.handle, to_ref.handle)) + def index_by_offset(self, offset:MemberOffset) -> Optional[MemberIndex]: + for i, member in enumerate(self.members): + if member.offset == offset: + return i + return None + def index_from(self, index:Optional[MemberIndex]=None, name:Optional[MemberName]=None, offset:Optional[MemberOffset]=None) -> MemberIndex: + if index is not None: + if index >= len(self.members): + raise IndexError("list index out of range") + elif name is not None: + index = self.index_by_name(name) + if index is None: + raise ValueError(f"StructureMember {name} doesn't exist") + elif offset is not None: + index = self.index_by_offset(offset) + if index is None: + raise ValueError(f"No StructureMember at offset exists") + else: + raise ValueError("One of the following must") -@dataclass(frozen=True) -class EnumerationMember: - name:str - value:int - default:bool + return index - def __repr__(self): - return f"<{self.name} = {self.value:#x}>" + def erase(self, index:MemberIndex=None, name:MemberName=None, offset:MemberOffset=None) -> None: + # removes the specified item shrinking the total size of the structure and adjusting + # the offset of any members with offsets greater than the offset of member[index]. + # In the case where there are multiple members which overlap the erased item they will be erased too + # but the structure will only be shrunken by the specified member's width + # raise exception if the index doesn't exist + # raises exception if more not exactly one of index/name/offset are not None + item = self.members[self.index_from(index, name, offset)] + self.clear_members(item.offset, len(item)) + self.adjust_space(item.offset, -len(item)) + def clear(self, index:MemberIndex=None, name:MemberName=None, offset:MemberOffset=None) -> None: + # clears the member at the index/member-name. No adjustment is made to other members or the structure's size + # raise exception if the index doesn't exist + del self.members[self.index_from(index, name, offset)] -class Enumeration: - def __init__(self, handle=None): - if handle is None: - _handle = core.BNCreateEnumerationBuilder() - self._mutable = True - else: - _handle = handle - self._mutable = isinstance(handle.contents, core.BNEnumerationBuilder) - assert _handle is not None - self._handle = _handle + def clear_members(self, offset:MemberOffset, size:int) -> None: + # clears members which overlap offset + to_clear = [] + for i, member in enumerate(self.members): + if member.offset >= offset and member.offset < offset + size: + to_clear.append(i) + elif member.offset < offset and member.offset + len(member) > offset: + to_clear.append(i) + for i in to_clear: + self.clear(index=i) + + def replace_member(self, new_name:MemberName, type:SomeType, index:MemberIndex=None, old_name:MemberName=None, offset:MemberOffset=None) -> None: + # replaces any members within the structure which overlap member[index] + index = self.index_from(index, old_name, offset) + item = self.members[index] + self.clear_members(item.offset, len(item)) + self.members.insert(index, StructureMember(type, new_name, item.offset)) + + def append(self, name:MemberName, type:SomeType) -> 'Structure': + # appends a structure at the end of the structure growing the structure + self.members.append(StructureMember(type.mutable_copy(), name, self._width)) + self._width += len(type) + return self + + def add_member_at_offset(self, name:MemberName, type:SomeType, offset:MemberOffset) -> 'Structure': + # Adds structure member to the given offset first clearing any members within the range offset-offset+len(type) + self.clear_members(offset, len(type)) + self.members.append(StructureMember(type, name, offset)) + self.members = sorted(self.members, key=lambda m: m.offset) + return self + + def adjust_space(self, offset:int, size:int) -> None: + # adds or removes undefined space at the given offset and size + # removes space and clears overlapping members if size is negative + self.members = sorted(self.members, key=lambda m: m.offset) + + if size < -self._width: + size = -self._width + + if size < 0: + self.clear_members(offset, size) + + alignment = 0 + if size % self._alignment != 0: + alignment = self._alignment - (abs(size) % self._alignment) + + new_members = [] + for member in self.members: + if member.offset > offset: + new_offset = member.offset + size + alignment + new_members.append(StructureMember(member.type, member.name, new_offset)) + self.members = new_members + self._width += size + + +class EnumerationType(RegisteredNameType, IntegerType): + def __init__(self, handle, platform:'_platform.Platform'=None, confidence:int=core.max_confidence): + assert handle is not None, "Attempted to create EnumerationType without handle" + super(EnumerationType, self).__init__(handle, platform, confidence) + enum_handle = core.BNGetTypeEnumeration(handle) + assert enum_handle is not None, "core.BNGetTypeEnumeration returned None" + self.enum_handle = enum_handle + + @classmethod + def create(cls, arch:Optional['architecture.Architecture'], members:EnumMembersType=[], width:Optional[int]=None, + sign:BoolWithConfidenceType=BoolWithConfidence(False), platform:'_platform.Platform'=None, confidence:int=core.max_confidence) -> 'EnumerationType': + if width is None: + if arch is None: + raise ValueError("One of the following parameters must not be None: (arch, width)") + width = arch.default_int_size + if width == 0: + raise ValueError("enumeration width must not be 0") + + builder = core.BNCreateEnumerationBuilder() + assert builder is not None, "core.BNCreateEnumerationType returned None" + for i, member in enumerate(members): + value = i + name = member + if isinstance(member, Tuple): + value, name = member + elif isinstance(member, EnumerationMember): + value = member.value + name = member.name + if value is None: + core.BNAddEnumerationBuilderMember(builder, name) + else: + core.BNAddEnumerationBuilderMemberWithValue(builder, name, value) + core_enum = core.BNFinalizeEnumerationBuilder(builder) + assert core_enum is not None, "core.BNFinalizeEnumerationBuilder returned None" + core_type = core.BNCreateEnumerationTypeOfWidth(core_enum, width, sign) + assert core_type is not None, "core.BNCreateEnumerationTypeOfWidth returned None" + return cls(core.BNNewTypeReference(core_type), platform, confidence) + + def create_mutable(self) -> 'Enumeration': + return Enumeration(self.members, len(self), self.signed) def __del__(self): - if self._mutable: - core.BNFreeEnumerationBuilder(self._handle) - else: - core.BNFreeEnumeration(self._handle) + if core is not None: + core.BNFreeEnumeration(self.enum_handle) - def __repr__(self): - return "<enum: %s>" % repr(self.members) + # def __repr__(self): + # return "<enum: %s>" % repr(self.members) def __eq__(self, other): if not isinstance(other, self.__class__): return NotImplemented - return ctypes.addressof(self.handle.contents) == ctypes.addressof(other.handle.contents) + return ctypes.addressof(self.enum_handle.contents) == ctypes.addressof(other.enum_handle.contents) def __ne__(self, other): if not isinstance(other, self.__class__): @@ -1333,57 +1453,587 @@ class Enumeration: return not (self == other) def __hash__(self): - return hash(ctypes.addressof(self.handle.contents)) - - @property - def handle(self): - 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 - return self._handle + return hash(ctypes.addressof(self.enum_handle.contents)) @property def members(self): """Enumeration member list (read-only)""" 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" + members = core.BNGetEnumerationMembers(self.enum_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)) + result.append(EnumerationMember(members[i].name, members[i].value)) core.BNFreeEnumerationMemberList(members, count.value) return result - def append(self, name, value = None): - if not self._mutable: - raise AttributeError("Finalized Enumeration object is immutable, use mutable_copy()") - if value is None: - core.BNAddEnumerationBuilderMember(self._handle, name) - else: - core.BNAddEnumerationBuilderMemberWithValue(self._handle, name, value) + def generate_named_type_reference(self, guid:str, name:QualifiedName): + ntr_type = NamedTypeReferenceClass.EnumNamedTypeClass + return NamedTypeReferenceType.create(ntr_type, guid, name, + platform=self.platform, confidence=self.confidence) + +@dataclass(frozen=True) +class EnumerationMember: + name:str + value:Optional[int] + + def __repr__(self): + value = f"{self.value:#x}" if self.value is not None else "auto()" + return f"<{self.name} = {value}>" + + +@dataclass +class Enumeration(MutableType): + members:List[EnumerationMember] = field(default_factory=list) + width:int=4 + sign:BoolWithConfidence=BoolWithConfidence(False) + + def __repr__(self): + return "<enum: %s>" % repr(self.members) + + def __len__(self): + return self.width + + def immutable_copy(self, platform:'_platform.Platform'=None, confidence:int=core.max_confidence) -> 'EnumerationType': + return EnumerationType.create(None, self.members, self.width, self.sign, platform, confidence) + + def append(self, name, value = None) -> 'Enumeration': + self.members.append(EnumerationMember(name, value)) + return self def remove(self, i): - if not self._mutable: - raise AttributeError("Finalized Enumeration object is immutable, use mutable_copy()") - core.BNRemoveEnumerationBuilderMember(self._handle, i) + del self.members[i] def replace(self, i, name, value): - if not self._mutable: - raise AttributeError("Finalized Enumeration object is immutable, use mutable_copy()") - core.BNReplaceEnumerationBuilderMember(self._handle, i, name, value) + self.remove(i) + self.members.insert(i, EnumerationMember(name, value)) + + def __iter__(self) -> Generator[EnumerationMember, None, None]: + for i, member in enumerate(self.members): + if member.value is None: + yield EnumerationMember(member.name, i) + else: + yield member + + def __getitem__(self, value:Union[str, int, slice]): + if isinstance(value, str) and value in self.__dict__: + return self.__dict__[value] + + if isinstance(value, str): + for member in self.members: + if member.name == value: + return member + return None + elif isinstance(value, int): + return self.members[value] + elif isinstance(value, slice): # not combined with the previous check due to pyright bug + return self.members[value] + else: + raise ValueError(f"Incompatible type {type(value)} for __getitem__") + + def __setitem__(self, item, value): + if isinstance(item, str): + for i, member in enumerate(self.members): + if member.name == item: + self.members[i] = EnumerationMember(member.name, value) + elif isinstance(item, int) and isinstance(value, EnumerationMember): + self.members[item] = value + raise ValueError(f"Incompatible type: {type(item)} for __setitem__") + + +@dataclass +class Pointer(MutableType): + _target:Optional[SomeType] = None + width:Optional[int] = None + arch:Optional['architecture.Architecture'] = None + const:BoolWithConfidenceType=BoolWithConfidence(False) + volatile:BoolWithConfidenceType=BoolWithConfidence(False) + ref_type:ReferenceType=ReferenceType.PointerReferenceType + + def __post_init__(self): + if self._target is not None: + self._target = self._target.mutable_copy() + assert self.arch is not None or self.width is not None, f"Must specify either an architecture or a width {self.arch} : {self.width}" + if self.arch is not None: + self.width = self.arch.address_size + + def immutable_copy(self, platform:'_platform.Platform'=None, confidence:int=core.max_confidence) -> 'PointerType': + assert self._target is not None and self.width is not None, f"Target not set for Pointer(MutableType) {self._target}, {self.width}" + return PointerType.create_with_width(self.width, self._target, self.const, self.volatile, + self.ref_type, platform, confidence) + + @property + def target(self) -> MutableType: + assert isinstance(self._target, MutableType) + return self._target + + @target.setter + def target(self, value:SomeType): # type: ignore + self._target = value.mutable_copy() + + def __len__(self): + return self.width + +class PointerType(PointerLike): + @property + def ref_type(self) -> ReferenceType: + return core.BNTypeGetReferenceType(self._handle) + + @classmethod + def create(cls, arch:'architecture.Architecture', type:SomeType, const:BoolWithConfidenceType=False, + volatile:BoolWithConfidenceType=False, ref_type:ReferenceType=ReferenceType.PointerReferenceType, + platform:'_platform.Platform'=None, confidence:int=core.max_confidence) -> 'PointerType': + return cls.create_with_width(arch.address_size, type, const, volatile, ref_type, platform, confidence) + + @classmethod + def create_with_width(cls, width:int, type:SomeType, const:BoolWithConfidenceType=False, + volatile:BoolWithConfidenceType=False, ref_type:ReferenceType=None, platform:'_platform.Platform'=None, + confidence:int=core.max_confidence) -> 'PointerType': + _const, _volatile = CVQualifiedType.from_bools(const, volatile) + type = type.immutable_copy() + if ref_type is None: + ref_type = ReferenceType.PointerReferenceType + + type_conf = type.to_core_struct() + core_type = core.BNCreatePointerTypeOfWidth(width, type_conf, _const.to_core_struct(), + _volatile.to_core_struct(), ref_type) + assert core_type is not None, "core.BNCreatePointerTypeOfWidth returned None" + return Type.create(core.BNNewTypeReference(core_type), platform, confidence) + + def create_mutable(self) -> 'Pointer': + return Pointer(self.target, self.width, None, self.const, self.volatile, self.ref_type) + + +@dataclass +class Array(MutableType): + count:int + element_type:MutableType + + def __post_init__(self): + self.element_type = self.element_type.mutable_copy() + + def immutable_copy(self, platform:'_platform.Platform'=None, confidence:int=core.max_confidence) -> 'ArrayType': + return ArrayType.create(self.element_type.immutable_copy(), self.count, platform, confidence) + + def __len__(self): + return self.count * len(self.element_type) + + +class ArrayType(Type): + @classmethod + def create(cls, element_type:Type, count:int, platform:'_platform.Platform'=None, confidence:int=core.max_confidence): + type_conf = element_type.to_core_struct() + core_array = core.BNCreateArrayType(type_conf, count) + assert core_array is not None, "core.BNCreateArrayType returned None" + return cls(core.BNNewTypeReference(core_array)) + + def create_mutable(self) -> 'Array': + return Array(self.count, self.element_type.mutable_copy()) + + @property + def count(self): + """Type count (read-only)""" + return core.BNGetTypeElementCount(self._handle) + + @property + def element_type(self) -> Type: + result = core.BNGetChildType(self._handle) + assert result is not None, "core.BNGetChildType returned None" + return Type.create(core.BNNewTypeReference(result.type), self._platform, result.confidence) + + +@dataclass +class Function(MutableType): + _return_type:Optional[SomeType] = None + parameters:List[FunctionParameter] = field(default_factory=list) + calling_convention:Optional['callingconvention.CallingConvention'] = None + variable_arguments:BoolWithConfidenceType = BoolWithConfidence(False) + stack_adjustment:OffsetWithConfidence = OffsetWithConfidence(0) + + def __post_init__(self): + if self._return_type is not None: + self._return_type = self._return_type.mutable_copy() + if len(self.parameters) > 0: + self.parameters = [param.mutable_copy() for param in self.parameters] + + def immutable_copy(self, platform:'_platform.Platform'=None, confidence:int=core.max_confidence) -> 'FunctionType': + immutable_parameters = [param.immutable_copy() for param in self.parameters] + if self._return_type is None: + self._return_type = Type.void() + return FunctionType.create(self._return_type.immutable_copy(), immutable_parameters, self.calling_convention, + self.variable_arguments, self.stack_adjustment, platform, confidence) + + @property + def return_type(self): + return self._return_type + + @return_type.setter + def return_type(self, value:SomeType): + self._return_type = value.mutable_copy() + + def append(self, type:Union[SomeType, FunctionParameter], name:str=""): + if isinstance(type, FunctionParameter): + self.parameters.append(type.mutable_copy()) + else: + self.parameters.append(FunctionParameter(type.mutable_copy(), name)) + + def __len__(self): + return 0 + +class FunctionType(Type): + @classmethod + def create(cls, ret:Optional[Type]=None, params:ParamsType=[], + calling_convention:'callingconvention.CallingConvention'=None, variable_arguments:BoolWithConfidenceType=BoolWithConfidence(False), + stack_adjust:OffsetWithConfidence=OffsetWithConfidence(0), platform:'_platform.Platform'=None, confidence:int=core.max_confidence) -> 'FunctionType': + if ret is None: + ret = VoidType.create() + param_buf = (core.BNFunctionParameter * len(params))() + for i in range(0, len(params)): + param = params[i] + core_param = param_buf[i] + if isinstance(param, Type): + core_param.name = "" + core_param.type = param.handle + core_param.typeConfidence = param.confidence + core_param.defaultLocation = True + elif isinstance(param, FunctionParameter): + core_param.name = param.name + core_param.type = param.type.immutable_copy().handle + core_param.typeConfidence = param.type.immutable_copy().confidence + if param.location is None: + core_param.defaultLocation = True + else: + core_param.defaultLocation = False + core_param.location.type = param.location.source_type + core_param.location.index = param.location.index + core_param.location.storage = param.location.storage + elif isinstance(param, tuple): + t, name = param + core_param.name = name + core_param.type = t.handle + core_param.typeConfidence = t.confidence + core_param.defaultLocation = True + + ret_conf = ret.to_core_struct() + + conv_conf = core.BNCallingConventionWithConfidence() + if calling_convention is None: + conv_conf.convention = None + conv_conf.confidence = 0 + else: + conv_conf.convention = calling_convention.handle + conv_conf.confidence = calling_convention.confidence + + if isinstance(variable_arguments, bool): + _variable_arguments = BoolWithConfidence(variable_arguments) + elif isinstance(variable_arguments, BoolWithConfidence): + _variable_arguments = variable_arguments + elif variable_arguments is None: + _variable_arguments = BoolWithConfidence(False) + else: + raise ValueError(f"variable_arguments parameter of unhandled type: {type(variable_arguments)}") + + if isinstance(stack_adjust, int): + _stack_adjust = OffsetWithConfidence(stack_adjust) + elif isinstance(stack_adjust, OffsetWithConfidence): + _stack_adjust = stack_adjust + elif stack_adjust is None: + _stack_adjust = OffsetWithConfidence(0) + else: + raise ValueError(f"stack_adjust parameter of unhandled type: {type(variable_arguments)}") + + func_type = core.BNCreateFunctionType(ret_conf, conv_conf, param_buf, len(params), + _variable_arguments.to_core_struct(), _stack_adjust.to_core_struct()) + return cls(core.BNNewTypeReference(func_type), platform, confidence) + + def create_mutable(self) -> 'Function': + return Function(self.return_value.mutable_copy(), self.parameters, self.calling_convention, self.has_variable_arguments, self.stack_adjustment) + + @property + def stack_adjustment(self) -> OffsetWithConfidence: + """Stack adjustment for function (read-only)""" + result = core.BNGetTypeStackAdjustment(self._handle) + return OffsetWithConfidence(result.value, confidence = result.confidence) + + @property + def return_value(self) -> Type: + """Return value (read-only)""" + result = core.BNGetChildType(self._handle) + if result is None: + return Type.void() + return Type.create(core.BNNewTypeReference(result.type), platform = self._platform, confidence = result.confidence) + + @property + def calling_convention(self) -> Optional[callingconvention.CallingConvention]: + """Calling convention (read-only)""" + result = core.BNGetTypeCallingConvention(self._handle) + if not result.convention: + return None + return callingconvention.CallingConvention(None, handle = result.convention, confidence = result.confidence) + + @property + def parameters(self) -> List[FunctionParameter]: + """Type parameters list (read-only)""" + count = ctypes.c_ulonglong() + 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.create(core.BNNewTypeReference(params[i].type), platform = self._platform, confidence = params[i].typeConfidence) + if params[i].defaultLocation: + param_location = None + else: + name = params[i].name + if (params[i].location.type == VariableSourceType.RegisterVariableSourceType) and (self._platform is not None): + name = self._platform.arch.get_reg_name(params[i].location.storage) + elif params[i].location.type == VariableSourceType.StackVariableSourceType: + name = "arg_%x" % params[i].location.storage + param_location = variable.VariableNameAndType(params[i].location.type, params[i].location.index, + params[i].location.storage, name, param_type) + result.append(FunctionParameter(param_type, params[i].name, param_location)) + core.BNFreeTypeParameterList(params, count.value) + return result + + @property + def has_variable_arguments(self) -> BoolWithConfidence: + """Whether type has variable arguments (read-only)""" + result = core.BNTypeHasVariableArguments(self._handle) + return BoolWithConfidence(result.value, confidence = result.confidence) + + @property + def can_return(self) -> BoolWithConfidence: + """Whether type can return""" + result = core.BNFunctionTypeCanReturn(self._handle) + return BoolWithConfidence(result.value, confidence = result.confidence) + + +# @dataclass +# class Value(MutableType): +# pass + + +# class ValueType(Type): +# pass + + +@dataclass +class NamedTypeReference(MutableType): + name:QualifiedName + id:str + named_type_class:NamedTypeReferenceClass=NamedTypeReferenceClass.UnknownNamedTypeClass + alignment:int = 0 + width:int = 0 + + def __repr__(self): + if self.named_type_class == NamedTypeReferenceClass.TypedefNamedTypeClass: + return "<named type: typedef %s>" % str(self.name) + if self.named_type_class == NamedTypeReferenceClass.StructNamedTypeClass: + return "<named type: struct %s>" % str(self.name) + if self.named_type_class == NamedTypeReferenceClass.UnionNamedTypeClass: + return "<named type: union %s>" % str(self.name) + if self.named_type_class == NamedTypeReferenceClass.EnumNamedTypeClass: + return "<named type: enum %s>" % str(self.name) + return "<named type: unknown >" + + def immutable_copy(self, platform:'_platform.Platform'=None, confidence:int=core.max_confidence) -> 'NamedTypeReferenceType': + return NamedTypeReferenceType.create(self.named_type_class, self.id, self.name, self.alignment, self.width, + platform, confidence) + + def __len__(self): + return self.width + +class NamedTypeReferenceType(RegisteredNameType): + def __init__(self, handle, platform:'_platform.Platform'=None, confidence:int=core.max_confidence, ntr_handle=None): + assert handle is not None, "Attempting to create NamedTypeReferenceType handle which is None" + super(NamedTypeReferenceType, self).__init__(handle, platform, confidence) + if ntr_handle is None: + ntr_handle = core.BNGetTypeNamedTypeReference(handle) + assert ntr_handle is not None, "core.BNGetTypeNamedTypeReference returned None" + self.ntr_handle = ntr_handle + + @classmethod + def create(cls, named_type_class:NamedTypeReferenceClass, guid:Optional[str], + name:QualifiedName, alignment:int=0, width:int=0, platform:'_platform.Platform'=None, + confidence:int=core.max_confidence) -> 'NamedTypeReferenceType': + _guid = guid + if guid is None: + _guid = str(uuid.uuid4()) + + _name = QualifiedName(name)._get_core_struct() + core_ntr = core.BNCreateNamedType(named_type_class, _guid, _name) + assert core_ntr is not None, "core.BNCreateNamedType returned None" + core_type = core.BNCreateNamedTypeReference(core_ntr, width, alignment) + assert core_type is not None, "core.BNCreateNamedTypeReference returned None" + return cls(core.BNNewTypeReference(core_type), platform, confidence) + + @classmethod + def create_from_type(cls, name:QualifiedName, type:Optional[Type], guid:Optional[str]=None, + platform:'_platform.Platform'=None, confidence:int=core.max_confidence) -> 'NamedTypeReferenceType': + _guid = guid + if _guid is None: + _guid = str(uuid.uuid4()) + + if type is None: + return cls.create(NamedTypeReferenceClass.UnknownNamedTypeClass, _guid, name) + else: + return type.generate_named_type_reference(_guid, name) + + @classmethod + def create_from_registered_type(cls, view:'binaryview.BinaryView', name:QualifiedName, + platform:'_platform.Platform'=None, confidence:int=core.max_confidence) -> 'NamedTypeReferenceType': + _name = QualifiedName(name)._get_core_struct() + core_type = core.BNCreateNamedTypeReferenceFromType(view.handle, _name) + assert core_type is not None, "core.BNCreateNamedTypeReferenceFromType returned None" + return cls(core.BNNewTypeReference(core_type), platform, confidence) + + def create_mutable(self) -> 'NamedTypeReference': + return NamedTypeReference(self.name, self.type_id, self.named_type_class, self.alignment, self.width) + + def __del__(self): + if core is not None: + core.BNFreeNamedTypeReference(self.ntr_handle) + + def __repr__(self): + if self.named_type_class == NamedTypeReferenceClass.TypedefNamedTypeClass: + return f"<named type: {self}>" + if self.named_type_class == NamedTypeReferenceClass.StructNamedTypeClass: + return f"<named type: {self}>" + if self.named_type_class == NamedTypeReferenceClass.UnionNamedTypeClass: + return f"<named type: {self}>" + if self.named_type_class == NamedTypeReferenceClass.EnumNamedTypeClass: + return f"<named type: {self}>" + return "<named type: unknown >" + + def __str__(self): + name = self.registered_name + if name is None: + name = "" + else: + name = " " + str(name.name) + return f"{self.get_string_before_name()}{name}{self.get_string_after_name()}" + + def __eq__(self, other): + if not isinstance(other, self.__class__): + return NotImplemented + assert other.handle is not None + return ctypes.addressof(self.ntr_handle.contents) == ctypes.addressof(other.handle.contents) + + def __ne__(self, other): + if not isinstance(other, self.__class__): + return NotImplemented + return not (self == other) + + def __hash__(self): + return hash(ctypes.addressof(self.ntr_handle.contents)) + + @property + def named_type_class(self) -> NamedTypeReferenceClass: + return NamedTypeReferenceClass(core.BNGetTypeReferenceClass(self.ntr_handle)) + + @property + def type_id(self) -> str: + return core.BNGetTypeReferenceId(self.ntr_handle) + + @property + def name(self) -> QualifiedName: + name = core.BNGetTypeReferenceName(self.ntr_handle) + result = QualifiedName._from_core_struct(name) + core.BNFreeQualifiedName(name) + return result + + @staticmethod + def generate_auto_type_ref(type_class, source, name): + type_id = RegisteredNameType.generate_auto_type_id(source, name) + return NamedTypeReferenceType.create(type_class, type_id, name) + + @staticmethod + def generate_auto_demangled_type_ref(type_class, name): + type_id = RegisteredNameType.generate_auto_demangled_type_id(name) + return NamedTypeReferenceType.create(type_class, type_id, name) + + def target(self, bv:'binaryview.BinaryView') -> Optional[Type]: + return bv.get_type_by_id(self.type_id) + + +# class TypedefType(NamedTypeReferenceType): +# def __init__(self, handle, platform:'_platform.Platform'=None, confidence:int=core.max_confidence): +# super(TypedefType,).__init__(handle, platform, confidence) + +# @classmethod +# def create(cls, guid:Optional[str], +# name:QualifiedName, alignment:int=None, width:int=None, platform:'_platform.Platform'=None, +# confidence:int=core.max_confidence) -> 'TypedefType' +# return cls(core_type, platform, confidence) + + +@dataclass +class WideChar(Integer): + def immutable_copy(self, platform:'_platform.Platform'=None, confidence:int=core.max_confidence) -> 'WideCharType': + return WideCharType.create(self.width, self.altname, platform, confidence) + + +class WideCharType(Type): + @classmethod + def create(cls, width:int, altname:str="", platform:'_platform.Platform'=None, confidence:int=core.max_confidence) -> 'WideCharType': + """ + ``wide_char`` class method for creating wide char Types. + + :param int width: width of the wide character in bytes + :param str altname: alternate name for type + """ + core_type = core.BNCreateWideCharType(width, altname) + assert core_type is not None, "core.BNCreateWideCharType returned None" + return cls(core.BNNewTypeReference(core_type), platform, confidence) + +Types = { + TypeClass.VoidTypeClass:VoidType, + TypeClass.BoolTypeClass:BoolType, + TypeClass.IntegerTypeClass:IntegerType, + TypeClass.FloatTypeClass:FloatType, + TypeClass.StructureTypeClass:StructureType, + TypeClass.EnumerationTypeClass:EnumerationType, + TypeClass.PointerTypeClass:PointerType, + TypeClass.ArrayTypeClass:ArrayType, + TypeClass.FunctionTypeClass:FunctionType, + TypeClass.NamedTypeReferenceClass:NamedTypeReferenceType, + TypeClass.WideCharTypeClass:WideCharType, +} + +MutableTypes = { + TypeClass.VoidTypeClass:Void, + TypeClass.BoolTypeClass:Bool, + TypeClass.IntegerTypeClass:Integer, + TypeClass.FloatTypeClass:Float, + TypeClass.StructureTypeClass:Structure, + TypeClass.EnumerationTypeClass:Enumeration, + TypeClass.PointerTypeClass:Pointer, + TypeClass.ArrayTypeClass:Array, + TypeClass.FunctionTypeClass:Function, + TypeClass.NamedTypeReferenceClass:NamedTypeReference, + TypeClass.WideCharTypeClass:WideChar, +} + + +@dataclass(frozen=True) +class RegisterSet: + regs:List['architecture.RegisterName'] + confidence:int=core.max_confidence + + def __iter__(self) -> Generator['architecture.RegisterName', None, None]: + for reg in self.regs: + yield reg + + def __getitem__(self, idx): + return self.regs[idx] + + def __len__(self): + return len(self.regs) + + def with_confidence(self, confidence): + return RegisterSet(list(self.regs), confidence=confidence) + + - def mutable_copy(self): - if self._mutable: - return Enumeration(core.BNDuplicateEnumerationBuilder(self._handle)) - return Enumeration(core.BNCreateEnumerationBuilderFromEnumeration(self._handle)) @dataclass(frozen=True) @@ -1393,7 +2043,7 @@ class TypeParserResult: functions:Mapping[QualifiedName, Type] def __repr__(self): - return "<types: %s, variables: %s, functions: %s>" % (self.types, self.variables, self.functions) + return f"<types: {self.types}, variables: {self.variables}, functions: {self.functions}>" def preprocess_source(source:str, filename:str=None, include_dirs:List[str]=[]) -> Tuple[Optional[str], str]: @@ -1434,7 +2084,7 @@ def preprocess_source(source:str, filename:str=None, include_dirs:List[str]=[]) @dataclass(frozen=True) class TypeFieldReference: - func:Optional['function.Function'] + func:Optional['_function.Function'] arch:Optional['architecture.Architecture'] address:int size:int |
