diff options
| -rw-r--r-- | architecture.cpp | 13 | ||||
| -rw-r--r-- | binaryninjaapi.h | 415 | ||||
| -rw-r--r-- | binaryninjacore.h | 186 | ||||
| -rw-r--r-- | binaryview.cpp | 58 | ||||
| -rw-r--r-- | demangle.cpp | 4 | ||||
| -rw-r--r-- | python/__init__.py | 2 | ||||
| -rw-r--r-- | python/generator.cpp | 12 | ||||
| -rw-r--r-- | python/typeparser.py | 589 | ||||
| -rw-r--r-- | python/typeprinter.py | 390 | ||||
| -rw-r--r-- | python/types.py | 157 | ||||
| -rw-r--r-- | rust/src/types.rs | 34 | ||||
| -rw-r--r-- | suite/api_test.py | 428 | ||||
| -rw-r--r-- | type.cpp | 203 | ||||
| -rw-r--r-- | typeparser.cpp | 513 | ||||
| -rw-r--r-- | typeprinter.cpp | 400 | ||||
| -rw-r--r-- | ui/createtypedialog.h | 7 |
16 files changed, 3319 insertions, 92 deletions
diff --git a/architecture.cpp b/architecture.cpp index cd5306ea..b1acceb5 100644 --- a/architecture.cpp +++ b/architecture.cpp @@ -140,6 +140,19 @@ BNInstructionTextToken* InstructionTextToken::CreateInstructionTextTokenList(con } +void InstructionTextToken::FreeInstructionTextTokenList(BNInstructionTextToken* tokens, size_t count) +{ + for (size_t i = 0; i < count; i++) + { + BNFreeString(tokens[i].text); + for (size_t j = 0; j < tokens[i].namesCount; j++) + BNFreeString(tokens[i].typeNames[j]); + delete[] tokens[i].typeNames; + } + delete[] tokens; +} + + vector<InstructionTextToken> InstructionTextToken::ConvertInstructionTextTokenList( const BNInstructionTextToken* tokens, size_t count) { diff --git a/binaryninjaapi.h b/binaryninjaapi.h index 0ae9097a..d0725e5c 100644 --- a/binaryninjaapi.h +++ b/binaryninjaapi.h @@ -1377,7 +1377,7 @@ namespace BinaryNinja { BNQualifiedName GetAPIObject() const; static void FreeAPIObject(BNQualifiedName* name); - static QualifiedName FromAPIObject(BNQualifiedName* name); + static QualifiedName FromAPIObject(const BNQualifiedName* name); }; class NameSpace : public NameList @@ -1506,6 +1506,8 @@ namespace BinaryNinja { InstructionTextToken WithConfidence(uint8_t conf); static BNInstructionTextToken* CreateInstructionTextTokenList(const std::vector<InstructionTextToken>& tokens); + static void FreeInstructionTextTokenList( + BNInstructionTextToken* tokens, size_t count); static std::vector<InstructionTextToken> ConvertAndFreeInstructionTextTokenList( BNInstructionTextToken* tokens, size_t count); static std::vector<InstructionTextToken> ConvertInstructionTextTokenList( @@ -1556,6 +1558,10 @@ namespace BinaryNinja { size_t fieldIndex; static TypeDefinitionLine FromAPIObject(BNTypeDefinitionLine* line); + static BNTypeDefinitionLine* CreateTypeDefinitionLineList( + const std::vector<TypeDefinitionLine>& lines); + static void FreeTypeDefinitionLineList( + BNTypeDefinitionLine* lines, size_t count); }; class DisassemblySettings; @@ -2117,7 +2123,9 @@ namespace BinaryNinja { QualifiedName GetTypeNameById(const std::string& id); bool IsTypeAutoDefined(const QualifiedName& name); QualifiedName DefineType(const std::string& id, const QualifiedName& defaultName, Ref<Type> type); + void DefineTypes(const std::vector<QualifiedNameAndType>& types, std::function<bool(size_t, size_t)> progress = {}); void DefineUserType(const QualifiedName& name, Ref<Type> type); + void DefineUserTypes(const std::vector<QualifiedNameAndType>& types, std::function<bool(size_t, size_t)> progress = {}); void UndefineType(const std::string& id); void UndefineUserType(const QualifiedName& name); void RenameType(const QualifiedName& oldName, const QualifiedName& newName); @@ -2980,12 +2988,78 @@ namespace BinaryNinja { Confidence<Ref<Type>> type; bool defaultLocation; Variable location; + + FunctionParameter() = default; + FunctionParameter(const std::string& name, Confidence<Ref<Type>> type): name(name), type(type), defaultLocation(true) + {} + + FunctionParameter(const std::string& name, const Confidence<Ref<Type>>& type, bool defaultLocation, + const Variable& location): + name(name), type(type), defaultLocation(defaultLocation), location(location) + {} }; struct QualifiedNameAndType { QualifiedName name; Ref<Type> type; + + QualifiedNameAndType() = default; + QualifiedNameAndType(const std::string& name, const Ref<Type>& type): name(name), type(type) + {} + QualifiedNameAndType(const QualifiedName& name, const Ref<Type>& type): name(name), type(type) + {} + + bool operator<(const QualifiedNameAndType& other) const + { + return name < other.name; + } + }; + + struct TypeAndId + { + std::string id; + Ref<Type> type; + + TypeAndId() = default; + TypeAndId(const std::string& id, const Ref<Type>& type): id(id), type(type) + {} + }; + + struct ParsedType + { + QualifiedName name; + Ref<Type> type; + bool isUser; + + ParsedType() = default; + ParsedType(const std::string& name, const Ref<Type>& type, bool isUser): name(name), type(type), isUser(isUser) + {} + ParsedType(const QualifiedName& name, const Ref<Type>& type, bool isUser): name(name), type(type), isUser(isUser) + {} + + bool operator<(const ParsedType& other) const + { + if (isUser != other.isUser) + return isUser; + return name < other.name; + } + }; + + struct TypeParserResult + { + std::vector<ParsedType> types; + std::vector<ParsedType> variables; + std::vector<ParsedType> functions; + }; + + struct TypeParserError + { + BNTypeParserErrorSeverity severity; + std::string message; + std::string fileName; + uint64_t line; + uint64_t column; }; class Type : public CoreRefCountObject<BNType, BNNewTypeReference, BNFreeType> @@ -3067,6 +3141,15 @@ namespace BinaryNinja { const Confidence<Ref<CallingConvention>>& callingConvention, const std::vector<FunctionParameter>& params, const Confidence<bool>& varArg = Confidence<bool>(false, 0), const Confidence<int64_t>& stackAdjust = Confidence<int64_t>(0, 0)); + static Ref<Type> FunctionType(const Confidence<Ref<Type>>& returnValue, + const Confidence<Ref<CallingConvention>>& callingConvention, + const std::vector<FunctionParameter>& params, + const Confidence<bool>& hasVariableArguments, + const Confidence<bool>& canReturn, + const Confidence<int64_t>& stackAdjust, + const std::map<uint32_t, Confidence<int32_t>>& regStackAdjust = std::map<uint32_t, Confidence<int32_t>>(), + const Confidence<std::vector<uint32_t>>& returnRegs = Confidence<std::vector<uint32_t>>(std::vector<uint32_t>(), 0), + BNNameType ft = NoNameType); static std::string GenerateAutoTypeId(const std::string& source, const QualifiedName& name); static std::string GenerateAutoDemangledTypeId(const QualifiedName& name); @@ -3212,6 +3295,15 @@ namespace BinaryNinja { const Confidence<Ref<CallingConvention>>& callingConvention, const std::vector<FunctionParameter>& params, const Confidence<bool>& varArg = Confidence<bool>(false, 0), const Confidence<int64_t>& stackAdjust = Confidence<int64_t>(0, 0)); + static TypeBuilder FunctionType(const Confidence<Ref<Type>>& returnValue, + const Confidence<Ref<CallingConvention>>& callingConvention, + const std::vector<FunctionParameter>& params, + const Confidence<bool>& hasVariableArguments, + const Confidence<bool>& canReturn, + const Confidence<int64_t>& stackAdjust, + const std::map<uint32_t, Confidence<int32_t>>& regStackAdjust = std::map<uint32_t, Confidence<int32_t>>(), + const Confidence<std::vector<uint32_t>>& returnRegs = Confidence<std::vector<uint32_t>>(std::vector<uint32_t>(), 0), + BNNameType ft = NoNameType); bool IsReferenceOfType(BNNamedTypeReferenceClass refType); bool IsStructReference() { return IsReferenceOfType(StructNamedTypeClass); } @@ -5572,6 +5664,327 @@ namespace BinaryNinja { const std::string& autoTypeSource = ""); }; + class TypeParser: public StaticCoreRefCountObject<BNTypeParser> + { + std::string m_nameForRegister; + protected: + explicit TypeParser(const std::string& name); + TypeParser(BNTypeParser* parser); + + static bool PreprocessSourceCallback(void* ctxt, + const char* source, const char* fileName, BNPlatform* platform, + const BNQualifiedNameTypeAndId* existingTypes, size_t existingTypeCount, + const char* const* options, size_t optionCount, + const char* const* includeDirs, size_t includeDirCount, + char** output, BNTypeParserError** errors, size_t* errorCount + ); + static bool ParseTypesFromSourceCallback(void* ctxt, + const char* source, const char* fileName, BNPlatform* platform, + const BNQualifiedNameTypeAndId* existingTypes, size_t existingTypeCount, + const char* const* options, size_t optionCount, + const char* const* includeDirs, size_t includeDirCount, + const char* autoTypeSource, BNTypeParserResult* result, + BNTypeParserError** errors, size_t* errorCount + ); + static bool ParseTypeStringCallback(void* ctxt, + const char* source, BNPlatform* platform, + const BNQualifiedNameTypeAndId* existingTypes, size_t existingTypeCount, + BNQualifiedNameAndType* result, + BNTypeParserError** errors, size_t* errorCount + ); + static void FreeStringCallback(void* ctxt, char* result); + static void FreeResultCallback(void* ctxt, BNTypeParserResult* result); + static void FreeErrorListCallback(void* ctxt, BNTypeParserError* errors, size_t errorCount); + + public: + static void Register(TypeParser* parser); + static std::vector<Ref<TypeParser>> GetList(); + static Ref<TypeParser> GetByName(const std::string& name); + static Ref<TypeParser> GetDefault(); + + /*! + Preprocess a block of source, returning the source that would be parsed + \param source Source code to process + \param fileName 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 existingTypes Map of all existing types to use for parsing context + \param options String arguments to pass as options, e.g. command line arguments + \param includeDirs List of directories to include in the header search path + \param output Reference to a string into which the preprocessed source will be written + \param errors Reference to a list into which any parse errors will be written + \return True if preprocessing was successful + */ + virtual bool 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, + std::string& output, + std::vector<TypeParserError>& errors + ) = 0; + + /*! + Parse an entire block of source into types, variables, and functions + \param source Source code to parse + \param fileName Name of the file containing the source (optional: exists on disk) + \param platform Platform to assume the types are relevant to + \param existingTypes Map of all existing types to use for parsing context + \param options String arguments to pass as options, e.g. command line arguments + \param includeDirs List of directories to include in the header search path + \param autoTypeSource Optional source of types if used for automatically generated types + \param result Reference to structure into which the results will be written + \param errors Reference to a list into which any parse errors will be written + \return True if parsing was successful + */ + virtual bool ParseTypesFromSource( + 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, + const std::string& autoTypeSource, + TypeParserResult& result, + std::vector<TypeParserError>& errors + ) = 0; + + /*! + 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 existingTypes Map of all existing types to use for parsing context + \param result Reference into which the resulting type and name will be written + \param errors Reference to a list into which any parse errors will be written + \return True if parsing was successful + */ + virtual bool ParseTypeString( + const std::string& source, + Ref<Platform> platform, + const std::map<QualifiedName, TypeAndId>& existingTypes, + QualifiedNameAndType& result, + std::vector<TypeParserError>& errors + ) = 0; + }; + + class CoreTypeParser: public TypeParser + { + public: + CoreTypeParser(BNTypeParser* parser); + virtual ~CoreTypeParser() {} + + virtual bool 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, + std::string& output, + std::vector<TypeParserError>& errors + ) override; + + virtual bool ParseTypesFromSource( + 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, + const std::string& autoTypeSource, + TypeParserResult& result, + std::vector<TypeParserError>& errors + ) override; + + virtual bool ParseTypeString( + const std::string& source, + Ref<Platform> platform, + const std::map<QualifiedName, TypeAndId>& existingTypes, + QualifiedNameAndType& result, + std::vector<TypeParserError>& errors + ) override; + }; + + class TypePrinter: public StaticCoreRefCountObject<BNTypePrinter> + { + std::string m_nameForRegister; + protected: + explicit TypePrinter(const std::string& name); + TypePrinter(BNTypePrinter* printer); + + static bool GetTypeTokensCallback(void* ctxt, BNType* type, BNPlatform* platform, + BNQualifiedName* name, uint8_t baseConfidence, BNTokenEscapingType escaping, + BNInstructionTextToken** result, size_t* resultCount); + static bool GetTypeTokensBeforeNameCallback(void* ctxt, BNType* type, + BNPlatform* platform, uint8_t baseConfidence, BNType* parentType, + BNTokenEscapingType escaping, BNInstructionTextToken** result, + size_t* resultCount); + static bool GetTypeTokensAfterNameCallback(void* ctxt, BNType* type, + BNPlatform* platform, uint8_t baseConfidence, BNType* parentType, + BNTokenEscapingType escaping, BNInstructionTextToken** result, + size_t* resultCount); + static bool GetTypeStringCallback(void* ctxt, BNType* type, BNPlatform* platform, + BNQualifiedName* name, BNTokenEscapingType escaping, char** result); + static bool GetTypeStringBeforeNameCallback(void* ctxt, BNType* type, + BNPlatform* platform, BNTokenEscapingType escaping, char** result); + static bool GetTypeStringAfterNameCallback(void* ctxt, BNType* type, + BNPlatform* platform, BNTokenEscapingType escaping, char** result); + static bool GetTypeLinesCallback(void* ctxt, BNType* type, BNBinaryView* data, + BNQualifiedName* name, int lineWidth, bool collapsed, + BNTokenEscapingType escaping, BNTypeDefinitionLine** result, size_t* resultCount); + static void FreeTokensCallback(void* ctxt, BNInstructionTextToken* tokens, size_t count); + static void FreeStringCallback(void* ctxt, char* string); + static void FreeLinesCallback(void* ctxt, BNTypeDefinitionLine* lines, size_t count); + + public: + static void Register(TypePrinter* printer); + static std::vector<Ref<TypePrinter>> GetList(); + static Ref<TypePrinter> GetByName(const std::string& name); + static Ref<TypePrinter> GetDefault(); + + /*! + Generate a single-line text representation of a type + \param type Type to print + \param platform Platform responsible for this type + \param name Name of the type + \param baseConfidence Confidence to use for tokens created for this type + \param escaping Style of escaping literals which may not be parsable + \return List of text tokens representing the type + */ + virtual std::vector<InstructionTextToken> GetTypeTokens( + Ref<Type> type, + Ref<Platform> platform, + const QualifiedName& name, + uint8_t baseConfidence = BN_FULL_CONFIDENCE, + BNTokenEscapingType escaping = NoTokenEscapingType + ); + /*! + In a single-line text representation of a type, generate the tokens that should + be printed before the type's name. + + \param type Type to print + \param platform Platform responsible for this type + \param baseConfidence Confidence to use for tokens created for this type + \param parentType Type of the parent of this type, or nullptr + \param escaping Style of escaping literals which may not be parsable + \return List of text tokens representing the type + */ + virtual std::vector<InstructionTextToken> GetTypeTokensBeforeName( + Ref<Type> type, + Ref<Platform> platform, + uint8_t baseConfidence = BN_FULL_CONFIDENCE, + Ref<Type> parentType = nullptr, + BNTokenEscapingType escaping = NoTokenEscapingType + ) = 0; + /*! + In a single-line text representation of a type, generate the tokens that should + be printed after the type's name. + + \param type Type to print + \param platform Platform responsible for this type + \param baseConfidence Confidence to use for tokens created for this type + \param parentType Type of the parent of this type, or nullptr + \param escaping Style of escaping literals which may not be parsable + \return List of text tokens representing the type + */ + virtual std::vector<InstructionTextToken> GetTypeTokensAfterName( + Ref<Type> type, + Ref<Platform> platform, + uint8_t baseConfidence = BN_FULL_CONFIDENCE, + Ref<Type> parentType = nullptr, + BNTokenEscapingType escaping = NoTokenEscapingType + ) = 0; + + /*! + Generate a single-line text representation of a type + \param type Type to print + \param platform Platform responsible for this type + \param name Name of the type + \param escaping Style of escaping literals which may not be parsable + \return String representing the type + */ + virtual std::string GetTypeString( + Ref<Type> type, + Ref<Platform> platform, + const QualifiedName& name, + BNTokenEscapingType escaping = NoTokenEscapingType + ); + /*! + In a single-line text representation of a type, generate the string that should + be printed before the type's name. + + \param type Type to print + \param platform Platform responsible for this type + \param escaping Style of escaping literals which may not be parsable + \return String representing the type + */ + virtual std::string GetTypeStringBeforeName( + Ref<Type> type, + Ref<Platform> platform, + BNTokenEscapingType escaping = NoTokenEscapingType + ); + /*! + In a single-line text representation of a type, generate the string that should + be printed after the type's name. + + \param type Type to print + \param platform Platform responsible for this type + \param escaping Style of escaping literals which may not be parsable + \return String representing the type + */ + virtual std::string GetTypeStringAfterName( + Ref<Type> type, + Ref<Platform> platform, + BNTokenEscapingType escaping = NoTokenEscapingType + ); + + /*! + Generate a multi-line representation of a type + \param type Type to print + \param data Binary View in which the type is defined + \param name Name of the type + \param lineWidth Maximum width of lines, in characters + \param collapsed Whether to collapse structure/enum blocks + \param escaping Style of escaping literals which may not be parsable + \return List of type definition lines + */ + virtual std::vector<TypeDefinitionLine> GetTypeLines( + Ref<Type> type, + Ref<BinaryView> data, + const QualifiedName& name, + int lineWidth = 80, + bool collapsed = false, + BNTokenEscapingType escaping = NoTokenEscapingType + ) = 0; + }; + + class CoreTypePrinter: public TypePrinter + { + public: + CoreTypePrinter(BNTypePrinter* printer); + virtual ~CoreTypePrinter() {} + + virtual std::vector<InstructionTextToken> GetTypeTokens(Ref<Type> type, + Ref<Platform> platform, const QualifiedName& name, + uint8_t baseConfidence, BNTokenEscapingType escaping) override; + virtual std::vector<InstructionTextToken> GetTypeTokensBeforeName(Ref<Type> type, + Ref<Platform> platform, uint8_t baseConfidence, + Ref<Type> parentType, BNTokenEscapingType escaping) override; + virtual std::vector<InstructionTextToken> GetTypeTokensAfterName(Ref<Type> type, + Ref<Platform> platform, uint8_t baseConfidence, + Ref<Type> parentType, BNTokenEscapingType escaping) override; + virtual std::string GetTypeString(Ref<Type> type, Ref<Platform> platform, + const QualifiedName& name, BNTokenEscapingType escaping) override; + virtual std::string GetTypeStringBeforeName(Ref<Type> type, Ref<Platform> platform, + BNTokenEscapingType escaping) override; + virtual std::string GetTypeStringAfterName(Ref<Type> type, Ref<Platform> platform, + BNTokenEscapingType escaping) override; + virtual std::vector<TypeDefinitionLine> GetTypeLines(Ref<Type> type, + Ref<BinaryView> data, const QualifiedName& name, int lineWidth, + bool collapsed, BNTokenEscapingType escaping) override; + }; + // DownloadProvider class DownloadProvider; diff --git a/binaryninjacore.h b/binaryninjacore.h index 3eaeb628..4fb254e7 100644 --- a/binaryninjacore.h +++ b/binaryninjacore.h @@ -34,14 +34,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 19 +#define BN_CURRENT_CORE_ABI_VERSION 20 // 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 19 +#define BN_MINIMUM_CORE_ABI_VERSION 20 #ifdef __GNUC__ #ifdef BINARYNINJACORE_LIBRARY @@ -178,6 +178,8 @@ extern "C" struct BNDownloadInstance; struct BNWebsocketProvider; struct BNWebsocketClient; + struct BNTypeParser; + struct BNTypePrinter; struct BNFlowGraph; struct BNFlowGraphNode; struct BNFlowGraphLayoutRequest; @@ -1991,6 +1993,13 @@ extern "C" BNType* type; }; + struct BNQualifiedNameTypeAndId + { + BNQualifiedName name; + char* id; + BNType* type; + }; + struct BNStructureMember { BNType* type; @@ -2028,14 +2037,40 @@ extern "C" BNLowLevelILFunction* il, BNRelocation* relocation); }; + struct BNParsedType + { + BNQualifiedName name; + BNType* type; + bool isUser; + }; + struct BNTypeParserResult { - BNQualifiedNameAndType* types; - BNQualifiedNameAndType* variables; - BNQualifiedNameAndType* functions; + BNParsedType* types; + BNParsedType* variables; + BNParsedType* functions; size_t typeCount, variableCount, functionCount; }; + enum BNTypeParserErrorSeverity + { + IgnoredSeverity = 0, + NoteSeverity = 1, + RemarkSeverity = 2, + WarningSeverity = 3, + ErrorSeverity = 4, + FatalSeverity = 5, + }; + + struct BNTypeParserError + { + BNTypeParserErrorSeverity severity; + char* message; + char* fileName; + uint64_t line; + uint64_t column; + }; + struct BNQualifiedNameList { BNQualifiedName* names; @@ -2409,6 +2444,63 @@ extern "C" void (*addAction)(void* ctxt, BNMainThreadAction* action); }; + struct BNTypeParserCallbacks + { + void* context; + bool (*preprocessSource)(void* ctxt, + const char* source, const char* fileName, BNPlatform* platform, + const BNQualifiedNameTypeAndId* existingTypes, size_t existingTypeCount, + const char* const* options, size_t optionCount, + const char* const* includeDirs, size_t includeDirCount, + char** output, BNTypeParserError** errors, size_t* errorCount + ); + bool (*parseTypesFromSource)(void* ctxt, + const char* source, const char* fileName, BNPlatform* platform, + const BNQualifiedNameTypeAndId* existingTypes, size_t existingTypeCount, + const char* const* options, size_t optionCount, + const char* const* includeDirs, size_t includeDirCount, + const char* autoTypeSource, BNTypeParserResult* result, + BNTypeParserError** errors, size_t* errorCount + ); + bool (*parseTypeString)(void* ctxt, + const char* source, BNPlatform* platform, + const BNQualifiedNameTypeAndId* existingTypes, size_t existingTypeCount, + BNQualifiedNameAndType* result, + BNTypeParserError** errors, size_t* errorCount + ); + void (*freeString)(void* ctxt, char* string); + void (*freeResult)(void* ctxt, BNTypeParserResult* result); + void (*freeErrorList)(void* ctxt, BNTypeParserError* errors, size_t errorCount); + }; + + struct BNTypePrinterCallbacks + { + void* context; + bool (*getTypeTokens)(void* ctxt, BNType* type, BNPlatform* platform, + BNQualifiedName* name, uint8_t baseConfidence, BNTokenEscapingType escaping, + BNInstructionTextToken** result, size_t* resultCount); + bool (*getTypeTokensBeforeName)(void* ctxt, BNType* type, + BNPlatform* platform, uint8_t baseConfidence, BNType* parentType, + BNTokenEscapingType escaping, BNInstructionTextToken** result, + size_t* resultCount); + bool (*getTypeTokensAfterName)(void* ctxt, BNType* type, + BNPlatform* platform, uint8_t baseConfidence, BNType* parentType, + BNTokenEscapingType escaping, BNInstructionTextToken** result, + size_t* resultCount); + bool (*getTypeString)(void* ctxt, BNType* type, BNPlatform* platform, + BNQualifiedName* name, BNTokenEscapingType escaping, char** result); + bool (*getTypeStringBeforeName)(void* ctxt, BNType* type, + BNPlatform* platform, BNTokenEscapingType escaping, char** result); + bool (*getTypeStringAfterName)(void* ctxt, BNType* type, + BNPlatform* platform, BNTokenEscapingType escaping, char** result); + bool (*getTypeLines)(void* ctxt, BNType* type, BNBinaryView* data, + BNQualifiedName* name, int lineWidth, bool collapsed, + BNTokenEscapingType escaping, BNTypeDefinitionLine** result, size_t* resultCount); + void (*freeTokens)(void* ctxt, BNInstructionTextToken* tokens, size_t count); + void (*freeString)(void* ctxt, char* string); + void (*freeLines)(void* ctxt, BNTypeDefinitionLine* lines, size_t count); + }; + struct BNConstantReference { int64_t value; @@ -4086,6 +4178,7 @@ extern "C" BINARYNINJACOREAPI BNQualifiedNameAndType* BNGetAnalysisTypeList(BNBinaryView* view, size_t* count); BINARYNINJACOREAPI void BNFreeTypeList(BNQualifiedNameAndType* types, size_t count); + BINARYNINJACOREAPI void BNFreeTypeIdList(BNQualifiedNameTypeAndId* types, size_t count); BINARYNINJACOREAPI BNQualifiedName* BNGetAnalysisTypeNames(BNBinaryView* view, size_t* count, const char* matching); BINARYNINJACOREAPI void BNFreeTypeNameList(BNQualifiedName* names, size_t count); BINARYNINJACOREAPI BNType* BNGetAnalysisTypeByName(BNBinaryView* view, BNQualifiedName* name); @@ -4096,6 +4189,8 @@ extern "C" BINARYNINJACOREAPI BNQualifiedName BNDefineAnalysisType( BNBinaryView* view, const char* id, BNQualifiedName* defaultName, BNType* type); BINARYNINJACOREAPI void BNDefineUserAnalysisType(BNBinaryView* view, BNQualifiedName* name, BNType* type); + BINARYNINJACOREAPI void BNDefineAnalysisTypes(BNBinaryView* view, BNQualifiedNameAndType* types, size_t count, bool (*progress)(void*, size_t, size_t), void* progressContext); + BINARYNINJACOREAPI void BNDefineUserAnalysisTypes(BNBinaryView* view, BNQualifiedNameAndType* types, size_t count, bool (*progress)(void*, size_t, size_t), void* progressContext); BINARYNINJACOREAPI void BNUndefineAnalysisType(BNBinaryView* view, const char* id); BINARYNINJACOREAPI void BNUndefineUserAnalysisType(BNBinaryView* view, BNQualifiedName* name); BINARYNINJACOREAPI void BNRenameAnalysisType( @@ -5009,9 +5104,11 @@ extern "C" BINARYNINJACOREAPI BNType* BNCreatePointerTypeOfWidth(size_t width, const BNTypeWithConfidence* const type, BNBoolWithConfidence* cnst, BNBoolWithConfidence* vltl, BNReferenceType refType); BINARYNINJACOREAPI BNType* BNCreateArrayType(const BNTypeWithConfidence* const type, uint64_t elem); - BINARYNINJACOREAPI BNType* BNCreateFunctionType(BNTypeWithConfidence* returnValue, - BNCallingConventionWithConfidence* callingConvention, BNFunctionParameter* params, size_t paramCount, - BNBoolWithConfidence* varArg, BNOffsetWithConfidence* stackAdjust); + BINARYNINJACOREAPI BNType* BNCreateFunctionType(BNTypeWithConfidence* returnValue, BNCallingConventionWithConfidence* callingConvention, + BNFunctionParameter* params, size_t paramCount, BNBoolWithConfidence* varArg, + BNBoolWithConfidence* canReturn, BNOffsetWithConfidence* stackAdjust, + uint32_t* regStackAdjustRegs, BNOffsetWithConfidence* regStackAdjustValues, size_t regStackAdjustCount, + BNRegisterSetWithConfidence* returnRegs, BNNameType ft); BINARYNINJACOREAPI BNType* BNNewTypeReference(BNType* type); BINARYNINJACOREAPI BNType* BNDuplicateType(BNType* type); BINARYNINJACOREAPI char* BNGetTypeAndName(BNType* type, BNQualifiedName* name, BNTokenEscapingType escaping); @@ -5037,9 +5134,11 @@ extern "C" const BNTypeWithConfidence* const type, BNBoolWithConfidence* cnst, BNBoolWithConfidence* vltl, BNReferenceType refType); BINARYNINJACOREAPI BNTypeBuilder* BNCreateArrayTypeBuilder(const BNTypeWithConfidence* const type, uint64_t elem); - BINARYNINJACOREAPI BNTypeBuilder* BNCreateFunctionTypeBuilder(BNTypeWithConfidence* returnValue, - BNCallingConventionWithConfidence* callingConvention, BNFunctionParameter* params, size_t paramCount, - BNBoolWithConfidence* varArg, BNOffsetWithConfidence* stackAdjust); + BINARYNINJACOREAPI BNTypeBuilder* BNCreateFunctionTypeBuilder(BNTypeWithConfidence* returnValue, BNCallingConventionWithConfidence* callingConvention, + BNFunctionParameter* params, size_t paramCount, BNBoolWithConfidence* varArg, + BNBoolWithConfidence* canReturn, BNOffsetWithConfidence* stackAdjust, + uint32_t* regStackAdjustRegs, BNOffsetWithConfidence* regStackAdjustValues, size_t regStackAdjustCount, + BNRegisterSetWithConfidence* returnRegs, BNNameType ft); BINARYNINJACOREAPI BNType* BNFinalizeTypeBuilder(BNTypeBuilder* type); BINARYNINJACOREAPI BNTypeBuilder* BNDuplicateTypeBuilder(BNTypeBuilder* type); BINARYNINJACOREAPI char* BNGetTypeBuilderTypeAndName(BNTypeBuilder* type, BNQualifiedName* name); @@ -5258,8 +5357,71 @@ extern "C" BINARYNINJACOREAPI bool BNParseTypesFromSourceFile(BNPlatform* platform, const char* fileName, BNTypeParserResult* result, char** errors, const char** includeDirs, size_t includeDirCount, const char* autoTypeSource); - BINARYNINJACOREAPI void BNFreeTypeParserResult(BNTypeParserResult* result); + BINARYNINJACOREAPI BNTypeParser* BNRegisterTypeParser( + const char* name, BNTypeParserCallbacks* callbacks); + BINARYNINJACOREAPI BNTypeParser** BNGetTypeParserList(size_t* count); + BINARYNINJACOREAPI void BNFreeTypeParserList(BNTypeParser** parsers); + BINARYNINJACOREAPI BNTypeParser* BNGetTypeParserByName(const char* name); + + BINARYNINJACOREAPI char* BNGetTypeParserName(BNTypeParser* parser); + + BINARYNINJACOREAPI bool BNTypeParserPreprocessSource(BNTypeParser* parser, + const char* source, const char* fileName, BNPlatform* platform, + const BNQualifiedNameTypeAndId* existingTypes, size_t existingTypeCount, + const char* const* options, size_t optionCount, + const char* const* includeDirs, size_t includeDirCount, + char** output, BNTypeParserError** errors, size_t* errorCount + ); + BINARYNINJACOREAPI bool BNTypeParserParseTypesFromSource(BNTypeParser* parser, + const char* source, const char* fileName, BNPlatform* platform, + const BNQualifiedNameTypeAndId* existingTypes, size_t existingTypeCount, + const char* const* options, size_t optionCount, + const char* const* includeDirs, size_t includeDirCount, + const char* autoTypeSource, BNTypeParserResult* result, + BNTypeParserError** errors, size_t* errorCount + ); + BINARYNINJACOREAPI bool BNTypeParserParseTypeString(BNTypeParser* parser, + const char* source, BNPlatform* platform, + const BNQualifiedNameTypeAndId* existingTypes, size_t existingTypeCount, + BNQualifiedNameAndType* result, + BNTypeParserError** errors, size_t* errorCount + ); + + BINARYNINJACOREAPI BNTypePrinter* BNRegisterTypePrinter( + const char* name, BNTypePrinterCallbacks* callbacks); + BINARYNINJACOREAPI BNTypePrinter** BNGetTypePrinterList(size_t* count); + BINARYNINJACOREAPI void BNFreeTypePrinterList(BNTypePrinter** printers); + BINARYNINJACOREAPI BNTypePrinter* BNGetTypePrinterByName(const char* name); + + BINARYNINJACOREAPI char* BNGetTypePrinterName(BNTypePrinter* printer); + + BINARYNINJACOREAPI bool BNGetTypePrinterTypeTokens(BNTypePrinter* printer, + BNType* type, BNPlatform* platform, BNQualifiedName* name, + uint8_t baseConfidence, BNTokenEscapingType escaping, + BNInstructionTextToken** result, size_t* resultCount); + BINARYNINJACOREAPI bool BNGetTypePrinterTypeTokensBeforeName(BNTypePrinter* printer, + BNType* type, BNPlatform* platform, uint8_t baseConfidence, BNType* parentType, + BNTokenEscapingType escaping, BNInstructionTextToken** result, + size_t* resultCount); + BINARYNINJACOREAPI bool BNGetTypePrinterTypeTokensAfterName(BNTypePrinter* printer, + BNType* type, BNPlatform* platform, uint8_t baseConfidence, BNType* parentType, + BNTokenEscapingType escaping, BNInstructionTextToken** result, + size_t* resultCount); + BINARYNINJACOREAPI bool BNGetTypePrinterTypeString(BNTypePrinter* printer, + BNType* type, BNPlatform* platform, BNQualifiedName* name, + BNTokenEscapingType escaping, char** result); + BINARYNINJACOREAPI bool BNGetTypePrinterTypeStringBeforeName(BNTypePrinter* printer, + BNType* type, BNPlatform* platform, BNTokenEscapingType escaping, char** result); + BINARYNINJACOREAPI bool BNGetTypePrinterTypeStringAfterName(BNTypePrinter* printer, + BNType* type, BNPlatform* platform, BNTokenEscapingType escaping, char** result); + BINARYNINJACOREAPI bool BNGetTypePrinterTypeLines(BNTypePrinter* printer, + BNType* type, BNBinaryView* data, + BNQualifiedName* name, int lineWidth, bool collapsed, + BNTokenEscapingType escaping, BNTypeDefinitionLine** result, size_t* resultCount); + + BINARYNINJACOREAPI void BNFreeTypeParserResult(BNTypeParserResult* result); + BINARYNINJACOREAPI void BNFreeTypeParserErrors(BNTypeParserError* errors, size_t count); // Updates BINARYNINJACOREAPI BNUpdateChannel* BNGetUpdateChannels(size_t* count, char** errors); BINARYNINJACOREAPI void BNFreeUpdateChannelList(BNUpdateChannel* list, size_t count); diff --git a/binaryview.cpp b/binaryview.cpp index d5390ddc..78b653a0 100644 --- a/binaryview.cpp +++ b/binaryview.cpp @@ -3171,6 +3171,64 @@ void BinaryView::DefineUserType(const QualifiedName& name, Ref<Type> type) } +struct ProgressCallback +{ + std::function<bool(size_t, size_t)> func; +}; + + +void BinaryView::DefineTypes(const vector<QualifiedNameAndType>& types, std::function<bool(size_t, size_t)> progress) +{ + BNQualifiedNameAndType* apiTypes = new BNQualifiedNameAndType[types.size()]; + for (size_t i = 0; i < types.size(); i++) + { + apiTypes[i].name = types[i].name.GetAPIObject(); + apiTypes[i].type = types[i].type->GetObject(); + } + + ProgressCallback cb; + cb.func = progress; + BNDefineAnalysisTypes(m_object, apiTypes, types.size(), [](void* ctxt, size_t cur, size_t total) { + ProgressCallback* cb = (ProgressCallback*)ctxt; + if (cb->func) + return cb->func(cur, total); + return true; + }, &cb); + + for (size_t i = 0; i < types.size(); i++) + { + QualifiedName::FreeAPIObject(&apiTypes[i].name); + } + delete [] apiTypes; +} + + +void BinaryView::DefineUserTypes(const vector<QualifiedNameAndType>& types, std::function<bool(size_t, size_t)> progress) +{ + BNQualifiedNameAndType* apiTypes = new BNQualifiedNameAndType[types.size()]; + for (size_t i = 0; i < types.size(); i++) + { + apiTypes[i].name = types[i].name.GetAPIObject(); + apiTypes[i].type = types[i].type->GetObject(); + } + + ProgressCallback cb; + cb.func = progress; + BNDefineUserAnalysisTypes(m_object, apiTypes, types.size(), [](void* ctxt, size_t cur, size_t total) { + ProgressCallback* cb = (ProgressCallback*)ctxt; + if (cb->func) + return cb->func(cur, total); + return true; + }, &cb); + + for (size_t i = 0; i < types.size(); i++) + { + QualifiedName::FreeAPIObject(&apiTypes[i].name); + } + delete [] apiTypes; +} + + void BinaryView::UndefineType(const string& id) { BNUndefineAnalysisType(m_object, id.c_str()); diff --git a/demangle.cpp b/demangle.cpp index 66395fc1..277c621f 100644 --- a/demangle.cpp +++ b/demangle.cpp @@ -72,13 +72,13 @@ namespace BinaryNinja { QualifiedName SimplifyName::to_qualified_name(const string& input, bool simplify) { - return (QualifiedName)SimplifyName(input, SimplifierDest::fqn, simplify); + return SimplifyName(input, SimplifierDest::fqn, simplify).operator QualifiedName(); } QualifiedName SimplifyName::to_qualified_name(const QualifiedName& input) { - return (QualifiedName)SimplifyName(input.GetString(), SimplifierDest::fqn, true); + return SimplifyName(input.GetString(), SimplifierDest::fqn, true).operator QualifiedName(); } diff --git a/python/__init__.py b/python/__init__.py index a230dec4..2d0f7065 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -65,6 +65,8 @@ from .workflow import * from .commonil import * from .database import * from .secretsprovider import * +from .typeparser import * +from .typeprinter 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/generator.cpp b/python/generator.cpp index 99d2d455..cc4b5102 100644 --- a/python/generator.cpp +++ b/python/generator.cpp @@ -239,7 +239,19 @@ int main(int argc, char* argv[]) map<QualifiedName, Ref<Type>> types, vars, funcs; string errors; auto arch = new CoreArchitecture(BNGetNativeTypeParserArchitecture()); + + string oldParser; + if (Settings::Instance()->Contains("analysis.types.parserNamer")) + oldParser = Settings::Instance()->Get<string>("analysis.types.parserNamer"); + Settings::Instance()->Set("analysis.types.parserName", "CoreTypeParser"); + bool ok = arch->GetStandalonePlatform()->ParseTypesFromSourceFile(argv[1], types, vars, funcs, errors); + + if (!oldParser.empty()) + Settings::Instance()->Set("analysis.types.parserName", oldParser); + else + Settings::Instance()->Reset("analysis.types.parserName"); + fprintf(stderr, "Errors: %s\n", errors.c_str()); if (!ok) return 1; diff --git a/python/typeparser.py b/python/typeparser.py new file mode 100644 index 00000000..ac5134f0 --- /dev/null +++ b/python/typeparser.py @@ -0,0 +1,589 @@ +# Copyright (c) 2015-2022 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 abc +import ctypes +import dataclasses +from json import dumps +from typing import List, Tuple, Optional + +import sys +import traceback + +# Binary Ninja Components +import binaryninja +import binaryninja._binaryninjacore as core + +from .settings import Settings +from . import platform +from . import types +from .log import log_error +from .enums import TypeParserErrorSeverity + + +@dataclasses.dataclass(frozen=True) +class QualifiedNameTypeAndId: + name: 'types.QualifiedNameType' + id: str + type: 'types.Type' + + @classmethod + def _from_core_struct(cls, struct: core.BNQualifiedNameTypeAndId) -> 'QualifiedNameTypeAndId': + name = types.QualifiedName._from_core_struct(struct.name) + type = types.Type.create(handle=core.BNNewTypeReference(struct.type)) + return QualifiedNameTypeAndId(name, struct.id, type) + + def _to_core_struct(self) -> core.BNQualifiedNameTypeAndId: + result = core.BNQualifiedNameTypeAndId() + result.name = types.QualifiedName(self.name)._to_core_struct() + result.type = core.BNNewTypeReference(self.type.handle) + result.id = self.id + return result + + +@dataclasses.dataclass(frozen=True) +class TypeParserError: + severity: TypeParserErrorSeverity + message: str + file_name: str + line: int + column: int + + def __str__(self): + text = "" + if self.severity == TypeParserErrorSeverity.ErrorSeverity \ + or self.severity == TypeParserErrorSeverity.FatalSeverity: + text += "error: " + if self.severity == TypeParserErrorSeverity.WarningSeverity: + text += "warning: " + if self.severity == TypeParserErrorSeverity.NoteSeverity: + text += "note: " + if self.severity == TypeParserErrorSeverity.RemarkSeverity: + text += "remark: " + if self.severity == TypeParserErrorSeverity.IgnoredSeverity: + text += "ignored: " + + if self.file_name == "": + text += f"<unknown>: {self.message}\n" + else: + text += f"{self.file_name}: {self.line}:{self.column} {self.message}\n" + return text + + @classmethod + def _from_core_struct(cls, struct: core.BNTypeParserError) -> 'TypeParserError': + return TypeParserError(struct.severity, struct.message, struct.fileName, struct.line, struct.column) + + def _to_core_struct(self) -> core.BNTypeParserError: + result = core.BNTypeParserError() + result.severity = self.severity + result.message = self.message + result.fileName = self.file_name + result.line = self.line + result.column = self.column + return result + + +@dataclasses.dataclass(frozen=True) +class ParsedType: + name: 'types.QualifiedNameType' + type: 'types.Type' + is_user: bool + + @classmethod + def _from_core_struct(cls, struct: core.BNParsedType) -> 'ParsedType': + name = types.QualifiedName._from_core_struct(struct.name) + type = types.Type.create(handle=core.BNNewTypeReference(struct.type)) + return ParsedType(name, type, struct.isUser) + + def _to_core_struct(self) -> core.BNParsedType: + result = core.BNParsedType() + result.name = types.QualifiedName(self.name)._to_core_struct() + result.type = core.BNNewTypeReference(self.type.handle) + result.isUser = self.is_user + return result + + +@dataclasses.dataclass(frozen=True) +class TypeParserResult: + types: List[ParsedType] + variables: List[ParsedType] + functions: List[ParsedType] + + def __repr__(self): + return f"<types: {self.types}, variables: {self.variables}, functions: {self.functions}>" + + @classmethod + def _from_core_struct(cls, struct: core.BNTypeParserResult) -> 'TypeParserResult': + types = [] + variables = [] + functions = [] + for i in range(struct.typeCount): + types.append(ParsedType._from_core_struct(struct.types[i])) + for i in range(struct.variableCount): + variables.append(ParsedType._from_core_struct(struct.variables[i])) + for i in range(struct.functionCount): + functions.append(ParsedType._from_core_struct(struct.functions[i])) + return TypeParserResult(types, variables, functions) + + def _to_core_struct(self) -> core.BNTypeParserResult: + result = core.BNTypeParserResult() + result.typeCount = len(self.types) + result.variableCount = len(self.variables) + result.functionCount = len(self.functions) + result.types = (core.BNParsedType * len(self.types))() + result.variables = (core.BNParsedType * len(self.variables))() + result.functions = (core.BNParsedType * len(self.functions))() + + for (i, type) in enumerate(self.types): + result.types[i] = type._to_core_struct() + for (i, variable) in enumerate(self.variables): + result.variables[i] = variable._to_core_struct() + for (i, function) in enumerate(self.functions): + result.functions[i] = function._to_core_struct() + + return result + + +def to_bytes(field): + if type(field) == bytes: + return field + if type(field) == str: + return field.encode() + return str(field).encode() + + +class _TypeParserMetaclass(type): + def __iter__(self): + binaryninja._init_plugins() + count = ctypes.c_ulonglong() + types = core.BNGetTypeParserList(count) + try: + for i in range(0, count.value): + yield CoreTypeParser(types[i]) + finally: + core.BNFreeTypeParserList(types) + + def __getitem__(self, value): + binaryninja._init_plugins() + handle = core.BNGetTypeParserByName(str(value)) + if handle is None: + raise KeyError(f"'{value}' is not a valid TypeParser") + return CoreTypeParser(handle) + + @property + def default(self): + name = binaryninja.Settings().get_string("analysis.types.parserName") + return CoreTypeParser[name] + + +class TypeParser(metaclass=_TypeParserMetaclass): + name = None + _registered_parsers = [] + _cached_string = None + _cached_result = None + _cached_error = None + + def __init__(self, handle=None): + if handle is not None: + self.handle = core.handle_of_type(handle, core.BNTypeParser) + self.__dict__["name"] = core.BNGetTypeParserName(handle) + + def register(self): + assert self.__class__.name is not None + + self._cb = core.BNTypeParserCallbacks() + self._cb.context = 0 + 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) + self._cb.freeString = self._cb.freeString.__class__(self._free_string) + self._cb.freeResult = self._cb.freeResult.__class__(self._free_result) + self._cb.freeErrorList = self._cb.freeErrorList.__class__(self._free_error_list) + self.handle = core.BNRegisterTypeParser(self.__class__.name, self._cb) + self.__class__._registered_parsers.append(self) + + def __str__(self): + return f'<TypeParser: {self.name}>' + + def __repr__(self): + return f'<TypeParser: {self.name}>' + + def _preprocess_source( + self, ctxt, source, fileName, platform_, existingTypes, existingTypeCount, + options, optionCount, includeDirs, includeDirCount, + output, errors, errorCount + ) -> bool: + try: + source_py = core.pyNativeStr(source) + file_name_py = core.pyNativeStr(fileName) + platform_py = platform.Platform(handle=core.BNNewPlatformReference(platform_)) + + existing_types_py = [] + for i in range(existingTypeCount): + existing_types_py.append(QualifiedNameTypeAndId._from_core_struct(existingTypes[i])) + + options_py = [] + for i in range(optionCount): + options_py.append(core.pyNativeStr(options[i])) + + include_dirs_py = [] + for i in range(includeDirCount): + include_dirs_py.append(core.pyNativeStr(includeDirs[i])) + + (output_py, errors_py) = self.preprocess_source( + source_py, file_name_py, platform_py, existing_types_py, options_py, + include_dirs_py) + + if output_py is not None and output is not None: + TypeParser._cached_string = core.cstr(output_py) + output[0] = TypeParser._cached_string + if errorCount is not None: + errorCount[0] = len(errors_py) + if errors is not None: + errors_out = (core.BNTypeParserError * len(errors_py))() + for i in range(len(errors_py)): + errors_out[i] = errors_py[i]._to_core_struct() + TypeParser._cached_error = errors_out + errors[0] = errors_out + + return output_py is not None + except: + errorCount[0] = 0 + log_error(traceback.format_exc()) + return False + + def _parse_types_from_source( + self, ctxt, source, fileName, platform_, existingTypes, existingTypeCount, + options, optionCount, includeDirs, includeDirCount, autoTypeSource, + result, errors, errorCount + ) -> bool: + try: + source_py = core.pyNativeStr(source) + file_name_py = core.pyNativeStr(fileName) + platform_py = platform.Platform(handle=core.BNNewPlatformReference(platform_)) + + existing_types_py = [] + for i in range(existingTypeCount): + existing_types_py.append(QualifiedNameTypeAndId._from_core_struct(existingTypes[i])) + + options_py = [] + for i in range(optionCount): + options_py.append(core.pyNativeStr(options[i])) + + include_dirs_py = [] + for i in range(includeDirCount): + include_dirs_py.append(core.pyNativeStr(includeDirs[i])) + + auto_type_source = core.pyNativeStr(autoTypeSource) + + (result_py, errors_py) = self.parse_types_from_source( + source_py, file_name_py, platform_py, existing_types_py, options_py, + include_dirs_py, auto_type_source) + + if result_py is not None and result is not None: + result_struct = result_py._to_core_struct() + TypeParser._cached_result = result_struct + result[0] = result_struct + + if errorCount is not None: + errorCount[0] = len(errors_py) + if errors is not None: + errors_out = (core.BNTypeParserError * len(errors_py))() + for i in range(len(errors_py)): + errors_out[i] = errors_py[i]._to_core_struct() + TypeParser._cached_error = errors_out + errors[0] = errors_out + + return result_py is not None + except: + result[0].typeCount = 0 + result[0].variableCount = 0 + result[0].functionCount = 0 + errorCount[0] = 0 + log_error(traceback.format_exc()) + return False + + def _parse_type_string( + self, ctxt, source, platform_, existingTypes, existingTypeCount, + result, errors, errorCount + ) -> bool: + try: + source_py = core.pyNativeStr(source) + platform_py = platform.Platform(handle=core.BNNewPlatformReference(platform_)) + + existing_types_py = [] + for i in range(existingTypeCount): + existing_types_py.append(QualifiedNameTypeAndId._from_core_struct(existingTypes[i])) + + (result_py, errors_py) = self.parse_type_string( + source_py, platform_py, existing_types_py) + + if result_py is not None and result is not None: + result[0].name = types.QualifiedName(result_py[0])._to_core_struct() + result[0].type = core.BNNewTypeReference(result_py[1].handle) + + if errorCount is not None: + errorCount[0] = len(errors_py) + if errors is not None: + errors_out = (core.BNTypeParserError * len(errors_py))() + for i in range(len(errors_py)): + errors_out[i] = errors_py[i]._to_core_struct() + TypeParser._cached_error = errors_out + errors[0] = errors_out + + return result_py is not None + except: + errorCount[0] = 0 + log_error(traceback.format_exc()) + return False + + def _free_string( + self, ctxt, string + ) -> bool: + try: + TypeParser._cached_string = None + return True + except: + log_error(traceback.format_exc()) + return False + + def _free_result( + self, ctxt, result + ) -> bool: + try: + if TypeParser._cached_result is not None: + for i in range(TypeParser._cached_result.typeCount): + core.BNFreeType(TypeParser._cached_result.types[i].type) + for i in range(TypeParser._cached_result.variableCount): + core.BNFreeType(TypeParser._cached_result.variables[i].type) + for i in range(TypeParser._cached_result.functionCount): + core.BNFreeType(TypeParser._cached_result.functions[i].type) + TypeParser._cached_result = None + return True + except: + log_error(traceback.format_exc()) + return False + + def _free_error_list( + self, ctxt, errors, errorCount + ) -> bool: + try: + TypeParser._cached_error = None + return True + except: + log_error(traceback.format_exc()) + return False + + 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]]: + raise NotImplementedError("Not implemented") + + def parse_types_from_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, + auto_type_source: str = "" + ) -> Tuple[Optional[TypeParserResult], List[TypeParserError]]: + 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]]: + raise NotImplementedError("Not implemented") + + +class CoreTypeParser(TypeParser): + + 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]]: + if existing_types is None: + existing_types = [] + if options is None: + options = [] + if include_dirs is None: + include_dirs = [] + + existing_types_cpp = (core.BNQualifiedNameTypeAndId * len(existing_types))() + for (i, qnatid) in enumerate(existing_types): + existing_types_cpp[i] = qnatid._to_core_struct() + + options_cpp = (ctypes.c_char_p * len(options))() + for (i, s) in enumerate(options): + options_cpp[i] = core.cstr(s) + + include_dirs_cpp = (ctypes.c_char_p * len(include_dirs))() + for (i, s) in enumerate(include_dirs): + include_dirs_cpp[i] = core.cstr(s) + + output_cpp = ctypes.c_char_p() + errors_cpp = ctypes.POINTER(core.BNTypeParserError)() + error_count = ctypes.c_size_t() + + success = core.BNTypeParserPreprocessSource( + self.handle, source, file_name, platform.handle, + existing_types_cpp, len(existing_types), options_cpp, len(options), + include_dirs_cpp, len(include_dirs), + output_cpp, errors_cpp, error_count + ) + + if success: + output = core.pyNativeStr(output_cpp.value) + core.free_string(output_cpp) + else: + output = None + + errors = [] + for i in range(error_count.value): + errors.append(TypeParserError._from_core_struct(errors_cpp[i])) + core.BNFreeTypeParserErrors(errors_cpp, error_count.value) + + return output, errors + + def parse_types_from_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, + auto_type_source: str = "" + ) -> Tuple[Optional[TypeParserResult], List[TypeParserError]]: + if existing_types is None: + existing_types = [] + if options is None: + options = [] + if include_dirs is None: + include_dirs = [] + + existing_types_cpp = (core.BNQualifiedNameTypeAndId * len(existing_types))() + for (i, qnatid) in enumerate(existing_types): + existing_types_cpp[i] = qnatid._to_core_struct() + + options_cpp = (ctypes.c_char_p * len(options))() + for (i, s) in enumerate(options): + options_cpp[i] = core.cstr(s) + + include_dirs_cpp = (ctypes.c_char_p * len(include_dirs))() + for (i, s) in enumerate(include_dirs): + include_dirs_cpp[i] = core.cstr(s) + + result_cpp = core.BNTypeParserResult() + errors_cpp = ctypes.POINTER(core.BNTypeParserError)() + error_count = ctypes.c_size_t() + + success = core.BNTypeParserParseTypesFromSource( + self.handle, source, file_name, platform.handle, + existing_types_cpp, len(existing_types), options_cpp, len(options), + include_dirs_cpp, len(include_dirs), auto_type_source, + result_cpp, errors_cpp, error_count + ) + + if success: + result = TypeParserResult._from_core_struct(result_cpp) + else: + result = None + core.BNFreeTypeParserResult(result_cpp) + + errors = [] + for i in range(error_count.value): + errors.append(TypeParserError._from_core_struct(errors_cpp[i])) + core.BNFreeTypeParserErrors(errors_cpp, error_count.value) + + return result, errors + + + 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]]: + if existing_types is None: + existing_types = [] + existing_types_cpp = (core.BNQualifiedNameTypeAndId * len(existing_types))() + for (i, qnatid) in enumerate(existing_types): + existing_types_cpp[i] = qnatid._to_core_struct() + + result_cpp = core.BNQualifiedNameAndType() + errors_cpp = ctypes.POINTER(core.BNTypeParserError)() + error_count = ctypes.c_size_t() + + success = core.BNTypeParserParseTypeString( + self.handle, source, platform.handle, + existing_types_cpp, len(existing_types), + result_cpp, errors_cpp, error_count + ) + + if success: + result = ( + types.QualifiedName._from_core_struct(result_cpp.name), + types.Type.create(handle=core.BNNewTypeReference(result_cpp.type)) + ) + core.BNFreeQualifiedNameAndType(result_cpp) + else: + result = None + + errors = [] + for i in range(error_count.value): + errors.append(TypeParserError._from_core_struct(errors_cpp[i])) + core.BNFreeTypeParserErrors(errors_cpp, error_count.value) + + return result, errors + + +def preprocess_source(source: str, filename: str = None, + include_dirs: Optional[List[str]] = None) -> Tuple[Optional[str], str]: + """ + ``preprocess_source`` run the C preprocessor on the given source or source filename. + + :param str source: source to pre-process + :param str filename: optional filename to pre-process + :param include_dirs: list of string directories to use as include directories. + :type include_dirs: list(str) + :return: returns a tuple of (preprocessed_source, error_string) + :rtype: tuple(str,str) + :Example: + + >>> source = "#define TEN 10\\nint x[TEN];\\n" + >>> preprocess_source(source) + ('#line 1 "input"\\n\\n#line 2 "input"\\n int x [ 10 ] ;\\n', '') + >>> + """ + if filename is None: + filename = "input" + if include_dirs is None: + include_dirs = [] + dir_buf = (ctypes.c_char_p * len(include_dirs))() + for i in range(0, len(include_dirs)): + dir_buf[i] = include_dirs[i].encode('charmap') + output = ctypes.c_char_p() + errors = ctypes.c_char_p() + result = core.BNPreprocessSource(source, filename, output, errors, dir_buf, len(include_dirs)) + assert output.value is not None + assert errors.value is not None + output_str = output.value.decode('utf-8') + error_str = errors.value.decode('utf-8') + core.free_string(output) + core.free_string(errors) + if result: + return output_str, error_str + return None, error_str diff --git a/python/typeprinter.py b/python/typeprinter.py new file mode 100644 index 00000000..354084dd --- /dev/null +++ b/python/typeprinter.py @@ -0,0 +1,390 @@ +# Copyright (c) 2015-2022 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 abc +import ctypes +import dataclasses +from json import dumps +from typing import List, Tuple, Optional + +import sys +import traceback + +# Binary Ninja Components +import binaryninja +import binaryninja._binaryninjacore as core + +from .settings import Settings +from . import platform as _platform +from . import types +from . import function as _function +from . import binaryview +from .log import log_error +from .enums import TokenEscapingType + + +def to_bytes(field): + if type(field) == bytes: + return field + if type(field) == str: + return field.encode() + return str(field).encode() + + +class _TypePrinterMetaclass(type): + def __iter__(self): + binaryninja._init_plugins() + count = ctypes.c_ulonglong() + types = core.BNGetTypePrinterList(count) + try: + for i in range(0, count.value): + yield CoreTypePrinter(types[i]) + finally: + core.BNFreeTypePrinterList(types) + + def __getitem__(self, value): + binaryninja._init_plugins() + handle = core.BNGetTypePrinterByName(str(value)) + if handle is None: + raise KeyError(f"'{value}' is not a valid TypePrinter") + return CoreTypePrinter(handle) + + @property + def default(self): + name = binaryninja.Settings().get_string("analysis.types.printerName") + return CoreTypePrinter[name] + + +class TypePrinter(metaclass=_TypePrinterMetaclass): + name = None + _registered_printers = [] + _cached_tokens = None + _cached_string = None + _cached_error = None + + def __init__(self, handle=None): + if handle is not None: + self.handle = core.handle_of_type(handle, core.BNTypePrinter) + self.__dict__["name"] = core.BNGetTypePrinterName(handle) + + def register(self): + assert self.__class__.name is not None + + self._cb = core.BNTypePrinterCallbacks() + self._cb.context = 0 + self._cb.getTypeTokens = self._cb.getTypeTokens.__class__(self._get_type_tokens) + self._cb.getTypeTokensBeforeName = self._cb.getTypeTokensBeforeName.__class__(self._get_type_tokens_before_name) + self._cb.getTypeTokensAfterName = self._cb.getTypeTokensAfterName.__class__(self._get_type_tokens_after_name) + self._cb.getTypeString = self._cb.getTypeString.__class__(self._get_type_string) + self._cb.getTypeStringBeforeName = self._cb.getTypeStringBeforeName.__class__(self._get_type_string_before_name) + self._cb.getTypeStringAfterName = self._cb.getTypeStringAfterName.__class__(self._get_type_string_after_name) + self._cb.getTypeLines = self._cb.getTypeLines.__class__(self._get_type_lines) + self._cb.freeTokens = self._cb.freeTokens.__class__(self._free_tokens) + self._cb.freeString = self._cb.freeString.__class__(self._free_string) + self._cb.freeLines = self._cb.freeLines.__class__(self._free_lines) + self.handle = core.BNRegisterTypePrinter(self.__class__.name, self._cb) + self.__class__._registered_printers.append(self) + + def __str__(self): + return f'<TypePrinter: {self.name}>' + + def __repr__(self): + return f'<TypePrinter: {self.name}>' + + def _get_type_tokens(self, ctxt, type, platform, name, base_confidence, escaping, result, result_count): + try: + platform_py = None + if platform: + platform_py = _platform.Platform(handle=core.BNNewPlatformReference(platform)) + result_py = self.get_type_tokens( + types.Type(handle=core.BNNewTypeReference(type)), platform_py, + types.QualifiedName._from_core_struct(name.contents), base_confidence, escaping) + + TypePrinter._cached_tokens = _function.InstructionTextToken._get_core_struct(result_py) + result[0] = TypePrinter._cached_tokens + result_count[0] = len(result_py) + + return True + except: + log_error(traceback.format_exc()) + return False + + def _get_type_tokens_before_name(self, ctxt, type, platform, base_confidence, parent_type, escaping, result, result_count): + try: + platform_py = None + if platform: + platform_py = _platform.Platform(handle=core.BNNewPlatformReference(platform)) + parent_type_py = None + if parent_type: + parent_type_py = types.Type(handle=core.BNNewTypeReference(parent_type)) + result_py = self.get_type_tokens_before_name( + types.Type(handle=core.BNNewTypeReference(type)), platform_py, + base_confidence, parent_type_py, escaping) + + TypePrinter._cached_tokens = _function.InstructionTextToken._get_core_struct(result_py) + result[0] = TypePrinter._cached_tokens + result_count[0] = len(result_py) + + return True + except: + log_error(traceback.format_exc()) + return False + + def _get_type_tokens_after_name(self, ctxt, type, platform, base_confidence, parent_type, escaping, result, result_count): + try: + platform_py = None + if platform: + platform_py = _platform.Platform(handle=core.BNNewPlatformReference(platform)) + parent_type_py = None + if parent_type: + parent_type_py = types.Type(handle=core.BNNewTypeReference(parent_type)) + result_py = self.get_type_tokens_after_name( + types.Type(handle=core.BNNewTypeReference(type)), platform_py, + base_confidence, parent_type_py, escaping) + + TypePrinter._cached_tokens = _function.InstructionTextToken._get_core_struct(result_py) + result[0] = TypePrinter._cached_tokens + result_count[0] = len(result_py) + + return True + except: + log_error(traceback.format_exc()) + return False + + def _get_type_string(self, ctxt, type, platform, name, escaping, result): + try: + platform_py = None + if platform: + platform_py = _platform.Platform(handle=core.BNNewPlatformReference(platform)) + result_py = self.get_type_string( + types.Type(handle=core.BNNewTypeReference(type)), platform_py, + types.QualifiedName._from_core_struct(name.contents), escaping) + + TypePrinter._cached_string = core.cstr(result_py) + result[0] = TypePrinter._cached_string + return True + except: + log_error(traceback.format_exc()) + return False + + def _get_type_string_before_name(self, ctxt, type, platform, escaping, result): + try: + platform_py = None + if platform: + platform_py = _platform.Platform(handle=core.BNNewPlatformReference(platform)) + result_py = self.get_type_string_before_name( + types.Type(handle=core.BNNewTypeReference(type)), platform_py, + escaping) + + TypePrinter._cached_string = core.cstr(result_py) + result[0] = TypePrinter._cached_string + return True + except: + log_error(traceback.format_exc()) + return False + + def _get_type_string_after_name(self, ctxt, type, platform, escaping, result): + try: + platform_py = None + if platform: + platform_py = _platform.Platform(handle=core.BNNewPlatformReference(platform)) + result_py = self.get_type_string_after_name( + types.Type(handle=core.BNNewTypeReference(type)), platform_py, + escaping) + + TypePrinter._cached_string = core.cstr(result_py) + result[0] = TypePrinter._cached_string + return True + except: + log_error(traceback.format_exc()) + return False + + def _get_type_lines(self, ctxt, type, data, name, line_width, collapsed, escaping, result, result_count): + try: + result_py = self.get_type_lines( + types.Type(handle=core.BNNewTypeReference(type)), + binaryview.BinaryView(handle=core.BNNewViewReference(data)), + types.QualifiedName._from_core_struct(name.contents), + line_width, collapsed, escaping) + + TypePrinter._cached_lines = (core.BNTypeDefinitionLine * len(result_py))() + for (i, line) in enumerate(result_py): + TypePrinter._cached_lines[i] = line._to_core_struct() + result[0] = TypePrinter._cached_lines + result_count[0] = len(result_py) + + return True + except: + log_error(traceback.format_exc()) + return False + + def _free_tokens(self, ctxt, tokens, count): + try: + TypePrinter._cached_tokens = None + return True + except: + log_error(traceback.format_exc()) + return False + + def _free_string(self, ctxt, string): + try: + TypePrinter._cached_string = None + return True + except: + log_error(traceback.format_exc()) + return False + + def _free_lines(self, ctxt, lines, count): + try: + for line in TypePrinter._cached_lines: + core.BNFreeType(line.type) + core.BNFreeType(line.rootType) + TypePrinter._cached_lines = None + return True + except: + log_error(traceback.format_exc()) + return False + + def get_type_tokens(self, type: types.Type, platform: Optional[_platform.Platform] = None, name: types.QualifiedNameType = "", base_confidence: int = core.max_confidence, escaping: TokenEscapingType = TokenEscapingType.BackticksTokenEscapingType) -> List[_function.InstructionTextToken]: + raise NotImplementedError() + + def get_type_tokens_before_name(self, type: types.Type, platform: Optional[_platform.Platform] = None, base_confidence: int = core.max_confidence, parent_type: Optional[types.Type] = None, escaping: TokenEscapingType = TokenEscapingType.BackticksTokenEscapingType) -> List[_function.InstructionTextToken]: + raise NotImplementedError() + + def get_type_tokens_after_name(self, type: types.Type, platform: Optional[_platform.Platform] = None, base_confidence: int = core.max_confidence, parent_type: Optional[types.Type] = None, escaping: TokenEscapingType = TokenEscapingType.BackticksTokenEscapingType) -> List[_function.InstructionTextToken]: + raise NotImplementedError() + + def get_type_string(self, type: types.Type, platform: Optional[_platform.Platform] = None, name: types.QualifiedNameType = "", escaping: TokenEscapingType = TokenEscapingType.BackticksTokenEscapingType) -> str: + raise NotImplementedError() + + def get_type_string_before_name(self, type: types.Type, platform: Optional[_platform.Platform] = None, escaping: TokenEscapingType = TokenEscapingType.BackticksTokenEscapingType) -> str: + raise NotImplementedError() + + def get_type_string_after_name(self, type: types.Type, platform: Optional[_platform.Platform] = None, escaping: TokenEscapingType = TokenEscapingType.BackticksTokenEscapingType) -> str: + raise NotImplementedError() + + def get_type_lines(self, type: types.Type, data: binaryview.BinaryView, name: types.QualifiedNameType, line_width = 80, collapsed = False, escaping: TokenEscapingType = TokenEscapingType.BackticksTokenEscapingType) -> List[types.TypeDefinitionLine]: + raise NotImplementedError() + + +class CoreTypePrinter(TypePrinter): + + def get_type_tokens(self, type: types.Type, platform: Optional[_platform.Platform] = None, + name: types.QualifiedNameType = "", base_confidence: int = core.max_confidence, + escaping: TokenEscapingType = TokenEscapingType.BackticksTokenEscapingType) -> List[ + _function.InstructionTextToken]: + if not isinstance(name, types.QualifiedName): + name = types.QualifiedName(name) + count = ctypes.c_ulonglong() + name_cpp = name._to_core_struct() + result_cpp = ctypes.POINTER(core.BNInstructionTextToken)() + if not core.BNGetTypePrinterTypeTokens(self.handle, type.handle, None if platform is None else platform.handle, name_cpp, base_confidence, ctypes.c_int(escaping), result_cpp, count): + raise RuntimeError("BNGetTypePrinterTypeTokens returned False") + + result = _function.InstructionTextToken._from_core_struct(result_cpp, count.value) + core.BNFreeInstructionText(result_cpp.contents, count.value) + return result + + def get_type_tokens_before_name(self, type: types.Type, platform: Optional[_platform.Platform] = None, + base_confidence: int = core.max_confidence, parent_type: Optional[types.Type] = None, + escaping: TokenEscapingType = TokenEscapingType.BackticksTokenEscapingType) -> List[ + _function.InstructionTextToken]: + count = ctypes.c_ulonglong() + result_cpp = ctypes.POINTER(core.BNInstructionTextToken)() + parent_type_cpp = None + if parent_type is not None: + parent_type_cpp = parent_type.handle + if not core.BNGetTypePrinterTypeTokensBeforeName(self.handle, type.handle, None if platform is None else platform.handle, base_confidence, parent_type_cpp, ctypes.c_int(escaping), result_cpp, count): + raise RuntimeError("BNGetTypePrinterTypeTokensBeforeName returned False") + + result = _function.InstructionTextToken._from_core_struct(result_cpp, count.value) + core.BNFreeInstructionText(result_cpp.contents, count.value) + return result + + def get_type_tokens_after_name(self, type: types.Type, platform: Optional[_platform.Platform] = None, + base_confidence: int = core.max_confidence, parent_type: Optional[types.Type] = None, + escaping: TokenEscapingType = TokenEscapingType.BackticksTokenEscapingType) -> List[ + _function.InstructionTextToken]: + count = ctypes.c_ulonglong() + result_cpp = ctypes.POINTER(core.BNInstructionTextToken)() + parent_type_cpp = None + if parent_type is not None: + parent_type_cpp = parent_type.handle + if not core.BNGetTypePrinterTypeTokensAfterName(self.handle, type.handle, None if platform is None else platform.handle, base_confidence, parent_type_cpp, ctypes.c_int(escaping), result_cpp, count): + raise RuntimeError("BNGetTypePrinterTypeTokensAfterName returned False") + + result = _function.InstructionTextToken._from_core_struct(result_cpp, count.value) + core.BNFreeInstructionText(result_cpp.contents, count.value) + return result + + def get_type_string(self, type: types.Type, platform: Optional[_platform.Platform] = None, + name: types.QualifiedNameType = "", + escaping: TokenEscapingType = TokenEscapingType.BackticksTokenEscapingType) -> str: + if not isinstance(name, types.QualifiedName): + name = types.QualifiedName(name) + result_cpp = ctypes.c_char_p() + if not core.BNGetTypePrinterTypeString(self.handle, type.handle, None if platform is None else platform.handle, name._to_core_struct(), ctypes.c_int(escaping), result_cpp): + raise RuntimeError("BNGetTypePrinterTypeString returned False") + + result = core.pyNativeStr(result_cpp.value) + core.free_string(result_cpp) + return result + + def get_type_string_before_name(self, type: types.Type, platform: Optional[_platform.Platform] = None, + escaping: TokenEscapingType = TokenEscapingType.BackticksTokenEscapingType) -> str: + result_cpp = ctypes.c_char_p() + if not core.BNGetTypePrinterTypeStringBeforeName(self.handle, type.handle, None if platform is None else platform.handle, ctypes.c_int(escaping), result_cpp): + raise RuntimeError("BNGetTypePrinterTypeStringBeforeName returned False") + + result = core.pyNativeStr(result_cpp.value) + core.free_string(result_cpp) + return result + + def get_type_string_after_name(self, type: types.Type, platform: Optional[_platform.Platform] = None, + escaping: TokenEscapingType = TokenEscapingType.BackticksTokenEscapingType) -> str: + result_cpp = ctypes.c_char_p() + if not core.BNGetTypePrinterTypeStringAfterName(self.handle, type.handle, None if platform is None else platform.handle, ctypes.c_int(escaping), result_cpp): + raise RuntimeError("BNGetTypePrinterTypeStringAfterName returned False") + + result = core.pyNativeStr(result_cpp.value) + core.free_string(result_cpp) + return result + + def get_type_lines(self, type: types.Type, data: binaryview.BinaryView, + name: types.QualifiedNameType, line_width = 80, collapsed = False, + escaping: TokenEscapingType = TokenEscapingType.BackticksTokenEscapingType + ) -> List[types.TypeDefinitionLine]: + if not isinstance(name, types.QualifiedName): + name = types.QualifiedName(name) + count = ctypes.c_ulonglong() + core_lines = ctypes.POINTER(core.BNTypeDefinitionLine)() + if not core.BNGetTypePrinterTypeLines(self.handle, type.handle, data.handle, name._to_core_struct(), line_width, collapsed, ctypes.c_int(escaping), core_lines, count): + raise RuntimeError("BNGetTypePrinterTypeLines returned False") + lines = [] + for i in range(count.value): + tokens = _function.InstructionTextToken._from_core_struct(core_lines[i].tokens, core_lines[i].count) + type_ = types.Type.create(handle=core.BNNewTypeReference(core_lines[i].type), platform=data.platform) + root_type = types.Type.create(handle=core.BNNewTypeReference(core_lines[i].rootType), platform=data.platform) + root_type_name = core.pyNativeStr(core_lines[i].rootTypeName) + line = types.TypeDefinitionLine(core_lines[i].lineType, tokens, type_, root_type, root_type_name, + core_lines[i].offset, core_lines[i].fieldIndex) + lines.append(line) + core.BNFreeTypeDefinitionLineList(core_lines, count.value) + return lines diff --git a/python/types.py b/python/types.py index 65f58192..e00b3408 100644 --- a/python/types.py +++ b/python/types.py @@ -19,7 +19,7 @@ # IN THE SOFTWARE. import ctypes -from typing import Generator, List, Union, Mapping, Tuple, Optional, Iterable +from typing import Generator, List, Union, Mapping, Tuple, Optional, Iterable, Dict from dataclasses import dataclass import uuid from abc import abstractmethod @@ -28,7 +28,8 @@ from abc import abstractmethod from . import _binaryninjacore as core from .enums import ( StructureVariant, SymbolType, SymbolBinding, TypeClass, NamedTypeReferenceClass, ReferenceType, VariableSourceType, - TypeReferenceType, MemberAccess, MemberScope, TypeDefinitionLineType, TokenEscapingType + TypeReferenceType, MemberAccess, MemberScope, TypeDefinitionLineType, TokenEscapingType, + NameType ) from . import callingconvention from . import function as _function @@ -37,6 +38,7 @@ from . import architecture from . import binaryview from . import platform as _platform from . import typelibrary +from . import typeparser QualifiedNameType = Union[Iterable[Union[str, bytes]], str, 'QualifiedName'] BoolWithConfidenceType = Union[bool, 'BoolWithConfidence'] @@ -47,6 +49,7 @@ EnumMembersType = Union[List[Tuple[str, int]], List[str], List['EnumerationMembe SomeType = Union['TypeBuilder', 'Type'] TypeContainer = Union['binaryview.BinaryView', 'typelibrary.TypeLibrary'] NameSpaceType = Optional[Union[str, List[str], 'NameSpace']] +TypeParserResult = typeparser.TypeParserResult # The following are needed to prevent the type checker from getting # confused as we have member functions in `Type` named the same thing _int = int @@ -223,6 +226,27 @@ class TypeDefinitionLine: def __repr__(self): return f"<typeDefinitionLine {self.type}: {self}>" + @staticmethod + def _from_core_struct(struct: core.BNTypeDefinitionLine, platform: Optional[_platform.Platform] = None): + tokens = _function.InstructionTextToken._from_core_struct(struct.tokens, struct.count) + type_ = Type.create(handle=core.BNNewTypeReference(struct.type), platform=platform) + root_type = Type.create(handle=core.BNNewTypeReference(struct.rootType), platform=platform) + root_type_name = core.pyNativeStr(struct.rootTypeName) + return TypeDefinitionLine(struct.lineType, tokens, type_, root_type, root_type_name, + struct.offset, struct.fieldIndex) + + def _to_core_struct(self): + struct = core.BNTypeDefinitionLine() + struct.lineType = self.line_type + struct.tokens = _function.InstructionTextToken._get_core_struct(self.tokens) + struct.count = len(self.tokens) + struct.type = core.BNNewTypeReference(self.type.handle) + struct.rootType = core.BNNewTypeReference(self.root_type.handle) + struct.rootTypeName = self.root_type_name + struct.offset = self.offset + struct.fieldIndex = self.field_index + return struct + class CoreSymbol: def __init__(self, handle: core.BNSymbolHandle): @@ -888,7 +912,10 @@ class FunctionBuilder(TypeBuilder): cls, return_type: Optional[SomeType] = None, calling_convention: Optional['callingconvention.CallingConvention'] = None, params: Optional[ParamsType] = None, var_args: Optional[BoolWithConfidenceType] = None, stack_adjust: Optional[OffsetWithConfidenceType] = None, - platform: Optional['_platform.Platform'] = None, confidence: int = core.max_confidence + platform: Optional['_platform.Platform'] = None, confidence: int = core.max_confidence, + can_return: Optional[BoolWithConfidence] = None, reg_stack_adjust: Optional[Dict['architecture.RegisterName', OffsetWithConfidenceType]] = None, + return_regs: Optional[Union['RegisterSet', List['architecture.RegisterType']]] = None, + name_type: 'NameType' = NameType.NoNameType ) -> 'FunctionBuilder': param_buf = FunctionBuilder._to_core_struct(params) if return_type is None: @@ -904,11 +931,38 @@ class FunctionBuilder(TypeBuilder): conv_conf.convention = calling_convention.handle conv_conf.confidence = calling_convention.confidence + if reg_stack_adjust is None: + reg_stack_adjust = {} + reg_stack_adjust_regs = (ctypes.c_uint32 * len(reg_stack_adjust))() + reg_stack_adjust_values = (core.BNOffsetWithConfidence * len(reg_stack_adjust))() + + for i, (reg, adjust) in enumerate(reg_stack_adjust.items()): + reg_stack_adjust_regs[i] = reg + reg_stack_adjust_values[i].value = adjust.value + reg_stack_adjust_values[i].confidence = adjust.confidence + + return_regs_set = core.BNRegisterSetWithConfidence() + if return_regs is None or platform is None: + return_regs_set.count = 0 + return_regs_set.confidence = 0 + else: + return_regs_set.count = len(return_regs) + return_regs_set.confidence = 255 + return_regs_set.regs = (ctypes.c_uint32 * len(return_regs))() + + for i, reg in enumerate(return_regs): + return_regs_set[i] = platform.arch.get_reg_index(reg) + if var_args is None: vararg_conf = BoolWithConfidence.get_core_struct(False, 0) else: vararg_conf = BoolWithConfidence.get_core_struct(var_args, core.max_confidence) + if can_return is None: + can_return_conf = BoolWithConfidence.get_core_struct(True, 0) + else: + can_return_conf = BoolWithConfidence.get_core_struct(can_return, core.max_confidence) + if stack_adjust is None: stack_adjust_conf = OffsetWithConfidence.get_core_struct(0, 0) else: @@ -916,7 +970,9 @@ class FunctionBuilder(TypeBuilder): if params is None: params = [] handle = core.BNCreateFunctionTypeBuilder( - ret_conf, conv_conf, param_buf, len(params), vararg_conf, stack_adjust_conf + ret_conf, conv_conf, param_buf, len(params), vararg_conf, can_return_conf, stack_adjust_conf, + reg_stack_adjust_regs, reg_stack_adjust_values, len(reg_stack_adjust), + return_regs_set, name_type ) assert handle is not None, "BNCreateFunctionTypeBuilder returned None" return cls(handle, platform, confidence) @@ -1783,13 +1839,7 @@ class Type: assert core_lines is not None, "core.BNGetTypeLines returned None" lines = [] for i in range(count.value): - tokens = _function.InstructionTextToken._from_core_struct(core_lines[i].tokens, core_lines[i].count) - type_ = Type.create(handle=core.BNNewTypeReference(core_lines[i].type), platform=self._platform) - root_type = Type.create(handle=core.BNNewTypeReference(core_lines[i].rootType), platform=self._platform) - root_type_name = core.pyNativeStr(core_lines[i].rootTypeName) - line = TypeDefinitionLine(core_lines[i].lineType, tokens, type_, root_type, root_type_name, - core_lines[i].offset, core_lines[i].fieldIndex) - lines.append(line) + lines.append(TypeDefinitionLine._from_core_struct(core_lines[i])) core.BNFreeTypeDefinitionLineList(core_lines, count.value) return lines @@ -2448,7 +2498,10 @@ class FunctionType(Type): calling_convention: Optional['callingconvention.CallingConvention'] = None, variable_arguments: BoolWithConfidenceType = BoolWithConfidence(False), stack_adjust: OffsetWithConfidence = OffsetWithConfidence(0), platform: Optional['_platform.Platform'] = None, - confidence: int = core.max_confidence + confidence: int = core.max_confidence, + can_return: BoolWithConfidence = True, reg_stack_adjust: Optional[Dict['architecture.RegisterName', OffsetWithConfidenceType]] = None, + return_regs: Optional[Union['RegisterSet', List['architecture.RegisterType']]] = None, + name_type: 'NameType' = NameType.NoNameType ) -> 'FunctionType': if ret is None: ret = VoidType.create() @@ -2474,9 +2527,38 @@ class FunctionType(Type): else: _stack_adjust = OffsetWithConfidence.get_core_struct(stack_adjust, core.max_confidence) + + if reg_stack_adjust is None: + reg_stack_adjust = {} + reg_stack_adjust_regs = (ctypes.c_uint32 * len(reg_stack_adjust))() + reg_stack_adjust_values = (core.BNOffsetWithConfidence * len(reg_stack_adjust))() + + for i, (reg, adjust) in enumerate(reg_stack_adjust.items()): + reg_stack_adjust_regs[i] = reg + reg_stack_adjust_values[i].value = adjust.value + reg_stack_adjust_values[i].confidence = adjust.confidence + + return_regs_set = core.BNRegisterSetWithConfidence() + if return_regs is None or platform is None: + return_regs_set.count = 0 + return_regs_set.confidence = 0 + else: + return_regs_set.count = len(return_regs) + return_regs_set.confidence = 255 + return_regs_set.regs = (ctypes.c_uint32 * len(return_regs))() + + for i, reg in enumerate(return_regs): + return_regs_set[i] = platform.arch.get_reg_index(reg) + + _can_return = BoolWithConfidence.get_core_struct(can_return) + if params is None: + params = [] func_type = core.BNCreateFunctionType( - ret_conf, conv_conf, param_buf, len(params), _variable_arguments, _stack_adjust + ret_conf, conv_conf, param_buf, len(params), _variable_arguments, _can_return, _stack_adjust, + reg_stack_adjust_regs, reg_stack_adjust_values, len(reg_stack_adjust), + return_regs_set, name_type ) + assert func_type is not None, f"core.BNCreateFunctionType returned None {ret_conf} {conv_conf} {param_buf} {_variable_arguments} {_stack_adjust}" return cls(core.BNNewTypeReference(func_type), platform, confidence) @@ -2735,55 +2817,6 @@ class RegisterSet: @dataclass(frozen=True) -class TypeParserResult: - types: Mapping[QualifiedName, Type] - variables: Mapping[QualifiedName, Type] - functions: Mapping[QualifiedName, Type] - - def __repr__(self): - return f"<types: {self.types}, variables: {self.variables}, functions: {self.functions}>" - - -def preprocess_source(source: str, filename: Optional[str] = None, - include_dirs: Optional[List[str]] = None) -> Tuple[Optional[str], str]: - """ - ``preprocess_source`` run the C preprocessor on the given source or source filename. - - :param str source: source to pre-process - :param str filename: optional filename to pre-process - :param include_dirs: list of string directories to use as include directories. - :type include_dirs: list(str) - :return: returns a tuple of (preprocessed_source, error_string) - :rtype: tuple(str,str) - :Example: - - >>> source = "#define TEN 10\\nint x[TEN];\\n" - >>> preprocess_source(source) - ('#line 1 "input"\\n\\n#line 2 "input"\\n int x [ 10 ] ;\\n', '') - >>> - """ - if filename is None: - filename = "input" - if include_dirs is None: - include_dirs = [] - dir_buf = (ctypes.c_char_p * len(include_dirs))() - for i in range(0, len(include_dirs)): - dir_buf[i] = include_dirs[i].encode('charmap') - output = ctypes.c_char_p() - errors = ctypes.c_char_p() - result = core.BNPreprocessSource(source, filename, output, errors, dir_buf, len(include_dirs)) - assert output.value is not None - assert errors.value is not None - output_str = output.value.decode('utf-8') - error_str = errors.value.decode('utf-8') - core.free_string(output) - core.free_string(errors) - if result: - return output_str, error_str - return None, error_str - - -@dataclass(frozen=True) class TypeFieldReference: func: Optional['_function.Function'] arch: Optional['architecture.Architecture'] diff --git a/rust/src/types.rs b/rust/src/types.rs index 3afc9c9d..01767853 100644 --- a/rust/src/types.rs +++ b/rust/src/types.rs @@ -783,6 +783,7 @@ impl Type { ) -> Ref<Self> { let mut return_type = return_type.into().into(); let mut variable_arguments = Conf::new(variable_arguments, max_confidence()).into(); + let mut can_return = Conf::new(true, min_confidence()).into(); let mut raw_calling_convention: BNCallingConventionWithConfidence = BNCallingConventionWithConfidence { @@ -809,6 +810,15 @@ impl Type { }); parameter_name_references.push(raw_name); } + let reg_stack_adjust_regs = ptr::null_mut(); + let reg_stack_adjust_values = ptr::null_mut(); + + let mut return_regs: BNRegisterSetWithConfidence = + BNRegisterSetWithConfidence{ + regs: ptr::null_mut(), + count: 0, + confidence: 0, + }; unsafe { Self::ref_from_raw(BNCreateFunctionType( @@ -817,7 +827,13 @@ impl Type { raw_parameters.as_mut_ptr(), raw_parameters.len(), &mut variable_arguments, + &mut can_return, &mut stack_adjust, + reg_stack_adjust_regs, + reg_stack_adjust_values, + 0, + &mut return_regs, + BNNameType::NoNameType, )) } } @@ -836,6 +852,7 @@ impl Type { ) -> Ref<Self> { let mut return_type = return_type.into().into(); let mut variable_arguments = Conf::new(variable_arguments, max_confidence()).into(); + let mut can_return = Conf::new(true, min_confidence()).into(); let mut raw_calling_convention: BNCallingConventionWithConfidence = calling_convention.into(); let mut stack_adjust = stack_adjust.into(); @@ -859,6 +876,17 @@ impl Type { parameter_name_references.push(raw_name); } + // TODO: Update type signature and include these (will be a breaking change) + let reg_stack_adjust_regs = ptr::null_mut(); + let reg_stack_adjust_values = ptr::null_mut(); + + let mut return_regs: BNRegisterSetWithConfidence = + BNRegisterSetWithConfidence{ + regs: ptr::null_mut(), + count: 0, + confidence: 0, + }; + unsafe { Self::ref_from_raw(BNCreateFunctionType( &mut return_type, @@ -866,7 +894,13 @@ impl Type { raw_parameters.as_mut_ptr(), raw_parameters.len(), &mut variable_arguments, + &mut can_return, &mut stack_adjust, + reg_stack_adjust_regs, + reg_stack_adjust_values, + 0, + &mut return_regs, + BNNameType::NoNameType, )) } } diff --git a/suite/api_test.py b/suite/api_test.py index bcb9beb4..803bd7e8 100644 --- a/suite/api_test.py +++ b/suite/api_test.py @@ -11,13 +11,13 @@ from binaryninja.pluginmanager import RepositoryManager from binaryninja.platform import Platform from binaryninja.enums import ( StructureVariant, NamedTypeReferenceClass, MemberAccess, MemberScope, ReferenceType, VariableSourceType, - SymbolBinding, SymbolType, TokenEscapingType + SymbolBinding, SymbolType, TokenEscapingType, InstructionTextTokenType, TypeDefinitionLineType ) from binaryninja.types import ( - QualifiedName, Type, TypeBuilder, EnumerationMember, FunctionParameter, OffsetWithConfidence, BoolWithConfidence, - EnumerationBuilder, NamedTypeReferenceBuilder, StructureBuilder, StructureMember, IntegerType, StructureType, - Symbol, NameSpace, MutableTypeBuilder, NamedTypeReferenceType + QualifiedName, Type, TypeBuilder, EnumerationMember, FunctionParameter, OffsetWithConfidence, BoolWithConfidence, + EnumerationBuilder, NamedTypeReferenceBuilder, StructureBuilder, StructureMember, IntegerType, StructureType, + Symbol, NameSpace, MutableTypeBuilder, NamedTypeReferenceType, QualifiedNameType, TypeDefinitionLine ) from binaryninja.function import * from binaryninja.basicblock import * @@ -26,6 +26,8 @@ from binaryninja.lowlevelil import * from binaryninja.mediumlevelil import * from binaryninja.highlevelil import * from binaryninja.variable import * +from binaryninja.typeparser import * +from binaryninja.typeprinter import * import zipfile @@ -577,9 +579,9 @@ class TypeParserTest(unittest.TestCase): assert member.offset == expect_offset, f"Structure member property: 'offset' {expect_offset} incorrect for {member.name} in {definition} got {member.offset} instead" def test_escaping(self): - escaped = [('test', 'test', 'test'), ('a0b', 'a0b', 'a0b'), ('a$b', 'a$b', 'a$b'), ('a_b', 'a_b', 'a_b'), - ('a@b', 'a@b', 'a@b'), ('a!b', 'a!b', 'a!b'), ('0a', '0a', '`0a`'), ('_a', '_a', '_a'), - ('$a', '$a', '$a'), ('@a', '@a', '`@a`'), ('!a', '!a', '`!a`'), ('a::b', 'a::b', '`a::b`'), + escaped = [('test', 'test', 'test'), ('a0b', 'a0b', 'a0b'), ('a$b', 'a$b', '`a$b`'), ('a_b', 'a_b', 'a_b'), + ('a@b', 'a@b', '`a@b`'), ('a!b', 'a!b', '`a!b`'), ('0a', '0a', '`0a`'), ('_a', '_a', '_a'), + ('$a', '$a', '`$a`'), ('@a', '@a', '`@a`'), ('!a', '!a', '`!a`'), ('a::b', 'a::b', '`a::b`'), ('a b', 'a b', '`a b`'), ('a`b', 'a`b', '`a\\`b`'), ('a\\b', 'a\\b', '`a\\\\b`'), ('a\\`b', 'a\\`b', '`a\\\\\\`b`'), ('a\\\\`b', 'a\\\\`b', '`a\\\\\\\\\\`b`'), ] for source, expect_none, expect_backticks in escaped: @@ -621,6 +623,418 @@ class TypeParserTest(unittest.TestCase): assert len(types.types['space struct'].members[2].type.target.parameters) == 1 assert types.types['space struct'].members[2].type.target.parameters[0].name == 'argument name' + def test_parse_class(self): + valid = r''' + class foo; + class bar + { + foo* foo; + }; + class baz + { + class + { + bar m_bar; + } bar; + struct + { + baz* m_baz; + } baz; + }; + ''' + types = self.p.parse_types_from_source(valid) + assert types.types['foo'].type_class == TypeClass.VoidTypeClass + assert types.types['bar'].type_class == TypeClass.StructureTypeClass + assert types.types['bar'].type == StructureVariant.ClassStructureType + assert types.types['baz'].type_class == TypeClass.StructureTypeClass + assert types.types['baz'].type == StructureVariant.ClassStructureType + assert types.types['baz'].members[0].type.type_class == TypeClass.StructureTypeClass + assert types.types['baz'].members[0].type.type == StructureVariant.ClassStructureType + assert types.types['baz'].members[1].type.type_class == TypeClass.StructureTypeClass + assert types.types['baz'].members[1].type.type == StructureVariant.StructStructureType + + def test_class_vs_struct(self): + # Trying to use `class foo` as `struct foo` + + # Clang says (TIL): + # "Class 'foo' was previously declared as a struct; this is valid, but may result in linker errors under the Microsoft C++ ABI" + valid = [ + r''' + class foo + { + int a; + }; + class bar + { + struct foo foo; + }; + ''', r''' + struct foo + { + int a; + }; + struct bar + { + class foo foo; + }; + ''' + ] + for source in valid: + with self.subTest(): + types = self.p.parse_types_from_source(source) + + + def test_parse_empty(self): + valid = [ + # Forward declarations + 'struct foo;', + 'class foo;', + 'union foo;', + # Definition with no members + 'struct foo {};', + 'class foo {};', + 'union foo {};', + 'enum foo {};', + # Inner structure is empty + 'struct foo { struct {} bar; class {} baz; union {} alpha; enum {} bravo; };', + 'class foo { struct {} bar; class {} baz; union {} alpha; enum {} bravo; };', + 'union foo { struct {} bar; class {} baz; union {} alpha; enum {} bravo; };', + ] + for source in valid: + with self.subTest(): + types = self.p.parse_types_from_source(source) + + invalid = [ + # Forward declaration of enum is not allowed + 'enum foo;' + ] + for source in invalid: + with self.subTest(): + with self.assertRaises(SyntaxError): + types = self.p.parse_types_from_source(source) + + def test_parse_nested(self): + source = r''' + struct foo + { + enum : uint32_t + { + a = 1, + b = 2, + c = 3 + } bar; + struct + { + uint32_t a; + } baz; + class + { + uint32_t a; + } alpha; + union + { + uint32_t a; + uint32_t b; + } bravo; + }; + ''' + types = self.p.parse_types_from_source(source) + assert types.types['foo'].members[0].type.type_class == TypeClass.EnumerationTypeClass + assert types.types['foo'].members[1].type.type_class == TypeClass.StructureTypeClass + assert types.types['foo'].members[1].type.type == StructureVariant.StructStructureType + assert types.types['foo'].members[2].type.type_class == TypeClass.StructureTypeClass + assert types.types['foo'].members[2].type.type == StructureVariant.ClassStructureType + assert types.types['foo'].members[3].type.type_class == TypeClass.StructureTypeClass + assert types.types['foo'].members[3].type.type == StructureVariant.UnionStructureType + assert types.types['foo'].members[0].type.members[0].name == 'a' + assert types.types['foo'].members[0].type.members[1].name == 'b' + assert types.types['foo'].members[0].type.members[2].name == 'c' + assert types.types['foo'].members[1].type.members[0].name == 'a' + assert types.types['foo'].members[2].type.members[0].name == 'a' + assert types.types['foo'].members[3].type.members[0].name == 'a' + assert types.types['foo'].members[3].type.members[1].name == 'b' + + def test_forward_declared(self): + # Via #2431 with a little extra sauce on LIST_ENTRY1 + source = r''' + struct _LIST_ENTRY; + typedef struct _LIST_ENTRY LIST_ENTRY; + typedef LIST_ENTRY LIST_ENTRY1; + struct _LIST_ENTRY { + LIST_ENTRY * ForwardLink; + LIST_ENTRY * BackLink; + }; + + struct Test { + long long Signature; + LIST_ENTRY1 Link; + int Action; + short DefaultId; + }; + ''' + types = self.p.parse_types_from_source(source) + assert types.types['_LIST_ENTRY'].width == 0x10 + assert types.types['LIST_ENTRY'].width == 0x10 + assert types.types['LIST_ENTRY1'].width == 0x10 + assert types.types['Test'].width == 0x20 + assert types.types['Test'].members[2].offset == 0x18 + + def test_custom_subclass(self): + class MyTypeParser(TypeParser): + name = "MyTypeParser" + + def preprocess_source( + self, source: str, file_name: str, platform: binaryninja.Platform, + existing_types: Optional[List[QualifiedNameTypeAndId]], + options: Optional[List[str]], include_dirs: Optional[List[str]] + ) -> Tuple[Optional[str], List[TypeParserError]]: + return ( + source, + [ + TypeParserError(TypeParserErrorSeverity.WarningSeverity, "Test Warning", "sources.hpp", 1, 1) + ] + ) + + def parse_types_from_source( + self, + source: str, + file_name: str, + platform: binaryninja.Platform, + existing_types: Optional[List[QualifiedNameTypeAndId]], + options: Optional[List[str]], + include_dirs: Optional[List[str]], + auto_type_source: str = "" + ) -> Tuple[Optional[TypeParserResult], List[TypeParserError]]: + return ( + TypeParserResult( + [ + ParsedType("my_type", Type.int(4, False), True) + ], [ + ParsedType("my_variable", Type.int(4, False), True) + ], [ + ParsedType("my_function", Type.function(Type.void(), []), True) + ] + ), + [ + TypeParserError(TypeParserErrorSeverity.WarningSeverity, "Test Warning", "sources.hpp", 1, 1) + ] + ) + + def parse_type_string( + self, source: str, platform: binaryninja.Platform, + existing_types: Optional[List[QualifiedNameTypeAndId]] + ) -> Tuple[Optional[Tuple[QualifiedNameType, binaryninja.Type]], + List[TypeParserError]]: + return ( + ("my_type", Type.int(4, False)), + [ + TypeParserError(TypeParserErrorSeverity.WarningSeverity, "Test Warning", "sources.hpp", 1, 1) + ] + ) + + MyTypeParser().register() + + tp = TypeParser['MyTypeParser'] + (result, errors) = tp.preprocess_source('some test source', 'source.h', Platform['windows-x86']) + assert result is not None + assert result == 'some test source' + + assert errors is not None + assert len(errors) == 1 + + assert errors[0].severity == TypeParserErrorSeverity.WarningSeverity + assert errors[0].message == "Test Warning" + assert errors[0].file_name == "sources.hpp" + assert errors[0].line == 1 + assert errors[0].column == 1 + + (result, errors) = tp.parse_types_from_source('some test source', 'source.h', Platform['windows-x86']) + assert result is not None + assert len(result.types) == 1 + assert len(result.variables) == 1 + assert len(result.functions) == 1 + + assert result.types[0].name == 'my_type' + assert result.types[0].type == Type.int(4, False) + assert result.types[0].is_user + + assert result.variables[0].name == 'my_variable' + assert result.variables[0].type == Type.int(4, False) + assert result.variables[0].is_user + + assert result.functions[0].name == 'my_function' + assert result.functions[0].type == Type.function(Type.void(), []) + assert result.functions[0].is_user + + assert errors is not None + assert len(errors) == 1 + + assert errors[0].severity == TypeParserErrorSeverity.WarningSeverity + assert errors[0].message == "Test Warning" + assert errors[0].file_name == "sources.hpp" + assert errors[0].line == 1 + assert errors[0].column == 1 + + (result, errors) = tp.parse_type_string('some test source', Platform['windows-x86']) + assert result is not None + assert result[0] == 'my_type' + assert result[1] == Type.int(4, False) + + assert errors is not None + assert len(errors) == 1 + + assert errors[0].severity == TypeParserErrorSeverity.WarningSeverity + assert errors[0].message == "Test Warning" + assert errors[0].file_name == "sources.hpp" + assert errors[0].line == 1 + assert errors[0].column == 1 + + +class TestTypePrinter(unittest.TestCase): + def test_getlines(self): + arch = 'x86' + platform = Platform['windows-x86'] + bv = BinaryView.new() + bv.platform = platform + + types = [ + (Type.int(4), 'basic_int', 'typedef int32_t basic_int;\n'), + (Type.array(Type.int(4), 4), 'basic_array', 'typedef int32_t basic_array[0x4];\n'), + (Type.pointer(platform.arch, Type.array( + Type.int(4), 4 + )), 'pointer_array', 'typedef int32_t (* pointer_array)[0x4];\n'), + (Type.array( + Type.pointer(platform.arch, Type.int(4)), 4 + ), 'array_pointer', 'typedef int32_t* array_pointer[0x4];\n'), + (Type.function( + Type.int(4), [] + ), 'basic_func', 'typedef int32_t basic_func();\n'), + (Type.function( + Type.int(4), [], platform.fastcall_calling_convention + ), 'convention_func', 'typedef int32_t __fastcall convention_func();\n'), + (Type.pointer(platform.arch, Type.function( + Type.int(4), [] + ), True), 'const_func_pointer', 'typedef int32_t (* const const_func_pointer)();\n'), + (Type.pointer(platform.arch, Type.function( + Type.int(4), [] + )), 'basic_func_pointer', 'typedef int32_t (* basic_func_pointer)();\n'), + (Type.function( + Type.pointer(platform.arch, Type.int(4)), [] + ), 'func_returning_ptr', 'typedef int32_t* func_returning_ptr();\n'), + (Type.structure([ + (Type.int(4), 'foo') + ]), 'basic_struct', 'struct basic_struct\n{\n int32_t foo;\n};\n'), + (Type.pointer(platform.arch, Type.structure([ + (Type.int(4), 'foo') + ])), 'pointer_struct', 'typedef struct { int32_t foo; }* pointer_struct;\n'), + (Type.pointer(platform.arch, Type.structure([ + (Type.pointer(platform.arch, Type.int(4)), 'foo') + ])), 'pointer_in_pointer_struct', 'typedef struct { int32_t* foo; }* pointer_in_pointer_struct;\n'), + (Type.pointer(platform.arch, Type.structure([ + (Type.pointer(platform.arch, Type.structure([ + (Type.pointer(platform.arch, Type.int(4)), 'foo') + ])), 'foo') + ])), 'nested_pointer_struct', 'typedef struct { struct { int32_t* foo; }* foo; }* nested_pointer_struct;\n'), + (Type.pointer(platform.arch, Type.structure([ + (Type.pointer(platform.arch, Type.function(Type.int(4), [('param', Type.int(4))])), 'foo') + ])), 'pointer_function_struct', 'typedef struct { int32_t (* foo)(int32_t param); }* pointer_function_struct;\n'), + (Type.pointer(platform.arch, Type.enumeration(platform.arch, [ + ('one', 1) + ])), 'pointer_enumeration', 'typedef enum {}* pointer_enumeration;\n'), + ] + + for t in types: + with self.subTest(t[1]): + lines = t[0].get_lines(bv, t[1]) + text = "" + for l in lines: + for tok in l.tokens: + text += tok.text + text += "\n" + assert text == t[2], f"Invalid printing of type, got {text} expected {t[2]}" + + + def test_custom_subclass(self): + class MyTypePrinter(TypePrinter): + name = "MyTypePrinter" + + def get_type_tokens(self, type: types.Type, platform: Optional[Platform], name: types.QualifiedName, + base_confidence: int, escaping: TokenEscapingType) -> List[InstructionTextToken]: + return [ + InstructionTextToken(InstructionTextTokenType.TextToken, "the type is: ", 0), + InstructionTextToken(InstructionTextTokenType.TypeNameToken, str(name), 0), + InstructionTextToken(InstructionTextTokenType.TextToken, " bottom text", 0) + ] + + def get_type_tokens_before_name(self, type: types.Type, platform: Optional[Platform], base_confidence: int, + parent_type: Optional[types.Type], escaping: TokenEscapingType) -> List[ + InstructionTextToken]: + return [ + InstructionTextToken(InstructionTextTokenType.TextToken, "the type is: ", 0), + ] + + def get_type_tokens_after_name(self, type: types.Type, platform: Optional[Platform], base_confidence: int, + parent_type: Optional[types.Type], escaping: TokenEscapingType) -> List[InstructionTextToken]: + return [ + InstructionTextToken(InstructionTextTokenType.TextToken, " bottom text", 0), + ] + + def get_type_string(self, type: types.Type, platform: Optional[Platform], name: types.QualifiedName, + escaping: TokenEscapingType) -> str: + return f"the type is: {name} bottom text" + + def get_type_string_before_name(self, type: types.Type, platform: Optional[Platform], + escaping: TokenEscapingType) -> str: + return f"the type is: " + + def get_type_string_after_name(self, type: types.Type, platform: Optional[Platform], + escaping: TokenEscapingType) -> str: + return f" bottom text" + + def get_type_lines(self, type: types.Type, data: binaryview.BinaryView, name: types.QualifiedName, line_width, + collapsed, escaping: TokenEscapingType) -> List[types.TypeDefinitionLine]: + return [ + TypeDefinitionLine(TypeDefinitionLineType.TypedefLineType, [ + InstructionTextToken(InstructionTextTokenType.TextToken, "the type is: ", 0), + InstructionTextToken(InstructionTextTokenType.TypeNameToken, str(name), 0), + InstructionTextToken(InstructionTextTokenType.TextToken, " bottom text", 0) + ], type, type, '', 0, 1) + ] + + MyTypePrinter().register() + + tp = TypePrinter['MyTypePrinter'] + result = tp.get_type_tokens(Type.void(), None, QualifiedName(['test']), 255, TokenEscapingType.NoTokenEscapingType) + assert result is not None + assert len(result) == 3 + assert result[0].text == "the type is: " + assert result[1].text == "test" + assert result[2].text == " bottom text" + result = tp.get_type_tokens_before_name(Type.void()) + assert result is not None + assert len(result) == 1 + assert result[0].text == "the type is: " + result = tp.get_type_tokens_after_name(Type.void()) + assert result is not None + assert len(result) == 1 + assert result[0].text == " bottom text" + result = tp.get_type_string(Type.void(), None, QualifiedName(['test'])) + assert result is not None + assert result == "the type is: test bottom text" + result = tp.get_type_string_before_name(Type.void()) + assert result is not None + assert result == "the type is: " + result = tp.get_type_string_after_name(Type.void()) + assert result is not None + assert result == " bottom text" + result = tp.get_type_lines(Type.void(), BinaryView.new(b''), QualifiedName(['test'])) + assert result is not None + assert len(result) == 1 + assert len(result[0].tokens) == 3 + assert result[0].tokens[0].text == "the type is: " + assert result[0].tokens[1].text == "test" + assert result[0].tokens[2].text == " bottom text" + class TestQualifiedName(unittest.TestCase): def test_constructors_and_equality(self): @@ -210,16 +210,16 @@ string NameList::GetString(BNTokenEscapingType escaping) const { if (!first) { - out += m_join + EscapeTypeName(name, escaping); + out += m_join + name; } else { - out += EscapeTypeName(name, escaping); + out += name; } if (name.length() != 0) first = false; } - return out; + return EscapeTypeName(out, escaping); } @@ -340,7 +340,7 @@ void QualifiedName::FreeAPIObject(BNQualifiedName* name) } -QualifiedName QualifiedName::FromAPIObject(BNQualifiedName* name) +QualifiedName QualifiedName::FromAPIObject(const BNQualifiedName* name) { QualifiedName result; for (size_t i = 0; i < name->nameCount; i++) @@ -451,6 +451,38 @@ TypeDefinitionLine TypeDefinitionLine::FromAPIObject(BNTypeDefinitionLine* line) } +BNTypeDefinitionLine* TypeDefinitionLine::CreateTypeDefinitionLineList( + const std::vector<TypeDefinitionLine>& lines) +{ + BNTypeDefinitionLine* result = new BNTypeDefinitionLine[lines.size()]; + for (size_t i = 0; i < lines.size(); ++i) + { + result[i].lineType = lines[i].lineType; + result[i].tokens = InstructionTextToken::CreateInstructionTextTokenList(lines[i].tokens); + result[i].count = lines[i].tokens.size(); + result[i].type = BNNewTypeReference(lines[i].type->GetObject()); + result[i].rootType = BNNewTypeReference(lines[i].rootType->GetObject()); + result[i].rootTypeName = BNAllocString(lines[i].rootTypeName.c_str()); + result[i].offset = lines[i].offset; + result[i].fieldIndex = lines[i].fieldIndex; + } + return result; +} + + +void TypeDefinitionLine::FreeTypeDefinitionLineList(BNTypeDefinitionLine* lines, size_t count) +{ + for (size_t i = 0; i < count; ++i) + { + InstructionTextToken::FreeInstructionTextTokenList(lines[i].tokens, lines[i].count); + BNFreeType(lines[i].type); + BNFreeType(lines[i].rootType); + BNFreeString(lines[i].rootTypeName); + } + delete[] lines; +} + + Type::Type(BNType* type) { m_object = type; @@ -843,16 +875,96 @@ Ref<Type> Type::FunctionType(const Confidence<Ref<Type>>& returnValue, varArgConf.value = varArg.GetValue(); varArgConf.confidence = varArg.GetConfidence(); + BNBoolWithConfidence canReturnConf; + canReturnConf.value = true; + canReturnConf.confidence = 0; + + BNOffsetWithConfidence stackAdjustConf; + stackAdjustConf.value = stackAdjust.GetValue(); + stackAdjustConf.confidence = stackAdjust.GetConfidence(); + + BNRegisterSetWithConfidence returnRegsConf; + returnRegsConf.regs = nullptr; + returnRegsConf.count = 0; + returnRegsConf.confidence = 0; + + Type* type = new Type(BNCreateFunctionType( + &returnValueConf, &callingConventionConf, paramArray, params.size(), &varArgConf, + &canReturnConf, &stackAdjustConf, nullptr, nullptr, 0, &returnRegsConf, NoNameType)); + delete[] paramArray; + return type; +} + + +Ref<Type> Type::FunctionType(const Confidence<Ref<Type>>& returnValue, + const Confidence<Ref<CallingConvention>>& callingConvention, + const std::vector<FunctionParameter>& params, + const Confidence<bool>& hasVariableArguments, + const Confidence<bool>& canReturn, + const Confidence<int64_t>& stackAdjust, + const std::map<uint32_t, Confidence<int32_t>>& regStackAdjust, + const Confidence<std::vector<uint32_t>>& returnRegs, + BNNameType ft) +{ + BNTypeWithConfidence returnValueConf; + returnValueConf.type = returnValue->GetObject(); + returnValueConf.confidence = returnValue.GetConfidence(); + + BNCallingConventionWithConfidence callingConventionConf; + callingConventionConf.convention = callingConvention ? callingConvention->GetObject() : nullptr; + callingConventionConf.confidence = callingConvention.GetConfidence(); + + BNFunctionParameter* paramArray = new BNFunctionParameter[params.size()]; + for (size_t i = 0; i < params.size(); i++) + { + paramArray[i].name = (char*)params[i].name.c_str(); + paramArray[i].type = params[i].type->GetObject(); + paramArray[i].typeConfidence = params[i].type.GetConfidence(); + paramArray[i].defaultLocation = params[i].defaultLocation; + paramArray[i].location.type = params[i].location.type; + paramArray[i].location.index = params[i].location.index; + paramArray[i].location.storage = params[i].location.storage; + } + + BNBoolWithConfidence varArgConf; + varArgConf.value = hasVariableArguments.GetValue(); + varArgConf.confidence = hasVariableArguments.GetConfidence(); + + BNBoolWithConfidence canReturnConf; + canReturnConf.value = canReturn.GetValue(); + canReturnConf.confidence = canReturn.GetConfidence(); + BNOffsetWithConfidence stackAdjustConf; stackAdjustConf.value = stackAdjust.GetValue(); stackAdjustConf.confidence = stackAdjust.GetConfidence(); + std::vector<uint32_t> regStackAdjustRegs; + std::vector<BNOffsetWithConfidence> regStackAdjustValues; + size_t i = 0; + for (const auto& adjust: regStackAdjust) + { + regStackAdjustRegs[i] = adjust.first; + regStackAdjustValues[i].value = adjust.second.GetValue(); + regStackAdjustValues[i].confidence = adjust.second.GetConfidence(); + i ++; + } + + std::vector<uint32_t> returnRegsRegs = returnRegs.GetValue(); + + BNRegisterSetWithConfidence returnRegsConf; + returnRegsConf.regs = returnRegsRegs.data(); + returnRegsConf.count = returnRegs->size(); + returnRegsConf.confidence = returnRegs.GetConfidence(); + Type* type = new Type(BNCreateFunctionType( - &returnValueConf, &callingConventionConf, paramArray, params.size(), &varArgConf, &stackAdjustConf)); + &returnValueConf, &callingConventionConf, paramArray, params.size(), &varArgConf, + &canReturnConf, &stackAdjustConf, regStackAdjustRegs.data(), + regStackAdjustValues.data(), regStackAdjust.size(), &returnRegsConf, NoNameType)); delete[] paramArray; return type; } + BNIntegerDisplayType Type::GetIntegerTypeDisplayType() const { return BNGetIntegerTypeDisplayType(m_object); @@ -1587,8 +1699,87 @@ TypeBuilder TypeBuilder::FunctionType(const Confidence<Ref<Type>>& returnValue, stackAdjustConf.value = stackAdjust.GetValue(); stackAdjustConf.confidence = stackAdjust.GetConfidence(); + BNBoolWithConfidence canReturnConf; + canReturnConf.value = true; + canReturnConf.confidence = 0; + + BNRegisterSetWithConfidence returnRegsConf; + returnRegsConf.regs = nullptr; + returnRegsConf.count = 0; + returnRegsConf.confidence = 0; + + TypeBuilder type(BNCreateFunctionTypeBuilder( + &returnValueConf, &callingConventionConf, paramArray, paramCount, &varArgConf, + &canReturnConf, &stackAdjustConf, nullptr, nullptr, 0, &returnRegsConf, NoNameType)); + delete[] paramArray; + return type; +} + + +TypeBuilder TypeBuilder::FunctionType(const Confidence<Ref<Type>>& returnValue, + const Confidence<Ref<CallingConvention>>& callingConvention, + const std::vector<FunctionParameter>& params, + const Confidence<bool>& hasVariableArguments, + const Confidence<bool>& canReturn, + const Confidence<int64_t>& stackAdjust, + const std::map<uint32_t, Confidence<int32_t>>& regStackAdjust, + const Confidence<std::vector<uint32_t>>& returnRegs, + BNNameType ft) +{ + BNTypeWithConfidence returnValueConf; + returnValueConf.type = returnValue->GetObject(); + returnValueConf.confidence = returnValue.GetConfidence(); + + BNCallingConventionWithConfidence callingConventionConf; + callingConventionConf.convention = callingConvention ? callingConvention->GetObject() : nullptr; + callingConventionConf.confidence = callingConvention.GetConfidence(); + + BNFunctionParameter* paramArray = new BNFunctionParameter[params.size()]; + for (size_t i = 0; i < params.size(); i++) + { + paramArray[i].name = (char*)params[i].name.c_str(); + paramArray[i].type = params[i].type->GetObject(); + paramArray[i].typeConfidence = params[i].type.GetConfidence(); + paramArray[i].defaultLocation = params[i].defaultLocation; + paramArray[i].location.type = params[i].location.type; + paramArray[i].location.index = params[i].location.index; + paramArray[i].location.storage = params[i].location.storage; + } + + BNBoolWithConfidence varArgConf; + varArgConf.value = hasVariableArguments.GetValue(); + varArgConf.confidence = hasVariableArguments.GetConfidence(); + + BNBoolWithConfidence canReturnConf; + canReturnConf.value = canReturn.GetValue(); + canReturnConf.confidence = canReturn.GetConfidence(); + + BNOffsetWithConfidence stackAdjustConf; + stackAdjustConf.value = stackAdjust.GetValue(); + stackAdjustConf.confidence = stackAdjust.GetConfidence(); + + std::vector<uint32_t> regStackAdjustRegs; + std::vector<BNOffsetWithConfidence> regStackAdjustValues; + size_t i = 0; + for (const auto& adjust: regStackAdjust) + { + regStackAdjustRegs[i] = adjust.first; + regStackAdjustValues[i].value = adjust.second.GetValue(); + regStackAdjustValues[i].confidence = adjust.second.GetConfidence(); + i ++; + } + + std::vector<uint32_t> returnRegsRegs = returnRegs.GetValue(); + + BNRegisterSetWithConfidence returnRegsConf; + returnRegsConf.regs = returnRegsRegs.data(); + returnRegsConf.count = returnRegs->size(); + returnRegsConf.confidence = returnRegs.GetConfidence(); + TypeBuilder type(BNCreateFunctionTypeBuilder( - &returnValueConf, &callingConventionConf, paramArray, paramCount, &varArgConf, &stackAdjustConf)); + &returnValueConf, &callingConventionConf, paramArray, params.size(), &varArgConf, + &canReturnConf, &stackAdjustConf, regStackAdjustRegs.data(), + regStackAdjustValues.data(), regStackAdjust.size(), &returnRegsConf, NoNameType)); delete[] paramArray; return type; } diff --git a/typeparser.cpp b/typeparser.cpp new file mode 100644 index 00000000..db6f8b21 --- /dev/null +++ b/typeparser.cpp @@ -0,0 +1,513 @@ +#include "binaryninjaapi.h" + +using namespace BinaryNinja; +using namespace std; + + + +TypeParser::TypeParser(const string& name) : m_nameForRegister(name) {} + + +TypeParser::TypeParser(BNTypeParser* parser) +{ + m_object = parser; +} + + +vector<Ref<TypeParser>> TypeParser::GetList() +{ + size_t count; + BNTypeParser** list = BNGetTypeParserList(&count); + vector<Ref<TypeParser>> result; + for (size_t i = 0; i < count; i++) + result.push_back(new CoreTypeParser(list[i])); + BNFreeTypeParserList(list); + return result; +} + + +Ref<TypeParser> TypeParser::GetByName(const string& name) +{ + BNTypeParser* result = BNGetTypeParserByName(name.c_str()); + if (!result) + return nullptr; + return new CoreTypeParser(result); +} + + +Ref<TypeParser> TypeParser::GetDefault() +{ + string name = Settings::Instance()->Get<string>("analysis.types.parserName"); + return GetByName(name); +} + + +void TypeParser::Register(TypeParser* parser) +{ + BNTypeParserCallbacks cb; + cb.context = parser; + cb.preprocessSource = PreprocessSourceCallback; + cb.parseTypesFromSource = ParseTypesFromSourceCallback; + cb.parseTypeString = ParseTypeStringCallback; + cb.freeString = FreeStringCallback; + cb.freeResult = FreeResultCallback; + cb.freeErrorList = FreeErrorListCallback; + parser->m_object = BNRegisterTypeParser(parser->m_nameForRegister.c_str(), &cb); +} + + +bool TypeParser::PreprocessSourceCallback(void* ctxt, + const char* source, const char* fileName, BNPlatform* platform, + const BNQualifiedNameTypeAndId* existingTypes, size_t existingTypeCount, + const char* const* options, size_t optionCount, + const char* const* includeDirs, size_t includeDirCount, + char** output, BNTypeParserError** errors, size_t* errorCount +) +{ + TypeParser* parser = (TypeParser*)ctxt; + + map<QualifiedName, TypeAndId> existingTypesCpp; + for (size_t i = 0; i < existingTypeCount; i ++) + { + QualifiedName qname = QualifiedName::FromAPIObject(&existingTypes[i].name); + TypeAndId type = { + existingTypes[i].id, + new Type(existingTypes[i].type), + }; + existingTypesCpp.insert({qname, type}); + } + + vector<string> optionsCpp; + for (size_t i = 0; i < optionCount; i ++) + { + optionsCpp.push_back(options[i]); + } + + vector<string> includeDirsCpp; + for (size_t i = 0; i < includeDirCount; i ++) + { + includeDirsCpp.push_back(includeDirs[i]); + } + + std::string outputCpp; + vector<TypeParserError> errorsCpp; + bool success = parser->PreprocessSource(source, fileName, new Platform(platform), + existingTypesCpp, optionsCpp, includeDirsCpp, outputCpp, errorsCpp); + + if (success) + { + *output = BNAllocString(outputCpp.c_str()); + } + else + { + *output = nullptr; + } + + *errorCount = errorsCpp.size(); + *errors = new BNTypeParserError[errorsCpp.size()]; + for (size_t i = 0; i < errorsCpp.size(); ++i) + { + (*errors)[i].severity = errorsCpp[i].severity; + (*errors)[i].message = BNAllocString(errorsCpp[i].message.c_str()); + (*errors)[i].fileName = BNAllocString(errorsCpp[i].fileName.c_str()); + (*errors)[i].line = errorsCpp[i].line; + (*errors)[i].column = errorsCpp[i].column; + } + + return success; +} + + +bool TypeParser::ParseTypesFromSourceCallback(void* ctxt, + const char* source, const char* fileName, BNPlatform* platform, + const BNQualifiedNameTypeAndId* existingTypes, size_t existingTypeCount, + const char* const* options, size_t optionCount, + const char* const* includeDirs, size_t includeDirCount, + const char* autoTypeSource, BNTypeParserResult* result, + BNTypeParserError** errors, size_t* errorCount +) +{ + TypeParser* parser = (TypeParser*)ctxt; + + map<QualifiedName, TypeAndId> existingTypesCpp; + for (size_t i = 0; i < existingTypeCount; i ++) + { + QualifiedName qname = QualifiedName::FromAPIObject(&existingTypes[i].name); + TypeAndId type = { + existingTypes[i].id, + new Type(existingTypes[i].type), + }; + existingTypesCpp.insert({qname, type}); + } + + vector<string> optionsCpp; + for (size_t i = 0; i < optionCount; i ++) + { + optionsCpp.push_back(options[i]); + } + + vector<string> includeDirsCpp; + for (size_t i = 0; i < includeDirCount; i ++) + { + includeDirsCpp.push_back(includeDirs[i]); + } + + TypeParserResult resultCpp; + vector<TypeParserError> errorsCpp; + bool success = parser->ParseTypesFromSource(source, fileName, new Platform(platform), + existingTypesCpp, optionsCpp, includeDirsCpp, autoTypeSource, resultCpp, errorsCpp); + + result->typeCount = resultCpp.types.size(); + result->variableCount = resultCpp.variables.size(); + result->functionCount = resultCpp.functions.size(); + result->types = new BNParsedType[resultCpp.types.size()]; + result->variables = new BNParsedType[resultCpp.variables.size()]; + result->functions = new BNParsedType[resultCpp.functions.size()]; + + size_t n = 0; + for (auto& i : resultCpp.types) + { + result->types[n].name = i.name.GetAPIObject(); + result->types[n].type = BNNewTypeReference(i.type->GetObject()); + result->types[n].isUser = i.isUser; + n++; + } + + n = 0; + for (auto& i : resultCpp.variables) + { + result->variables[n].name = i.name.GetAPIObject(); + result->variables[n].type = BNNewTypeReference(i.type->GetObject()); + result->variables[n].isUser = i.isUser; + n++; + } + + n = 0; + for (auto& i : resultCpp.functions) + { + result->functions[n].name = i.name.GetAPIObject(); + result->functions[n].type = BNNewTypeReference(i.type->GetObject()); + result->functions[n].isUser = i.isUser; + n++; + } + + *errorCount = errorsCpp.size(); + *errors = new BNTypeParserError[errorsCpp.size()]; + for (size_t i = 0; i < errorsCpp.size(); ++i) + { + (*errors)[i].severity = errorsCpp[i].severity; + (*errors)[i].message = BNAllocString(errorsCpp[i].message.c_str()); + (*errors)[i].fileName = BNAllocString(errorsCpp[i].fileName.c_str()); + (*errors)[i].line = errorsCpp[i].line; + (*errors)[i].column = errorsCpp[i].column; + } + + return success; +} + + +bool TypeParser::ParseTypeStringCallback(void* ctxt, + const char* source, BNPlatform* platform, + const BNQualifiedNameTypeAndId* existingTypes, size_t existingTypeCount, + BNQualifiedNameAndType* result, + BNTypeParserError** errors, size_t* errorCount +) +{ + TypeParser* parser = (TypeParser*)ctxt; + + map<QualifiedName, TypeAndId> existingTypesCpp; + for (size_t i = 0; i < existingTypeCount; i ++) + { + QualifiedName qname = QualifiedName::FromAPIObject(&existingTypes[i].name); + TypeAndId type = { + existingTypes[i].id, + new Type(existingTypes[i].type), + }; + existingTypesCpp.insert({qname, type}); + } + + QualifiedNameAndType resultCpp; + vector<TypeParserError> errorsCpp; + bool success = parser->ParseTypeString(source, new Platform(platform), existingTypesCpp, + resultCpp, errorsCpp); + + result->name = resultCpp.name.GetAPIObject(); + result->type = BNNewTypeReference(resultCpp.type->GetObject()); + + *errorCount = errorsCpp.size(); + *errors = new BNTypeParserError[errorsCpp.size()]; + for (size_t i = 0; i < errorsCpp.size(); ++i) + { + (*errors)[i].severity = errorsCpp[i].severity; + (*errors)[i].message = BNAllocString(errorsCpp[i].message.c_str()); + (*errors)[i].fileName = BNAllocString(errorsCpp[i].fileName.c_str()); + (*errors)[i].line = errorsCpp[i].line; + (*errors)[i].column = errorsCpp[i].column; + } + + return success; +} + + +void TypeParser::FreeStringCallback(void* ctxt, char* result) +{ + BNFreeString(result); +} + + +void TypeParser::FreeResultCallback(void* ctxt, BNTypeParserResult* result) +{ + for (size_t i = 0; i < result->typeCount; i++) + { + QualifiedName::FreeAPIObject(&result->types[i].name); + BNFreeType(result->types[i].type); + } + for (size_t i = 0; i < result->variableCount; i++) + { + QualifiedName::FreeAPIObject(&result->variables[i].name); + BNFreeType(result->variables[i].type); + } + for (size_t i = 0; i < result->functionCount; i++) + { + QualifiedName::FreeAPIObject(&result->functions[i].name); + BNFreeType(result->functions[i].type); + } + delete[] result->types; + delete[] result->variables; + delete[] result->functions; +} + + +void TypeParser::FreeErrorListCallback(void* ctxt, BNTypeParserError* errors, size_t errorCount) +{ + for (size_t i = 0; i < errorCount; i ++) + { + BNFreeString(errors[i].message); + BNFreeString(errors[i].fileName); + } + delete[] errors; +} + + +CoreTypeParser::CoreTypeParser(BNTypeParser* parser): TypeParser(parser) +{ + +} + + +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, + std::string& output, std::vector<TypeParserError>& errors) +{ + const char** apiOptions = new const char*[options.size()]; + for (size_t i = 0; i < options.size(); ++i) + { + apiOptions[i] = options[i].c_str(); + } + const char** apiIncludeDirs = new const char*[includeDirs.size()]; + for (size_t i = 0; i < includeDirs.size(); ++i) + { + apiIncludeDirs[i] = includeDirs[i].c_str(); + } + + BNQualifiedNameTypeAndId* apiExistingTypes = new BNQualifiedNameTypeAndId[existingTypes.size()]; + size_t i = 0; + for (const auto& pair: existingTypes) + { + apiExistingTypes[i].name = pair.first.GetAPIObject(); + apiExistingTypes[i].id = BNAllocString(pair.second.id.c_str()); + apiExistingTypes[i].type = pair.second.type->GetObject(); + i++; + } + + char* apiOutput; + BNTypeParserError* apiErrors; + size_t errorCount; + + auto success = BNTypeParserPreprocessSource(m_object, source.c_str(), fileName.c_str(), + platform->GetObject(), apiExistingTypes, existingTypes.size(), + apiOptions, options.size(), apiIncludeDirs, includeDirs.size(), &apiOutput, + &apiErrors, &errorCount); + + delete [] apiOptions; + delete [] apiIncludeDirs; + + for (size_t j = 0; j < existingTypes.size(); j ++) + { + QualifiedName::FreeAPIObject(&apiExistingTypes[j].name); + BNFreeString(apiExistingTypes[j].id); + } + delete [] apiExistingTypes; + + for (size_t j = 0; j < errorCount; j ++) + { + TypeParserError error; + error.severity = apiErrors[j].severity, + error.message = apiErrors[j].message, + error.fileName = apiErrors[j].fileName, + error.line = apiErrors[j].line, + error.column = apiErrors[j].column, + errors.push_back(error); + } + BNFreeTypeParserErrors(apiErrors, errorCount); + + if (!success) + { + return false; + } + + output = apiOutput; + BNFreeString(apiOutput); + return true; +} + + +bool CoreTypeParser::ParseTypesFromSource(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, + const std::string& autoTypeSource, TypeParserResult& result, std::vector<TypeParserError>& errors) +{ + const char** apiOptions = new const char*[options.size()]; + for (size_t i = 0; i < options.size(); ++i) + { + apiOptions[i] = options[i].c_str(); + } + const char** apiIncludeDirs = new const char*[includeDirs.size()]; + for (size_t i = 0; i < includeDirs.size(); ++i) + { + apiIncludeDirs[i] = includeDirs[i].c_str(); + } + + BNQualifiedNameTypeAndId* apiExistingTypes = new BNQualifiedNameTypeAndId[existingTypes.size()]; + size_t i = 0; + for (const auto& pair: existingTypes) + { + apiExistingTypes[i].name = pair.first.GetAPIObject(); + apiExistingTypes[i].id = BNAllocString(pair.second.id.c_str()); + apiExistingTypes[i].type = pair.second.type->GetObject(); + i++; + } + + BNTypeParserResult apiResult; + BNTypeParserError* apiErrors; + size_t errorCount; + + auto success = BNTypeParserParseTypesFromSource(m_object, source.c_str(), fileName.c_str(), + platform->GetObject(), apiExistingTypes, existingTypes.size(), + apiOptions, options.size(), apiIncludeDirs, includeDirs.size(), autoTypeSource.c_str(), &apiResult, + &apiErrors, &errorCount); + + delete [] apiOptions; + delete [] apiIncludeDirs; + + for (size_t j = 0; j < existingTypes.size(); j ++) + { + QualifiedName::FreeAPIObject(&apiExistingTypes[j].name); + BNFreeString(apiExistingTypes[j].id); + } + delete [] apiExistingTypes; + + for (size_t j = 0; j < errorCount; j ++) + { + TypeParserError error; + error.severity = apiErrors[j].severity, + error.message = apiErrors[j].message, + error.fileName = apiErrors[j].fileName, + error.line = apiErrors[j].line, + error.column = apiErrors[j].column, + errors.push_back(error); + } + BNFreeTypeParserErrors(apiErrors, errorCount); + + if (!success) + { + return false; + } + + result.types.clear(); + for (size_t j = 0; j < apiResult.typeCount; ++j) + { + result.types.push_back({ + QualifiedName::FromAPIObject(&apiResult.types[j].name), + new Type(BNNewTypeReference(apiResult.types[j].type)), + apiResult.types[j].isUser + }); + } + + result.variables.clear(); + for (size_t j = 0; j < apiResult.variableCount; ++j) + { + result.variables.push_back({ + QualifiedName::FromAPIObject(&apiResult.variables[j].name), + new Type(BNNewTypeReference(apiResult.variables[j].type)), + apiResult.types[j].isUser + }); + } + + result.functions.clear(); + for (size_t j = 0; j < apiResult.functionCount; ++j) + { + result.functions.push_back({ + QualifiedName::FromAPIObject(&apiResult.functions[j].name), + new Type(BNNewTypeReference(apiResult.functions[j].type)), + apiResult.types[j].isUser + }); + } + + BNFreeTypeParserResult(&apiResult); + return true; +} + + +bool CoreTypeParser::ParseTypeString(const std::string& source, Ref<Platform> platform, + const std::map<QualifiedName, TypeAndId>& existingTypes, + QualifiedNameAndType& result, std::vector<TypeParserError>& errors) +{ + BNQualifiedNameTypeAndId* apiExistingTypes = new BNQualifiedNameTypeAndId[existingTypes.size()]; + size_t i = 0; + for (const auto& pair: existingTypes) + { + apiExistingTypes[i].name = pair.first.GetAPIObject(); + apiExistingTypes[i].id = BNAllocString(pair.second.id.c_str()); + apiExistingTypes[i].type = pair.second.type->GetObject(); + } + + BNQualifiedNameAndType apiResult; + BNTypeParserError* apiErrors; + size_t errorCount; + + auto success = BNTypeParserParseTypeString(m_object, source.c_str(), platform->GetObject(), + apiExistingTypes, existingTypes.size(), &apiResult, + &apiErrors, &errorCount); + + for (size_t j = 0; j < existingTypes.size(); j ++) + { + QualifiedName::FreeAPIObject(&apiExistingTypes[j].name); + BNFreeString(apiExistingTypes[j].id); + } + delete [] apiExistingTypes; + + for (size_t j = 0; j < errorCount; j ++) + { + TypeParserError error; + error.severity = apiErrors[j].severity, + error.message = apiErrors[j].message, + error.fileName = apiErrors[j].fileName, + error.line = apiErrors[j].line, + error.column = apiErrors[j].column, + errors.push_back(error); + } + BNFreeTypeParserErrors(apiErrors, errorCount); + + if (!success) + { + return false; + } + + result.name = QualifiedName::FromAPIObject(&apiResult.name); + result.type = new Type(BNNewTypeReference(apiResult.type)); + BNFreeQualifiedNameAndType(&apiResult); + + return true; +} diff --git a/typeprinter.cpp b/typeprinter.cpp new file mode 100644 index 00000000..aed010b2 --- /dev/null +++ b/typeprinter.cpp @@ -0,0 +1,400 @@ +#include "binaryninjaapi.h" + +using namespace BinaryNinja; +using namespace std; + +TypePrinter::TypePrinter(const std::string& name): m_nameForRegister(name) +{ + +} + + +TypePrinter::TypePrinter(BNTypePrinter* printer) +{ + m_object = printer; +} + + +bool TypePrinter::GetTypeTokensCallback(void* ctxt, BNType* type, BNPlatform* platform, + BNQualifiedName* name, uint8_t baseConfidence, BNTokenEscapingType escaping, + BNInstructionTextToken** result, size_t* resultCount) +{ + TypePrinter* printer = (TypePrinter*)ctxt; + vector<InstructionTextToken> tokens = printer->GetTypeTokens( + new Type(BNNewTypeReference(type)), platform ? new Platform(BNNewPlatformReference(platform)) : nullptr, + QualifiedName::FromAPIObject(name), baseConfidence, escaping); + + *resultCount = tokens.size(); + *result = InstructionTextToken::CreateInstructionTextTokenList(tokens); + return true; +} + + +bool TypePrinter::GetTypeTokensBeforeNameCallback(void* ctxt, BNType* type, + BNPlatform* platform, uint8_t baseConfidence, BNType* parentType, + BNTokenEscapingType escaping, BNInstructionTextToken** result, + size_t* resultCount) +{ + TypePrinter* printer = (TypePrinter*)ctxt; + vector<InstructionTextToken> tokens = printer->GetTypeTokensBeforeName( + new Type(BNNewTypeReference(type)), platform ? new Platform(BNNewPlatformReference(platform)) : nullptr, + baseConfidence, parentType ? new Type(BNNewTypeReference(parentType)) : nullptr, escaping); + + *resultCount = tokens.size(); + *result = InstructionTextToken::CreateInstructionTextTokenList(tokens); + return true; +} + + +bool TypePrinter::GetTypeTokensAfterNameCallback(void* ctxt, BNType* type, + BNPlatform* platform, uint8_t baseConfidence, BNType* parentType, + BNTokenEscapingType escaping, BNInstructionTextToken** result, + size_t* resultCount) +{ + TypePrinter* printer = (TypePrinter*)ctxt; + vector<InstructionTextToken> tokens = printer->GetTypeTokensAfterName( + new Type(BNNewTypeReference(type)), platform ? new Platform(BNNewPlatformReference(platform)) : nullptr, + baseConfidence, parentType ? new Type(BNNewTypeReference(parentType)) : nullptr, escaping); + + *resultCount = tokens.size(); + *result = InstructionTextToken::CreateInstructionTextTokenList(tokens); + return true; +} + + +bool TypePrinter::GetTypeStringCallback(void* ctxt, BNType* type, BNPlatform* platform, + BNQualifiedName* name, BNTokenEscapingType escaping, char** result) +{ + TypePrinter* printer = (TypePrinter*)ctxt; + string text = printer->GetTypeString( + new Type(BNNewTypeReference(type)), platform ? new Platform(BNNewPlatformReference(platform)) : nullptr, + QualifiedName::FromAPIObject(name), escaping); + + *result = BNAllocString(text.c_str()); + return true; +} + + +bool TypePrinter::GetTypeStringBeforeNameCallback(void* ctxt, BNType* type, + BNPlatform* platform, BNTokenEscapingType escaping, char** result) +{ + TypePrinter* printer = (TypePrinter*)ctxt; + string text = printer->GetTypeStringBeforeName( + new Type(BNNewTypeReference(type)), platform ? new Platform(BNNewPlatformReference(platform)) : nullptr, + escaping); + + *result = BNAllocString(text.c_str()); + return true; +} + + +bool TypePrinter::GetTypeStringAfterNameCallback(void* ctxt, BNType* type, + BNPlatform* platform, BNTokenEscapingType escaping, char** result) +{ + TypePrinter* printer = (TypePrinter*)ctxt; + string text = printer->GetTypeStringAfterName( + new Type(BNNewTypeReference(type)), platform ? new Platform(BNNewPlatformReference(platform)) : nullptr, + escaping); + + *result = BNAllocString(text.c_str()); + return true; +} + + +bool TypePrinter::GetTypeLinesCallback(void* ctxt, BNType* type, BNBinaryView* data, + BNQualifiedName* name, int lineWidth, bool collapsed, + BNTokenEscapingType escaping, BNTypeDefinitionLine** result, size_t* resultCount) +{ + TypePrinter* printer = (TypePrinter*)ctxt; + vector<TypeDefinitionLine> lines = printer->GetTypeLines( + new Type(BNNewTypeReference(type)), new BinaryView(BNNewViewReference(data)), + QualifiedName::FromAPIObject(name), lineWidth, collapsed, escaping); + + *resultCount = lines.size(); + *result = TypeDefinitionLine::CreateTypeDefinitionLineList(lines); + return true; +} + + +void TypePrinter::FreeTokensCallback(void* ctxt, BNInstructionTextToken* tokens, size_t count) +{ + InstructionTextToken::FreeInstructionTextTokenList(tokens, count); +} + + +void TypePrinter::FreeStringCallback(void* ctxt, char* string) +{ + BNFreeString(string); +} + + +void TypePrinter::FreeLinesCallback(void* ctxt, BNTypeDefinitionLine* lines, size_t count) +{ + TypeDefinitionLine::FreeTypeDefinitionLineList(lines, count); +} + + +void TypePrinter::Register(TypePrinter* printer) +{ + BNTypePrinterCallbacks cb; + cb.context = printer; + cb.getTypeTokens = GetTypeTokensCallback; + cb.getTypeTokensBeforeName = GetTypeTokensBeforeNameCallback; + cb.getTypeTokensAfterName = GetTypeTokensAfterNameCallback; + cb.getTypeString = GetTypeStringCallback; + cb.getTypeStringBeforeName = GetTypeStringBeforeNameCallback; + cb.getTypeStringAfterName = GetTypeStringAfterNameCallback; + cb.getTypeLines = GetTypeLinesCallback; + cb.freeTokens = FreeTokensCallback; + cb.freeString = FreeStringCallback; + cb.freeLines = FreeLinesCallback; + printer->m_object = BNRegisterTypePrinter(printer->m_nameForRegister.c_str(), &cb); +} + + +std::vector<Ref<TypePrinter>> TypePrinter::GetList() +{ + size_t count; + BNTypePrinter** list = BNGetTypePrinterList(&count); + vector<Ref<TypePrinter>> result; + for (size_t i = 0; i < count; i++) + result.push_back(new CoreTypePrinter(list[i])); + BNFreeTypePrinterList(list); + return result; +} + + +Ref<TypePrinter> TypePrinter::GetByName(const std::string& name) +{ + BNTypePrinter* result = BNGetTypePrinterByName(name.c_str()); + if (!result) + return nullptr; + return new CoreTypePrinter(result); +} + + +Ref<TypePrinter> TypePrinter::GetDefault() +{ + string name = Settings::Instance()->Get<string>("analysis.types.printerName"); + return GetByName(name); +} + + +std::vector<InstructionTextToken> TypePrinter::GetTypeTokens( + Ref<Type> type, + Ref<Platform> platform, + const QualifiedName& name, + uint8_t baseConfidence, + BNTokenEscapingType escaping +) +{ + vector<InstructionTextToken> before = GetTypeTokensBeforeName(type, platform, baseConfidence, nullptr, escaping); + vector<InstructionTextToken> after = GetTypeTokensAfterName(type, platform, baseConfidence, nullptr, escaping); + if (before.size() > 0 && before.back().text.back() != ' ' && before.back().text.back() != '*' && + before.back().text.back() != '&' && after.size() > 0 && after.front().text.front() != ' ') + { + if (type->GetClass() != FunctionTypeClass) + before.emplace_back(TextToken, " "); + } + before.insert(before.end(), after.begin(), after.end()); + return before; +} + + +std::string TypePrinter::GetTypeString( + Ref<Type> type, + Ref<Platform> platform, + const QualifiedName& name, + BNTokenEscapingType escaping +) +{ + const string before = GetTypeStringBeforeName(type, platform, escaping); + const string qName = name.GetString(escaping); + const string after = GetTypeStringAfterName(type, platform, escaping); + if (((before.size() > 0) && (qName.size() > 0) && (before.back() != ' ') && (qName.front() != ' ')) || + ((before.size() > 0) && (after.size() > 0) && (before.back() != ' ') && (after.front() != ' '))) + return before + " " + qName + after; + return before + qName + after; +} + + +std::string TypePrinter::GetTypeStringBeforeName( + Ref<Type> type, + Ref<Platform> platform, + BNTokenEscapingType escaping +) +{ + vector<InstructionTextToken> tokens = GetTypeTokensBeforeName(type, platform, BN_FULL_CONFIDENCE, nullptr, escaping); + string result; + for (const auto& i : tokens) + result += i.text; + return result; +} + + +std::string TypePrinter::GetTypeStringAfterName( + Ref<Type> type, + Ref<Platform> platform, + BNTokenEscapingType escaping +) +{ + vector<InstructionTextToken> tokens = GetTypeTokensAfterName(type, platform, BN_FULL_CONFIDENCE, nullptr, escaping); + string result; + for (const auto& i : tokens) + result += i.text; + return result; +} + + +CoreTypePrinter::CoreTypePrinter(BNTypePrinter* printer): TypePrinter(printer) +{ + +} + + +std::vector<InstructionTextToken> CoreTypePrinter::GetTypeTokens(Ref<Type> type, + Ref<Platform> platform, const QualifiedName& name, + uint8_t baseConfidence, BNTokenEscapingType escaping) +{ + BNQualifiedName qname = name.GetAPIObject(); + + BNInstructionTextToken* tokens; + size_t tokenCount; + + bool success = BNGetTypePrinterTypeTokens(GetObject(), type->GetObject(), + platform ? platform->GetObject() : nullptr, &qname, baseConfidence, escaping, &tokens, &tokenCount); + + QualifiedName::FreeAPIObject(&qname); + if (!success) + return {}; + + vector<InstructionTextToken> cppTokens = + InstructionTextToken::ConvertInstructionTextTokenList(tokens, tokenCount); + BNFreeInstructionText(tokens, tokenCount); + + return cppTokens; +} + + +std::vector<InstructionTextToken> CoreTypePrinter::GetTypeTokensBeforeName(Ref<Type> type, + Ref<Platform> platform, uint8_t baseConfidence, + Ref<Type> parentType, BNTokenEscapingType escaping) +{ + BNInstructionTextToken* tokens; + size_t tokenCount; + + bool success = BNGetTypePrinterTypeTokensBeforeName(GetObject(), type->GetObject(), + platform ? platform->GetObject() : nullptr, baseConfidence, + parentType ? parentType->GetObject() : nullptr, escaping, &tokens, &tokenCount); + + if (!success) + return {}; + + vector<InstructionTextToken> cppTokens = + InstructionTextToken::ConvertInstructionTextTokenList(tokens, tokenCount); + BNFreeInstructionText(tokens, tokenCount); + + return cppTokens; +} + + +std::vector<InstructionTextToken> CoreTypePrinter::GetTypeTokensAfterName(Ref<Type> type, + Ref<Platform> platform, uint8_t baseConfidence, + Ref<Type> parentType, BNTokenEscapingType escaping) +{ + BNInstructionTextToken* tokens; + size_t tokenCount; + + bool success = BNGetTypePrinterTypeTokensAfterName(GetObject(), type->GetObject(), + platform ? platform->GetObject() : nullptr, baseConfidence, + parentType ? parentType->GetObject() : nullptr, escaping, &tokens, &tokenCount); + + if (!success) + return {}; + + vector<InstructionTextToken> cppTokens = + InstructionTextToken::ConvertInstructionTextTokenList(tokens, tokenCount); + BNFreeInstructionText(tokens, tokenCount); + + return cppTokens; +} + + +std::string CoreTypePrinter::GetTypeString(Ref<Type> type, Ref<Platform> platform, + const QualifiedName& name, BNTokenEscapingType escaping) +{ + BNQualifiedName qname = name.GetAPIObject(); + + char* result; + bool success = BNGetTypePrinterTypeString(GetObject(), type->GetObject(), platform ? platform->GetObject() : nullptr, &qname, escaping, &result); + + QualifiedName::FreeAPIObject(&qname); + if (!success) + return {}; + + string cppResult = result; + BNFreeString(result); + + return cppResult; + +} + + +std::string CoreTypePrinter::GetTypeStringBeforeName(Ref<Type> type, Ref<Platform> platform, + BNTokenEscapingType escaping) +{ + char* result; + bool success = BNGetTypePrinterTypeStringBeforeName(GetObject(), type->GetObject(), platform ? platform->GetObject() : nullptr, escaping, &result); + + if (!success) + return {}; + + string cppResult = result; + BNFreeString(result); + + return cppResult; +} + + +std::string CoreTypePrinter::GetTypeStringAfterName(Ref<Type> type, Ref<Platform> platform, + BNTokenEscapingType escaping) +{ + char* result; + bool success = BNGetTypePrinterTypeStringAfterName(GetObject(), type->GetObject(), platform ? platform->GetObject() : nullptr, escaping, &result); + + if (!success) + return {}; + + string cppResult = result; + BNFreeString(result); + + return cppResult; +} + + +std::vector<TypeDefinitionLine> CoreTypePrinter::GetTypeLines(Ref<Type> type, + Ref<BinaryView> data, const QualifiedName& name, int lineWidth, + bool collapsed, BNTokenEscapingType escaping) +{ + BNTypeDefinitionLine* lines; + size_t lineCount; + + BNQualifiedName qname = name.GetAPIObject(); + + bool success = BNGetTypePrinterTypeLines(GetObject(), type->GetObject(), data->GetObject(), &qname, lineWidth, collapsed, escaping, &lines, &lineCount); + + QualifiedName::FreeAPIObject(&qname); + if (!success) + return {}; + + vector<TypeDefinitionLine> cppLines; + + for (size_t i = 0; i < lineCount; ++i) + { + cppLines.push_back(TypeDefinitionLine::FromAPIObject(&lines[i])); + } + BNFreeTypeDefinitionLineList(lines, lineCount); + + return cppLines; + +} diff --git a/ui/createtypedialog.h b/ui/createtypedialog.h index 0df1c613..a2bd6e2c 100644 --- a/ui/createtypedialog.h +++ b/ui/createtypedialog.h @@ -2,6 +2,7 @@ #include <QtWidgets/QDialog> #include <QtWidgets/QLineEdit> +#include <QtWidgets/QTextEdit> #include "binaryninjaapi.h" #include "dialogtextedit.h" #include "uicontext.h" @@ -10,16 +11,18 @@ class BINARYNINJAUIAPI CreateTypeDialog : public QDialog { Q_OBJECT + QLineEdit* m_arguments; DialogTextEdit* m_code; + QTextEdit* m_errors; BinaryViewRef m_data; - std::map<BinaryNinja::QualifiedName, TypeRef> m_results; + std::vector<BinaryNinja::ParsedType> m_results; std::set<BinaryNinja::QualifiedName> m_typesAllowRedefinition; public: CreateTypeDialog(QWidget* parent, BinaryViewRef data, const QString& title, const QString& definition, const std::set<BinaryNinja::QualifiedName>& typesAllowRedefinition = {}); - std::map<BinaryNinja::QualifiedName, TypeRef> getResults() { return m_results; } + std::vector<BinaryNinja::ParsedType> getResults() { return m_results; } private Q_SLOTS: void createType(); |
