Source code for binaryninja.typeprinter

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

import ctypes
import dataclasses
from json import dumps
from typing import List, Tuple, Optional

import sys
import traceback

# Binary Ninja Components
import binaryninja
import binaryninja._binaryninjacore as core

from .settings import Settings
from . import platform as _platform
from . import types
from . import function as _function
from . import binaryview
from . import typecontainer
from .log import log_error
from .enums import TokenEscapingType


[docs] def to_bytes(field): if type(field) == bytes: return field if type(field) == str: return field.encode() return str(field).encode()
class _TypePrinterMetaclass(type): def __iter__(self): binaryninja._init_plugins() count = ctypes.c_ulonglong() types = core.BNGetTypePrinterList(count) try: for i in range(0, count.value): yield CoreTypePrinter(types[i]) finally: core.BNFreeTypePrinterList(types) def __getitem__(self, value): binaryninja._init_plugins() handle = core.BNGetTypePrinterByName(str(value)) if handle is None: raise KeyError(f"'{value}' is not a valid TypePrinter") return CoreTypePrinter(handle) @property def default(self): name = binaryninja.Settings().get_string("analysis.types.printerName") return CoreTypePrinter[name]
[docs] class TypePrinter(metaclass=_TypePrinterMetaclass): """ Class for turning Type objects into strings and tokens. """ name = None _registered_printers = [] _cached_tokens = None _cached_string = None _cached_error = None def __init__(self, handle=None): if handle is not None: self.handle = core.handle_of_type(handle, core.BNTypePrinter) self.__dict__["name"] = core.BNGetTypePrinterName(handle)
[docs] def register(self): assert self.__class__.name is not None self._cb = core.BNTypePrinterCallbacks() self._cb.context = 0 self._cb.getTypeTokens = self._cb.getTypeTokens.__class__(self._get_type_tokens) self._cb.getTypeTokensBeforeName = self._cb.getTypeTokensBeforeName.__class__(self._get_type_tokens_before_name) self._cb.getTypeTokensAfterName = self._cb.getTypeTokensAfterName.__class__(self._get_type_tokens_after_name) self._cb.getTypeString = self._cb.getTypeString.__class__(self._get_type_string) self._cb.getTypeStringBeforeName = self._cb.getTypeStringBeforeName.__class__(self._get_type_string_before_name) self._cb.getTypeStringAfterName = self._cb.getTypeStringAfterName.__class__(self._get_type_string_after_name) self._cb.getTypeLines = self._cb.getTypeLines.__class__(self._get_type_lines) self._cb.printAllTypes = self._cb.printAllTypes.__class__(self._print_all_types) self._cb.freeTokens = self._cb.freeTokens.__class__(self._free_tokens) self._cb.freeString = self._cb.freeString.__class__(self._free_string) self._cb.freeLines = self._cb.freeLines.__class__(self._free_lines) self.handle = core.BNRegisterTypePrinter(self.__class__.name, self._cb) self.__class__._registered_printers.append(self)
def __str__(self): return f'<TypePrinter: {self.name}>' def __repr__(self): return f'<TypePrinter: {self.name}>' def _get_type_tokens(self, ctxt, type, platform, name, base_confidence, escaping, result, result_count): try: platform_py = None if platform: platform_py = _platform.Platform(handle=core.BNNewPlatformReference(platform)) result_py = self.get_type_tokens( types.Type.create(handle=core.BNNewTypeReference(type)), platform_py, types.QualifiedName._from_core_struct(name.contents), base_confidence, escaping) TypePrinter._cached_tokens = _function.InstructionTextToken._get_core_struct(result_py) result[0] = TypePrinter._cached_tokens result_count[0] = len(result_py) return True except: log_error(traceback.format_exc()) return False def _get_type_tokens_before_name(self, ctxt, type, platform, base_confidence, parent_type, escaping, result, result_count): try: platform_py = None if platform: platform_py = _platform.Platform(handle=core.BNNewPlatformReference(platform)) parent_type_py = None if parent_type: parent_type_py = types.Type.create(handle=core.BNNewTypeReference(parent_type)) result_py = self.get_type_tokens_before_name( types.Type.create(handle=core.BNNewTypeReference(type)), platform_py, base_confidence, parent_type_py, escaping) TypePrinter._cached_tokens = _function.InstructionTextToken._get_core_struct(result_py) result[0] = TypePrinter._cached_tokens result_count[0] = len(result_py) return True except: log_error(traceback.format_exc()) return False def _get_type_tokens_after_name(self, ctxt, type, platform, base_confidence, parent_type, escaping, result, result_count): try: platform_py = None if platform: platform_py = _platform.Platform(handle=core.BNNewPlatformReference(platform)) parent_type_py = None if parent_type: parent_type_py = types.Type.create(handle=core.BNNewTypeReference(parent_type)) result_py = self.get_type_tokens_after_name( types.Type.create(handle=core.BNNewTypeReference(type)), platform_py, base_confidence, parent_type_py, escaping) TypePrinter._cached_tokens = _function.InstructionTextToken._get_core_struct(result_py) result[0] = TypePrinter._cached_tokens result_count[0] = len(result_py) return True except: log_error(traceback.format_exc()) return False def _get_type_string(self, ctxt, type, platform, name, escaping, result): try: platform_py = None if platform: platform_py = _platform.Platform(handle=core.BNNewPlatformReference(platform)) result_py = self.get_type_string( types.Type.create(handle=core.BNNewTypeReference(type)), platform_py, types.QualifiedName._from_core_struct(name.contents), escaping) TypePrinter._cached_string = core.cstr(result_py) result[0] = TypePrinter._cached_string return True except: log_error(traceback.format_exc()) return False def _get_type_string_before_name(self, ctxt, type, platform, escaping, result): try: platform_py = None if platform: platform_py = _platform.Platform(handle=core.BNNewPlatformReference(platform)) result_py = self.get_type_string_before_name( types.Type.create(handle=core.BNNewTypeReference(type)), platform_py, escaping) TypePrinter._cached_string = core.cstr(result_py) result[0] = TypePrinter._cached_string return True except: log_error(traceback.format_exc()) return False def _get_type_string_after_name(self, ctxt, type, platform, escaping, result): try: platform_py = None if platform: platform_py = _platform.Platform(handle=core.BNNewPlatformReference(platform)) result_py = self.get_type_string_after_name( types.Type.create(handle=core.BNNewTypeReference(type)), platform_py, escaping) TypePrinter._cached_string = core.cstr(result_py) result[0] = TypePrinter._cached_string return True except: log_error(traceback.format_exc()) return False def _get_type_lines(self, ctxt, type, container, name, padding_cols, collapsed, escaping, result, result_count): try: result_py = self.get_type_lines( types.Type.create(handle=core.BNNewTypeReference(type)), typecontainer.TypeContainer(handle=container), types.QualifiedName._from_core_struct(name.contents), padding_cols, collapsed, escaping) TypePrinter._cached_lines = (core.BNTypeDefinitionLine * len(result_py))() for (i, line) in enumerate(result_py): TypePrinter._cached_lines[i] = line._to_core_struct() result[0] = TypePrinter._cached_lines result_count[0] = len(result_py) return True except: log_error(traceback.format_exc()) return False def _print_all_types(self, ctxt, names, types_, type_count, data, padding_cols, escaping, result): try: types_py = [] for i in range(type_count): types_py.append(( types.QualifiedName._from_core_struct(names[i]), types.Type.create(handle=core.BNNewTypeReference(types_[i])) )) result_py = self.print_all_types( types_py, binaryview.BinaryView(handle=core.BNNewViewReference(data)), padding_cols, escaping) TypePrinter._cached_string = core.cstr(result_py) result[0] = TypePrinter._cached_string return True except: log_error(traceback.format_exc()) return False def _free_tokens(self, ctxt, tokens, count): try: TypePrinter._cached_tokens = None return True except: log_error(traceback.format_exc()) return False def _free_string(self, ctxt, string): try: TypePrinter._cached_string = None return True except: log_error(traceback.format_exc()) return False def _free_lines(self, ctxt, lines, count): try: for line in TypePrinter._cached_lines: core.BNFreeType(line.type) core.BNFreeType(line.rootType) TypePrinter._cached_lines = None return True except: log_error(traceback.format_exc()) return False def _default_print_all_types(self, types_: List[Tuple[types.QualifiedNameType, types.Type]], data: binaryview.BinaryView, padding_cols = 64, escaping: TokenEscapingType = TokenEscapingType.BackticksTokenEscapingType) -> str: cpp_names = (core.BNQualifiedName * len(types_))() cpp_types = (ctypes.POINTER(core.BNType) * len(types_))() i = 0 for (name, type) in types_: cpp_names[i] = types.QualifiedName(name)._to_core_struct() cpp_types[i] = type.handle i += 1 result = ctypes.c_char_p() core.BNTypePrinterDefaultPrintAllTypes(self.handle, cpp_names, cpp_types, len(types_), data.handle, padding_cols, ctypes.c_int(escaping), result) return core.pyNativeStr(result.value)
[docs] def get_type_tokens(self, type: types.Type, platform: Optional[_platform.Platform] = None, name: types.QualifiedNameType = "", base_confidence: int = core.max_confidence, escaping: TokenEscapingType = TokenEscapingType.BackticksTokenEscapingType) -> List[_function.InstructionTextToken]: """ Generate a single-line text representation of a type :param type: Type to print :param platform: Platform responsible for this type :param name: Name of the type :param base_confidence: Confidence to use for tokens created for this type :param escaping: Style of escaping literals which may not be parsable :return: List of text tokens representing the type """ raise NotImplementedError()
[docs] def get_type_tokens_before_name(self, type: types.Type, platform: Optional[_platform.Platform] = None, base_confidence: int = core.max_confidence, parent_type: Optional[types.Type] = None, escaping: TokenEscapingType = TokenEscapingType.BackticksTokenEscapingType) -> List[_function.InstructionTextToken]: """ In a single-line text representation of a type, generate the tokens that should be printed before the type's name. :param type: Type to print :param platform: Platform responsible for this type :param base_confidence: Confidence to use for tokens created for this type :param parent_type: Type of the parent of this type, or None :param escaping: Style of escaping literals which may not be parsable :return: List of text tokens representing the type """ raise NotImplementedError()
[docs] def get_type_tokens_after_name(self, type: types.Type, platform: Optional[_platform.Platform] = None, base_confidence: int = core.max_confidence, parent_type: Optional[types.Type] = None, escaping: TokenEscapingType = TokenEscapingType.BackticksTokenEscapingType) -> List[_function.InstructionTextToken]: """ In a single-line text representation of a type, generate the tokens that should be printed after the type's name. :param type: Type to print :param platform: Platform responsible for this type :param base_confidence: Confidence to use for tokens created for this type :param parent_type: Type of the parent of this type, or None :param escaping: Style of escaping literals which may not be parsable :return: List of text tokens representing the type """ raise NotImplementedError()
[docs] def get_type_string(self, type: types.Type, platform: Optional[_platform.Platform] = None, name: types.QualifiedNameType = "", escaping: TokenEscapingType = TokenEscapingType.BackticksTokenEscapingType) -> str: """ Generate a single-line text representation of a type :param type: Type to print :param platform: Platform responsible for this type :param name: Name of the type :param escaping: Style of escaping literals which may not be parsable :return: String representing the type """ raise NotImplementedError()
[docs] def get_type_string_before_name(self, type: types.Type, platform: Optional[_platform.Platform] = None, escaping: TokenEscapingType = TokenEscapingType.BackticksTokenEscapingType) -> str: """ In a single-line text representation of a type, generate the string that should be printed before the type's name. :param type: Type to print :param platform: Platform responsible for this type :param escaping: Style of escaping literals which may not be parsable :return: String representing the type """ raise NotImplementedError()
[docs] def get_type_string_after_name(self, type: types.Type, platform: Optional[_platform.Platform] = None, escaping: TokenEscapingType = TokenEscapingType.BackticksTokenEscapingType) -> str: """ In a single-line text representation of a type, generate the string that should be printed after the type's name. :param type: Type to print :param platform: Platform responsible for this type :param escaping: Style of escaping literals which may not be parsable :return: String representing the type """ raise NotImplementedError()
[docs] def get_type_lines(self, type: types.Type, container: 'typecontainer.TypeContainer', name: types.QualifiedNameType, padding_cols = 64, collapsed = False, escaping: TokenEscapingType = TokenEscapingType.BackticksTokenEscapingType) -> List[types.TypeDefinitionLine]: """ Generate a multi-line representation of a type :param type: Type to print :param container: Type Container containing the type and dependencies :param name: Name of the type :param padding_cols: Maximum number of bytes represented by each padding line :param collapsed: Whether to collapse structure/enum blocks :param escaping: Style of escaping literals which may not be parsable :return: List of type definition lines """ raise NotImplementedError()
[docs] def print_all_types(self, types: List[Tuple[types.QualifiedNameType, types.Type]], data: binaryview.BinaryView, padding_cols = 64, escaping: TokenEscapingType = TokenEscapingType.BackticksTokenEscapingType) -> str: """ Print all types to a single big string, including headers, sections, etc :param types: All types to print :param data: Binary View in which all the types are defined :param padding_cols: Maximum number of bytes represented by each padding line :param escaping: Style of escaping literals which may not be parsable :return: All the types in a string """ return self._default_print_all_types(types, data, padding_cols, escaping)
[docs] class CoreTypePrinter(TypePrinter):
[docs] def get_type_tokens(self, type: types.Type, platform: Optional[_platform.Platform] = None, name: types.QualifiedNameType = "", base_confidence: int = core.max_confidence, escaping: TokenEscapingType = TokenEscapingType.BackticksTokenEscapingType) -> List[ _function.InstructionTextToken]: if not isinstance(name, types.QualifiedName): name = types.QualifiedName(name) count = ctypes.c_ulonglong() name_cpp = name._to_core_struct() result_cpp = ctypes.POINTER(core.BNInstructionTextToken)() if not core.BNGetTypePrinterTypeTokens(self.handle, type.handle, None if platform is None else platform.handle, name_cpp, base_confidence, ctypes.c_int(escaping), result_cpp, count): raise RuntimeError("BNGetTypePrinterTypeTokens returned False") result = _function.InstructionTextToken._from_core_struct(result_cpp, count.value) core.BNFreeInstructionText(result_cpp.contents, count.value) return result
[docs] def get_type_tokens_before_name(self, type: types.Type, platform: Optional[_platform.Platform] = None, base_confidence: int = core.max_confidence, parent_type: Optional[types.Type] = None, escaping: TokenEscapingType = TokenEscapingType.BackticksTokenEscapingType) -> List[ _function.InstructionTextToken]: count = ctypes.c_ulonglong() result_cpp = ctypes.POINTER(core.BNInstructionTextToken)() parent_type_cpp = None if parent_type is not None: parent_type_cpp = parent_type.handle if not core.BNGetTypePrinterTypeTokensBeforeName(self.handle, type.handle, None if platform is None else platform.handle, base_confidence, parent_type_cpp, ctypes.c_int(escaping), result_cpp, count): raise RuntimeError("BNGetTypePrinterTypeTokensBeforeName returned False") result = _function.InstructionTextToken._from_core_struct(result_cpp, count.value) core.BNFreeInstructionText(result_cpp.contents, count.value) return result
[docs] def get_type_tokens_after_name(self, type: types.Type, platform: Optional[_platform.Platform] = None, base_confidence: int = core.max_confidence, parent_type: Optional[types.Type] = None, escaping: TokenEscapingType = TokenEscapingType.BackticksTokenEscapingType) -> List[ _function.InstructionTextToken]: count = ctypes.c_ulonglong() result_cpp = ctypes.POINTER(core.BNInstructionTextToken)() parent_type_cpp = None if parent_type is not None: parent_type_cpp = parent_type.handle if not core.BNGetTypePrinterTypeTokensAfterName(self.handle, type.handle, None if platform is None else platform.handle, base_confidence, parent_type_cpp, ctypes.c_int(escaping), result_cpp, count): raise RuntimeError("BNGetTypePrinterTypeTokensAfterName returned False") result = _function.InstructionTextToken._from_core_struct(result_cpp, count.value) core.BNFreeInstructionText(result_cpp.contents, count.value) return result
[docs] def get_type_string(self, type: types.Type, platform: Optional[_platform.Platform] = None, name: types.QualifiedNameType = "", escaping: TokenEscapingType = TokenEscapingType.BackticksTokenEscapingType) -> str: if not isinstance(name, types.QualifiedName): name = types.QualifiedName(name) result_cpp = ctypes.c_char_p() if not core.BNGetTypePrinterTypeString(self.handle, type.handle, None if platform is None else platform.handle, name._to_core_struct(), ctypes.c_int(escaping), result_cpp): raise RuntimeError("BNGetTypePrinterTypeString returned False") result = core.pyNativeStr(result_cpp.value) core.free_string(result_cpp) return result
[docs] def get_type_string_before_name(self, type: types.Type, platform: Optional[_platform.Platform] = None, escaping: TokenEscapingType = TokenEscapingType.BackticksTokenEscapingType) -> str: result_cpp = ctypes.c_char_p() if not core.BNGetTypePrinterTypeStringBeforeName(self.handle, type.handle, None if platform is None else platform.handle, ctypes.c_int(escaping), result_cpp): raise RuntimeError("BNGetTypePrinterTypeStringBeforeName returned False") result = core.pyNativeStr(result_cpp.value) core.free_string(result_cpp) return result
[docs] def get_type_string_after_name(self, type: types.Type, platform: Optional[_platform.Platform] = None, escaping: TokenEscapingType = TokenEscapingType.BackticksTokenEscapingType) -> str: result_cpp = ctypes.c_char_p() if not core.BNGetTypePrinterTypeStringAfterName(self.handle, type.handle, None if platform is None else platform.handle, ctypes.c_int(escaping), result_cpp): raise RuntimeError("BNGetTypePrinterTypeStringAfterName returned False") result = core.pyNativeStr(result_cpp.value) core.free_string(result_cpp) return result
[docs] def get_type_lines(self, type: types.Type, container: 'typecontainer.TypeContainer', name: types.QualifiedNameType, padding_cols = 64, collapsed = False, escaping: TokenEscapingType = TokenEscapingType.BackticksTokenEscapingType ) -> List[types.TypeDefinitionLine]: if not isinstance(name, types.QualifiedName): name = types.QualifiedName(name) count = ctypes.c_ulonglong() core_lines = ctypes.POINTER(core.BNTypeDefinitionLine)() if not core.BNGetTypePrinterTypeLines(self.handle, type.handle, container.handle, name._to_core_struct(), padding_cols, collapsed, ctypes.c_int(escaping), core_lines, count): raise RuntimeError("BNGetTypePrinterTypeLines returned False") lines = [] for i in range(count.value): line = types.TypeDefinitionLine._from_core_struct(core_lines[i], container.platform) lines.append(line) core.BNFreeTypeDefinitionLineList(core_lines, count.value) return lines
[docs] def print_all_types(self, types_: List[Tuple[types.QualifiedNameType, types.Type]], data: binaryview.BinaryView, padding_cols = 64, escaping: TokenEscapingType = TokenEscapingType.BackticksTokenEscapingType) -> str: cpp_names = (core.BNQualifiedName * len(types_))() cpp_types = (ctypes.POINTER(core.BNType) * len(types_))() i = 0 for (name, type) in types_: cpp_names[i] = types.QualifiedName(name)._to_core_struct() cpp_types[i] = type.handle i += 1 result = ctypes.c_char_p() core.BNTypePrinterPrintAllTypes(self.handle, cpp_names, cpp_types, len(types_), data.handle, padding_cols, ctypes.c_int(escaping), result) return core.pyNativeStr(result.value)