From e0fbcc712f8ca31914d2f1e04b7f8b16eff44d66 Mon Sep 17 00:00:00 2001 From: Peter LaFosse Date: Fri, 12 Jun 2020 16:27:36 -0400 Subject: Add BinaryView.parse_types_from_string to the python API and update how the underlying BNParseTypesString works --- binaryninjaapi.h | 3 ++- binaryninjacore.h | 3 +-- binaryview.cpp | 38 +++++++++++++++++++++++++------------- platform.cpp | 7 ++++--- python/binaryview.py | 52 +++++++++++++++++++++++++++++++++++++++++++++++----- python/platform.py | 4 ++-- 6 files changed, 81 insertions(+), 26 deletions(-) diff --git a/binaryninjaapi.h b/binaryninjaapi.h index 60320769..0328b58f 100644 --- a/binaryninjaapi.h +++ b/binaryninjaapi.h @@ -1649,7 +1649,8 @@ __attribute__ ((format (printf, 1, 2))) uint64_t GetPreviousDataVariableStartBeforeAddress(uint64_t addr); bool ParseTypeString(const std::string& text, QualifiedNameAndType& result, std::string& errors); - bool ParseTypeString(const std::string& text, std::map>& result, std::string& errors); + bool ParseTypeString(const std::string& text, std::map>& types, + std::map>& variables, std::map>& functions, std::string& errors); std::map> GetTypes(); std::vector GetTypeNames(const std::string& matching=""); diff --git a/binaryninjacore.h b/binaryninjacore.h index 35dcc31b..b09a917a 100644 --- a/binaryninjacore.h +++ b/binaryninjacore.h @@ -3232,8 +3232,7 @@ __attribute__ ((format (printf, 1, 2))) BINARYNINJACOREAPI bool BNParseTypeString(BNBinaryView* view, const char* text, BNQualifiedNameAndType* result, char** errors); - BINARYNINJACOREAPI bool BNParseTypesString(BNBinaryView* view, const char* text, - BNQualifiedNameAndType** result, size_t* count, char** errors); + BINARYNINJACOREAPI bool BNParseTypesString(BNBinaryView* view, const char* text, BNTypeParserResult* result, char** errors); BINARYNINJACOREAPI void BNFreeQualifiedNameAndType(BNQualifiedNameAndType* obj); BINARYNINJACOREAPI void BNFreeQualifiedNameAndTypeArray(BNQualifiedNameAndType* obj, size_t count); diff --git a/binaryview.cpp b/binaryview.cpp index 0dfd0a8d..287dc94b 100644 --- a/binaryview.cpp +++ b/binaryview.cpp @@ -2466,26 +2466,38 @@ bool BinaryView::ParseTypeString(const string& text, QualifiedNameAndType& resul } -bool BinaryView::ParseTypeString(const string& text, std::map>& result, string& errors) +bool BinaryView::ParseTypeString(const string& source, map>& types, + map>& variables, map>& functions, string& errors) { - BNQualifiedNameAndType* nt; + BNTypeParserResult result; char* errorStr; - size_t count; - if (!BNParseTypesString(m_object, text.c_str(), &nt, &count, &errorStr)) - { - errors = errorStr; - BNFreeString(errorStr); + types.clear(); + variables.clear(); + functions.clear(); + + bool ok = BNParseTypesString(m_object, source.c_str(), &result, &errorStr); + errors = errorStr; + BNFreeString(errorStr); + if (!ok) return false; - } - for(size_t i = 0; i < count; i++) + for (size_t i = 0; i < result.typeCount; i++) { - result[QualifiedName::FromAPIObject(&nt[i].name)] = new Type(BNNewTypeReference(nt[i].type)); + QualifiedName name = QualifiedName::FromAPIObject(&result.types[i].name); + types[name] = new Type(BNNewTypeReference(result.types[i].type)); } - BNFreeQualifiedNameAndTypeArray(nt, count); - - errors = ""; + for (size_t i = 0; i < result.variableCount; i++) + { + QualifiedName name = QualifiedName::FromAPIObject(&result.variables[i].name); + variables[name] = new Type(BNNewTypeReference(result.variables[i].type)); + } + for (size_t i = 0; i < result.functionCount; i++) + { + QualifiedName name = QualifiedName::FromAPIObject(&result.functions[i].name); + functions[name] = new Type(BNNewTypeReference(result.functions[i].type)); + } + BNFreeTypeParserResult(&result); return true; } diff --git a/platform.cpp b/platform.cpp index 070c6d94..1a94b34d 100644 --- a/platform.cpp +++ b/platform.cpp @@ -430,6 +430,7 @@ bool Platform::ParseTypesFromSource(const string& source, const string& fileName &errorStr, includeDirList, includeDirs.size(), autoTypeSource.c_str()); errors = errorStr; BNFreeString(errorStr); + delete[] includeDirList; if (!ok) return false; @@ -441,14 +442,13 @@ bool Platform::ParseTypesFromSource(const string& source, const string& fileName for (size_t i = 0; i < result.variableCount; i++) { QualifiedName name = QualifiedName::FromAPIObject(&result.variables[i].name); - types[name] = new Type(BNNewTypeReference(result.variables[i].type)); + variables[name] = new Type(BNNewTypeReference(result.variables[i].type)); } for (size_t i = 0; i < result.functionCount; i++) { QualifiedName name = QualifiedName::FromAPIObject(&result.functions[i].name); - types[name] = new Type(BNNewTypeReference(result.functions[i].type)); + functions[name] = new Type(BNNewTypeReference(result.functions[i].type)); } - delete[] includeDirList; BNFreeTypeParserResult(&result); return true; } @@ -473,6 +473,7 @@ bool Platform::ParseTypesFromSourceFile(const string& fileName, map, 'foo') >>> """ - if not isinstance(text, str): - raise AttributeError("Text must be a string") + if not (isinstance(text, str) or isinstance(text, unicode)): + raise AttributeError("Source must be a string") result = core.BNQualifiedNameAndType() errors = ctypes.c_char_p() if not core.BNParseTypeString(self.handle, text, result, errors): - error_str = errors.value + error_str = errors.value.decode("utf-8") core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) raise SyntaxError(error_str) type_obj = types.Type(core.BNNewTypeReference(result.type), platform = self.platform) @@ -4603,6 +4604,47 @@ class BinaryView(object): core.BNFreeQualifiedNameAndType(result) return type_obj, name + def parse_types_from_string(self, text): + """ + ``parse_types_from_string`` parses string containing C into a :py:Class:`TypeParserResult` objects. This API + unlike the :py:Function:`platform.Platform.parse_types_from_source` allows the reference of types already defined + in the BinaryView. + + :param str text: C source code string of types, variables, and function types, to create + :return: :py:class:`TypeParserResult` (a SyntaxError is thrown on parse error) + :rtype: TypeParserResult + :Example: + + >>> bv.parse_types_from_string('int foo;\\nint bar(int x);\\nstruct bas{int x,y;};\\n') + ({types: {'bas': }, variables: {'foo': }, functions:{'bar': + }}, '') + >>> + """ + if not (isinstance(text, str) or isinstance(text, unicode)): + raise AttributeError("Source must be a string") + + parse = core.BNTypeParserResult() + errors = ctypes.c_char_p() + if not core.BNParseTypesString(self.handle, text, parse, errors): + error_str = errors.value.decode("utf-8") + core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) + raise SyntaxError(error_str) + + type_dict = {} + variables = {} + functions = {} + for i in range(0, parse.typeCount): + name = types.QualifiedName._from_core_struct(parse.types[i].name) + type_dict[name] = types.Type(core.BNNewTypeReference(parse.types[i].type), platform = self.platform) + for i in range(0, parse.variableCount): + name = types.QualifiedName._from_core_struct(parse.variables[i].name) + variables[name] = types.Type(core.BNNewTypeReference(parse.variables[i].type), platform = self.platform) + for i in range(0, parse.functionCount): + name = types.QualifiedName._from_core_struct(parse.functions[i].name) + functions[name] = types.Type(core.BNNewTypeReference(parse.functions[i].type), platform = self.platform) + core.BNFreeTypeParserResult(parse) + return types.TypeParserResult(type_dict, variables, functions) + def get_type_by_name(self, name): """ ``get_type_by_name`` returns the defined type whose name corresponds with the provided ``name`` diff --git a/python/platform.py b/python/platform.py index 5031d2d6..8d7d1d99 100644 --- a/python/platform.py +++ b/python/platform.py @@ -411,7 +411,7 @@ class Platform(with_metaclass(_PlatformMetaClass, object)): errors = ctypes.c_char_p() result = core.BNParseTypesFromSource(self.handle, source, filename, parse, errors, dir_buf, len(include_dirs), auto_type_source) - error_str = errors.value + error_str = errors.value.decode("utf-8") core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) if not result: raise SyntaxError(error_str) @@ -460,7 +460,7 @@ class Platform(with_metaclass(_PlatformMetaClass, object)): errors = ctypes.c_char_p() result = core.BNParseTypesFromSourceFile(self.handle, filename, parse, errors, dir_buf, len(include_dirs), auto_type_source) - error_str = errors.value + error_str = errors.value.decode("utf-8") core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) if not result: raise SyntaxError(error_str) -- cgit v1.3.1