# 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.
from typing import Optional, Union
from dataclasses import dataclass
import ctypes
import binaryninja
from . import _binaryninjacore as core
from .log import log_error_for_exception
from . import types
from . import highlevelil
from . import binaryview
class _CustomStringTypeMetaClass(type):
def __iter__(self):
binaryninja._init_plugins()
count = ctypes.c_ulonglong()
types = core.BNGetCustomStringTypeList(count)
assert types is not None, "core.BNGetCustomStringTypeList returned None"
try:
for i in range(0, count.value):
yield CustomStringType(handle=types[i])
finally:
core.BNFreeCustomStringTypeList(types)
def __getitem__(cls, value):
binaryninja._init_plugins()
string_type = core.BNGetCustomStringTypeByName(str(value))
if string_type is None:
raise KeyError("'%s' is not a valid type" % str(value))
return CustomStringType(handle=string_type)
[docs]
class CustomStringType(metaclass=_CustomStringTypeMetaClass):
[docs]
def __init__(self, handle):
self.handle = core.handle_of_type(handle, core.BNCustomStringType)
def __str__(self):
return self.name
def __repr__(self):
return f"<{self.__class__.__name__}: {self.name}>"
def __eq__(self, other):
if not isinstance(other, self.__class__):
return NotImplemented
return ctypes.addressof(self.handle.contents) == ctypes.addressof(other.handle.contents)
def __ne__(self, other):
if not isinstance(other, self.__class__):
return NotImplemented
return not (self == other)
def __hash__(self):
return hash(ctypes.addressof(self.handle.contents))
[docs]
@staticmethod
def register(name: str, string_prefix="", string_postfix="") -> 'CustomStringType':
info = core.BNCustomStringTypeInfo()
info.name = name
info.stringPrefix = string_prefix
info.stringPostfix = string_postfix
handle = core.BNRegisterCustomStringType(info)
return CustomStringType(handle)
@property
def name(self) -> str:
return core.BNGetCustomStringTypeName(self.handle)
@property
def string_prefix(self) -> str:
return core.BNGetCustomStringTypePrefix(self.handle)
@property
def string_postfix(self) -> str:
return core.BNGetCustomStringTypePostfix(self.handle)
class _StringRecognizerMetaClass(type):
def __iter__(self):
binaryninja._init_plugins()
count = ctypes.c_ulonglong()
recognizers = core.BNGetStringRecognizerList(count)
assert recognizers is not None, "core.BNGetStringRecognizerList returned None"
try:
for i in range(0, count.value):
yield CoreStringRecognizer(handle=recognizers[i])
finally:
core.BNFreeStringRecognizerList(recognizers)
def __getitem__(cls, value):
binaryninja._init_plugins()
recognizer = core.BNGetStringRecognizerByName(str(value))
if recognizer is None:
raise KeyError("'%s' is not a valid recognizer" % str(value))
return CoreStringRecognizer(handle=recognizer)
[docs]
class StringRecognizer(metaclass=_StringRecognizerMetaClass):
_registered_recognizers = []
recognizer_name = None
[docs]
def __init__(self, handle=None):
if handle is not None:
self.handle = core.handle_of_type(handle, core.BNStringRecognizer)
[docs]
def register(self):
if self.__class__.recognizer_name is None:
raise ValueError("Recognizer name is missing")
self._cb = core.BNCustomStringRecognizer()
self._cb.context = 0
if self.is_valid_for_type.__func__ != StringRecognizer.is_valid_for_type:
self._cb.isValidForType = self._cb.isValidForType.__class__(self._is_valid_for_type)
if self.recognize_constant.__func__ != StringRecognizer.recognize_constant:
self._cb.recognizeConstant = self._cb.recognizeConstant.__class__(self._recognize_constant)
if self.recognize_constant_pointer.__func__ != StringRecognizer.recognize_constant_pointer:
self._cb.recognizeConstantPointer = self._cb.recognizeConstantPointer.__class__(
self._recognize_constant_pointer)
if self.recognize_extern_pointer.__func__ != StringRecognizer.recognize_extern_pointer:
self._cb.recognizeExternPointer = self._cb.recognizeExternPointer.__class__(self._recognize_extern_pointer)
if self.recognize_import.__func__ != StringRecognizer.recognize_import:
self._cb.recognizeImport = self._cb.recognizeImport.__class__(self._recognize_import)
self.handle = core.BNRegisterStringRecognizer(self.__class__.recognizer_name, self._cb)
self.__class__._registered_recognizers.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 StringRecognizer._is_valid_for_type")
return False
def _recognize_constant(self, ctxt, hlil, expr, type, val, result):
try:
hlil = highlevelil.HighLevelILFunction(handle=core.BNNewHighLevelILFunctionReference(hlil))
type = types.Type.create(handle=core.BNNewTypeReference(type))
instr = hlil.get_expr(highlevelil.ExpressionIndex(expr))
ref = self.recognize_constant(instr, type, val)
if ref is None:
return False
result[0] = ref._to_core_struct(True)
return True
except:
log_error_for_exception("Unhandled Python exception in StringRecognizer._recognize_constant")
return False
def _recognize_constant_pointer(self, ctxt, hlil, expr, type, val, result):
try:
hlil = highlevelil.HighLevelILFunction(handle=core.BNNewHighLevelILFunctionReference(hlil))
type = types.Type.create(handle=core.BNNewTypeReference(type))
instr = hlil.get_expr(highlevelil.ExpressionIndex(expr))
ref = self.recognize_constant_pointer(instr, type, val)
if ref is None:
return False
result[0] = ref._to_core_struct(True)
return True
except:
log_error_for_exception("Unhandled Python exception in StringRecognizer._recognize_constant_pointer")
return False
def _recognize_extern_pointer(self, ctxt, hlil, expr, type, val, offset, result):
try:
hlil = highlevelil.HighLevelILFunction(handle=core.BNNewHighLevelILFunctionReference(hlil))
type = types.Type.create(handle=core.BNNewTypeReference(type))
instr = hlil.get_expr(highlevelil.ExpressionIndex(expr))
ref = self.recognize_extern_pointer(instr, type, val, offset)
if ref is None:
return False
result[0] = ref._to_core_struct(True)
return True
except:
log_error_for_exception("Unhandled Python exception in StringRecognizer._recognize_extern_pointer")
return False
def _recognize_import(self, ctxt, hlil, expr, type, val, result):
try:
hlil = highlevelil.HighLevelILFunction(handle=core.BNNewHighLevelILFunctionReference(hlil))
type = types.Type.create(handle=core.BNNewTypeReference(type))
instr = hlil.get_expr(highlevelil.ExpressionIndex(expr))
ref = self.recognize_import(instr, type, val)
if ref is None:
return False
result[0] = ref._to_core_struct(True)
return True
except:
log_error_for_exception("Unhandled Python exception in StringRecognizer._recognize_import")
return False
@property
def name(self) -> str:
if hasattr(self, 'handle'):
return core.BNGetStringRecognizerName(self.handle)
return self.__class__.recognizer_name
[docs]
def is_valid_for_type(self, func: 'highlevelil.HighLevelILFunction', type: 'types.Type') -> bool:
return True
[docs]
def recognize_constant(
self, instr: 'highlevelil.HighLevelILInstruction', type: 'types.Type', val: int
) -> Optional['binaryview.DerivedString']:
return None
[docs]
def recognize_constant_pointer(
self, instr: 'highlevelil.HighLevelILInstruction', type: 'types.Type', val: int
) -> Optional['binaryview.DerivedString']:
return None
[docs]
def recognize_extern_pointer(
self, instr: 'highlevelil.HighLevelILInstruction', type: 'types.Type', val: int, offset: int
) -> Optional['binaryview.DerivedString']:
return None
[docs]
def recognize_import(
self, instr: 'highlevelil.HighLevelILInstruction', type: 'types.Type', val: int
) -> Optional['binaryview.DerivedString']:
return None
_recognizer_cache = {}
[docs]
class CoreStringRecognizer(StringRecognizer):
[docs]
def __init__(self, handle: core.BNStringRecognizer):
super(CoreStringRecognizer, self).__init__(handle=handle)
if type(self) is CoreStringRecognizer:
global _recognizer_cache
_recognizer_cache[ctypes.addressof(handle.contents)] = self
@classmethod
def _from_cache(cls, handle) -> 'StringRecognizer':
"""
Look up a recognizer from a given BNStringRecognizer handle
:param handle: BNStringRecognizer pointer
:return: Recognizer instance responsible for this handle
"""
global _recognizer_cache
return _recognizer_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.BNIsStringRecognizerValidForType(self.handle, func.handle, type.handle)
[docs]
def recognize_constant(
self, instr: 'highlevelil.HighLevelILInstruction', type: 'types.Type', val: int
) -> Optional['binaryview.DerivedString']:
string = core.BNDerivedString()
if not core.BNStringRecognizerRecognizeConstant(self.handle, instr.function.handle, instr.expr_index, type.handle, val, string):
return None
return binaryview.DerivedString._from_core_struct(string, True)
[docs]
def recognize_constant_pointer(
self, instr: 'highlevelil.HighLevelILInstruction', type: 'types.Type', val: int
) -> Optional['binaryview.DerivedString']:
string = core.BNDerivedString()
if not core.BNStringRecognizerRecognizeConstantPointer(self.handle, instr.function.handle, instr.expr_index, type.handle, val, string):
return None
return binaryview.DerivedString._from_core_struct(string, True)
[docs]
def recognize_extern_pointer(
self, instr: 'highlevelil.HighLevelILInstruction', type: 'types.Type', val: int, offset: int
) -> Optional['binaryview.DerivedString']:
string = core.BNDerivedString()
if not core.BNStringRecognizerRecognizeExternPointer(self.handle, instr.function.handle, instr.expr_index, type.handle, val, offset, string):
return None
return binaryview.DerivedString._from_core_struct(string, True)
[docs]
def recognize_import(
self, instr: 'highlevelil.HighLevelILInstruction', type: 'types.Type', val: int
) -> Optional['binaryview.DerivedString']:
string = core.BNDerivedString()
if not core.BNStringRecognizerRecognizeImport(self.handle, instr.function.handle, instr.expr_index, type.handle, val, string):
return None
return binaryview.DerivedString._from_core_struct(string, True)