summaryrefslogtreecommitdiff
path: root/python
diff options
context:
space:
mode:
authorRusty Wagner <rusty.wagner@gmail.com>2025-01-20 18:44:25 -0500
committerRusty Wagner <rusty.wagner@gmail.com>2025-01-20 18:44:25 -0500
commite78cae77103b5396ce42d9e33593ea55f9135be0 (patch)
treeb44cb4bf7732c1e2e77ae39335eb4e2f02d9af84 /python
parentcbd4d7f12d54ddc4b6d3d90a8d7b49591f468a94 (diff)
Revert "Add line formatter API and a generic line formatter plugin"
This reverts commit 1699c71999d29d32aba5c9f8fea193a661a4b02b.
Diffstat (limited to 'python')
-rw-r--r--python/__init__.py1
-rw-r--r--python/examples/pseudo_python.py13
-rw-r--r--python/function.py12
-rw-r--r--python/highlevelil.py53
-rw-r--r--python/languagerepresentation.py34
-rw-r--r--python/lineformatter.py291
6 files changed, 32 insertions, 372 deletions
diff --git a/python/__init__.py b/python/__init__.py
index 1cdc541e..9b5a394e 100644
--- a/python/__init__.py
+++ b/python/__init__.py
@@ -80,7 +80,6 @@ from .externallibrary import *
from .undo import *
from .fileaccessor import *
from .languagerepresentation import *
-from .lineformatter import *
# We import each of these by name to prevent conflicts between
# log.py and the function 'log' which we don't import below
from .log import (
diff --git a/python/examples/pseudo_python.py b/python/examples/pseudo_python.py
index 655039fe..6a9e401b 100644
--- a/python/examples/pseudo_python.py
+++ b/python/examples/pseudo_python.py
@@ -316,7 +316,16 @@ class PseudoPythonFunction(LanguageRepresentationFunction):
tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ": "))
tokens.append(instr.var.type.get_tokens())
elif instr.operation == HighLevelILOperation.HLIL_FLOAT_CONST:
- tokens.append(InstructionTextToken(InstructionTextTokenType.FloatingPointToken, f"{instr.constant:g}"))
+ # The constant value in the instruction contains the raw bits of the floating point value. Convert
+ # this to a floating point value and display it.
+ if instr.size == 4:
+ value = struct.unpack("<f", struct.pack("<I", instr.constant))[0]
+ tokens.append(InstructionTextToken(InstructionTextTokenType.FloatToken, f"{value:g}"))
+ elif instr.size == 8:
+ value = struct.unpack("<d", struct.pack("<Q", instr.constant))[0]
+ tokens.append(InstructionTextToken(InstructionTextTokenType.FloatToken, f"{value:g}"))
+ else:
+ tokens.append_integer_text_token(instr, instr.constant, instr.size)
elif instr.operation == HighLevelILOperation.HLIL_CONST:
# Check for bool type. Display these as True or False. The default handling will use C style
# booleans instead of Python style.
@@ -1066,7 +1075,7 @@ class PseudoPythonFunctionType(LanguageRepresentationFunctionType):
language_name = "Pseudo Python"
def create(self, arch: Architecture, owner: Function, hlil: HighLevelILFunction):
- return PseudoPythonFunction(self, arch, owner, hlil)
+ return PseudoPythonFunction(arch, owner, hlil)
def function_type_tokens(self, func: Function, settings: DisassemblySettings) -> DisassemblyTextLine:
tokens = []
diff --git a/python/function.py b/python/function.py
index 0fc60ffc..e962d91f 100644
--- a/python/function.py
+++ b/python/function.py
@@ -135,18 +135,6 @@ class DisassemblySettings:
option = DisassemblyOption[option]
core.BNSetDisassemblySettingsOption(self.handle, option, state)
- @staticmethod
- def default_settings() -> 'DisassemblySettings':
- return DisassemblySettings(core.BNDefaultDisassemblySettings())
-
- @staticmethod
- def default_graph_settings() -> 'DisassemblySettings':
- return DisassemblySettings(core.BNDefaultGraphDisassemblySettings())
-
- @staticmethod
- def default_linear_settings() -> 'DisassemblySettings':
- return DisassemblySettings(core.BNDefaultLinearDisassemblySettings())
-
@dataclass
class ILReferenceSource:
diff --git a/python/highlevelil.py b/python/highlevelil.py
index 85be4794..1d7feb71 100644
--- a/python/highlevelil.py
+++ b/python/highlevelil.py
@@ -26,10 +26,7 @@ from enum import Enum
# Binary Ninja components
from . import _binaryninjacore as core
-from .enums import (
- HighLevelILOperation, DataFlowQueryOption, FunctionGraphType, ILInstructionAttribute, StringType,
- DisassemblyOption
-)
+from .enums import HighLevelILOperation, DataFlowQueryOption, FunctionGraphType, ILInstructionAttribute, StringType
from . import function
from . import binaryview
from . import architecture
@@ -334,9 +331,7 @@ class HighLevelILInstruction(BaseILInstruction):
return ILInstruction[instr.operation](func, expr_index, core_instr, as_ast, instr_index)
def __str__(self):
- settings = function.DisassemblySettings.default_settings()
- settings.set_option(DisassemblyOption.DisableLineFormatting)
- lines = self.get_lines(settings)
+ lines = self.lines
if lines is None:
return "invalid"
result = []
@@ -348,9 +343,7 @@ class HighLevelILInstruction(BaseILInstruction):
return '\n'.join(result)
def __repr__(self):
- settings = function.DisassemblySettings.default_settings()
- settings.set_option(DisassemblyOption.DisableLineFormatting)
- lines = self.get_lines(settings)
+ lines = self.lines
continuation = ""
if lines is None:
first_line = "<invalid>"
@@ -393,14 +386,26 @@ class HighLevelILInstruction(BaseILInstruction):
@property
def tokens(self) -> TokenList:
"""HLIL tokens taken from the HLIL text lines (read-only) -- does not include newlines or indentation, use lines for that information"""
- settings = function.DisassemblySettings.default_settings()
- settings.set_option(DisassemblyOption.DisableLineFormatting)
- return [token for line in self.get_lines(settings) for token in line.tokens]
+ return [token for line in self.lines for token in line.tokens]
@property
def lines(self) -> LinesType:
"""HLIL text lines (read-only)"""
- return self.get_lines()
+ count = ctypes.c_ulonglong()
+ lines = core.BNGetHighLevelILExprText(self.function.handle, self.expr_index, self.as_ast, count, None)
+ assert lines is not None, "core.BNGetHighLevelILExprText returned None"
+ try:
+ for i in range(0, count.value):
+ addr = lines[i].addr
+ if lines[i].instrIndex != 0xffffffffffffffff:
+ il_instr = self.function[lines[i].instrIndex]
+ else:
+ il_instr = None
+ color = highlight.HighlightColor._from_core_struct(lines[i].highlight)
+ tokens = function.InstructionTextToken._from_core_struct(lines[i].tokens, lines[i].count)
+ yield function.DisassemblyTextLine(tokens, addr, il_instr, color)
+ finally:
+ core.BNFreeDisassemblyTextLines(lines, count.value)
@property
def prefix_operands(self) -> List[Union[HighLevelILOperandType, HighLevelILOperationAndSize]]:
@@ -913,26 +918,6 @@ class HighLevelILInstruction(BaseILInstruction):
def has_side_effects(self) -> bool:
return core.BNHighLevelILHasSideEffects(self.function.handle, self.expr_index)
- def get_lines(self, settings: Optional['function.DisassemblySettings'] = None) -> LinesType:
- """Gets HLIL text lines with optional settings"""
- if settings is not None:
- settings = settings.handle
- count = ctypes.c_ulonglong()
- lines = core.BNGetHighLevelILExprText(self.function.handle, self.expr_index, self.as_ast, count, settings)
- assert lines is not None, "core.BNGetHighLevelILExprText returned None"
- try:
- for i in range(0, count.value):
- addr = lines[i].addr
- if lines[i].instrIndex != 0xffffffffffffffff:
- il_instr = self.function[lines[i].instrIndex]
- else:
- il_instr = None
- color = highlight.HighlightColor._from_core_struct(lines[i].highlight)
- tokens = function.InstructionTextToken._from_core_struct(lines[i].tokens, lines[i].count)
- yield function.DisassemblyTextLine(tokens, addr, il_instr, color)
- finally:
- core.BNFreeDisassemblyTextLines(lines, count.value)
-
@dataclass(frozen=True, repr=False, eq=False)
class HighLevelILUnaryBase(HighLevelILInstruction, UnaryOperation):
diff --git a/python/languagerepresentation.py b/python/languagerepresentation.py
index a0a5a234..ac34c860 100644
--- a/python/languagerepresentation.py
+++ b/python/languagerepresentation.py
@@ -30,7 +30,6 @@ from . import binaryview
from . import function
from . import highlevelil
from . import highlight
-from . import lineformatter
from . import variable
from . import types
from .log import log_error
@@ -328,8 +327,7 @@ class LanguageRepresentationFunction:
annotation_end_string = "}"
def __init__(
- self, func_type: Optional['LanguageRepresentationFunctionType'] = None,
- arch: Optional['architecture.Architecture'] = None, owner: Optional['function.Function'] = None,
+ self, arch: Optional['architecture.Architecture'] = None, owner: Optional['function.Function'] = None,
hlil: Optional['highlevelil.HighLevelILFunction'] = None, handle=None
):
if handle is None:
@@ -356,9 +354,7 @@ class LanguageRepresentationFunction:
self.comment_end_string = self.__class__.comment_end_string
self.annotation_start_string = self.__class__.annotation_start_string
self.annotation_end_string = self.__class__.annotation_end_string
- _handle = core.BNCreateCustomLanguageRepresentationFunction(
- func_type.handle, arch.handle, owner.handle, hlil.handle, self._cb
- )
+ _handle = core.BNCreateCustomLanguageRepresentationFunction(arch.handle, owner.handle, hlil.handle, self._cb)
assert _handle is not None
else:
self.comment_start_string = core.BNGetLanguageRepresentationFunctionCommentStartString(handle)
@@ -625,7 +621,6 @@ class LanguageRepresentationFunctionType(metaclass=_LanguageRepresentationFuncti
self._cb.isValid = self._cb.isValid.__class__(self._is_valid)
self._cb.getTypePrinter = self._cb.getTypePrinter.__class__(self._type_printer)
self._cb.getTypeParser = self._cb.getTypeParser.__class__(self._type_parser)
- self._cb.getLineFormatter = self._cb.getLineFormatter.__class__(self._line_formatter)
self._cb.getFunctionTypeTokens = self._cb.getFunctionTypeTokens.__class__(self._function_type_tokens)
self._cb.freeLines = self._cb.freeLines.__class__(self._free_lines)
self.handle = core.BNRegisterLanguageRepresentationFunctionType(self.__class__.language_name, self._cb)
@@ -677,16 +672,6 @@ class LanguageRepresentationFunctionType(metaclass=_LanguageRepresentationFuncti
log_error(traceback.format_exc())
return None
- def _line_formatter(self, ctxt):
- try:
- result = self.line_formatter
- if result is None:
- return None
- return ctypes.cast(result.handle, ctypes.c_void_p).value
- except:
- log_error(traceback.format_exc())
- return None
-
def _function_type_tokens(
self, ctxt, func: core.BNFunctionHandle, settings: Optional[core.BNDisassemblySettingsHandle],
count: ctypes.POINTER(ctypes.c_ulonglong)
@@ -766,14 +751,6 @@ class LanguageRepresentationFunctionType(metaclass=_LanguageRepresentationFuncti
"""
return None
- @property
- def line_formatter(self) -> Optional['lineformatter.LineFormatter']:
- """
- Returns the line formatter for formatting lines in this language. If ``None`` is returned, the default
- line formatter will be used.
- """
- return None
-
def function_type_tokens(
self, func: 'function.Function', settings: Optional['function.DisassemblySettings']
) -> List['function.DisassemblyTextLine']:
@@ -829,13 +806,6 @@ class CoreLanguageRepresentationFunctionType(LanguageRepresentationFunctionType)
return None
return binaryninja.typeparser.TypeParser(handle=result)
- @property
- def line_formatter(self) -> Optional['lineformatter.LineFormatter']:
- result = core.BNGetLanguageRepresentationFunctionTypeLineFormatter(self.handle)
- if result is None:
- return None
- return binaryninja.lineformatter.LineFormatter(handle=result)
-
def function_type_tokens(
self, func: 'function.Function', settings: Optional['function.DisassemblySettings']
) -> List['function.DisassemblyTextLine']:
diff --git a/python/lineformatter.py b/python/lineformatter.py
deleted file mode 100644
index 7f74572b..00000000
--- a/python/lineformatter.py
+++ /dev/null
@@ -1,291 +0,0 @@
-# Copyright (c) 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 ctypes
-import traceback
-from dataclasses import dataclass
-from typing import List, Optional, Union
-
-# Binary Ninja components
-import binaryninja
-from . import _binaryninjacore as core
-from . import function
-from . import highlevelil
-from . import highlight
-from . import languagerepresentation
-from .log import log_error
-from .enums import HighlightStandardColor
-
-
-@dataclass(frozen=True)
-class LineFormatterSettings:
- hlil: highlevelil.HighLevelILFunction
- desired_line_length: int
- minimum_content_length: int
- tab_width: int
- language_name: Optional[str]
- comment_start_string: str
- comment_end_string: str
- annotation_start_string: str
- annotation_end_string: str
-
- @staticmethod
- def default(settings: Optional['function.DisassemblySettings'], hlil: 'highlevelil.HighLevelILFunction') -> 'LineFormatterSettings':
- """
- Gets the default line formatter settings for High Level IL code.
- """
- if settings is not None:
- settings = settings.handle
- api_obj = core.BNGetDefaultLineFormatterSettings(settings, hlil.handle)
- result = LineFormatterSettings._from_core_struct(api_obj[0])
- core.BNFreeLineFormatterSettings(api_obj)
- return result
-
- @staticmethod
- def language_representation_settings(
- settings: Optional['function.DisassemblySettings'], func: 'languagerepresentation.LanguageRepresentationFunction'
- ) -> 'LineFormatterSettings':
- """
- Gets the default line formatter settings for a language representation function.
- """
- if settings is not None:
- settings = settings.handle
- api_obj = core.BNGetLanguageRepresentationLineFormatterSettings(settings, func.handle)
- result = LineFormatterSettings._from_core_struct(api_obj[0])
- core.BNFreeLineFormatterSettings(api_obj)
- return result
-
- @staticmethod
- def _from_core_struct(settings: core.BNLineFormatterSettings) -> 'LineFormatterSettings':
- if len(settings.languageName) == 0:
- language_name = None
- else:
- language_name = settings.languageName
- hlil = highlevelil.HighLevelILFunction(handle=core.BNNewHighLevelILFunctionReference(settings.highLevelIL))
- return LineFormatterSettings(
- hlil, settings.desiredLineLength, settings.minimumContentLength, settings.tabWidth, language_name,
- settings.commentStartString, settings.commentEndString,
- settings.annotationStartString, settings.annotationEndString
- )
-
- def _to_core_struct(self) -> core.BNLineFormatterSettings:
- result = core.BNLineFormatterSettings()
- result.highLevelIL = self.hlil.handle
- result.desiredLineLength = self.desired_line_length
- result.minimumContentLength = self.minimum_content_length
- result.tabWidth = self.tab_width
- result.languageName = self.language_name if self.language_name is not None else ""
- result.commentStartString = self.comment_start_string
- result.commentEndString = self.comment_end_string
- result.annotationStartString = self.annotation_start_string
- result.annotationEndString = self.annotation_end_string
- return result
-
-
-class _LineFormatterMetaClass(type):
- def __iter__(self):
- binaryninja._init_plugins()
- count = ctypes.c_ulonglong()
- types = core.BNGetLineFormatterList(count)
- assert types is not None, "core.BNGetLineFormatterList returned None"
- try:
- for i in range(0, count.value):
- yield CoreLineFormatter(handle=types[i])
- finally:
- core.BNFreeLineFormatterList(types)
-
- def __getitem__(cls, value):
- binaryninja._init_plugins()
- lang = core.BNGetLineFormatterByName(str(value))
- if lang is None:
- raise KeyError("'%s' is not a valid formatter" % str(value))
- return CoreLineFormatter(handle=lang)
-
-
-class LineFormatter(metaclass=_LineFormatterMetaClass):
- """
- ``class LineFormatter`` represents a custom line formatter, which can reformat code in High Level IL
- and high level language representations.
- """
- _registered_formatters = []
- formatter_name = None
-
- def __init__(self, handle=None):
- if handle is not None:
- self.handle = core.handle_of_type(handle, core.BNLineFormatter)
-
- def register(self):
- """Registers the line formatter."""
- if self.__class__.formatter_name is None:
- raise ValueError("formatter_name is missing")
- self._cb = core.BNCustomLineFormatter()
- self._cb.context = 0
- self._cb.formatLines = self._cb.formatLines.__class__(self._format_lines)
- self._cb.freeLines = self._cb.freeLines.__class__(self._free_lines)
- self.handle = core.BNRegisterLineFormatter(self.__class__.formatter_name, self._cb)
- self.__class__._registered_formatters.append(self)
-
- def _format_lines(
- self, ctxt, in_lines, in_count: int, settings: core.BNLineFormatterSettingsHandle,
- out_count: ctypes.POINTER(ctypes.c_ulonglong)
- ):
- try:
- settings = settings[0]
- if len(settings.languageName) == 0:
- language_name = None
- else:
- language_name = settings.languageName
- hlil = highlevelil.HighLevelILFunction(handle=core.BNNewHighLevelILFunctionReference(settings.highLevelIL))
- settings = LineFormatterSettings(
- hlil, settings.desiredLineLength, settings.minimumContentLength, settings.tabWidth, language_name,
- settings.commentStartString, settings.commentEndString,
- settings.annotationStartString, settings.annotationEndString
- )
-
- lines = []
- if in_lines is not None:
- for i in range(0, in_count):
- addr = in_lines[i].addr
- if in_lines[i].instrIndex != 0xffffffffffffffff:
- il_instr = hlil[in_lines[i].instrIndex] # type: ignore
- else:
- il_instr = None
- color = highlight.HighlightColor._from_core_struct(in_lines[i].highlight)
- tokens = function.InstructionTextToken._from_core_struct(in_lines[i].tokens, in_lines[i].count)
- lines.append(function.DisassemblyTextLine(tokens, addr, il_instr, color))
-
- lines = self.format_lines(lines, settings)
-
- out_count[0] = len(lines)
- self.line_buf = (core.BNDisassemblyTextLine * len(lines))()
- for i in range(len(lines)):
- line = lines[i]
- color = line.highlight
- if not isinstance(color, HighlightStandardColor) and not isinstance(color, highlight.HighlightColor):
- raise ValueError("Specified color is not one of HighlightStandardColor, highlight.HighlightColor")
- if isinstance(color, HighlightStandardColor):
- color = highlight.HighlightColor(color)
- self.line_buf[i].highlight = color._to_core_struct()
- if line.address is None:
- if len(line.tokens) > 0:
- self.line_buf[i].addr = line.tokens[0].address
- else:
- self.line_buf[i].addr = 0
- else:
- self.line_buf[i].addr = line.address
- if line.il_instruction is not None:
- self.line_buf[i].instrIndex = line.il_instruction.instr_index
- else:
- self.line_buf[i].instrIndex = 0xffffffffffffffff
-
- self.line_buf[i].count = len(line.tokens)
- self.line_buf[i].tokens = function.InstructionTextToken._get_core_struct(line.tokens)
-
- return ctypes.cast(self.line_buf, ctypes.c_void_p).value
- except:
- log_error(traceback.format_exc())
- out_count[0] = 0
- return None
-
- def _free_lines(self, ctxt, lines, count):
- self.line_buf = None
-
- def format_lines(
- self, in_lines: List['function.DisassemblyTextLine'], settings: 'LineFormatterSettings'
- ) -> List['function.DisassemblyTextLine']:
- """
- Reformats the given list of lines. Returns a new list of lines containing the reformatted code.
- """
- raise NotImplementedError
-
- @property
- def name(self) -> str:
- if hasattr(self, 'handle'):
- return core.BNGetLineFormatterName(self.handle)
- return self.__class__.formatter_name
-
- def __repr__(self):
- return f"<LineFormatter: {self.name}>"
-
-
-_formatter_cache = {}
-
-
-class CoreLineFormatter(LineFormatter):
- def __init__(self, handle: core.BNLineFormatter):
- super(CoreLineFormatter, self).__init__(handle=handle)
- if type(self) is CoreLineFormatter:
- global _formatter_cache
- _formatter_cache[ctypes.addressof(handle.contents)] = self
-
- def format_lines(
- self, in_lines: List['function.DisassemblyTextLine'], settings: 'LineFormatterSettings'
- ) -> List['function.DisassemblyTextLine']:
- line_buf = (core.BNDisassemblyTextLine * len(in_lines))()
- for i in range(len(in_lines)):
- line = in_lines[i]
- color = line.highlight
- if not isinstance(color, HighlightStandardColor) and not isinstance(color, highlight.HighlightColor):
- raise ValueError("Specified color is not one of HighlightStandardColor, highlight.HighlightColor")
- if isinstance(color, HighlightStandardColor):
- color = highlight.HighlightColor(color)
- line_buf[i].highlight = color._to_core_struct()
- if line.address is None:
- if len(line.tokens) > 0:
- line_buf[i].addr = line.tokens[0].address
- else:
- line_buf[i].addr = 0
- else:
- line_buf[i].addr = line.address
- if line.il_instruction is not None:
- line_buf[i].instrIndex = line.il_instruction.instr_index
- else:
- line_buf[i].instrIndex = 0xffffffffffffffff
-
- line_buf[i].count = len(line.tokens)
- line_buf[i].tokens = function.InstructionTextToken._get_core_struct(line.tokens)
-
- count = ctypes.c_ulonglong()
- lines = core.BNFormatLines(self.handle, line_buf, len(in_lines), settings._to_core_struct(), count)
-
- result = []
- if lines is not None:
- result = []
- for i in range(0, count.value):
- addr = lines[i].addr
- if lines[i].instrIndex != 0xffffffffffffffff:
- il_instr = settings.hlil[lines[i].instrIndex] # type: ignore
- else:
- il_instr = None
- color = highlight.HighlightColor._from_core_struct(lines[i].highlight)
- tokens = function.InstructionTextToken._from_core_struct(lines[i].tokens, lines[i].count)
- result.append(function.DisassemblyTextLine(tokens, addr, il_instr, color))
- core.BNFreeDisassemblyTextLines(lines, count.value)
- return result
-
- @classmethod
- def _from_cache(cls, handle) -> 'LineFormatter':
- """
- Look up a representation type from a given BNLineFormatter handle
- :param handle: BNLineFormatter pointer
- :return: Formatter instance responsible for this handle
- """
- global _formatter_cache
- return _formatter_cache.get(ctypes.addressof(handle.contents)) or cls(handle)