From cae26921cf2b2e564f66e58b77a3963fe36f6d2d Mon Sep 17 00:00:00 2001 From: Rusty Wagner Date: Mon, 8 Sep 2025 20:04:36 -0400 Subject: Add constant renderer API --- python/constantrenderer.py | 180 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 180 insertions(+) create mode 100644 python/constantrenderer.py (limited to 'python/constantrenderer.py') diff --git a/python/constantrenderer.py b/python/constantrenderer.py new file mode 100644 index 00000000..abd98be7 --- /dev/null +++ b/python/constantrenderer.py @@ -0,0 +1,180 @@ +# 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) -- cgit v1.3.1