# 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)
[docs]
class ConstantRenderer(metaclass=_ConstantRendererMetaClass):
_registered_renderers = []
renderer_name = None
[docs]
def __init__(self, handle=None):
if handle is not None:
self.handle = core.handle_of_type(handle, core.BNConstantRenderer)
[docs]
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
[docs]
def is_valid_for_type(self, func: 'highlevelil.HighLevelILFunction', type: 'types.Type') -> bool:
return True
[docs]
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
[docs]
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 = {}
[docs]
class CoreConstantRenderer(ConstantRenderer):
[docs]
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)
[docs]
def is_valid_for_type(self, func: 'highlevelil.HighLevelILFunction', type: 'types.Type') -> bool:
return core.BNIsConstantRendererValidForType(self.handle, func.handle, type.handle)
[docs]
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)
[docs]
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)