diff options
| -rw-r--r-- | binaryninjaapi.h | 12 | ||||
| -rw-r--r-- | binaryninjacore.h | 13 | ||||
| -rw-r--r-- | python/typeparser.py | 70 | ||||
| -rw-r--r-- | typeparser.cpp | 33 |
4 files changed, 125 insertions, 3 deletions
diff --git a/binaryninjaapi.h b/binaryninjaapi.h index a6a98da2..efebbfe7 100644 --- a/binaryninjaapi.h +++ b/binaryninjaapi.h @@ -11529,6 +11529,7 @@ namespace BinaryNinja { explicit TypeParser(const std::string& name); TypeParser(BNTypeParser* parser); + static bool GetOptionTextCallback(void* ctxt, BNTypeParserOption option, const char* value, char** result); static bool PreprocessSourceCallback(void* ctxt, const char* source, const char* fileName, BNPlatform* platform, const BNQualifiedNameTypeAndId* existingTypes, size_t existingTypeCount, @@ -11560,6 +11561,15 @@ namespace BinaryNinja { static Ref<TypeParser> GetByName(const std::string& name); static Ref<TypeParser> GetDefault(); + /** + Get the string representation of an option for passing to ParseTypes* + \param option Option type + \param value Option value + \param result String representing the option + \return True if the parser supports the option + */ + virtual bool GetOptionText(BNTypeParserOption option, std::string value, std::string& result) const; + /*! Preprocess a block of source, returning the source that would be parsed \param source Source code to process @@ -11635,6 +11645,8 @@ namespace BinaryNinja { CoreTypeParser(BNTypeParser* parser); virtual ~CoreTypeParser() {} + virtual bool GetOptionText(BNTypeParserOption option, std::string value, std::string& result) const override; + virtual bool PreprocessSource( const std::string& source, const std::string& fileName, diff --git a/binaryninjacore.h b/binaryninjacore.h index cb9a83c4..f45fa675 100644 --- a/binaryninjacore.h +++ b/binaryninjacore.h @@ -36,14 +36,14 @@ // Current ABI version for linking to the core. This is incremented any time // there are changes to the API that affect linking, including new functions, // new types, or modifications to existing functions or types. -#define BN_CURRENT_CORE_ABI_VERSION 27 +#define BN_CURRENT_CORE_ABI_VERSION 28 // Minimum ABI version that is supported for loading of plugins. Plugins that // are linked to an ABI version less than this will not be able to load and // will require rebuilding. The minimum version is increased when there are // incompatible changes that break binary compatibility, such as changes to // existing types or functions. -#define BN_MINIMUM_CORE_ABI_VERSION 24 +#define BN_MINIMUM_CORE_ABI_VERSION 28 #ifdef __GNUC__ #ifdef BINARYNINJACORE_LIBRARY @@ -2104,6 +2104,12 @@ extern "C" BNLowLevelILFunction* il, BNRelocation* relocation); }; + enum BNTypeParserOption + { + IncludeSystemTypes, + BuiltinMacros, + }; + struct BNParsedType { BNQualifiedName name; @@ -2515,6 +2521,7 @@ extern "C" struct BNTypeParserCallbacks { void* context; + bool (*getOptionText)(void* ctxt, BNTypeParserOption option, const char* value, char** result); bool (*preprocessSource)(void* ctxt, const char* source, const char* fileName, BNPlatform* platform, const BNQualifiedNameTypeAndId* existingTypes, size_t existingTypeCount, @@ -5484,6 +5491,8 @@ extern "C" BINARYNINJACOREAPI char* BNGetTypeParserName(BNTypeParser* parser); + BINARYNINJACOREAPI bool BNGetTypeParserOptionText(BNTypeParser* parser, BNTypeParserOption option, + const char* value, char** result); BINARYNINJACOREAPI bool BNTypeParserPreprocessSource(BNTypeParser* parser, const char* source, const char* fileName, BNPlatform* platform, const BNQualifiedNameTypeAndId* existingTypes, size_t existingTypeCount, diff --git a/python/typeparser.py b/python/typeparser.py index b6ea0c59..1b8fe57a 100644 --- a/python/typeparser.py +++ b/python/typeparser.py @@ -35,7 +35,7 @@ from .settings import Settings from . import platform from . import types from .log import log_error -from .enums import TypeParserErrorSeverity +from .enums import TypeParserErrorSeverity, TypeParserOption @dataclasses.dataclass(frozen=True) @@ -199,6 +199,10 @@ class _TypeParserMetaclass(type): @property def default(self): + """ + Get the default parser as specified by the user's settings + :return: Default parser + """ name = binaryninja.Settings().get_string("analysis.types.parserName") return CoreTypeParser[name] @@ -216,10 +220,14 @@ class TypeParser(metaclass=_TypeParserMetaclass): self.__dict__["name"] = core.BNGetTypeParserName(handle) def register(self): + """ + Register a custom parser with the API + """ assert self.__class__.name is not None self._cb = core.BNTypeParserCallbacks() self._cb.context = 0 + self._cb.getOptionText = self._cb.getOptionText.__class__(self._get_option_text) self._cb.preprocessSource = self._cb.preprocessSource.__class__(self._preprocess_source) self._cb.parseTypesFromSource = self._cb.parseTypesFromSource.__class__(self._parse_types_from_source) self._cb.parseTypeString = self._cb.parseTypeString.__class__(self._parse_type_string) @@ -235,6 +243,19 @@ class TypeParser(metaclass=_TypeParserMetaclass): def __repr__(self): return f'<TypeParser: {self.name}>' + def _get_option_text(self, option, value, result) -> bool: + try: + value_py = core.pyNativeStr(value) + result_py = self.get_option_text(option, value_py) + if result_py is None: + return False + TypeParser._cached_string = core.cstr(result_py) + result[0] = TypeParser._cached_string + return True + except: + log_error(traceback.format_exc()) + return False + def _preprocess_source( self, ctxt, source, fileName, platform_, existingTypes, existingTypeCount, options, optionCount, includeDirs, includeDirCount, @@ -401,11 +422,31 @@ class TypeParser(metaclass=_TypeParserMetaclass): log_error(traceback.format_exc()) return False + def get_option_text(self, option: TypeParserOption, value: str) -> Optional[str]: + """ + Get the string representation of an option for passing to parse_type_* + :param option: Option type + :param value: Option value + :return: A string representing the option if the parser supports it, otherwise None + """ + return None + def preprocess_source( self, source: str, file_name: str, platform: 'platform.Platform', existing_types: Optional[List[QualifiedNameTypeAndId]] = None, options: Optional[List[str]] = None, include_dirs: Optional[List[str]] = None ) -> Tuple[Optional[str], List[TypeParserError]]: + """ + Preprocess a block of source, returning the source that would be parsed + :param source: Source code to process + :param file_name: Name of the file containing the source (does not need to exist on disk) + :param platform: Platform to assume the source is relevant to + :param existing_types: Optional map of all existing types to use for parsing context + :param options: Optional string arguments to pass as options, e.g. command line arguments + :param include_dirs: Optional list of directories to include in the header search path + :return: A tuple of (preproccessed source, errors), where the preproccessed source + is None if there was a fatal error. + """ raise NotImplementedError("Not implemented") def parse_types_from_source( @@ -414,17 +455,44 @@ class TypeParser(metaclass=_TypeParserMetaclass): options: Optional[List[str]] = None, include_dirs: Optional[List[str]] = None, auto_type_source: str = "" ) -> Tuple[Optional[TypeParserResult], List[TypeParserError]]: + """ + Parse an entire block of source into types, variables, and functions + :param source: Source code to parse + :param file_name: Name of the file containing the source (optional: exists on disk) + :param platform: Platform to assume the types are relevant to + :param existing_types: Optional map of all existing types to use for parsing context + :param options: Optional string arguments to pass as options, e.g. command line arguments + :param include_dirs: Optional list of directories to include in the header search path + :param auto_type_source: Optional source of types if used for automatically generated types + :return: A tuple of (result, errors) where the result is None if there was a fatal error + """ raise NotImplementedError("Not implemented") def parse_type_string( self, source: str, platform: 'platform.Platform', existing_types: Optional[List[QualifiedNameTypeAndId]] = None ) -> Tuple[Optional[Tuple['types.QualifiedNameType', 'types.Type']], List[TypeParserError]]: + """ + Parse a single type and name from a string containing their definition. + :param source: Source code to parse + :param platform: Platform to assume the types are relevant to + :param existing_types: Optional map of all existing types to use for parsing context + :return: A tuple of (result, errors) where result is a tuple of (type, name) or + None of there was a fatal error. + """ raise NotImplementedError("Not implemented") class CoreTypeParser(TypeParser): + def get_option_text(self, option: TypeParserOption, value: str) -> Optional[str]: + result_cpp = ctypes.c_char_p() + if not core.BNGetTypeParserOptionText(self.handle, option, value, result_cpp): + return None + result = core.pyNativeStr(result_cpp.value) + core.free_string(result_cpp) + return result + def preprocess_source( self, source: str, file_name: str, platform: 'platform.Platform', existing_types: Optional[List[QualifiedNameTypeAndId]] = None, diff --git a/typeparser.cpp b/typeparser.cpp index 8351de2f..c93c03b3 100644 --- a/typeparser.cpp +++ b/typeparser.cpp @@ -46,6 +46,7 @@ void TypeParser::Register(TypeParser* parser) { BNTypeParserCallbacks cb; cb.context = parser; + cb.getOptionText = GetOptionTextCallback; cb.preprocessSource = PreprocessSourceCallback; cb.parseTypesFromSource = ParseTypesFromSourceCallback; cb.parseTypeString = ParseTypeStringCallback; @@ -56,6 +57,27 @@ void TypeParser::Register(TypeParser* parser) } +bool TypeParser::GetOptionText(BNTypeParserOption option, std::string value, std::string& result) const +{ + // Default: Don't accept anything + return false; +} + + +bool TypeParser::GetOptionTextCallback(void* ctxt, BNTypeParserOption option, const char* value, + char** result) +{ + TypeParser* parser = (TypeParser*)ctxt; + string resultCpp; + if (!parser->GetOptionText(option, value, resultCpp)) + { + return false; + } + *result = BNAllocString(resultCpp.c_str()); + return true; +} + + bool TypeParser::PreprocessSourceCallback(void* ctxt, const char* source, const char* fileName, BNPlatform* platform, const BNQualifiedNameTypeAndId* existingTypes, size_t existingTypeCount, @@ -295,6 +317,17 @@ CoreTypeParser::CoreTypeParser(BNTypeParser* parser): TypeParser(parser) } +bool CoreTypeParser::GetOptionText(BNTypeParserOption option, std::string value, std::string& result) const +{ + char* apiResult; + if (!BNGetTypeParserOptionText(m_object, option, value.c_str(), &apiResult)) + return false; + result = apiResult; + BNFreeString(apiResult); + return true; +} + + bool CoreTypeParser::PreprocessSource(const std::string& source, const std::string& fileName, Ref<Platform> platform, const std::map<QualifiedName, TypeAndId>& existingTypes, const std::vector<std::string>& options, const std::vector<std::string>& includeDirs, |
