# Copyright (c) 2015-2025 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 traceback import ctypes from typing import Optional import binaryninja from . import _binaryninjacore as core from . import function from . import enums from .log import log_error_for_exception from . import types from . import highlevelil from . import languagerepresentation class _ConstantRendererMetaClass(type): def __iter__(self): binaryninja._init_plugins() count = ctypes.c_ulonglong() renderers = core.BNGetConstantRendererList(count) assert renderers is not None, "core.BNGetConstantRendererList returned None" try: for i in range(0, count.value): yield CoreConstantRenderer(handle=renderers[i]) finally: core.BNFreeConstantRendererList(renderers) def __getitem__(cls, value): binaryninja._init_plugins() renderer = core.BNGetConstantRendererByName(str(value)) if renderer is None: raise KeyError("'%s' is not a valid renderer" % str(value)) return CoreConstantRenderer(handle=renderer) class ConstantRenderer(metaclass=_ConstantRendererMetaClass): _registered_renderers = [] renderer_name = None def __init__(self, handle=None): if handle is not None: self.handle = core.handle_of_type(handle, core.BNConstantRenderer) def register(self): if self.__class__.renderer_name is None: raise ValueError("Renderer name is missing") self._cb = core.BNCustomConstantRenderer() self._cb.context = 0 if self.is_valid_for_type.__func__ != ConstantRenderer.is_valid_for_type: self._cb.isValidForType = self._cb.isValidForType.__class__(self._is_valid_for_type) if self.render_constant.__func__ != ConstantRenderer.render_constant: self._cb.renderConstant = self._cb.renderConstant.__class__(self._render_constant) if self.render_constant_pointer.__func__ != ConstantRenderer.render_constant_pointer: self._cb.renderConstantPointer = self._cb.renderConstantPointer.__class__(self._render_constant_pointer) self.handle = core.BNRegisterConstantRenderer(self.__class__.renderer_name, self._cb) self.__class__._registered_renderers.append(self) def _is_valid_for_type(self, ctxt, hlil, type): try: hlil = highlevelil.HighLevelILFunction(handle=core.BNNewHighLevelILFunctionReference(hlil)) type = types.Type.create(handle=core.BNNewTypeReference(type)) return self.is_valid_for_type(hlil, type) except: log_error_for_exception("Unhandled Python exception in ConstantRenderer._is_valid_for_type") return False def _render_constant(self, ctxt, hlil, expr, type, val, tokens, settings, precedence): try: hlil = highlevelil.HighLevelILFunction(handle=core.BNNewHighLevelILFunctionReference(hlil)) type = types.Type.create(handle=core.BNNewTypeReference(type)) tokens = languagerepresentation.HighLevelILTokenEmitter(core.BNNewHighLevelILTokenEmitterReference(tokens)) if settings: settings = function.DisassemblySettings(core.BNNewDisassemblySettingsReference(settings)) instr = hlil.get_expr(highlevelil.ExpressionIndex(expr)) return self.render_constant(instr, type, val, tokens, settings, precedence) except: log_error_for_exception("Unhandled Python exception in ConstantRenderer._render_constant_pointer") return False def _render_constant_pointer(self, ctxt, hlil, expr, type, val, tokens, settings, symbol_display, precedence): try: hlil = highlevelil.HighLevelILFunction(handle=core.BNNewHighLevelILFunctionReference(hlil)) type = types.Type.create(handle=core.BNNewTypeReference(type)) tokens = languagerepresentation.HighLevelILTokenEmitter(core.BNNewHighLevelILTokenEmitterReference(tokens)) if settings: settings = function.DisassemblySettings(core.BNNewDisassemblySettingsReference(settings)) symbol_display = enums.SymbolDisplayType(symbol_display) instr = hlil.get_expr(highlevelil.ExpressionIndex(expr)) return self.render_constant_pointer(instr, type, val, tokens, settings, symbol_display, precedence) except: log_error_for_exception("Unhandled Python exception in ConstantRenderer._render_constant_pointer") return False @property def name(self) -> str: if hasattr(self, 'handle'): return core.BNGetConstantRendererName(self.handle) return self.__class__.renderer_name def is_valid_for_type(self, func: 'highlevelil.HighLevelILFunction', type: 'types.Type') -> bool: return True def render_constant( self, instr: 'highlevelil.HighLevelILInstruction', type: 'types.Type', val: int, tokens: 'languagerepresentation.HighLevelILTokenEmitter', settings: Optional['function.DisassemblySettings'], precedence: 'enums.OperatorPrecedence' ) -> bool: return False def render_constant_pointer( self, instr: 'highlevelil.HighLevelILInstruction', type: 'types.Type', val: int, tokens: 'languagerepresentation.HighLevelILTokenEmitter', settings: Optional['function.DisassemblySettings'], symbol_display: 'enums.SymbolDisplayType', precedence: 'enums.OperatorPrecedence' ) -> bool: return False _renderer_cache = {} class CoreConstantRenderer(ConstantRenderer): def __init__(self, handle: core.BNConstantRenderer): super(CoreConstantRenderer, self).__init__(handle=handle) if type(self) is CoreConstantRenderer: global _renderer_cache _renderer_cache[ctypes.addressof(handle.contents)] = self @classmethod def _from_cache(cls, handle) -> 'ConstantRenderer': """ Look up a renderer type from a given BNConstantRenderer handle :param handle: BNConstantRenderer pointer :return: Renderer type instance responsible for this handle """ global _renderer_cache return _renderer_cache.get(ctypes.addressof(handle.contents)) or cls(handle) def is_valid_for_type(self, func: 'highlevelil.HighLevelILFunction', type: 'types.Type') -> bool: return core.BNIsConstantRendererValidForType(self.handle, func.handle, type.handle) def render_constant( self, instr: 'highlevelil.HighLevelILInstruction', type: 'types.Type', val: int, tokens: 'languagerepresentation.HighLevelILTokenEmitter', settings: Optional['function.DisassemblySettings'], precedence: 'enums.OperatorPrecedence' ) -> bool: if settings is not None: settings = settings.handle return core.BNConstantRendererRenderConstant(self.handle, instr.function.handle, instr.expr_index, type.handle, val, tokens.handle, settings, precedence) def render_constant_pointer( self, instr: 'highlevelil.HighLevelILInstruction', type: 'types.Type', val: int, tokens: 'languagerepresentation.HighLevelILTokenEmitter', settings: Optional['function.DisassemblySettings'], symbol_display: 'enums.SymbolDisplayType', precedence: 'enums.OperatorPrecedence' ) -> bool: if settings is not None: settings = settings.handle return core.BNConstantRendererRenderConstantPointer(self.handle, instr.function.handle, instr.expr_index, type.handle, val, tokens.handle, settings, symbol_display, precedence)