diff options
| author | Rusty Wagner <rusty.wagner@gmail.com> | 2025-10-13 19:35:01 -0400 |
|---|---|---|
| committer | Rusty Wagner <rusty.wagner@gmail.com> | 2025-10-21 13:52:39 -0400 |
| commit | 5d4fc5f1fb7a0c368d2a048a5d750564c970c9c9 (patch) | |
| tree | cd740b4af25fe5184935fd67067025733f17b099 /python/stringrecognizer.py | |
| parent | cae26921cf2b2e564f66e58b77a3963fe36f6d2d (diff) | |
Add derived strings and string recognizer API
Diffstat (limited to 'python/stringrecognizer.py')
| -rw-r--r-- | python/stringrecognizer.py | 293 |
1 files changed, 293 insertions, 0 deletions
diff --git a/python/stringrecognizer.py b/python/stringrecognizer.py new file mode 100644 index 00000000..8f503d8b --- /dev/null +++ b/python/stringrecognizer.py @@ -0,0 +1,293 @@ +# 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) + + +class CustomStringType(metaclass=_CustomStringTypeMetaClass): + 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)) + + @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) + + +class StringRecognizer(metaclass=_StringRecognizerMetaClass): + _registered_recognizers = [] + recognizer_name = None + + def __init__(self, handle=None): + if handle is not None: + self.handle = core.handle_of_type(handle, core.BNStringRecognizer) + + 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 + + def is_valid_for_type(self, func: 'highlevelil.HighLevelILFunction', type: 'types.Type') -> bool: + return True + + def recognize_constant( + self, instr: 'highlevelil.HighLevelILInstruction', type: 'types.Type', val: int + ) -> Optional['binaryview.DerivedString']: + return None + + def recognize_constant_pointer( + self, instr: 'highlevelil.HighLevelILInstruction', type: 'types.Type', val: int + ) -> Optional['binaryview.DerivedString']: + return None + + def recognize_extern_pointer( + self, instr: 'highlevelil.HighLevelILInstruction', type: 'types.Type', val: int, offset: int + ) -> Optional['binaryview.DerivedString']: + return None + + def recognize_import( + self, instr: 'highlevelil.HighLevelILInstruction', type: 'types.Type', val: int + ) -> Optional['binaryview.DerivedString']: + return None + + +_recognizer_cache = {} + + +class CoreStringRecognizer(StringRecognizer): + 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) + + def is_valid_for_type(self, func: 'highlevelil.HighLevelILFunction', type: 'types.Type') -> bool: + return core.BNIsStringRecognizerValidForType(self.handle, func.handle, type.handle) + + 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) + + 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) + + 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) + + 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) |
